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 Hardened memory allocator coming from GrapheneOS project.
13 The default configuration template has all normal optional security
14 features enabled and is quite aggressive in terms of sacrificing
15 performance and memory usage for security.
16 '';
17 };
18
19 graphene-hardened-light = {
20 libPath = "${pkgs.graphene-hardened-malloc}/lib/libhardened_malloc-light.so";
21 description = ''
22 Hardened memory allocator coming from GrapheneOS project.
23 The light configuration template disables the slab quarantines,
24 write after free check, slot randomization and raises the guard
25 slab interval from 1 to 8 but leaves zero-on-free and slab canaries enabled.
26 The light configuration has solid performance and memory usage while still
27 being far more secure than mainstream allocators with much better security
28 properties.
29 '';
30 };
31
32 jemalloc = {
33 libPath = "${pkgs.jemalloc}/lib/libjemalloc.so";
34 description = ''
35 A general purpose allocator that emphasizes fragmentation avoidance
36 and scalable concurrency support.
37 '';
38 };
39
40 scudo = let
41 platformMap = {
42 aarch64-linux = "aarch64";
43 x86_64-linux = "x86_64";
44 };
45
46 systemPlatform = platformMap.${pkgs.stdenv.hostPlatform.system} or (throw "scudo not supported on ${pkgs.stdenv.hostPlatform.system}");
47 in {
48 libPath = "${pkgs.llvmPackages_14.compiler-rt}/lib/linux/libclang_rt.scudo-${systemPlatform}.so";
49 description = ''
50 A user-mode allocator based on LLVM Sanitizer’s CombinedAllocator,
51 which aims at providing additional mitigations against heap based
52 vulnerabilities, while maintaining good performance.
53 '';
54 };
55
56 mimalloc = {
57 libPath = "${pkgs.mimalloc}/lib/libmimalloc.so";
58 description = ''
59 A compact and fast general purpose allocator, which may
60 optionally be built with mitigations against various heap
61 vulnerabilities.
62 '';
63 };
64 };
65
66 providerConf = providers.${cfg.provider};
67
68 # An output that contains only the shared library, to avoid
69 # needlessly bloating the system closure
70 mallocLib = pkgs.runCommand "malloc-provider-${cfg.provider}"
71 rec {
72 preferLocalBuild = true;
73 allowSubstitutes = false;
74 origLibPath = providerConf.libPath;
75 libName = baseNameOf origLibPath;
76 }
77 ''
78 mkdir -p $out/lib
79 cp -L $origLibPath $out/lib/$libName
80 '';
81
82 # The full path to the selected provider shlib.
83 providerLibPath = "${mallocLib}/lib/${mallocLib.libName}";
84in
85
86{
87 meta = {
88 maintainers = [ maintainers.joachifm ];
89 };
90
91 options = {
92 environment.memoryAllocator.provider = mkOption {
93 type = types.enum ([ "libc" ] ++ attrNames providers);
94 default = "libc";
95 description = ''
96 The system-wide memory allocator.
97
98 Briefly, the system-wide memory allocator providers are:
99
100 - `libc`: the standard allocator provided by libc
101 ${concatStringsSep "\n" (mapAttrsToList
102 (name: value: "- `${name}`: ${replaceStrings [ "\n" ] [ " " ] value.description}")
103 providers)}
104
105 ::: {.warning}
106 Selecting an alternative allocator (i.e., anything other than
107 `libc`) may result in instability, data loss,
108 and/or service failure.
109 :::
110 '';
111 };
112 };
113
114 config = mkIf (cfg.provider != "libc") {
115 environment.etc."ld-nix.so.preload".text = ''
116 ${providerLibPath}
117 '';
118 security.apparmor.includes = {
119 "abstractions/base" = ''
120 r /etc/ld-nix.so.preload,
121 r ${config.environment.etc."ld-nix.so.preload".source},
122 include "${pkgs.apparmorRulesFromClosure {
123 name = "mallocLib";
124 baseRules = ["mr $path/lib/**.so*"];
125 } [ mallocLib ] }"
126 '';
127 };
128 };
129}