1{ config, lib, pkgs, ... }:
2with lib;
3
4let
5 cfg = config.environment.memoryAllocator;
6
7 # The set of alternative malloc(3) providers.
8 providers = {
9 graphene-hardened = {
10 libPath = "${pkgs.graphene-hardened-malloc}/lib/libhardened_malloc.so";
11 description = ''
12 An allocator designed to mitigate memory corruption attacks, such as
13 those caused by use-after-free bugs.
14 '';
15 };
16
17 jemalloc = {
18 libPath = "${pkgs.jemalloc}/lib/libjemalloc.so";
19 description = ''
20 A general purpose allocator that emphasizes fragmentation avoidance
21 and scalable concurrency support.
22 '';
23 };
24
25 scudo = let
26 platformMap = {
27 aarch64-linux = "aarch64";
28 x86_64-linux = "x86_64";
29 };
30
31 systemPlatform = platformMap.${pkgs.stdenv.hostPlatform.system} or (throw "scudo not supported on ${pkgs.stdenv.hostPlatform.system}");
32 in {
33 libPath = "${pkgs.llvmPackages_latest.compiler-rt}/lib/linux/libclang_rt.scudo-${systemPlatform}.so";
34 description = ''
35 A user-mode allocator based on LLVM Sanitizer’s CombinedAllocator,
36 which aims at providing additional mitigations against heap based
37 vulnerabilities, while maintaining good performance.
38 '';
39 };
40
41 mimalloc = {
42 libPath = "${pkgs.mimalloc}/lib/libmimalloc.so";
43 description = ''
44 A compact and fast general purpose allocator, which may
45 optionally be built with mitigations against various heap
46 vulnerabilities.
47 '';
48 };
49 };
50
51 providerConf = providers.${cfg.provider};
52
53 # An output that contains only the shared library, to avoid
54 # needlessly bloating the system closure
55 mallocLib = pkgs.runCommand "malloc-provider-${cfg.provider}"
56 rec {
57 preferLocalBuild = true;
58 allowSubstitutes = false;
59 origLibPath = providerConf.libPath;
60 libName = baseNameOf origLibPath;
61 }
62 ''
63 mkdir -p $out/lib
64 cp -L $origLibPath $out/lib/$libName
65 '';
66
67 # The full path to the selected provider shlib.
68 providerLibPath = "${mallocLib}/lib/${mallocLib.libName}";
69in
70
71{
72 meta = {
73 maintainers = [ maintainers.joachifm ];
74 };
75
76 options = {
77 environment.memoryAllocator.provider = mkOption {
78 type = types.enum ([ "libc" ] ++ attrNames providers);
79 default = "libc";
80 description = lib.mdDoc ''
81 The system-wide memory allocator.
82
83 Briefly, the system-wide memory allocator providers are:
84
85 - `libc`: the standard allocator provided by libc
86 ${concatStringsSep "\n" (mapAttrsToList
87 (name: value: "- `${name}`: ${replaceStrings [ "\n" ] [ " " ] value.description}")
88 providers)}
89
90 ::: {.warning}
91 Selecting an alternative allocator (i.e., anything other than
92 `libc`) may result in instability, data loss,
93 and/or service failure.
94 :::
95 '';
96 };
97 };
98
99 config = mkIf (cfg.provider != "libc") {
100 environment.etc."ld-nix.so.preload".text = ''
101 ${providerLibPath}
102 '';
103 security.apparmor.includes = {
104 "abstractions/base" = ''
105 r /etc/ld-nix.so.preload,
106 r ${config.environment.etc."ld-nix.so.preload".source},
107 include "${pkgs.apparmorRulesFromClosure {
108 name = "mallocLib";
109 baseRules = ["mr $path/lib/**.so*"];
110 } [ mallocLib ] }"
111 '';
112 };
113 };
114}