From 4bc5a3510fa3735798f9ed3a2a30a3ea7b32343a Mon Sep 17 00:00:00 2001 From: Tom Bereknyei Date: Fri, 1 Mar 2024 03:45:39 -0500 Subject: [PATCH] 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). Co-authored-by: Theophane Hufschmitt Co-authored-by: Valentin Gagarin --- src/libstore/build/local-derivation-goal.cc | 6 ++++++ src/libutil/filesystem.cc | 6 ++++++ src/libutil/util.hh | 7 +++++++ 3 files changed, 19 insertions(+) diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc index 64b55ca6a..f1e22f829 100644 --- a/src/libstore/build/local-derivation-goal.cc +++ b/src/libstore/build/local-derivation-goal.cc @@ -2558,6 +2558,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, .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 b302d6f45..59d42e0a5 100644 --- a/src/libutil/util.hh +++ b/src/libutil/util.hh @@ -274,6 +274,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