diff --git a/nixos/modules/installer/tools/nix-fallback-paths.nix b/nixos/modules/installer/tools/nix-fallback-paths.nix index 68f6494510cb..98bd3cec0de2 100644 --- a/nixos/modules/installer/tools/nix-fallback-paths.nix +++ b/nixos/modules/installer/tools/nix-fallback-paths.nix @@ -1,8 +1,8 @@ { - x86_64-linux = "/nix/store/mxn3vxpvk2b42kgd08aw4bn27qhf434w-nix-2.31.3"; - i686-linux = "/nix/store/b7d35ifww9683l74ydkb0qhrq6lqcisi-nix-2.31.3"; - aarch64-linux = "/nix/store/ysz7dwmy4zd1zm3wzx5dh9g999xv7pbm-nix-2.31.3"; - riscv64-linux = "/nix/store/avfv2lqbnphj2ap8y5ihg1l0sqhpjll7-nix-riscv64-unknown-linux-gnu-2.31.3"; - x86_64-darwin = "/nix/store/s8lcl6nrd5ia7nr4zx9mg720i9f8qm37-nix-2.31.3"; - aarch64-darwin = "/nix/store/vfjzbcl3kf9jjwh0g2x03cvz2x5hg8py-nix-2.31.3"; + x86_64-linux = "/nix/store/vals1fs2rl6yn5f8gbqj9mvly4r27shs-nix-2.31.4"; + i686-linux = "/nix/store/fyrlz8cdzvf5csdh5885wifpxc8ywdii-nix-2.31.4"; + aarch64-linux = "/nix/store/19p3nc892m7idfg2ngd1614660xqbhnm-nix-2.31.4"; + riscv64-linux = "/nix/store/x1isvq0xnyrg0l29qk2xlp929cgjsmqy-nix-riscv64-unknown-linux-gnu-2.31.4"; + x86_64-darwin = "/nix/store/4gqxzd5zkxcq271wi5saml4zd92rdkws-nix-2.31.4"; + aarch64-darwin = "/nix/store/r3gz609kdqchxcmil7dhbravbq8kwm93-nix-2.31.4"; } diff --git a/pkgs/tools/package-management/nix/default.nix b/pkgs/tools/package-management/nix/default.nix index 4460014ad689..0be4bdb9205a 100644 --- a/pkgs/tools/package-management/nix/default.nix +++ b/pkgs/tools/package-management/nix/default.nix @@ -155,108 +155,75 @@ lib.makeExtensible ( ( { nix_2_28 = commonMeson { - version = "2.28.5"; - hash = "sha256-oIfAHxO+BCtHXJXLHBnsKkGl1Pw+Uuq1PwNxl+lZ+Oc="; + version = "2.28.6"; + hash = "sha256-jg2YDTFt8CY4kMg4ha3UK5C+mQY+Zg67nwNy+CmTk5w="; self_attribute_name = "nix_2_28"; patches = patches_common ++ [ - (fetchpatch2 { - name = "nix-2.28-14764-mdbook-0.5-support.patch"; - url = "https://github.com/NixOS/nix/commit/5a64138e862fe364e751c5c286e8db8c466aaee7.patch?full_index=1"; - hash = "sha256-vFv/D08x9urtoIE9wiC7Lln4Eq3sgNBwU7TBE1iyrfI="; - }) lowdown30PatchOld - ./patches/ghsa-g3g9-5vj6-r3gj-2.28.patch ]; }; nixComponents_2_30 = (nixDependencies.callPackage ./modular/packages.nix rec { - version = "2.30.3"; + version = "2.30.4"; inherit teams; otherSplices = generateSplicesForNixComponents "nixComponents_2_30"; src = fetchFromGitHub { owner = "NixOS"; repo = "nix"; tag = version; - hash = "sha256-kBuwzMgIE9Tmve0Rpp+q+YCsE2mw9d62M/950ViWeJ0="; + hash = "sha256-cJ96IBZCYoX0Tdlo5Q7qDSAKfL6QcUq/4Kr1UplH50E="; }; }).appendPatches - ( - patches_common - ++ [ - (fetchpatch2 { - name = "nix-2.30-14695-mdbook-0.5-support.patch"; - url = "https://github.com/NixOS/nix/commit/5cbd7856de0a9c13351f98e32a1e26d0854d87fd.patch?full_index=1"; - hash = "sha256-r2ZF1zBZDKMvyX6X4VsaTMrg0zdjn59Jf6Hqg56r29E="; - }) - lowdown30PatchOld - ./patches/ghsa-g3g9-5vj6-r3gj-2.30.patch - ] - ); + (patches_common ++ [ lowdown30PatchOld ]); nix_2_30 = addTests "nix_2_30" self.nixComponents_2_30.nix-everything; nixComponents_2_31 = (nixDependencies.callPackage ./modular/packages.nix rec { - version = "2.31.3"; + version = "2.31.4"; inherit teams; otherSplices = generateSplicesForNixComponents "nixComponents_2_31"; src = fetchFromGitHub { owner = "NixOS"; repo = "nix"; tag = version; - hash = "sha256-oe0YWe8f+pwQH4aYD2XXLW5iEHyXNUddurqJ5CUVCIk="; + hash = "sha256-f/haYfcI+9IiYVH+g6cjhF8cK7QWHAFfcPtF+57ujZ0="; }; }).appendPatches - [ - lowdown30Patch - ./patches/ghsa-g3g9-5vj6-r3gj-2.31.patch - ./patches/landlock-abstract-socket-hardening-2.31.patch - ]; + [ ]; nix_2_31 = addTests "nix_2_31" self.nixComponents_2_31.nix-everything; nixComponents_2_34 = (nixDependencies.callPackage ./modular/packages.nix rec { - version = "2.34.4"; + version = "2.34.5"; inherit teams; otherSplices = generateSplicesForNixComponents "nixComponents_2_34"; src = fetchFromGitHub { owner = "NixOS"; repo = "nix"; tag = version; - hash = "sha256-WPuGqMQGepXoRYjtRudMAMHEoLsIObw2x4sVfho5feA="; + hash = "sha256-/S2bnz+TbRFGmNyR31Hfa70uFvJoMM9wYDjpyEw8I+U="; }; }).appendPatches - ( - patches_common - ++ [ - ./patches/ghsa-g3g9-5vj6-r3gj-2.34.patch - ./patches/landlock-abstract-socket-hardening-2.34.patch - ] - ); + patches_common; nix_2_34 = addTests "nix_2_34" self.nixComponents_2_34.nix-everything; nixComponents_git = (nixDependencies.callPackage ./modular/packages.nix rec { - version = "2.35pre20260328_${lib.substring 0 8 src.rev}"; + version = "2.35pre20260407_${lib.substring 0 8 src.rev}"; inherit teams; otherSplices = generateSplicesForNixComponents "nixComponents_git"; src = fetchFromGitHub { owner = "NixOS"; repo = "nix"; - rev = "7edcd0a24dc71abb7caa600527833ef540c1bc86"; - hash = "sha256-fybp46IQmRN7lEUTChc3MTqxmRutmDO4RNSPEQfJQsQ="; + rev = "a37db9d249afd61a81ae26368696f60e065d6f61"; + hash = "sha256-RpfExg4DcWZ/SanVuwVbdijqPylsjvtMrHTQHemE+t8="; }; }).appendPatches - ( - patches_common - ++ [ - ./patches/ghsa-g3g9-5vj6-r3gj-git.patch - ./patches/landlock-abstract-socket-hardening-git.patch - ] - ); + patches_common; git = addTests "git" self.nixComponents_git.nix-everything; diff --git a/pkgs/tools/package-management/nix/modular/src/nix/package.nix b/pkgs/tools/package-management/nix/modular/src/nix/package.nix index d96823a991c7..deb49c9e7b4d 100644 --- a/pkgs/tools/package-management/nix/modular/src/nix/package.nix +++ b/pkgs/tools/package-management/nix/modular/src/nix/package.nix @@ -7,6 +7,8 @@ nix-main, nix-cmd, + mimalloc, + # Configuration Options version, @@ -23,6 +25,7 @@ mkMesonExecutable (finalAttrs: { nix-expr nix-main nix-cmd + mimalloc ]; mesonFlags = [ diff --git a/pkgs/tools/package-management/nix/patches/ghsa-g3g9-5vj6-r3gj-2.28.patch b/pkgs/tools/package-management/nix/patches/ghsa-g3g9-5vj6-r3gj-2.28.patch deleted file mode 100644 index ae50b007a30b..000000000000 --- a/pkgs/tools/package-management/nix/patches/ghsa-g3g9-5vj6-r3gj-2.28.patch +++ /dev/null @@ -1,126 +0,0 @@ -From 32b09e0bfeb33434866610994d89f48da8a2bf41 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= -Date: Mon, 6 Apr 2026 16:49:13 +0200 -Subject: [PATCH] Fixes for GHSA-g3g9-5vj6-r3gj -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Squashed commit of the following: - -commit 716dba6692c42e301a1e769e5eac02a4d6e63150 -Author: Sergei Zimmerman -Date: Fri Apr 3 00:21:31 2026 +0300 - - derivation-builder: Don't use copyFile for FOD output copying, put the output in a temporary directory in the store - -commit a3215e7c5c260fab5f2cb034c4df01dfa3b284e5 -Author: Sergei Zimmerman -Date: Fri Apr 3 00:21:21 2026 +0300 - - libstore: Make temporary in-store directory not world-readable - -Signed-off-by: Jörg Thalheim ---- - src/libstore/include/nix/store/local-store.hh | 2 ++ - src/libstore/local-store.cc | 5 +-- - .../unix/build/local-derivation-goal.cc | 36 ++++++++++++++----- - 3 files changed, 33 insertions(+), 10 deletions(-) - -diff --git a/src/libstore/include/nix/store/local-store.hh b/src/libstore/include/nix/store/local-store.hh -index 5893b7d8b..c9266f6b4 100644 ---- a/src/libstore/include/nix/store/local-store.hh -+++ b/src/libstore/include/nix/store/local-store.hh -@@ -408,6 +408,8 @@ private: - friend struct PathSubstitutionGoal; - friend struct SubstitutionGoal; - friend struct DerivationGoal; -+ /* Only used for createTempDirInStore. */ -+ friend class DerivationBuilderImpl; - }; - - } // namespace nix -diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc -index 63108fab4..ab3a0d034 100644 ---- a/src/libstore/local-store.cc -+++ b/src/libstore/local-store.cc -@@ -1311,8 +1311,9 @@ std::pair LocalStore::createTempDirInStore() - do { - /* There is a slight possibility that `tmpDir' gets deleted by - the GC between createTempDir() and when we acquire a lock on it. -- We'll repeat until 'tmpDir' exists and we've locked it. */ -- tmpDirFn = createTempDir(realStoreDir, "tmp"); -+ We'll repeat until 'tmpDir' exists and we've locked it. -+ Make the directory accessible only to the current user.*/ -+ tmpDirFn = createTempDir(realStoreDir, "tmp", /*mode=*/0700); - tmpDirFd = openDirectory(tmpDirFn); - if (!tmpDirFd) { - continue; -diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc -index 88c82e063..3e4c3017d 100644 ---- a/src/libstore/unix/build/local-derivation-goal.cc -+++ b/src/libstore/unix/build/local-derivation-goal.cc -@@ -2547,6 +2547,13 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() - assert(output && scratchPath); - auto actualPath = toRealPathChroot(worker.store.printStorePath(*scratchPath)); - -+ /* An optional file descriptor of a directory used for intermediate -+ operations. */ -+ AutoCloseFD tempDirFd; -+ /* RAII cleanup of a temporary directory inside the store that is used -+ for intermediate operations. */ -+ std::optional delTempDir; -+ - auto finish = [&](StorePath finalStorePath) { - /* Store the final path */ - finalOutputs.insert_or_assign(outputName, finalStorePath); -@@ -2681,6 +2688,25 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() - return newInfo0; - }; - -+ auto moveOutputToTempDir = [&]() -> void { -+ std::filesystem::path tempDir; -+ std::tie(tempDir, tempDirFd) = getLocalStore().createTempDirInStore(); -+ delTempDir.emplace(tempDir); -+ -+ auto tmpOutput = tempDir / "x"; -+ -+ /* Serialise and create a fresh copy of the output to break -+ any stale writable file descriptors. Copy through the -+ serialisation/deserialisation. TODO: Use copyRecursive here and -+ make use of reflinking. */ -+ auto source = sinkToSource([&](Sink & nextSink) { dumpPath(actualPath, nextSink); }); -+ restorePath(tmpOutput, *source, settings.fsyncStorePaths); -+ /* This makes it slightly harder to make sense of the control flow. The rule -+ of thumb is that actualPath points to the current location of the stuff -+ that we'll end up registering. */ -+ actualPath = std::move(tmpOutput); -+ }; -+ - ValidPathInfo newInfo = std::visit( - overloaded{ - -@@ -2708,14 +2734,7 @@ 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(std::filesystem::path(actualPath), std::filesystem::path(tmpOutput), true); -- -- std::filesystem::rename(tmpOutput, actualPath); -- -+ moveOutputToTempDir(); - auto newInfo0 = newInfoFromCA( - DerivationOutput::CAFloating{ - .method = dof.ca.method, -@@ -2756,6 +2775,7 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() - }, - - [&](const DerivationOutput::Impure & doi) { -+ moveOutputToTempDir(); - return newInfoFromCA( - DerivationOutput::CAFloating{ - .method = doi.method, diff --git a/pkgs/tools/package-management/nix/patches/ghsa-g3g9-5vj6-r3gj-2.30.patch b/pkgs/tools/package-management/nix/patches/ghsa-g3g9-5vj6-r3gj-2.30.patch deleted file mode 100644 index d7514f864afa..000000000000 --- a/pkgs/tools/package-management/nix/patches/ghsa-g3g9-5vj6-r3gj-2.30.patch +++ /dev/null @@ -1,126 +0,0 @@ -From 4d0a078f1dae9a07d04e1a72e7e62fbf2ca249e0 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= -Date: Mon, 6 Apr 2026 16:49:13 +0200 -Subject: [PATCH] Fixes for GHSA-g3g9-5vj6-r3gj -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Squashed commit of the following: - -commit a8e5b27728e3b18e63c2503a83afd04031994623 -Author: Sergei Zimmerman -Date: Fri Apr 3 00:21:31 2026 +0300 - - derivation-builder: Don't use copyFile for FOD output copying, put the output in a temporary directory in the store - -commit 49ff4d6779ec20c5339b237dec9b240c3eabf535 -Author: Sergei Zimmerman -Date: Fri Apr 3 00:21:21 2026 +0300 - - libstore: Make temporary in-store directory not world-readable - -Signed-off-by: Jörg Thalheim ---- - src/libstore/include/nix/store/local-store.hh | 2 ++ - src/libstore/local-store.cc | 5 +-- - src/libstore/unix/build/derivation-builder.cc | 36 ++++++++++++++----- - 3 files changed, 33 insertions(+), 10 deletions(-) - -diff --git a/src/libstore/include/nix/store/local-store.hh b/src/libstore/include/nix/store/local-store.hh -index 5f3a249f8..74fa1c2e8 100644 ---- a/src/libstore/include/nix/store/local-store.hh -+++ b/src/libstore/include/nix/store/local-store.hh -@@ -446,6 +446,8 @@ private: - friend struct PathSubstitutionGoal; - friend struct SubstitutionGoal; - friend struct DerivationGoal; -+ /* Only used for createTempDirInStore. */ -+ friend class DerivationBuilderImpl; - }; - - } // namespace nix -diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc -index 49c499e3f..5f897856f 100644 ---- a/src/libstore/local-store.cc -+++ b/src/libstore/local-store.cc -@@ -1316,8 +1316,9 @@ std::pair LocalStore::createTempDirInStore() - do { - /* There is a slight possibility that `tmpDir' gets deleted by - the GC between createTempDir() and when we acquire a lock on it. -- We'll repeat until 'tmpDir' exists and we've locked it. */ -- tmpDirFn = createTempDir(config->realStoreDir, "tmp"); -+ We'll repeat until 'tmpDir' exists and we've locked it. -+ Make the directory accessible only to the current user.*/ -+ tmpDirFn = createTempDir(config->realStoreDir, "tmp", /*mode=*/0700); - tmpDirFd = openDirectory(tmpDirFn); - if (!tmpDirFd) { - continue; -diff --git a/src/libstore/unix/build/derivation-builder.cc b/src/libstore/unix/build/derivation-builder.cc -index 5f9dafb57..609d88a79 100644 ---- a/src/libstore/unix/build/derivation-builder.cc -+++ b/src/libstore/unix/build/derivation-builder.cc -@@ -1581,6 +1581,13 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs() - assert(output && scratchPath); - auto actualPath = realPathInSandbox(store.printStorePath(*scratchPath)); - -+ /* An optional file descriptor of a directory used for intermediate -+ operations. */ -+ AutoCloseFD tempDirFd; -+ /* RAII cleanup of a temporary directory inside the store that is used -+ for intermediate operations. */ -+ std::optional delTempDir; -+ - auto finish = [&](StorePath finalStorePath) { - /* Store the final path */ - finalOutputs.insert_or_assign(outputName, finalStorePath); -@@ -1715,6 +1722,25 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs() - return newInfo0; - }; - -+ auto moveOutputToTempDir = [&]() -> void { -+ std::filesystem::path tempDir; -+ std::tie(tempDir, tempDirFd) = getLocalStore(store).createTempDirInStore(); -+ delTempDir.emplace(tempDir); -+ -+ auto tmpOutput = tempDir / "x"; -+ -+ /* Serialise and create a fresh copy of the output to break -+ any stale writable file descriptors. Copy through the -+ serialisation/deserialisation. TODO: Use copyRecursive here and -+ make use of reflinking. */ -+ auto source = sinkToSource([&](Sink & nextSink) { dumpPath(actualPath, nextSink); }); -+ restorePath(tmpOutput, *source, settings.fsyncStorePaths); -+ /* This makes it slightly harder to make sense of the control flow. The rule -+ of thumb is that actualPath points to the current location of the stuff -+ that we'll end up registering. */ -+ actualPath = std::move(tmpOutput); -+ }; -+ - ValidPathInfo newInfo = std::visit( - overloaded{ - -@@ -1742,14 +1768,7 @@ SingleDrvOutputs DerivationBuilderImpl::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(std::filesystem::path(actualPath), std::filesystem::path(tmpOutput), true); -- -- std::filesystem::rename(tmpOutput, actualPath); -- -+ moveOutputToTempDir(); - auto newInfo0 = newInfoFromCA( - DerivationOutput::CAFloating{ - .method = dof.ca.method, -@@ -1790,6 +1809,7 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs() - }, - - [&](const DerivationOutput::Impure & doi) { -+ moveOutputToTempDir(); - return newInfoFromCA( - DerivationOutput::CAFloating{ - .method = doi.method, diff --git a/pkgs/tools/package-management/nix/patches/ghsa-g3g9-5vj6-r3gj-2.31.patch b/pkgs/tools/package-management/nix/patches/ghsa-g3g9-5vj6-r3gj-2.31.patch deleted file mode 100644 index 4d08316d67a7..000000000000 --- a/pkgs/tools/package-management/nix/patches/ghsa-g3g9-5vj6-r3gj-2.31.patch +++ /dev/null @@ -1,126 +0,0 @@ -From df0153a9eca42c4ed5ac784657c8c5d0664c9e0e Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= -Date: Mon, 6 Apr 2026 16:49:13 +0200 -Subject: [PATCH] Fixes for GHSA-g3g9-5vj6-r3gj -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Squashed commit of the following: - -commit bf1d95ae399e47daeafa9e0dfac967f415c52447 -Author: Sergei Zimmerman -Date: Fri Apr 3 00:21:31 2026 +0300 - - derivation-builder: Don't use copyFile for FOD output copying, put the output in a temporary directory in the store - -commit ca5986fa353d402738acf961bcaa017c61019809 -Author: Sergei Zimmerman -Date: Fri Apr 3 00:21:21 2026 +0300 - - libstore: Make temporary in-store directory not world-readable - -Signed-off-by: Jörg Thalheim ---- - src/libstore/include/nix/store/local-store.hh | 2 ++ - src/libstore/local-store.cc | 5 +-- - src/libstore/unix/build/derivation-builder.cc | 36 ++++++++++++++----- - 3 files changed, 33 insertions(+), 10 deletions(-) - -diff --git a/src/libstore/include/nix/store/local-store.hh b/src/libstore/include/nix/store/local-store.hh -index ec88ffb4f..91df9cd77 100644 ---- a/src/libstore/include/nix/store/local-store.hh -+++ b/src/libstore/include/nix/store/local-store.hh -@@ -455,6 +455,8 @@ private: - - friend struct PathSubstitutionGoal; - friend struct DerivationGoal; -+ /* Only used for createTempDirInStore. */ -+ friend class DerivationBuilderImpl; - }; - - } // namespace nix -diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc -index d24bc415c..524a9fdda 100644 ---- a/src/libstore/local-store.cc -+++ b/src/libstore/local-store.cc -@@ -1342,8 +1342,9 @@ std::pair LocalStore::createTempDirInStore() - do { - /* There is a slight possibility that `tmpDir' gets deleted by - the GC between createTempDir() and when we acquire a lock on it. -- We'll repeat until 'tmpDir' exists and we've locked it. */ -- tmpDirFn = createTempDir(config->realStoreDir, "tmp"); -+ We'll repeat until 'tmpDir' exists and we've locked it. -+ Make the directory accessible only to the current user.*/ -+ tmpDirFn = createTempDir(config->realStoreDir, "tmp", /*mode=*/0700); - tmpDirFd = openDirectory(tmpDirFn); - if (!tmpDirFd) { - continue; -diff --git a/src/libstore/unix/build/derivation-builder.cc b/src/libstore/unix/build/derivation-builder.cc -index 73bb026a2..d11f80159 100644 ---- a/src/libstore/unix/build/derivation-builder.cc -+++ b/src/libstore/unix/build/derivation-builder.cc -@@ -1473,6 +1473,13 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs() - assert(output && scratchPath); - auto actualPath = realPathInSandbox(store.printStorePath(*scratchPath)); - -+ /* An optional file descriptor of a directory used for intermediate -+ operations. */ -+ AutoCloseFD tempDirFd; -+ /* RAII cleanup of a temporary directory inside the store that is used -+ for intermediate operations. */ -+ std::optional delTempDir; -+ - auto finish = [&](StorePath finalStorePath) { - /* Store the final path */ - finalOutputs.insert_or_assign(outputName, finalStorePath); -@@ -1607,6 +1614,25 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs() - return newInfo0; - }; - -+ auto moveOutputToTempDir = [&]() -> void { -+ std::filesystem::path tempDir; -+ std::tie(tempDir, tempDirFd) = store.createTempDirInStore(); -+ delTempDir.emplace(tempDir); -+ -+ auto tmpOutput = tempDir / "x"; -+ -+ /* Serialise and create a fresh copy of the output to break -+ any stale writable file descriptors. Copy through the -+ serialisation/deserialisation. TODO: Use copyRecursive here and -+ make use of reflinking. */ -+ auto source = sinkToSource([&](Sink & nextSink) { dumpPath(actualPath, nextSink); }); -+ restorePath(tmpOutput, *source, settings.fsyncStorePaths); -+ /* This makes it slightly harder to make sense of the control flow. The rule -+ of thumb is that actualPath points to the current location of the stuff -+ that we'll end up registering. */ -+ actualPath = std::move(tmpOutput); -+ }; -+ - ValidPathInfo newInfo = std::visit( - overloaded{ - -@@ -1634,14 +1660,7 @@ SingleDrvOutputs DerivationBuilderImpl::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(std::filesystem::path(actualPath), std::filesystem::path(tmpOutput), true); -- -- std::filesystem::rename(tmpOutput, actualPath); -- -+ moveOutputToTempDir(); - auto newInfo0 = newInfoFromCA( - DerivationOutput::CAFloating{ - .method = dof.ca.method, -@@ -1682,6 +1701,7 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs() - }, - - [&](const DerivationOutput::Impure & doi) { -+ moveOutputToTempDir(); - return newInfoFromCA( - DerivationOutput::CAFloating{ - .method = doi.method, diff --git a/pkgs/tools/package-management/nix/patches/ghsa-g3g9-5vj6-r3gj-2.34.patch b/pkgs/tools/package-management/nix/patches/ghsa-g3g9-5vj6-r3gj-2.34.patch deleted file mode 100644 index ea140e3fb149..000000000000 --- a/pkgs/tools/package-management/nix/patches/ghsa-g3g9-5vj6-r3gj-2.34.patch +++ /dev/null @@ -1,126 +0,0 @@ -From dfda33d358f4522450737c40407ba8d8403a86a1 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= -Date: Mon, 6 Apr 2026 16:49:13 +0200 -Subject: [PATCH] Fixes for GHSA-g3g9-5vj6-r3gj -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Squashed commit of the following: - -commit d1f2de7d683bef1e2a164f23f33172c79c7725ea -Author: Sergei Zimmerman -Date: Fri Apr 3 00:21:31 2026 +0300 - - derivation-builder: Don't use copyFile for FOD output copying, put the output in a temporary directory in the store - -commit 2f13ee6dc4ade1d919c14757891d5d8a1001eee8 -Author: Sergei Zimmerman -Date: Fri Apr 3 00:21:21 2026 +0300 - - libstore: Make temporary in-store directory not world-readable - -Signed-off-by: Jörg Thalheim ---- - src/libstore/include/nix/store/local-store.hh | 2 ++ - src/libstore/local-store.cc | 5 +-- - src/libstore/unix/build/derivation-builder.cc | 36 ++++++++++++++----- - 3 files changed, 33 insertions(+), 10 deletions(-) - -diff --git a/src/libstore/include/nix/store/local-store.hh b/src/libstore/include/nix/store/local-store.hh -index 512c198b0..0b1e13b51 100644 ---- a/src/libstore/include/nix/store/local-store.hh -+++ b/src/libstore/include/nix/store/local-store.hh -@@ -505,6 +505,8 @@ private: - - friend struct PathSubstitutionGoal; - friend struct DerivationGoal; -+ /* Only used for createTempDirInStore. */ -+ friend class DerivationBuilderImpl; - }; - - } // namespace nix -diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc -index 649ad70dd..686e9988e 100644 ---- a/src/libstore/local-store.cc -+++ b/src/libstore/local-store.cc -@@ -1283,8 +1283,9 @@ std::pair LocalStore::createTempDirInStore() - do { - /* There is a slight possibility that `tmpDir' gets deleted by - the GC between createTempDir() and when we acquire a lock on it. -- We'll repeat until 'tmpDir' exists and we've locked it. */ -- tmpDirFn = createTempDir(std::filesystem::path{config->realStoreDir.get()}, "tmp"); -+ We'll repeat until 'tmpDir' exists and we've locked it. -+ Make the directory accessible only to the current user. */ -+ tmpDirFn = createTempDir(std::filesystem::path{config->realStoreDir.get()}, "tmp", /*mode=*/0700); - tmpDirFd = openDirectory(tmpDirFn); - if (!tmpDirFd) { - continue; -diff --git a/src/libstore/unix/build/derivation-builder.cc b/src/libstore/unix/build/derivation-builder.cc -index da225dc0d..fc877dd56 100644 ---- a/src/libstore/unix/build/derivation-builder.cc -+++ b/src/libstore/unix/build/derivation-builder.cc -@@ -1595,6 +1595,13 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs() - assert(output && scratchPath); - auto actualPath = realPathInHost(store.printStorePath(*scratchPath)); - -+ /* An optional file descriptor of a directory used for intermediate -+ operations. */ -+ AutoCloseFD tempDirFd; -+ /* RAII cleanup of a temporary directory inside the store that is used -+ for intermediate operations. */ -+ AutoDelete delTempDir; -+ - auto finish = [&](StorePath finalStorePath) { - /* Store the final path */ - finalOutputs.insert_or_assign(outputName, finalStorePath); -@@ -1742,6 +1749,25 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs() - return newInfo0; - }; - -+ auto moveOutputToTempDir = [&]() -> void { -+ std::filesystem::path tempDir; -+ std::tie(tempDir, tempDirFd) = store.createTempDirInStore(); -+ delTempDir = AutoDelete(tempDir); -+ -+ auto tmpOutput = tempDir / "x"; -+ -+ /* Serialise and create a fresh copy of the output to break -+ any stale writable file descriptors. Copy through the -+ serialisation/deserialisation. TODO: Use copyRecursive here and -+ make use of reflinking. */ -+ auto source = sinkToSource([&](Sink & nextSink) { dumpPath(actualPath, nextSink); }); -+ restorePath(tmpOutput, *source, store.config->getLocalSettings().fsyncStorePaths); -+ /* This makes it slightly harder to make sense of the control flow. The rule -+ of thumb is that actualPath points to the current location of the stuff -+ that we'll end up registering. */ -+ actualPath = std::move(tmpOutput); -+ }; -+ - ValidPathInfo newInfo = std::visit( - overloaded{ - -@@ -1769,14 +1795,7 @@ SingleDrvOutputs DerivationBuilderImpl::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 -- std::filesystem::path tmpOutput = actualPath.native() + ".tmp"; -- copyFile(actualPath, tmpOutput, true); -- -- std::filesystem::rename(tmpOutput, actualPath); -- -+ moveOutputToTempDir(); - return newInfoFromCA( - DerivationOutput::CAFloating{ - .method = dof.ca.method, -@@ -1793,6 +1812,7 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs() - }, - - [&](const DerivationOutput::Impure & doi) { -+ moveOutputToTempDir(); - return newInfoFromCA( - DerivationOutput::CAFloating{ - .method = doi.method, diff --git a/pkgs/tools/package-management/nix/patches/ghsa-g3g9-5vj6-r3gj-git.patch b/pkgs/tools/package-management/nix/patches/ghsa-g3g9-5vj6-r3gj-git.patch deleted file mode 100644 index 05fb52902018..000000000000 --- a/pkgs/tools/package-management/nix/patches/ghsa-g3g9-5vj6-r3gj-git.patch +++ /dev/null @@ -1,126 +0,0 @@ -From 0af4c1f88d0646bda0a90a37105360cab466a550 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= -Date: Mon, 6 Apr 2026 16:49:13 +0200 -Subject: [PATCH] Fixes for GHSA-g3g9-5vj6-r3gj -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Squashed commit of the following: - -commit a760af86b3a42aa5ac9d9002929107fe357bf128 -Author: Sergei Zimmerman -Date: Fri Apr 3 00:21:31 2026 +0300 - - derivation-builder: Don't use copyFile for FOD output copying, put the output in a temporary directory in the store - -commit 0e3412a93f43a017342c267000c152e5c45327e7 -Author: Sergei Zimmerman -Date: Fri Apr 3 00:21:21 2026 +0300 - - libstore: Make temporary in-store directory not world-readable - -Signed-off-by: Jörg Thalheim ---- - src/libstore/include/nix/store/local-store.hh | 2 ++ - src/libstore/local-store.cc | 5 +-- - src/libstore/unix/build/derivation-builder.cc | 36 ++++++++++++++----- - 3 files changed, 33 insertions(+), 10 deletions(-) - -diff --git a/src/libstore/include/nix/store/local-store.hh b/src/libstore/include/nix/store/local-store.hh -index 63a1da67d..bf3437e95 100644 ---- a/src/libstore/include/nix/store/local-store.hh -+++ b/src/libstore/include/nix/store/local-store.hh -@@ -512,6 +512,8 @@ private: - - friend struct PathSubstitutionGoal; - friend struct DerivationGoal; -+ /* Only used for createTempDirInStore. */ -+ friend class DerivationBuilderImpl; - }; - - } // namespace nix -diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc -index e9eb48bfe..d5e457731 100644 ---- a/src/libstore/local-store.cc -+++ b/src/libstore/local-store.cc -@@ -1304,8 +1304,9 @@ std::pair LocalStore::createTempDirInStore() - do { - /* There is a slight possibility that `tmpDir' gets deleted by - the GC between createTempDir() and when we acquire a lock on it. -- We'll repeat until 'tmpDir' exists and we've locked it. */ -- tmpDirFn = createTempDir(std::filesystem::path{config->realStoreDir.get()}, "tmp"); -+ We'll repeat until 'tmpDir' exists and we've locked it. -+ Make the directory accessible only to the current user. */ -+ tmpDirFn = createTempDir(std::filesystem::path{config->realStoreDir.get()}, "tmp", /*mode=*/0700); - tmpDirFd = openDirectory(tmpDirFn, FinalSymlink::DontFollow); - if (!tmpDirFd) { - continue; -diff --git a/src/libstore/unix/build/derivation-builder.cc b/src/libstore/unix/build/derivation-builder.cc -index 8f6343e0f..8288a4a31 100644 ---- a/src/libstore/unix/build/derivation-builder.cc -+++ b/src/libstore/unix/build/derivation-builder.cc -@@ -1597,6 +1597,13 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs() - assert(output && scratchPath); - auto actualPath = realPathInHost(store.printStorePath(*scratchPath)); - -+ /* An optional file descriptor of a directory used for intermediate -+ operations. */ -+ AutoCloseFD tempDirFd; -+ /* RAII cleanup of a temporary directory inside the store that is used -+ for intermediate operations. */ -+ AutoDelete delTempDir; -+ - auto finish = [&](StorePath finalStorePath) { - /* Store the final path */ - finalOutputs.insert_or_assign(outputName, finalStorePath); -@@ -1744,6 +1751,25 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs() - return newInfo0; - }; - -+ auto moveOutputToTempDir = [&]() -> void { -+ std::filesystem::path tempDir; -+ std::tie(tempDir, tempDirFd) = store.createTempDirInStore(); -+ delTempDir = AutoDelete(tempDir); -+ -+ auto tmpOutput = tempDir / "x"; -+ -+ /* Serialise and create a fresh copy of the output to break -+ any stale writable file descriptors. Copy through the -+ serialisation/deserialisation. TODO: Use copyRecursive here and -+ make use of reflinking. */ -+ auto source = sinkToSource([&](Sink & nextSink) { dumpPath(actualPath, nextSink); }); -+ restorePath(tmpOutput, *source, store.config->getLocalSettings().fsyncStorePaths); -+ /* This makes it slightly harder to make sense of the control flow. The rule -+ of thumb is that actualPath points to the current location of the stuff -+ that we'll end up registering. */ -+ actualPath = std::move(tmpOutput); -+ }; -+ - ValidPathInfo newInfo = std::visit( - overloaded{ - -@@ -1771,14 +1797,7 @@ SingleDrvOutputs DerivationBuilderImpl::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 -- std::filesystem::path tmpOutput = actualPath.native() + ".tmp"; -- copyFile(actualPath, tmpOutput, true); -- -- std::filesystem::rename(tmpOutput, actualPath); -- -+ moveOutputToTempDir(); - return newInfoFromCA( - DerivationOutput::CAFloating{ - .method = dof.ca.method, -@@ -1795,6 +1814,7 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs() - }, - - [&](const DerivationOutput::Impure & doi) { -+ moveOutputToTempDir(); - return newInfoFromCA( - DerivationOutput::CAFloating{ - .method = doi.method, diff --git a/pkgs/tools/package-management/nix/patches/landlock-abstract-socket-hardening-2.31.patch b/pkgs/tools/package-management/nix/patches/landlock-abstract-socket-hardening-2.31.patch deleted file mode 100644 index 7584a608db7f..000000000000 --- a/pkgs/tools/package-management/nix/patches/landlock-abstract-socket-hardening-2.31.patch +++ /dev/null @@ -1,232 +0,0 @@ -From b743f908b10aa56b88fb490de8a0d893bd2d83d5 Mon Sep 17 00:00:00 2001 -From: Sergei Zimmerman -Date: Sun, 5 Apr 2026 16:39:58 +0300 -Subject: [PATCH] libstore: Use landlock with - LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET for new enough kernels -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -This partially fixes the issue with cooperating processes being able -to communicate via abstract sockets. The fix is partial, because processes -outside the landlock domain of the sandboxed process can still connect to -a socket created by the FOD. There's no equivalent way of restricting inbound -connections. This closes the gap when there's no cooperating process on the host -(i.e. 2 separate FODs). - ->= 6.12 kernel is widespread enough (NixOS 25.11 ships it by -default) that we have no reason not to apply this hardening, even though -it's incomplete. - -ca-fd-leak test exercises this exact code path and now the smuggling -process fails with (on new enough kernels that have landlock support enabled): - -vm-test-run-ca-fd-leak> machine # sandbox setup: applied landlock sandboxing -vm-test-run-ca-fd-leak> machine # building '/nix/store/s7brgi6pdr5f3n8yqlgmdlz8blb89njc-smuggled.drv'... -vm-test-run-ca-fd-leak> machine # building derivation '/nix/store/s7brgi6pdr5f3n8yqlgmdlz8blb89njc-smuggled.drv': woken up -vm-test-run-ca-fd-leak> machine # connect: Operation not permitted -vm-test-run-ca-fd-leak> machine # sendmsg: Socket not connected - -(cherry picked from commit 44017ca497c8b44d5dac179f5afc63e91fe45ed6) -Signed-off-by: Jörg Thalheim ---- - src/libstore/meson.build | 5 + - .../unix/build/linux-derivation-builder.cc | 103 ++++++++++++++++++ - tests/nixos/ca-fd-leak/default.nix | 8 +- - 3 files changed, 114 insertions(+), 2 deletions(-) - -diff --git a/src/libstore/meson.build b/src/libstore/meson.build -index a275f4edc..dc8a7a940 100644 ---- a/src/libstore/meson.build -+++ b/src/libstore/meson.build -@@ -77,6 +77,11 @@ foreach funcspec : check_funcs - configdata_priv.set(define_name, define_value) - endforeach - -+if host_machine.system() == 'linux' -+ has_landlock = cxx.has_header('linux/landlock.h') -+ configdata_priv.set('HAVE_LANDLOCK', has_landlock.to_int()) -+endif -+ - has_acl_support = cxx.has_header('sys/xattr.h') \ - and cxx.has_function('llistxattr') \ - and cxx.has_function('lremovexattr') -diff --git a/src/libstore/unix/build/linux-derivation-builder.cc b/src/libstore/unix/build/linux-derivation-builder.cc -index eaac2bc28..dc5abf808 100644 ---- a/src/libstore/unix/build/linux-derivation-builder.cc -+++ b/src/libstore/unix/build/linux-derivation-builder.cc -@@ -1,10 +1,16 @@ - #ifdef __linux__ - -+# include "store-config-private.hh" -+ - # include "nix/store/personality.hh" - # include "nix/util/cgroup.hh" - # include "nix/util/linux-namespaces.hh" - # include "linux/fchmodat2-compat.hh" - -+# include -+# include -+# include -+ - # include - # include - # include -@@ -13,11 +19,16 @@ - # include - # include - # include -+# include - - # if HAVE_SECCOMP - # include - # endif - -+# if HAVE_LANDLOCK -+# include -+# endif -+ - # define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old)) - - namespace nix { -@@ -121,6 +132,77 @@ static void setupSeccomp() - # endif - } - -+# if HAVE_LANDLOCK && defined(LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET) -+ -+# define DO_LANDLOCK 1 -+ -+/* We are using LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET on best-effort basis. There are no glibc wrappers for now. */ -+ -+static int landlockCreateRuleset(const ::landlock_ruleset_attr * attr, std::size_t size, std::uint32_t flags) -+{ -+ return ::syscall(__NR_landlock_create_ruleset, attr, size, flags); -+} -+ -+static int landlockRestrictSelf(Descriptor rulesetFd, std::uint32_t flags) -+{ -+ return ::syscall(__NR_landlock_restrict_self, rulesetFd, flags); -+} -+ -+static int getLandlockAbiVersion() -+{ -+ int abiVersion = landlockCreateRuleset(nullptr, 0, LANDLOCK_CREATE_RULESET_VERSION); -+ return abiVersion; -+} -+ -+static void setupLandlock() -+{ -+ bool landlockSupportsScopeAbstractUnixSocket = []() { -+ int abiVersion = getLandlockAbiVersion(); -+ if (abiVersion >= 6) -+ /* All good, we can use LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET. See -+ https://docs.kernel.org/userspace-api/landlock.html#abstract-unix-socket-abi-6 */ -+ return true; -+ -+ if (abiVersion == -1) { -+ debug("landlock is not available"); -+ return false; -+ } -+ -+ debug("landlock version %d does not support LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET", abiVersion); -+ return false; -+ }(); -+ -+ /* Bail out early if landlock is not enabled or LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET wouldn't work. -+ TODO: Consider adding more landlock rules for filesystem access as defense-in-depth on top. */ -+ if (!landlockSupportsScopeAbstractUnixSocket) -+ return; -+ -+ ::landlock_ruleset_attr attr = { -+ /* This prevents multiple FODs from communicating with each other -+ via abstract sockets. Note that cooperating processes outside the -+ sandbox can still connect to an abstract socket created by the FOD. To -+ mitigate that issue entirely we'd still need network namespaces. */ -+ .scoped = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET, -+ }; -+ -+ /* This better not fail - if the kernel reports a new enough ABI version we -+ should treat any errors as fatal from now on. */ -+ AutoCloseFD rulesetFd = landlockCreateRuleset(&attr, sizeof(attr), 0); -+ if (!rulesetFd) -+ throw SysError("failed to create a landlock ruleset"); -+ -+ if (landlockRestrictSelf(rulesetFd.get(), 0) == -1) -+ throw SysError("failed to apply landlock"); -+ -+ debug("applied landlock sandboxing"); -+} -+ -+# else -+ -+# define DO_LANDLOCK 0 -+ -+# endif -+ - static void doBind(const Path & source, const Path & target, bool optional = false) - { - debug("bind mounting '%1%' to '%2%'", source, target); -@@ -159,8 +241,27 @@ struct LinuxDerivationBuilder : virtual DerivationBuilderImpl - - void enterChroot() override - { -+ /* Set the NO_NEW_PRIVS before doing seccomp/landlock setup. -+ landlock_restrict_self requires either NO_NEW_PRIVS or CAP_SYS_ADMIN. -+ With user namespaces we do get CAP_SYS_ADMIN. */ -+ if (!settings.allowNewPrivileges) -+ if (::prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) -+ throw SysError("failed to set PR_SET_NO_NEW_PRIVS"); -+ - setupSeccomp(); - -+# if DO_LANDLOCK -+ try { -+ setupLandlock(); -+ } catch (SysError & e) { -+ if (e.errNo != EPERM) -+ throw; -+ /* If allowNewPrivileges is true and we don't have CAP_SYS_ADMIN -+ this code path might be hit. */ -+ warn("setting up landlock: %s", e.message()); -+ } -+# endif -+ - linux::setPersonality(drv.platform); - } - }; -@@ -709,4 +810,6 @@ struct ChrootLinuxDerivationBuilder : ChrootDerivationBuilder, LinuxDerivationBu - - } // namespace nix - -+# undef DO_LANDLOCK -+ - #endif -diff --git a/tests/nixos/ca-fd-leak/default.nix b/tests/nixos/ca-fd-leak/default.nix -index 902aacdc6..dc944290f 100644 ---- a/tests/nixos/ca-fd-leak/default.nix -+++ b/tests/nixos/ca-fd-leak/default.nix -@@ -78,7 +78,7 @@ in - - # Build the smuggled derivation. - # This will connect to the smuggler server and send it the file descriptor -- machine.succeed(r""" -+ sender_output = machine.succeed(r""" - nix-build -E ' - builtins.derivation { - name = "smuggled"; -@@ -89,9 +89,13 @@ in - outputHash = builtins.hashString "sha256" "hello, world\n"; - builder = "${pkgs.busybox-sandbox-shell}/bin/sh"; - args = [ "-c" "echo \"hello, world\" > $out; ''${${sender}} ${socketName}" ]; -- }' -+ }' 2>&1 - """.strip()) - -+ # Landlock's LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET prevents a sandboxed process -+ # from connecting to an abstract socket created in an unrelated landlock domain. -+ # There's no such flag for preventing inbound connections. -+ assert "connect: Operation not permitted" in sender_output - - # Tell the smuggler server that we're done - machine.execute("echo done | ${pkgs.socat}/bin/socat - ABSTRACT-CONNECT:${socketName}") diff --git a/pkgs/tools/package-management/nix/patches/landlock-abstract-socket-hardening-2.34.patch b/pkgs/tools/package-management/nix/patches/landlock-abstract-socket-hardening-2.34.patch deleted file mode 100644 index 1ca40ee24b17..000000000000 --- a/pkgs/tools/package-management/nix/patches/landlock-abstract-socket-hardening-2.34.patch +++ /dev/null @@ -1,234 +0,0 @@ -From 23c7a6490a829a978af1acd56d76cc2dee7f4ad6 Mon Sep 17 00:00:00 2001 -From: Sergei Zimmerman -Date: Sun, 5 Apr 2026 16:39:58 +0300 -Subject: [PATCH] libstore: Use landlock with - LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET for new enough kernels -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -This partially fixes the issue with cooperating processes being able -to communicate via abstract sockets. The fix is partial, because processes -outside the landlock domain of the sandboxed process can still connect to -a socket created by the FOD. There's no equivalent way of restricting inbound -connections. This closes the gap when there's no cooperating process on the host -(i.e. 2 separate FODs). - ->= 6.12 kernel is widespread enough (NixOS 25.11 ships it by -default) that we have no reason not to apply this hardening, even though -it's incomplete. - -ca-fd-leak test exercises this exact code path and now the smuggling -process fails with (on new enough kernels that have landlock support enabled): - -vm-test-run-ca-fd-leak> machine # sandbox setup: applied landlock sandboxing -vm-test-run-ca-fd-leak> machine # building '/nix/store/s7brgi6pdr5f3n8yqlgmdlz8blb89njc-smuggled.drv'... -vm-test-run-ca-fd-leak> machine # building derivation '/nix/store/s7brgi6pdr5f3n8yqlgmdlz8blb89njc-smuggled.drv': woken up -vm-test-run-ca-fd-leak> machine # connect: Operation not permitted -vm-test-run-ca-fd-leak> machine # sendmsg: Socket not connected - -(cherry picked from commit 44017ca497c8b44d5dac179f5afc63e91fe45ed6) -Signed-off-by: Jörg Thalheim ---- - src/libstore/meson.build | 5 + - .../unix/build/linux-derivation-builder.cc | 103 ++++++++++++++++++ - tests/nixos/ca-fd-leak/default.nix | 8 +- - 3 files changed, 114 insertions(+), 2 deletions(-) - -diff --git a/src/libstore/meson.build b/src/libstore/meson.build -index 9e52517d4..edc476ae7 100644 ---- a/src/libstore/meson.build -+++ b/src/libstore/meson.build -@@ -79,6 +79,11 @@ foreach funcspec : check_funcs - configdata_priv.set(define_name, define_value) - endforeach - -+if host_machine.system() == 'linux' -+ has_landlock = cxx.has_header('linux/landlock.h') -+ configdata_priv.set('HAVE_LANDLOCK', has_landlock.to_int()) -+endif -+ - has_acl_support = cxx.has_header('sys/xattr.h') \ - and cxx.has_function('llistxattr') \ - and cxx.has_function('lremovexattr') -diff --git a/src/libstore/unix/build/linux-derivation-builder.cc b/src/libstore/unix/build/linux-derivation-builder.cc -index 555a3b6da..476baabe2 100644 ---- a/src/libstore/unix/build/linux-derivation-builder.cc -+++ b/src/libstore/unix/build/linux-derivation-builder.cc -@@ -1,5 +1,7 @@ - #ifdef __linux__ - -+# include "store-config-private.hh" -+ - # include "nix/store/globals.hh" - # include "nix/store/personality.hh" - # include "nix/store/filetransfer.hh" -@@ -9,6 +11,10 @@ - # include "nix/util/serialise.hh" - # include "linux/fchmodat2-compat.hh" - -+# include -+# include -+# include -+ - # include - # include - # include -@@ -17,11 +23,16 @@ - # include - # include - # include -+# include - - # if HAVE_SECCOMP - # include - # endif - -+# if HAVE_LANDLOCK -+# include -+# endif -+ - # define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old)) - - namespace nix { -@@ -125,6 +136,77 @@ static void setupSeccomp(const LocalSettings & localSettings) - # endif - } - -+# if HAVE_LANDLOCK && defined(LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET) -+ -+# define DO_LANDLOCK 1 -+ -+/* We are using LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET on best-effort basis. There are no glibc wrappers for now. */ -+ -+static int landlockCreateRuleset(const ::landlock_ruleset_attr * attr, std::size_t size, std::uint32_t flags) -+{ -+ return ::syscall(__NR_landlock_create_ruleset, attr, size, flags); -+} -+ -+static int landlockRestrictSelf(Descriptor rulesetFd, std::uint32_t flags) -+{ -+ return ::syscall(__NR_landlock_restrict_self, rulesetFd, flags); -+} -+ -+static int getLandlockAbiVersion() -+{ -+ int abiVersion = landlockCreateRuleset(nullptr, 0, LANDLOCK_CREATE_RULESET_VERSION); -+ return abiVersion; -+} -+ -+static void setupLandlock() -+{ -+ bool landlockSupportsScopeAbstractUnixSocket = []() { -+ int abiVersion = getLandlockAbiVersion(); -+ if (abiVersion >= 6) -+ /* All good, we can use LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET. See -+ https://docs.kernel.org/userspace-api/landlock.html#abstract-unix-socket-abi-6 */ -+ return true; -+ -+ if (abiVersion == -1) { -+ debug("landlock is not available"); -+ return false; -+ } -+ -+ debug("landlock version %d does not support LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET", abiVersion); -+ return false; -+ }(); -+ -+ /* Bail out early if landlock is not enabled or LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET wouldn't work. -+ TODO: Consider adding more landlock rules for filesystem access as defense-in-depth on top. */ -+ if (!landlockSupportsScopeAbstractUnixSocket) -+ return; -+ -+ ::landlock_ruleset_attr attr = { -+ /* This prevents multiple FODs from communicating with each other -+ via abstract sockets. Note that cooperating processes outside the -+ sandbox can still connect to an abstract socket created by the FOD. To -+ mitigate that issue entirely we'd still need network namespaces. */ -+ .scoped = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET, -+ }; -+ -+ /* This better not fail - if the kernel reports a new enough ABI version we -+ should treat any errors as fatal from now on. */ -+ AutoCloseFD rulesetFd = landlockCreateRuleset(&attr, sizeof(attr), 0); -+ if (!rulesetFd) -+ throw SysError("failed to create a landlock ruleset"); -+ -+ if (landlockRestrictSelf(rulesetFd.get(), 0) == -1) -+ throw SysError("failed to apply landlock"); -+ -+ debug("applied landlock sandboxing"); -+} -+ -+# else -+ -+# define DO_LANDLOCK 0 -+ -+# endif -+ - static void doBind(const std::filesystem::path & source, const std::filesystem::path & target, bool optional = false) - { - debug("bind mounting %1% to %2%", PathFmt(source), PathFmt(target)); -@@ -165,8 +247,27 @@ struct LinuxDerivationBuilder : virtual DerivationBuilderImpl - { - auto & localSettings = store.config->getLocalSettings(); - -+ /* Set the NO_NEW_PRIVS before doing seccomp/landlock setup. -+ landlock_restrict_self requires either NO_NEW_PRIVS or CAP_SYS_ADMIN. -+ With user namespaces we do get CAP_SYS_ADMIN. */ -+ if (!localSettings.allowNewPrivileges) -+ if (::prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) -+ throw SysError("failed to set PR_SET_NO_NEW_PRIVS"); -+ - setupSeccomp(localSettings); - -+# if DO_LANDLOCK -+ try { -+ setupLandlock(); -+ } catch (SysError & e) { -+ if (e.errNo != EPERM) -+ throw; -+ /* If allowNewPrivileges is true and we don't have CAP_SYS_ADMIN -+ this code path might be hit. */ -+ warn("setting up landlock: %s", e.message()); -+ } -+# endif -+ - linux::setPersonality({ - .system = drv.platform, - .impersonateLinux26 = localSettings.impersonateLinux26, -@@ -760,4 +861,6 @@ struct ChrootLinuxDerivationBuilder : ChrootDerivationBuilder, LinuxDerivationBu - - } // namespace nix - -+# undef DO_LANDLOCK -+ - #endif -diff --git a/tests/nixos/ca-fd-leak/default.nix b/tests/nixos/ca-fd-leak/default.nix -index 902aacdc6..dc944290f 100644 ---- a/tests/nixos/ca-fd-leak/default.nix -+++ b/tests/nixos/ca-fd-leak/default.nix -@@ -78,7 +78,7 @@ in - - # Build the smuggled derivation. - # This will connect to the smuggler server and send it the file descriptor -- machine.succeed(r""" -+ sender_output = machine.succeed(r""" - nix-build -E ' - builtins.derivation { - name = "smuggled"; -@@ -89,9 +89,13 @@ in - outputHash = builtins.hashString "sha256" "hello, world\n"; - builder = "${pkgs.busybox-sandbox-shell}/bin/sh"; - args = [ "-c" "echo \"hello, world\" > $out; ''${${sender}} ${socketName}" ]; -- }' -+ }' 2>&1 - """.strip()) - -+ # Landlock's LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET prevents a sandboxed process -+ # from connecting to an abstract socket created in an unrelated landlock domain. -+ # There's no such flag for preventing inbound connections. -+ assert "connect: Operation not permitted" in sender_output - - # Tell the smuggler server that we're done - machine.execute("echo done | ${pkgs.socat}/bin/socat - ABSTRACT-CONNECT:${socketName}") diff --git a/pkgs/tools/package-management/nix/patches/landlock-abstract-socket-hardening-git.patch b/pkgs/tools/package-management/nix/patches/landlock-abstract-socket-hardening-git.patch deleted file mode 100644 index 5b0fbdb46536..000000000000 --- a/pkgs/tools/package-management/nix/patches/landlock-abstract-socket-hardening-git.patch +++ /dev/null @@ -1,231 +0,0 @@ -From 44017ca497c8b44d5dac179f5afc63e91fe45ed6 Mon Sep 17 00:00:00 2001 -From: Sergei Zimmerman -Date: Sun, 5 Apr 2026 16:39:58 +0300 -Subject: [PATCH] libstore: Use landlock with - LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET for new enough kernels -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -This partially fixes the issue with cooperating processes being able -to communicate via abstract sockets. The fix is partial, because processes -outside the landlock domain of the sandboxed process can still connect to -a socket created by the FOD. There's no equivalent way of restricting inbound -connections. This closes the gap when there's no cooperating process on the host -(i.e. 2 separate FODs). - ->= 6.12 kernel is widespread enough (NixOS 25.11 ships it by -default) that we have no reason not to apply this hardening, even though -it's incomplete. - -ca-fd-leak test exercises this exact code path and now the smuggling -process fails with (on new enough kernels that have landlock support enabled): - -vm-test-run-ca-fd-leak> machine # sandbox setup: applied landlock sandboxing -vm-test-run-ca-fd-leak> machine # building '/nix/store/s7brgi6pdr5f3n8yqlgmdlz8blb89njc-smuggled.drv'... -vm-test-run-ca-fd-leak> machine # building derivation '/nix/store/s7brgi6pdr5f3n8yqlgmdlz8blb89njc-smuggled.drv': woken up -vm-test-run-ca-fd-leak> machine # connect: Operation not permitted -vm-test-run-ca-fd-leak> machine # sendmsg: Socket not connected - -Signed-off-by: Jörg Thalheim ---- - src/libstore/meson.build | 5 + - .../unix/build/linux-derivation-builder.cc | 101 ++++++++++++++++++ - tests/nixos/ca-fd-leak/default.nix | 8 +- - 3 files changed, 112 insertions(+), 2 deletions(-) - -diff --git a/src/libstore/meson.build b/src/libstore/meson.build -index 445798544..753c78687 100644 ---- a/src/libstore/meson.build -+++ b/src/libstore/meson.build -@@ -79,6 +79,11 @@ foreach funcspec : check_funcs - configdata_priv.set(define_name, define_value) - endforeach - -+if host_machine.system() == 'linux' -+ has_landlock = cxx.has_header('linux/landlock.h') -+ configdata_priv.set('HAVE_LANDLOCK', has_landlock.to_int()) -+endif -+ - has_acl_support = cxx.has_header('sys/xattr.h') \ - and cxx.has_function('llistxattr') \ - and cxx.has_function('lremovexattr') -diff --git a/src/libstore/unix/build/linux-derivation-builder.cc b/src/libstore/unix/build/linux-derivation-builder.cc -index 9cfd3cbcd..c71d23e15 100644 ---- a/src/libstore/unix/build/linux-derivation-builder.cc -+++ b/src/libstore/unix/build/linux-derivation-builder.cc -@@ -1,5 +1,7 @@ - #ifdef __linux__ - -+# include "store-config-private.hh" -+ - # include "nix/store/globals.hh" - # include "nix/store/personality.hh" - # include "nix/store/filetransfer.hh" -@@ -11,6 +13,8 @@ - - # include - # include -+# include -+ - # include - # include - # include -@@ -19,11 +23,16 @@ - # include - # include - # include -+# include - - # if HAVE_SECCOMP - # include - # endif - -+# if HAVE_LANDLOCK -+# include -+# endif -+ - # define pivot_root(new_root, put_old) (syscall(SYS_pivot_root, new_root, put_old)) - - namespace nix { -@@ -129,6 +138,77 @@ static void setupSeccomp(const LocalSettings & localSettings) - # endif - } - -+# if HAVE_LANDLOCK && defined(LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET) -+ -+# define DO_LANDLOCK 1 -+ -+/* We are using LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET on best-effort basis. There are no glibc wrappers for now. */ -+ -+static int landlockCreateRuleset(const ::landlock_ruleset_attr * attr, std::size_t size, std::uint32_t flags) -+{ -+ return ::syscall(__NR_landlock_create_ruleset, attr, size, flags); -+} -+ -+static int landlockRestrictSelf(Descriptor rulesetFd, std::uint32_t flags) -+{ -+ return ::syscall(__NR_landlock_restrict_self, rulesetFd, flags); -+} -+ -+static int getLandlockAbiVersion() -+{ -+ int abiVersion = landlockCreateRuleset(nullptr, 0, LANDLOCK_CREATE_RULESET_VERSION); -+ return abiVersion; -+} -+ -+static void setupLandlock() -+{ -+ bool landlockSupportsScopeAbstractUnixSocket = []() { -+ int abiVersion = getLandlockAbiVersion(); -+ if (abiVersion >= 6) -+ /* All good, we can use LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET. See -+ https://docs.kernel.org/userspace-api/landlock.html#abstract-unix-socket-abi-6 */ -+ return true; -+ -+ if (abiVersion == -1) { -+ debug("landlock is not available"); -+ return false; -+ } -+ -+ debug("landlock version %d does not support LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET", abiVersion); -+ return false; -+ }(); -+ -+ /* Bail out early if landlock is not enabled or LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET wouldn't work. -+ TODO: Consider adding more landlock rules for filesystem access as defense-in-depth on top. */ -+ if (!landlockSupportsScopeAbstractUnixSocket) -+ return; -+ -+ ::landlock_ruleset_attr attr = { -+ /* This prevents multiple FODs from communicating with each other -+ via abstract sockets. Note that cooperating processes outside the -+ sandbox can still connect to an abstract socket created by the FOD. To -+ mitigate that issue entirely we'd still need network namespaces. */ -+ .scoped = LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET, -+ }; -+ -+ /* This better not fail - if the kernel reports a new enough ABI version we -+ should treat any errors as fatal from now on. */ -+ AutoCloseFD rulesetFd = landlockCreateRuleset(&attr, sizeof(attr), 0); -+ if (!rulesetFd) -+ throw SysError("failed to create a landlock ruleset"); -+ -+ if (landlockRestrictSelf(rulesetFd.get(), 0) == -1) -+ throw SysError("failed to apply landlock"); -+ -+ debug("applied landlock sandboxing"); -+} -+ -+# else -+ -+# define DO_LANDLOCK 0 -+ -+# endif -+ - static void doBind(const std::filesystem::path & source, const std::filesystem::path & target, bool optional = false) - { - debug("bind mounting %1% to %2%", PathFmt(source), PathFmt(target)); -@@ -169,8 +249,27 @@ struct LinuxDerivationBuilder : virtual DerivationBuilderImpl - { - auto & localSettings = store.config->getLocalSettings(); - -+ /* Set the NO_NEW_PRIVS before doing seccomp/landlock setup. -+ landlock_restrict_self requires either NO_NEW_PRIVS or CAP_SYS_ADMIN. -+ With user namespaces we do get CAP_SYS_ADMIN. */ -+ if (!localSettings.allowNewPrivileges) -+ if (::prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) == -1) -+ throw SysError("failed to set PR_SET_NO_NEW_PRIVS"); -+ - setupSeccomp(localSettings); - -+# if DO_LANDLOCK -+ try { -+ setupLandlock(); -+ } catch (SysError & e) { -+ if (e.errNo != EPERM) -+ throw; -+ /* If allowNewPrivileges is true and we don't have CAP_SYS_ADMIN -+ this code path might be hit. */ -+ warn("setting up landlock: %s", e.message()); -+ } -+# endif -+ - linux::setPersonality({ - .system = drv.platform, - .impersonateLinux26 = localSettings.impersonateLinux26, -@@ -765,4 +864,6 @@ struct ChrootLinuxDerivationBuilder : ChrootDerivationBuilder, LinuxDerivationBu - - } // namespace nix - -+# undef DO_LANDLOCK -+ - #endif -diff --git a/tests/nixos/ca-fd-leak/default.nix b/tests/nixos/ca-fd-leak/default.nix -index 902aacdc6..dc944290f 100644 ---- a/tests/nixos/ca-fd-leak/default.nix -+++ b/tests/nixos/ca-fd-leak/default.nix -@@ -78,7 +78,7 @@ in - - # Build the smuggled derivation. - # This will connect to the smuggler server and send it the file descriptor -- machine.succeed(r""" -+ sender_output = machine.succeed(r""" - nix-build -E ' - builtins.derivation { - name = "smuggled"; -@@ -89,9 +89,13 @@ in - outputHash = builtins.hashString "sha256" "hello, world\n"; - builder = "${pkgs.busybox-sandbox-shell}/bin/sh"; - args = [ "-c" "echo \"hello, world\" > $out; ''${${sender}} ${socketName}" ]; -- }' -+ }' 2>&1 - """.strip()) - -+ # Landlock's LANDLOCK_SCOPE_ABSTRACT_UNIX_SOCKET prevents a sandboxed process -+ # from connecting to an abstract socket created in an unrelated landlock domain. -+ # There's no such flag for preventing inbound connections. -+ assert "connect: Operation not permitted" in sender_output - - # Tell the smuggler server that we're done - machine.execute("echo done | ${pkgs.socat}/bin/socat - ABSTRACT-CONNECT:${socketName}")