diff options
author | Puck Meerburg <puck@puckipedia.com> | 2024-03-06 23:26:40 +0000 |
---|---|---|
committer | Puck Meerburg <puck@puckipedia.com> | 2024-03-07 01:44:58 +0000 |
commit | 6f36a8834c1d7a2de548a943ec93a4ad02e27083 (patch) | |
tree | a814af53877c3cfa4a33098cf94edfda18e4d904 /src | |
parent | 89e99d94e4ae492db09c0ebc0c35e4890ef7db25 (diff) |
Copy the output of fixed-output derivations before registering them
It is possible to exfiltrate a file descriptor out of the build sandbox
of FODs, and use it to modify the store path after it has been
registered. To avoid that issue, don't register the output of the build,
but a copy of it (that will be free of any leaked file descriptor).
Test that we can't leverage abstract unix domain sockets to leak file
descriptors out of the sandbox and modify the path after it has been
registered.
(cherry picked from commit 2dadfeb690e7f4b8f97298e29791d202fdba5ca6)
(tests cherry picked from commit c854ae5b3078ac5d99fa75fe148005044809e18c)
Co-authored-by: Valentin Gagarin <valentin.gagarin@tweag.io>
Co-authored-by: Theophane Hufschmitt <theophane.hufschmitt@tweag.io>
Co-authored-by: Tom Bereknyei <tomberek@gmail.com>
Change-Id: I87cd58f1c0a4f7b7a610d354206b33301e47b1a4
Diffstat (limited to 'src')
-rw-r--r-- | src/libstore/build/local-derivation-goal.cc | 6 | ||||
-rw-r--r-- | src/libutil/filesystem.cc | 6 | ||||
-rw-r--r-- | src/libutil/util.hh | 7 |
3 files changed, 19 insertions, 0 deletions
diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index 1fc966951..8e5ab7594 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -2528,6 +2528,12 @@ SingleDrvOutputs LocalDerivationGoal::registerOutputs() [&](const DerivationOutput::CAFixed & dof) { auto & wanted = dof.ca.hash; + // Replace the output by a fresh copy of itself to make sure + // that there's no stale file descriptor pointing to it + Path tmpOutput = actualPath + ".tmp"; + movePath(actualPath, tmpOutput); + copyFile(tmpOutput, actualPath, true); + auto newInfo0 = newInfoFromCA(DerivationOutput::CAFloating { .method = dof.ca.method, .hashType = wanted.type, diff --git a/src/libutil/filesystem.cc b/src/libutil/filesystem.cc index 11cc0c0e7..2a7787c0e 100644 --- a/src/libutil/filesystem.cc +++ b/src/libutil/filesystem.cc @@ -133,6 +133,12 @@ void copy(const fs::directory_entry & from, const fs::path & to, bool andDelete) } } + +void copyFile(const Path & oldPath, const Path & newPath, bool andDelete) +{ + return copy(fs::directory_entry(fs::path(oldPath)), fs::path(newPath), andDelete); +} + void renameFile(const Path & oldName, const Path & newName) { fs::rename(oldName, newName); diff --git a/src/libutil/util.hh b/src/libutil/util.hh index bcee42327..acd77ee33 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -275,6 +275,13 @@ void renameFile(const Path & src, const Path & dst); */ void moveFile(const Path & src, const Path & dst); +/** + * Recursively copy the content of `oldPath` to `newPath`. If `andDelete` is + * `true`, then also remove `oldPath` (making this equivalent to `moveFile`, but + * with the guaranty that the destination will be “fresh”, with no stale inode + * or file descriptor pointing to it). + */ +void copyFile(const Path & oldPath, const Path & newPath, bool andDelete); /** * Wrappers arount read()/write() that read/write exactly the |