diff --git a/nixos/modules/config/malloc.nix b/nixos/modules/config/malloc.nix
new file mode 100644
index 000000000000..7a42b0803be5
--- /dev/null
+++ b/nixos/modules/config/malloc.nix
@@ -0,0 +1,91 @@
+{ config, lib, pkgs, ... }:
+with lib;
+
+let
+ cfg = config.environment.memoryAllocator;
+
+ # The set of alternative malloc(3) providers.
+ providers = {
+ "graphene-hardened" = rec {
+ libPath = "${pkgs.graphene-hardened-malloc}/lib/libhardened_malloc.so";
+ description = ''
+ An allocator designed to mitigate memory corruption attacks, such as
+ those caused by use-after-free bugs.
+ '';
+ };
+
+ "jemalloc" = {
+ libPath = "${pkgs.jemalloc}/lib/libjemalloc.so";
+ description = ''
+ A general purpose allocator that emphasizes fragmentation avoidance
+ and scalable concurrency support.
+ '';
+ };
+ };
+
+ providerConf = providers."${cfg.provider}";
+
+ # An output that contains only the shared library, to avoid
+ # needlessly bloating the system closure
+ mallocLib = pkgs.runCommand "malloc-provider-${cfg.provider}"
+ rec {
+ preferLocalBuild = true;
+ allowSubstitutes = false;
+ origLibPath = providerConf.libPath;
+ libName = baseNameOf origLibPath;
+ }
+ ''
+ mkdir -p $out/lib
+ cp -L $origLibPath $out/lib/$libName
+ '';
+
+ # The full path to the selected provider shlib.
+ providerLibPath = "${mallocLib}/lib/${mallocLib.libName}";
+in
+
+{
+ meta = {
+ maintainers = [ maintainers.joachifm ];
+ };
+
+ options = {
+ environment.memoryAllocator.provider = mkOption {
+ type = types.enum ([ "libc" ] ++ attrNames providers);
+ default = "libc";
+ description = ''
+ The system-wide memory allocator.
+
+
+
+ Briefly, the system-wide memory allocator providers are:
+
+ libc: the standard allocator provided by libc
+ ${toString (mapAttrsToList
+ (name: value: "${name}: ${value.description}")
+ providers)}
+
+
+
+
+
+ Selecting an alternative allocator (i.e., anything other than
+ libc) may result in instability, data loss,
+ and/or service failure.
+
+
+
+
+
+ Changing this option does not affect the current session.
+
+
+
+
+ '';
+ };
+ };
+
+ config = mkIf (cfg.provider != "libc") {
+ environment.variables.LD_PRELOAD = providerLibPath;
+ };
+}
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index dee850f47f27..a66747f03844 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -19,6 +19,7 @@
./config/iproute2.nix
./config/krb5/default.nix
./config/ldap.nix
+ ./config/malloc.nix
./config/networking.nix
./config/no-x-libs.nix
./config/nsswitch.nix
diff --git a/nixos/modules/profiles/hardened.nix b/nixos/modules/profiles/hardened.nix
index 9ab2ee87a19e..87bf66333c61 100644
--- a/nixos/modules/profiles/hardened.nix
+++ b/nixos/modules/profiles/hardened.nix
@@ -14,6 +14,8 @@ with lib;
nix.allowedUsers = mkDefault [ "@users" ];
+ environment.memoryAllocator.provider = mkDefault "graphene-hardened";
+
security.hideProcessInformation = mkDefault true;
security.lockKernelModules = mkDefault true;
diff --git a/nixos/tests/hardened.nix b/nixos/tests/hardened.nix
index 614889c4d73c..1ff329bd98de 100644
--- a/nixos/tests/hardened.nix
+++ b/nixos/tests/hardened.nix
@@ -27,6 +27,20 @@ import ./make-test.nix ({ pkgs, ...} : {
};
testScript =
+ let
+ hardened-malloc-tests = pkgs.stdenv.mkDerivation rec {
+ name = "hardened-malloc-tests-${pkgs.graphene-hardened-malloc.version}";
+ src = pkgs.graphene-hardened-malloc.src;
+ buildPhase = ''
+ cd test/simple-memory-corruption
+ make -j4
+ '';
+
+ installPhase = ''
+ find . -type f -executable -exec install -Dt $out/bin '{}' +
+ '';
+ };
+ in
''
$machine->waitForUnit("multi-user.target");
@@ -93,5 +107,18 @@ import ./make-test.nix ({ pkgs, ...} : {
$machine->fail("systemctl hibernate");
$machine->fail("systemctl kexec");
};
+
+ # Test hardened memory allocator
+ sub runMallocTestProg {
+ my ($progName, $errorText) = @_;
+ my $text = "fatal allocator error: " . $errorText;
+ $machine->fail("${hardened-malloc-tests}/bin/" . $progName) =~ $text;
+ };
+
+ subtest "hardenedmalloc", sub {
+ runMallocTestProg("double_free_large", "invalid free");
+ runMallocTestProg("unaligned_free_small", "invalid unaligned free");
+ runMallocTestProg("write_after_free_small", "detected write after free");
+ };
'';
})