Loading nixos/modules/installer/tools/nix-fallback-paths.nix +6 −6 Original line number Diff line number Diff line { 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"; } pkgs/tools/package-management/nix/default.nix +15 −48 Original line number Diff line number Diff line Loading @@ -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; Loading pkgs/tools/package-management/nix/modular/src/nix/package.nix +3 −0 Original line number Diff line number Diff line Loading @@ -7,6 +7,8 @@ nix-main, nix-cmd, mimalloc, # Configuration Options version, Loading @@ -23,6 +25,7 @@ mkMesonExecutable (finalAttrs: { nix-expr nix-main nix-cmd mimalloc ]; mesonFlags = [ Loading pkgs/tools/package-management/nix/patches/ghsa-g3g9-5vj6-r3gj-2.28.patchdeleted 100644 → 0 +0 −126 Original line number Diff line number Diff line From 32b09e0bfeb33434866610994d89f48da8a2bf41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= <joerg@thalheim.io> 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 <sergei@zimmerman.foo> 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 <sergei@zimmerman.foo> Date: Fri Apr 3 00:21:21 2026 +0300 libstore: Make temporary in-store directory not world-readable Signed-off-by: Jörg Thalheim <joerg@thalheim.io> --- 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<std::filesystem::path, AutoCloseFD> 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<AutoDelete> 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, pkgs/tools/package-management/nix/patches/ghsa-g3g9-5vj6-r3gj-2.30.patchdeleted 100644 → 0 +0 −126 Original line number Diff line number Diff line From 4d0a078f1dae9a07d04e1a72e7e62fbf2ca249e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= <joerg@thalheim.io> 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 <sergei@zimmerman.foo> 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 <sergei@zimmerman.foo> Date: Fri Apr 3 00:21:21 2026 +0300 libstore: Make temporary in-store directory not world-readable Signed-off-by: Jörg Thalheim <joerg@thalheim.io> --- 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<std::filesystem::path, AutoCloseFD> 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<AutoDelete> 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, Loading
nixos/modules/installer/tools/nix-fallback-paths.nix +6 −6 Original line number Diff line number Diff line { 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"; }
pkgs/tools/package-management/nix/default.nix +15 −48 Original line number Diff line number Diff line Loading @@ -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; Loading
pkgs/tools/package-management/nix/modular/src/nix/package.nix +3 −0 Original line number Diff line number Diff line Loading @@ -7,6 +7,8 @@ nix-main, nix-cmd, mimalloc, # Configuration Options version, Loading @@ -23,6 +25,7 @@ mkMesonExecutable (finalAttrs: { nix-expr nix-main nix-cmd mimalloc ]; mesonFlags = [ Loading
pkgs/tools/package-management/nix/patches/ghsa-g3g9-5vj6-r3gj-2.28.patchdeleted 100644 → 0 +0 −126 Original line number Diff line number Diff line From 32b09e0bfeb33434866610994d89f48da8a2bf41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= <joerg@thalheim.io> 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 <sergei@zimmerman.foo> 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 <sergei@zimmerman.foo> Date: Fri Apr 3 00:21:21 2026 +0300 libstore: Make temporary in-store directory not world-readable Signed-off-by: Jörg Thalheim <joerg@thalheim.io> --- 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<std::filesystem::path, AutoCloseFD> 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<AutoDelete> 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,
pkgs/tools/package-management/nix/patches/ghsa-g3g9-5vj6-r3gj-2.30.patchdeleted 100644 → 0 +0 −126 Original line number Diff line number Diff line From 4d0a078f1dae9a07d04e1a72e7e62fbf2ca249e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rg=20Thalheim?= <joerg@thalheim.io> 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 <sergei@zimmerman.foo> 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 <sergei@zimmerman.foo> Date: Fri Apr 3 00:21:21 2026 +0300 libstore: Make temporary in-store directory not world-readable Signed-off-by: Jörg Thalheim <joerg@thalheim.io> --- 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<std::filesystem::path, AutoCloseFD> 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<AutoDelete> 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,