daemon: Bind-mount all the inputs, not just directories.

* nix/libstore/build.cc (DerivationGoal::startBuilder): Add all of
‘inputPaths’ to ‘dirsInChroot’ instead of hard-linking regular files.
Special-case symlinks.
(DerivationGoal)[regularInputPaths]: Remove.

Reported-by: Reepca Russelstein <reepca@russelstein.xyz>
Change-Id: I070987f92d73f187f7826a975bee9ee309d67f56
This commit is contained in:
Ludovic Courtès 2025-02-10 15:22:11 +01:00
parent 5c0b93b244
commit 550ca89744
No known key found for this signature in database
GPG key ID: 090B11993D9AEBB5

View file

@ -659,9 +659,6 @@ private:
/* RAII object to delete the chroot directory. */ /* RAII object to delete the chroot directory. */
std::shared_ptr<AutoDelete> autoDelChroot; std::shared_ptr<AutoDelete> autoDelChroot;
/* All inputs that are regular files. */
PathSet regularInputPaths;
/* Whether this is a fixed-output derivation. */ /* Whether this is a fixed-output derivation. */
bool fixedOutput; bool fixedOutput;
@ -1850,9 +1847,7 @@ void DerivationGoal::startBuilder()
/* Make the closure of the inputs available in the chroot, /* Make the closure of the inputs available in the chroot,
rather than the whole store. This prevents any access rather than the whole store. This prevents any access
to undeclared dependencies. Directories are bind-mounted, to undeclared dependencies. !!! As an extra security
while other inputs are hard-linked (since only directories
can be bind-mounted). !!! As an extra security
precaution, make the fake store only writable by the precaution, make the fake store only writable by the
build user. */ build user. */
Path chrootStoreDir = chrootRootDir + settings.nixStore; Path chrootStoreDir = chrootRootDir + settings.nixStore;
@ -1863,28 +1858,22 @@ void DerivationGoal::startBuilder()
throw SysError(format("cannot change ownership of %1%") % chrootStoreDir); throw SysError(format("cannot change ownership of %1%") % chrootStoreDir);
foreach (PathSet::iterator, i, inputPaths) { foreach (PathSet::iterator, i, inputPaths) {
struct stat st; struct stat st;
if (lstat(i->c_str(), &st)) if (lstat(i->c_str(), &st))
throw SysError(format("getting attributes of path `%1%'") % *i); throw SysError(format("getting attributes of path `%1%'") % *i);
if (S_ISDIR(st.st_mode))
dirsInChroot[*i] = *i;
else {
Path p = chrootRootDir + *i;
if (link(i->c_str(), p.c_str()) == -1) {
/* Hard-linking fails if we exceed the maximum
link count on a file (e.g. 32000 of ext3),
which is quite possible after a `nix-store
--optimise'. */
if (errno != EMLINK)
throw SysError(format("linking `%1%' to `%2%'") % p % *i);
StringSink sink;
dumpPath(*i, sink);
StringSource source(sink.s);
restorePath(p, source);
}
regularInputPaths.insert(*i); if (S_ISLNK(st.st_mode)) {
} /* Since bind-mounts follow symlinks, thus representing their
target and not the symlink itself, special-case
symlinks. XXX: When running unprivileged, TARGET can be
deleted by the build process. Use 'open_tree' & co. when
it's more widely available. */
Path target = chrootRootDir + *i;
if (symlink(readLink(*i).c_str(), target.c_str()) == -1)
throw SysError(format("failed to create symlink '%1%' to '%2%'") % target % readLink(*i));
}
else
dirsInChroot[*i] = *i;
} }
/* If we're repairing, checking or rebuilding part of a /* If we're repairing, checking or rebuilding part of a