Skip to content

Commit

Permalink
Copy the output of fixed-output derivations before registering them
Browse files Browse the repository at this point in the history
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).
  • Loading branch information
Théophane Hufschmitt authored and domenkozar committed Mar 14, 2024
1 parent 6709f08 commit 10d5a3a
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 0 deletions.
6 changes: 6 additions & 0 deletions src/libstore/build/local-derivation-goal.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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";
copyFile(actualPath, tmpOutput, true);
renameFile(tmpOutput, actualPath);

auto newInfo0 = newInfoFromCA(DerivationOutput::CAFloating {
.method = dof.ca.method,
.hashAlgo = wanted.algo,
Expand Down
5 changes: 5 additions & 0 deletions src/libutil/file-system.cc
Original file line number Diff line number Diff line change
Expand Up @@ -628,6 +628,11 @@ void copy(const fs::directory_entry & from, const fs::path & to, bool andDelete)
}
}

void copyFile(const Path & oldPath, const Path & newPath, bool andDelete)
{
return copy(fs::directory_entry(fs::path(oldPath)), fs::path(newPath), andDelete);
}

void renameFile(const Path & oldName, const Path & newName)
{
fs::rename(oldName, newName);
Expand Down
7 changes: 7 additions & 0 deletions src/libutil/file-system.hh
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,13 @@ void renameFile(const Path & src, const Path & dst);
*/
void moveFile(const Path & src, const Path & dst);

/**
* Recursively copy the content of `oldPath` to `newPath`. If `andDelete` is
* `true`, then also remove `oldPath` (making this equivalent to `moveFile`, but
* with the guaranty that the destination will be “fresh”, with no stale inode
* or file descriptor pointing to it).
*/
void copyFile(const Path & oldPath, const Path & newPath, bool andDelete);

/**
* Automatic cleanup of resources.
Expand Down

0 comments on commit 10d5a3a

Please sign in to comment.