Loading pkgs/tools/package-management/nix/default.nix +18 −2 Original line number Diff line number Diff line Loading @@ -165,6 +165,7 @@ lib.makeExtensible ( hash = "sha256-vFv/D08x9urtoIE9wiC7Lln4Eq3sgNBwU7TBE1iyrfI="; }) lowdown30PatchOld ./patches/ghsa-g3g9-5vj6-r3gj-2.28.patch ]; }; Loading @@ -189,6 +190,7 @@ lib.makeExtensible ( hash = "sha256-r2ZF1zBZDKMvyX6X4VsaTMrg0zdjn59Jf6Hqg56r29E="; }) lowdown30PatchOld ./patches/ghsa-g3g9-5vj6-r3gj-2.30.patch ] ); Loading @@ -208,6 +210,8 @@ lib.makeExtensible ( }).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; Loading @@ -224,7 +228,13 @@ lib.makeExtensible ( hash = "sha256-WPuGqMQGepXoRYjtRudMAMHEoLsIObw2x4sVfho5feA="; }; }).appendPatches patches_common; ( patches_common ++ [ ./patches/ghsa-g3g9-5vj6-r3gj-2.34.patch ./patches/landlock-abstract-socket-hardening-2.34.patch ] ); nix_2_34 = addTests "nix_2_34" self.nixComponents_2_34.nix-everything; Loading @@ -240,7 +250,13 @@ lib.makeExtensible ( hash = "sha256-fybp46IQmRN7lEUTChc3MTqxmRutmDO4RNSPEQfJQsQ="; }; }).appendPatches patches_common; ( patches_common ++ [ ./patches/ghsa-g3g9-5vj6-r3gj-git.patch ./patches/landlock-abstract-socket-hardening-git.patch ] ); git = addTests "git" self.nixComponents_git.nix-everything; Loading pkgs/tools/package-management/nix/patches/ghsa-g3g9-5vj6-r3gj-2.28.patch 0 → 100644 +126 −0 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.patch 0 → 100644 +126 −0 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, pkgs/tools/package-management/nix/patches/ghsa-g3g9-5vj6-r3gj-2.31.patch 0 → 100644 +126 −0 Original line number Diff line number Diff line From df0153a9eca42c4ed5ac784657c8c5d0664c9e0e 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 bf1d95ae399e47daeafa9e0dfac967f415c52447 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 ca5986fa353d402738acf961bcaa017c61019809 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 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<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 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<AutoDelete> 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, pkgs/tools/package-management/nix/patches/ghsa-g3g9-5vj6-r3gj-2.34.patch 0 → 100644 +126 −0 Original line number Diff line number Diff line From dfda33d358f4522450737c40407ba8d8403a86a1 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 d1f2de7d683bef1e2a164f23f33172c79c7725ea 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 2f13ee6dc4ade1d919c14757891d5d8a1001eee8 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 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<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(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, Loading
pkgs/tools/package-management/nix/default.nix +18 −2 Original line number Diff line number Diff line Loading @@ -165,6 +165,7 @@ lib.makeExtensible ( hash = "sha256-vFv/D08x9urtoIE9wiC7Lln4Eq3sgNBwU7TBE1iyrfI="; }) lowdown30PatchOld ./patches/ghsa-g3g9-5vj6-r3gj-2.28.patch ]; }; Loading @@ -189,6 +190,7 @@ lib.makeExtensible ( hash = "sha256-r2ZF1zBZDKMvyX6X4VsaTMrg0zdjn59Jf6Hqg56r29E="; }) lowdown30PatchOld ./patches/ghsa-g3g9-5vj6-r3gj-2.30.patch ] ); Loading @@ -208,6 +210,8 @@ lib.makeExtensible ( }).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; Loading @@ -224,7 +228,13 @@ lib.makeExtensible ( hash = "sha256-WPuGqMQGepXoRYjtRudMAMHEoLsIObw2x4sVfho5feA="; }; }).appendPatches patches_common; ( patches_common ++ [ ./patches/ghsa-g3g9-5vj6-r3gj-2.34.patch ./patches/landlock-abstract-socket-hardening-2.34.patch ] ); nix_2_34 = addTests "nix_2_34" self.nixComponents_2_34.nix-everything; Loading @@ -240,7 +250,13 @@ lib.makeExtensible ( hash = "sha256-fybp46IQmRN7lEUTChc3MTqxmRutmDO4RNSPEQfJQsQ="; }; }).appendPatches patches_common; ( patches_common ++ [ ./patches/ghsa-g3g9-5vj6-r3gj-git.patch ./patches/landlock-abstract-socket-hardening-git.patch ] ); git = addTests "git" self.nixComponents_git.nix-everything; Loading
pkgs/tools/package-management/nix/patches/ghsa-g3g9-5vj6-r3gj-2.28.patch 0 → 100644 +126 −0 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.patch 0 → 100644 +126 −0 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,
pkgs/tools/package-management/nix/patches/ghsa-g3g9-5vj6-r3gj-2.31.patch 0 → 100644 +126 −0 Original line number Diff line number Diff line From df0153a9eca42c4ed5ac784657c8c5d0664c9e0e 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 bf1d95ae399e47daeafa9e0dfac967f415c52447 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 ca5986fa353d402738acf961bcaa017c61019809 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 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<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 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<AutoDelete> 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,
pkgs/tools/package-management/nix/patches/ghsa-g3g9-5vj6-r3gj-2.34.patch 0 → 100644 +126 −0 Original line number Diff line number Diff line From dfda33d358f4522450737c40407ba8d8403a86a1 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 d1f2de7d683bef1e2a164f23f33172c79c7725ea 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 2f13ee6dc4ade1d919c14757891d5d8a1001eee8 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 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<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(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,