Commit Graph
62 Commits
Author SHA1 Message Date
Reepca Russelstein 3e5c3217f3 daemon: libutil: make nar parser much stricter.
The prior implementation of 'parse' was, to put it mildly, very accepting of
invalid input.  This made it very difficult to review its security properties,
which ended up relying entirely on the fact that it still didn't allow "/" in
directory entry names and that O_CREAT|O_EXCL implicitly causes symlinks to
not be followed in the final component, and mkdir and symlink never follow
symlinks in the final component, and all of the above throw exceptions when
they fail.

Some examples of what was allowed prior:

1. Empty archives:
   "nix-archive-1" "(" ")"
2. Archives with only a type:
   "nix-archive-1" "(" "type" "regular" ")"
3. Archives of regular files with multiple contents, which would be
   concatenated (e.g. this has contents "foobar"):
   "nix-archive-1" "(" "type" "regular" "contents" "foo" "contents" "bar" ")"
4. Archives of directories with multiple entries with the same name, and that
   aren't sorted:
   "nix-archive-1" "(" "type" "directory"
                       "entry" "(" "name" "c" "node" "(" ")" ")"
                       "entry" "(" "name" "b" "node" "(" ")" ")"
                       "entry" "(" "name" "b" "node" "(" ")" ")"
                       "entry" "(" "name" "a" "node" "(" ")" ")" ")"
5. Archives of directories with empty entries:
   "nix-archive-1" "(" "type" "directory" "entry" "(" ")" ")"
6. Archives of directories with entries with no name:
   "nix-archive-1" "(" "type" "directory" "entry" "(" "node" "(" ")" ")" ")"
7. Archives of directories with entries with no node:
   "nix-archive-1" "(" "type" "directory" "entry" "(" "name" "a" ")" ")"
7. Archives of directories with entries with multiple names:
   "nix-archive-1" "(" "type" "directory" "entry" "(" "name" "a"
                                                      "node" "(" ")"
                                                      "name" "b" ")" ")"
8. Archives of directories with entries with multiple nodes:
   "nix-archive-1" "(" "type" "directory" "entry" "(" "node" "(" ")"
                                                      "node" "(" ")" ")" ")"
9. Archives of directories with entries with multiple names and multiple
   nodes:
   "nix-archive-1" "(" "type" "directory"
                       "entry" "(" "name" "a" "node" "(" ")"
                                   "name" "b" "node" "(" ")" ")" ")"

Such permissiveness rather defeats the "normalized" part of "Normalized
ARchive".

Additionally, 'parse' previously used recursion with no explicit depth limit.
In practice Linux would still limit the depth to around 2048, since filenames
longer than 4096 bytes yield ENAMETOOLONG, but no such restriction exists on
Hurd.

The permissiveness issue is resolved by rewriting 'parse', and the unbounded
recursion issue is resolved by adding a nestLimit parameter so that no more
than DIRECTORY_NESTING_LIMIT (currently 256) levels of recursion are used.  An
alternate implementation that doesn't use recursion, 'parse_unbounded', is
included in case it is determined that the nesting limit should be removed,
but currently only 'parse' is used.

* nix/libutil/archive.cc (parse): rewrite to add nestLimit parameter.
  (parseDump): pass DIRECTORY_NESTING_LIMIT as nestLimit argument to 'parse'.
  (parse_unbounded): new function.

Change-Id: Ia3c32cc645b609f6ae7f3bcafc491ec0073cbb51
Signed-off-by: Ludovic Courtès <ludo@gnu.org>
2026-07-02 19:42:47 +02:00
Reepca Russelstein 694785dc13 daemon: libutil: add isHash32.
* nix/libutil/hash.hh (isHash32): new function.
* nix/libutil/hash.hh (isHash32): provide implementation.

Signed-off-by: Ludovic Courtès <ludo@gnu.org>
2026-06-24 15:20:16 +02:00
Reepca Russelstein 64a0824955 daemon: libutil: add base32Values and use them.
Recognizing base32 characters and/or parsing them into byte sequences is done
in several places, so it makes sense to provide a common lookup table.

* nix/libutil/hash.hh (base32Values, base32ValuesInitialized): new variables.
  (initializeBase32Values, getBase32Value): new functions.
* nix/libutil/hash.cc (base32Values, base32ValuesInitialized): provide
  definition for variables.
  (initializeBase32Values, getBase32Value): provide function implementations.
  (parseHash32): use base32Values.
* nix/libstore/references.cc (search): use base32Values.

Signed-off-by: Ludovic Courtès <ludo@gnu.org>
2026-06-24 15:20:16 +02:00
Reepca Russelstein 865cb0188c daemon: Actually remove unreadable directories.
Fixes a regression introduced in 7173c2c0ca.  Additional discussion at
https://codeberg.org/guix/guix/pulls/5977.

* nix/libutil/util.cc (_deletePathAt): chmod directory and retry open when it
  fails with EACCES.  Do this using an O_PATH file descriptor referenced via
  /proc/self/fd whenever possible to avoid it being replaced by a
  non-directory immediately before being chmod'ed.
* nix/libutil/util.hh (deletePath): document TOCTTOU race on non-linux systems
  where hardlinks aren't protected.
* tests/derivations.scm ("unreadable directories in build tree can be
  removed"): new test.

Fixes: guix/guix#5891
Reported-by: Liliana Marie Prikler <liliana.prikler@gmail.com>
Change-Id: I749127fe5254ebabc8387a2f0ef47e3c116bfcc5
Signed-off-by: Ludovic Courtès <ludo@gnu.org>
Merges: #6460
2026-02-27 23:54:00 +01:00
Rutherther 7c0cd7e45b Merge branch 'version-1.5.0'
Change-Id: Id73c0c74600a3da3373b3a37236b505af65bfe31
2026-01-22 13:27:13 +01:00
Efraim Flashner 9efc2a690e nix: Spelling corrections.
* nix/libstore/globals.hh,
nix/libstore/store-api.hh,
nix/libutil/util.hh: Fix misspellings in comments.

Change-Id: Id77ea2bde1c2582d1a7bec4ed256ea900998c4b8
2026-01-14 09:16:45 +01:00
Ludovic Courtès 5ff038473e daemon: Fix several format strings.
This is a followup to 3af52f845f and
6f1448ef89.

* nix/libstore/local-store.cc (LocalStore::queryValidPathId): Change %1% to {}.
* nix/libstore/optimise-store.cc (LocalStore::optimisePath_): Likewise.
* nix/nix-daemon/guix-daemon.cc (open_unix_domain_socket): Likewise.
(main): Likewise.
* nix/libutil/affinity.cc (restoreAffinity): Remove useless %1%.

Change-Id: I3a7cd79cd69ab58f14118662f1dcf2fef067a6b7
2026-01-12 10:56:37 +01:00
Congcong Kuo d1a9fb85a4 daemon: Use inline functions and variables instead of extern
Avoid separate declarations and definitions for so-called 'global' objects.

* nix/libstore/derivations.{cc, hh} (drvHashes): Use inline instead of separate
declaration and definition.
* nix/libstore/globals.{cc, hh} (settings, nixVersion): Same.
* nix/libstore/local-store.hh (drvsLogDir, deduplicationMinSize): Same.
* nix/libstore/optimise-store.cc (deduplicationMinSize): Same.
* nix/libstore/store-api.{cc, hh} (store): Same.
* nix/libutil/archive.{cc, hh} (defaultPathFilter): Same.
* nix/libutil/hash.{cc, hh} (base32Chars): Same and modify header files.
* nix/libutil/util.{cc, hh} (logType, verbosity, _writeToStderr,
_isInterrupted): Same.
* nix/local.mk: Modified according to the rename of shared.hh.
* nix/nix-daemon/guix-daemon.cc (blockInt, argvSaved, run): Same and
modify header files.
* nix/nix-daemon/nix-daemon.cc: Modify header files.
* nix/nix-daemon/shared.hh: Renamed to nix-daemon.hh

Signed-off-by: Ludovic Courtès <ludo@gnu.org>
2025-11-12 18:34:06 +01:00
Congcong Kuo 85a72ed28e daemon: Use starts_with() and ends_with() instead of string() or hasSuffix()
* nix/libstore/build.cc (DerivationGoal::tryBuildHook): Use starts_with instead
of string()
* nix/libstore/builtins.cc (lookupBuiltinBuilder): Same.
* nix/libstore/builtins.hh (isBuiltin): Same and fix indentation of the file.
* nix/libstore/derivations.cc (DerivationOutput::parseHashInfo, isDerivation):
Same and clean header files.
* nix/libstore/gc.cc (addPermRoot, LocalStore::isActiveTempFile): Same.
* nix/libstore/globals.cc: Same.
* nix/libstore/local-store.cc: Same.
* nix/libstore/misc.cc: Same.
* nix/libstore/store-api.cc (checkStoreName): Same.
* nix/libutil/affinity.cc: Same.
* nix/libutil/archive.cc: Same.
* nix/libutil/spawn.cc: Same.
* nix/libutil/util.{cc, hh} (hasSuffix): Removed.

Signed-off-by: Ludovic Courtès <ludo@gnu.org>
2025-11-03 17:18:37 +01:00
Congcong Kuo 3af52f845f daemon: Bump to C++20 and use ‘std::format’ instead of ‘boost::format’.
* nix/boost: This directory and all files inside it are removed.
* nix/libstore/build.cc (Goal::trace): Use ‘std::string’ instead of ‘const format &’.
(DerivationGoal::startBuilder, ...): Use ‘std::format’ or ‘std::vformat’ instead of ‘boost::format’.
* nix/libstore/builtins.cc (builtinDownload): Same.
* nix/libstore/derivations.cc (DerivationOutput::parseHashInfo, ...): Same.
* nix/libstore/gc.cc (LocalStore::openGCLock, ...): Same.
* nix/libstore/globals.cc (Settings::_get): Same.
* nix/libstore/local-store.cc: (checkStoreNotSymlink, ...): Same.
* nix/libstore/misc.cc (dfsVisit, showBytes): Same
* nix/libstore/optimise-store.cc (makeWritable, ...): Same.
* nix/libstore/pathlocks.cc (openLockFile, ...): Same.
* nix/libstore/references.cc (search, scanForReferences): Same.
* nix/libstore/sqlite.hh (throwSQLiteError): Use ‘std::string’ instead of ‘const format &’.
* nix/libstore/sqlite.cc (throwSQLiteError): Use ‘std::string’ instead of ‘const format &’.
* nix/libstore/store-api.cc (assertStorePath, ...): Use ‘std::format’ instead of ‘boost::format’.
* nix/libutil/affinity.cc (setAffinityTo): Same.
* nix/libutil/archive.cc (dumpContents, ...): Same.
* nix/libutil/hash.cc (parseHash, parseHash32, parseHash16or32, hashFile): Same.
* nix/libutil/hash.hh (parseHash, parseHash32, parseHash16or32, isHash): Same.
* nix/libutil/serialise.cc : Add ‘<cassert>’ header file.
* nix/libutil/spawn.cc (addPhaseAfter, ...): Use ‘std::format’ instead of ‘boost::format’.
* nix/libutil/types.hh (FormatOrString): Removed.
(BaseError, BaseError::addPrefix, SysError, MakeError):
Use ‘std::string or std::string_view’ instead of ‘FormatOrString’.
* nix/libutil/util.hh (Nest::open, printMsg_, warnOnce, expect): Same.
* nix/libutil/util.cc (BaseError::BaseError, ...): Same.
(writeToStderr, _interrupted): Use std::uncaught_exceptions() instead of std::uncaught_exception()
* nix/nix-daemon/nix-daemon.cc (performOp, ...): Same.
* nix/nix-daemon/guix-daemon.cc (string_to_bool, ...): Same.
* nix/local.mk: Remove ‘libformat.a’ from ‘noinst_LIBRARIES’,
remove ‘libformat_a_SOURCES’ and ‘libformat_headers’,
remove ‘libformat_a_CPPFLAGS’ from ‘libutil_a_CPPFLAGS’ and ‘guix_daemon_LDADD’,
update ‘AM_CXXFLAGS’ to ‘-std=c++20’.

Signed-off-by: Ludovic Courtès <ludo@gnu.org>
2025-10-19 21:29:39 +02:00
Ludovic Courtès d1910384d3 daemon: Export as little as needed from libutil/spawn.cc.
* nix/libutil/spawn.cc (reset_writeToStderrAction, restoreAffinityAction)
(setsidAction, earlyIOSetupAction, dropAmbientCapabilitiesAction)
(chrootAction, chdirAction, closeMostFDsAction, setPersonalityAction)
(oomSacrificeAction, setIDsAction, setNoNewPrivsAction)
(addSeccompFilterAction, restoreSIGPIPEAction, setupSuccessAction)
(usernsInitSyncAction, usernsSetIDsAction, initLoopbackAction)
(setHostAndDomainAction, makeFilesystemsPrivateAction)
(makeChrootSeparateFilesystemAction, bindMount)
(mountIntoChroot, mountIntoChrootAction, mountProcAction)
(mountDevshmAction, mountDevptsAction, pivotRootAction)
(idMapToIdentityMap, lockMountsAction, runChildSetupEntry): Add ‘static’
qualifier.
* nix/libutil/spawn.hh: Remove the corresponding ‘extern’ declarations.

Change-Id: I3156d72d866f22fa31aa9a843f116771763ccb61
2025-10-16 15:14:37 +02:00
Ludovic Courtès c7b8f3ec1a daemon: ‘runProgram’ exits with 127 upon ENOENT or similar.
This is in accordance with widespread conventions.  Previously it would
exit with code 1, which was misleading.

* nix/libutil/util.cc (runProgram): Exit with 127 if ‘execv’ or ‘execvp’
fails.

Change-Id: I5df214afffda69aa329a25afbc48f6cbfdd0961c
2025-10-16 15:14:37 +02:00
Congcong Kuo bd963ec99d daemon: Use std::string or std::vector instead of variable-length array (VLA).
* libutil/util.h (waitForMessage): Use std::string instead of char* to unify coding style.
* libutil/util.cc (waitForMessage): Use std::string instead of variable-length array (VLA).
(readLink, copyFileRecursively, expect): Use std::vector instead of VLA.
* libutil/hash.cc (printHash): Use std::vector instead of VLA.

Signed-off-by: Ludovic Courtès <ludo@gnu.org>
2025-07-16 23:50:38 +02:00
Reepca Russelstein 05a669efa5 daemon: Gracefully handle target mount point already existing.
Fixes guix/guix#903.

* nix/libutil/spawn.cc (bindMount): in the "regular file" case, only create a
  placeholder file if one doesn't already exist.

Change-Id: Ie46b2fef2cea5b2a052c4ec48d00e97bfc1ee506
Reported-by: Hilton Chain <hako@ultrarare.space>
Signed-off-by: Ludovic Courtès <ludo@gnu.org>
2025-07-04 18:41:43 +02:00
Reepca Russelstein 1ee291139f daemon: Fix build of spawn.cc for non-Linux and newer GCC.
* nix/libutil/spawn.cc: Include <fcntl.h> and <cstdint>.
(cloneChild): Wrap body in #if CLONE_ENABLED.

Change-Id: I0be7e6dbe80ac2f7022793149472270a4e49d23c
Signed-off-by: Ludovic Courtès <ludo@gnu.org>
2025-06-26 15:41:57 +02:00
Andreas Enge 3eb03534e6 daemon: Fix build with gcc-14.
* nix/libutil/seccomp.hh: Include cstdint.

Change-Id: I454c7abd56abbe4cf03a5bc8ebc402678e07a0f0
2025-06-25 14:33:26 +02:00
Ludovic Courtès 0e79d5b655 daemon: Protect ‘copyFileRecursively’ from race conditions.
Previously, if an attacker managed to introduce a hard link or a symlink
on one of the destination file names before it is opened,
‘copyFileRecursively’ would overwrite the symlink’s target or the hard
link’s content.

This kind of attack could be carried out while guix-daemon is copying
the output or the chroot directory of a failed fixed-output derivation
build, possibly allowing the attacker to escalate to the privileges of
the build user.

* nix/libutil/util.cc (copyFileRecursively): In the ‘S_ISREG’ case, open
‘destination’ with O_NOFOLLOW | O_EXCL.  In the ‘S_ISDIR’ case, open
‘destination’ with O_NOFOLLOW.

Reported-by: Reepca Russelstein <reepca@russelstein.xyz>
Change-Id: I94273efe4e92c1a4270a98c5ec47bd098e9227c9
Signed-off-by: John Kehayias <john.kehayias@protonmail.com>
2025-06-24 10:07:59 -04:00
Reepca Russelstein c659f977bb daemon: add seccomp filter for slirp4netns.
The container that slirp4netns runs in should already be quite difficult to do
anything malicious in beyond basic denial of service or sending of network
traffic.  There is, however, one hole remaining in the case in which there is
an adversary able to run code locally: abstract unix sockets.  Because these
are governed by network namespaces, not IPC namespaces, and slirp4netns is in
the root network namespace, any process in the root network namespace can
cooperate with the slirp4netns process to take over its user.

To close this, we use seccomp to block the creation of unix-domain sockets by
slirp4netns.  This requires some finesse, since slirp4netns absolutely needs
to be able to create other types of sockets - at minimum AF_INET and AF_INET6

Seccomp has many, many pitfalls.  To name a few:

1. Seccomp provides you with an "arch" field, but this does not uniquely
   determine the ABI being used; the actual meaning of a system call number
   depends on both the number (which is often the result of ORing a related
   system call with a flag for an alternate ABI) and the architecture.

2. Seccomp provides no direct way of knowing what the native value for the
   arch field should be; the user must do configure/compile-time testing for
   every architecture+ABI combination they want to support.  Amusingly enough,
   the linux-internal header files have this exact information
   (SECCOMP_ARCH_NATIVE), but they aren't sharing it.

3. The only system call numbers we naturally have are the native ones in
   asm/unistd.h.  __NR_socket will always refer to the system call number for
   the target system's ABI.

4. Seccomp can only manipulate 32-bit words, but represents every system call
   argument as a uint64.

5. New system call numbers with as-yet-unknown semantics can be added to the
   kernel at any time.

6. Based on this comment in arch/x86/entry/syscalls/syscall_32.tbl:

   # 251 is available for reuse (was briefly sys_set_zone_reclaim)

   previously-invalid system call numbers may later be reused for new system
   calls.

7. Most architecture+ABI combinations have system call tables with many gaps
   in them.  arm-eabi, for example, has 35 such gaps (note: this is just the
   number of distinct gaps, not the number of system call numbers contained in
   those gaps).

8. Seccomp's BPF filters require a fully-acyclic control flow graph.
   Any operation on a data structure must therefore first be fully
   unrolled before it can be run.

9. Seccomp cannot dereference pointers.  Only the raw bits provided to the
   system calls can be inspected.

10. Some architecture+ABI combos have multiplexer system calls.  For example,
    socketcall can perform any socket-related system call.  The arguments to
    the multiplexed system call are passed indirectly, via a pointer to user
    memory.  They therefore cannot be inspected by seccomp.

11. Some valid system calls are not listed in any table in the kernel source.
    For example, __ARM_NR_cacheflush is an "ARM private" system call.  It does
    not appear in any *.tbl file.

12. Conditional branches are limited to relative jumps of at most 256
    instructions forward.

13. Prior to Linux 4.8, any process able to spawn another process and call
    ptrace could bypass seccomp restrictions.

To address (1), (2), and (3), we include preprocessor checks to identify the
native architecture value, and reject all system calls that don't use the
native architecture.

To address (4), we use the AC_C_BIGENDIAN autoconf check to conditionally
define WORDS_BIGENDIAN, and match up the proper portions of any uint64 we test
for with the value in the accumulator being tested against.

To address (5) and (6), we use system call pinning.  That is, we hardcode a
snapshot of all the valid system call numbers at the time of writing, and
reject any system call numbers not in the recorded set.  A set is recorded for
every architecture+ABI combo, and the native one is chosen at compile-time.
This ensures that not only are non-native architectures rejected, but so are
non-native ABIs.  For the sake of conciseness, we represent these sets as sets
of disjoint ranges.  Due to (7), checking each range in turn could add a lot
of overhead to each system call, so we instead binary search through the
ranges.  Due to (8), this binary search has to be fully unrolled, so we do
that too.

It can be tedious and error-prone to manually produce the syscall ranges by
looking at linux's *.tbl files, since the gaps are often small and
uncommented.  To address this, a script, build-aux/extract-syscall-ranges.sh,
is added that will produce them given a *.tbl filename and an ABI regex (some
tables seem to abuse the ABI field with strange values like "memfd_secret").
Note that producing the final values still requires looking at the proper
asm/unistd.h file to find any private numbers and to identify any offsets and
ABI variants used.

(10) used to have no good solution, but in the past decade most architectures
have gained dedicated system call alternatives to at least socketcall, so we
can (hopefully) just block it entirely.

To address (13), we block ptrace also.

* build-aux/extract-syscall-ranges.sh: new script.
* Makefile.am (EXTRA_DIST): register it.
* config-daemon.ac: use AC_C_BIGENDIAN.
* nix/libutil/spawn.cc (setNoNewPrivsAction, addSeccompFilterAction): new
  functions.
* nix/libutil/spawn.hh (setNoNewPrivsAction, addSeccompFilterAction): new
  declarations.
  (SpawnContext)[setNoNewPrivs, addSeccompFilter]: new fields.
* nix/libutil/seccomp.hh: new header file.
* nix/libutil/seccomp.cc: new file.
* nix/local.mk (libutil_a_SOURCES, libutil_headers): register them.
* nix/libstore/build.cc (slirpSeccompFilter, writeSeccompFilterDot):
  new functions.
  (spawnSlirp4netns): use them, set seccomp filter for slirp4netns.

Change-Id: Ic92c7f564ab12596b87ed0801b22f88fbb543b95
Signed-off-by: John Kehayias <john.kehayias@protonmail.com>
2025-06-24 10:07:58 -04:00
Reepca Russelstein fb42611b8f daemon: Use slirp4netns to provide networking to fixed-output derivations.
Previously, the builder of a fixed-output derivation could communicate with an
external process via an abstract Unix-domain socket.  In particular, it could
send an open file descriptor to the store, granting write access to some of
its output files in the store provided the derivation build fails—the fix for
CVE-2024-27297 did not address this specific case.  It could also send an open
file descriptor to a setuid program, which could then be executed using
execveat to gain the privileges of the build user.

With this change, fixed-output derivations other than “builtin:download”
and “builtin:git-download” always run in a separate network namespace
and have network access provided by a TAP device backed by slirp4netns,
thereby closing the abstract Unix-domain socket channel.

* nix/libstore/globals.hh (Settings)[useHostLoopback, slirp4netns]: new
fields.
* config-daemon.ac (SLIRP4NETNS): new C preprocessor definition.
* nix/libstore/globals.cc (Settings::Settings): initialize them to defaults.
* nix/nix-daemon/guix-daemon.cc (options): add --isolate-host-loopback option.
* doc/guix.texi: document it.
* nix/libstore/build.cc (DerivationGoal)[slirp]: New field.
(setupTap, setupTapAction, waitForSlirpReadyAction, enableRouteLocalnetAction,
 prepareSlirpChrootAction, spawnSlirp4netns, haveGlobalIPv6Address,
 remapIdsTo0Action): New functions.
(initializeUserNamespace): allow the guest UID and GID to be specified.
(DerivationGoal::killChild): When ‘slirp’ is not -1, call ‘kill’.
(DerivationGoal::startBuilder): Unconditionally add CLONE_NEWNET to FLAGS.
When ‘fixedOutput’ is true, spawn ‘slirp4netns’.
When ‘fixedOutput’ and ‘useChroot’ are true, add setupTapAction,
waitForSlirpReadyAction, and enableRouteLocalnetAction to builder setup
phases.
Create a /etc/resolv.conf for fixed-output derivations that directs them to
slirp4netns's dns address.
When settings.useHostLoopback is true, supply fixed-output derivations with a
/etc/hosts that resolves "localhost" to slirp4netns's address for accessing
the host loopback.
* nix/libutil/util.cc (keepOnExec, decodeOctalEscaped, sendFD, receiveFD,
  findProgram): New functions.
* nix/libutil/util.hh (keepOnExec, decodeOctalEscaped, sendFD, receiveFD,
  findProgram): New declarations.
* gnu/packages/package-management.scm (guix): add slirp4netns input for linux
  targets.
* tests/derivations.scm (builder-network-isolated?): new variable.
  ("fixed-output derivation, network access, localhost", "fixed-output
  derivation, network access, external host"):
  skip test case if fixed output derivations are isolated from the network.

Change-Id: Ia3fea2ab7add56df66800071cf15cdafe7bfab96
Signed-off-by: John Kehayias <john.kehayias@protonmail.com>
2025-06-24 10:07:57 -04:00
Reepca Russelstein be8aca0651 daemon: add and use spawn.cc and spawn.hh.
This adds a mechanism for manipulating and running "spawn phases" similarly to
how builder-side code manipulates "build phases".  The main difference is that
spawn phases take a (reference to a) single structure that they can both read
from and write to, with their writes being visible to subsequent phases.  The
base structure type for this is SpawnContext.

It also adds some predefined phase sequences, namely basicSpawnPhases and
cloneSpawnPhases, and exposes each of the actions performed by these phases.

Finally, it modifies build.cc to replace runChild() with use of this new code.

* nix/libutil/util.cc (keepOnExec, waitForMessage): new functions.
* nix/libutil.util.hh (keepOnExec, waitForMessage): add prototypes.
* nix/libutil/spawn.cc, nix/libutil/spawn.hh: new files.
  (addPhaseAfter, addPhaseBefore, prependPhase, appendPhase, deletePhase,
  replacePhase, reset_writeToStderrAction, restoreAffinityAction,
  setsidAction, earlyIOSetupAction, dropAmbientCapabilitiesAction,
  chrootAction, chdirAction, closeMostFDsAction, setPersonalityAction,
  oomSacrificeAction, setIDsAction, restoreSIGPIPEAction, setupSuccessAction,
  execAction, getBasicSpawnPhases, usernsInitSyncAction, usernsSetIDsAction,
  initLoopbackAction, setHostAndDomainAction, makeFilesystemsPrivateAction,
  makeChrootSeparateFilesystemAction, statfsToMountFlags, bindMount,
  mountIntoChroot, mountIntoChrootAction, mountProcAction, mountDevshmAction,
  mountDevptsAction, pivotRootAction, lockMountsAction, getCloneSpawnPhases,
  runChildSetup, runChildSetupEntry, cloneChild, idMapToIdentityMap,
  unshareAndInitUserns): new procedures.
* nix/local.mk (libutil_a_SOURCES): add spawn.cc.
  (libutil_headers): add spawn.hh.
* nix/libstore/build.cc (restoreSIGPIPE, DerivationGoal::runChild,
  childEntry): removed procedures.
  (DerivationGoal::{dirsInChroot,env,readiness}): removed.
  (execBuilderOrBuiltin, execBuilderOrBuiltinAction,
  clearRootWritePermsAction): new procedures.
  (DerivationGoal::startBuilder): modified to use a CloneSpawnContext if
  chroot builds are available, otherwise a SpawnContext.

Change-Id: Ifd50110de077378ee151502eda62b99973d083bf

Change-Id: I76e10d3f928cc30566e1e6ca79077196972349f8

spawn.cc, util.cc, util.hh changes

Change-Id: I287320e63197cb4f65665ee5b3fdb3a0e125ebac
Signed-off-by: John Kehayias <john.kehayias@protonmail.com>
2025-06-24 10:07:56 -04:00
Reepca Russelstein 7173c2c0ca daemon: Implement ‘deletePath’ in terms of the *at functions.
deletePath needs to be able to operate securely in unfriendly environments,
where adversaries may be concurrently modifying the files being operated on.
For example, directories that we are currently recursing through may be
replaced with symbolic links.

We err on the side of early failure here: if a file or directory is
concurrently modified in a way that causes one of the system calls to fail, we
throw an exception immediately instead of trying to adapt to the change.

Note that we use fstat instead of fstatat for verifying the directory's
st_mode field because AT_EMPTY_PATH is linux-specific.

* nix/libutil/util.cc (_deletePathAt): new procedure.
(_deletePath): use it.

Change-Id: I7ccfe6f1f74dbab95617b24034494e0f63030582
Signed-off-by: Ludovic Courtès <ludo@gnu.org>
Signed-off-by: John Kehayias <john.kehayias@protonmail.com>
2025-06-24 10:07:55 -04:00
Congcong Kuo 3721eb1d6a daemon: Remove ‘foreach’ and ‘foreach_reverse’
‘foreach_reverse’ is not used anywhere

* nix/libutil/util.hh (foreach, foreach_reverse): Remove.
* nix/libstore/build.cc (addToWeakGoals): Use ‘std::none_of’ instead of macro ‘foreach’.
(Goal::waiteeDone, Goal::amDone, UserLock::acquire, rewriteHashes,
DerivationGoal::addWantedOutputs, DerivationGoal::haveDerivation,
DerivationGoal::outputsSubstituted, DerivationGoal::repairClosure,
DerivationGoal::inputsRealised, DerivationGoal::tryToBuild,
DerivationGoal::buildDone, DerivationGoal::tryBuildHook,
DerivationGoal::startBuilder, DerivationGoal::runChild,
parseReferenceSpecifiers, DerivationGoal::registerOutputs,
DerivationGoal::checkPathValidity, SubstitutionGoal::tryNext,
SubstitutionGoal::referencesValid, Worker::removeGoal,
Worker::childTerminated, Worker::run, Worker::waitForInput): Use range-based
‘for’ instead of macro ‘foreach’.
* nix/libstore/derivations.cc (writeDerivation, unparseDerivation,
hashDerivationModulo): Likewise.
* nix/libstore/gc.cc (addAdditionalRoots, LocalStore::deletePathRecursive,
LocalStore::canReachRoot, LocalStore::collectGarbage): Likewise.
* nix/libstore/globals.cc (Settings::pack): Likewise.
* nix/libstore/local-store.cc (checkDerivationOutputs, queryValidPaths,
querySubstitutablePaths, querySubstitutablePathInfos, registerValidPaths, verifyStore,
verifyPath): Likewise.
* nix/libstore/misc.cc (computeFSClosure, dfsVisit, topoSortPaths): Likewise.
* nix/libstore/optimise-store.cc (LocalStore::optimisePath_, LocalStore::optimiseStore): Likewise.
* nix/libstore/pathlocks.cc (PathLocks::lockPaths, PathLocks::~PathLocks): Likewise.
* nix/libstore/references.cc (search, scanForReferences): Likewise.
* nix/libstore/store-api.cc (checkStoreName, computeStorePathForText,
StoreAPI::makeValidityRegistration, showPaths, readStorePaths): Likewise.
* nix/libutil/serialise.cc (writeStrings): Likewise.
* nix/libutil/util.cc (concatStringsSep): Likewise.
* nix/nix-daemon/nix-daemon.cc (performOp): Likewise.

Signed-off-by: Ludovic Courtès <ludo@gnu.org>
2025-06-09 22:05:13 +02:00
Congcong Kuo 4b9d14378f daemon: Remove ‘singleton’ and replace ‘typedef’ with ‘using’ in ‘types.hh’
* nix/libutil/util.hh (singleton): Remove.
* nix/libstore/build.cc (DerivationGoal::startBuilder)
(SubstitutionGoal::tryNext, SubstitutionGoal::tryToRun)
(LocalStore::ensurePath, LocalStore::repairPath): Use normal
construction function instead of ‘singleton’.
* nix/libstore/local-store.cc (LocalStore::addToStoreFromDump)
(LocalStore::addTextToStore, LocalStore::importPath): Likewise.
* nix/nix-daemon/nix-daemon.cc (performOp): Likewise.

Change-Id: If0d929407c09482f3b506a1c51dfda70e29696dd
Signed-off-by: Ludovic Courtès <ludo@gnu.org>
2025-06-03 15:09:55 +02:00
Congcong Kuo 583e0688e3 daemon: Remove ‘AutoDeleteArray’.
* libutil/util.hh (AutoDeleteArray): Remove.
* libutil/util.cc (readString, readStrings): Use ‘std::vector’ instead
of ‘AutoDeleteArray’.
* libutil/serialise.cc (readFile): Likewise.

Change-Id: I45362998dbb8226874f66b77cd19f071f7bb2ab3
Signed-off-by: Ludovic Courtès <ludo@gnu.org>
2025-05-28 19:31:45 +02:00
Ludovic Courtès 7bad04fac0 daemon: Close the read end of the logging pipe.
* nix/libutil/util.cc (commonChildInit): Close ‘logPipe.readSide’.

Reported-by: Reepca Russelstein <reepca@russelstein.xyz>
Change-Id: Ia9e48d1afb85d7af52770e016f2b6832792044dd
2025-03-26 17:57:43 +01:00
Ludovic Courtès f03e6eff2f daemon: Use ‘close_range’ where available.
* nix/libutil/util.cc (closeMostFDs) [HAVE_CLOSE_RANGE]: Use
‘close_range’ when ‘exceptions’ is empty.
* config-daemon.ac: Check for <linux/close_range.h> and the
‘close_range’ symbol.

Change-Id: I12fa3bde58b003fcce5ea5a1fee1dcf9a92c0359
2025-03-26 17:57:42 +01:00
Ludovic Courtès 7757fdd491 daemon: Loop over ‘copy_file_range’ upon short writes.
Fixes <https://issues.guix.gnu.org/70877>.

* nix/libutil/util.cc (copyFile): Loop over ‘copy_file_range’ instead of
throwing upon short write.

Reported-by: Ricardo Wurmus <rekado@elephly.net>
Change-Id: Id7b8a65ea59006c2d91bc23732309a68665b9ca0
2024-05-13 16:31:34 +02:00
Ludovic Courtès ff1251de0b daemon: Address shortcoming in previous security fix for CVE-2024-27297.
This is a followup to 8f4ffb3fae.

Commit 8f4ffb3fae fell short in two
ways: (1) it didn’t have any effet for fixed-output derivations
performed in a chroot, which is the case for all of them except those
using “builtin:download” and “builtin:git-download”, and (2) it did not
preserve ownership when copying, leading to “suspicious ownership or
permission […] rejecting this build output” errors.

* nix/libstore/build.cc (DerivationGoal::buildDone): Account for
‘chrootRootDir’ when copying ‘drv.outputs’.
* nix/libutil/util.cc (copyFileRecursively): Add ‘fchown’ and ‘fchownat’
calls to preserve file ownership; this is necessary for chrooted
fixed-output derivation builds.
* nix/libutil/util.hh: Update comment.

Change-Id: Ib59f040e98fed59d1af81d724b874b592cbef156
2024-03-12 14:07:28 +01:00
Ludovic Courtès 8f4ffb3fae daemon: Protect against FD escape when building fixed-output derivations (CVE-2024-27297).
This fixes a security issue (CVE-2024-27297) whereby a fixed-output
derivation build process could open a writable file descriptor to its
output, send it to some outside process for instance over an abstract
AF_UNIX socket, which would then allow said process to modify the file
in the store after it has been marked as “valid”.

Vulnerability discovered by puck <https://github.com/puckipedia>.

Nix security advisory:
https://github.com/NixOS/nix/security/advisories/GHSA-2ffj-w4mj-pg37

Nix fix:
https://github.com/NixOS/nix/commit/244f3eee0bbc7f11e9b383a15ed7368e2c4becc9

* nix/libutil/util.cc (readDirectory): Add variants that take a DIR* and
a file descriptor.  Rewrite the ‘Path’ variant accordingly.
(copyFile, copyFileRecursively): New functions.
* nix/libutil/util.hh (copyFileRecursively): New declaration.
* nix/libstore/build.cc (DerivationGoal::buildDone): When ‘fixedOutput’
is true, call ‘copyFileRecursively’ followed by ‘rename’ on each output.

Change-Id: I7952d41093eed26e123e38c14a4c1424be1ce1c4

Reported-by: Picnoir <picnoir@alternativebit.fr>, Théophane Hufschmitt <theophane.hufschmitt@tweag.io>
Change-Id: Idb5f2757f35af86b032a9851cecb19b70227bd88
2024-03-11 22:12:34 +01:00
Ludovic Courtès 407175a1d0 daemon: Improve error message for wrong hash sizes.
* nix/libutil/hash.cc (parseHash): Show the hash algorithm name and
expected size in the error message.
* tests/derivations.scm ("fixed-output derivation, invalid hash size"):
New test.
2023-01-09 17:40:54 +01:00
Ludovic Courtès 2d4d26769d daemon: Make "opening file" error messages distinguishable.
* nix/libstore/build.cc (DerivationGoal::openLogFile): Customize
"opening file" error message.
* nix/libutil/hash.cc (hashFile): Likewise.
* nix/libutil/util.cc (readFile, writeFile): Likewise.
2022-12-18 01:16:47 +01:00
Ludovic Courtès 24224530d1 daemon: Micro-optimize 'deletePath'.
'remove' calls 'unlink' first and falls back to 'rmdir' upon EISDIR.
This change gets rid of the 'unlink' call for every directory being
removed.

* nix/libutil/util.cc (_deletePath): Call 'unlink' or 'rmdir' depending
on 'st.st_mode', rather than call 'remove'.
2021-11-19 23:04:19 +01:00
Ludovic Courtès 2e308238ad daemon: 'Agent' constructor takes a list of environment variables.
* nix/libutil/util.hh (struct Agent)[Agent]: Add 'env' parameter.
* nix/libutil/util.cc (Agent::Agent): Honor it.
2020-12-08 22:30:07 +01:00
Ludovic Courtès ee9dff34f9 daemon: Move 'Agent' to libutil.
* nix/libstore/build.cc (DerivationGoal::tryBuildHook): Add "offload" to
'args' and pass settings.guixProgram as the first argument to
Agent::Agent.
(pathNullDevice, commonChildInit, Agent, Agent::Agent)
(Agent::~Agent): Move to...
* nix/libutil/util.cc: ... here.
* nix/libutil/util.hh (struct Agent, commonChildInit): New
declarations.
2020-09-14 15:42:55 +02:00
Ludovic Courtès 8e6c1415d8 daemon: Recognize SHA3 and BLAKE2s.
* nix/libutil/hash.hh (HashType): Add htSHA3_256, htSHA3_512, and
htBLAKE2s_256.
* nix/libutil/hash.cc (parseHashType, printHashType): Recognize them.
* tests/store.scm ("add-to-store"): Test these algorithms.
2020-06-27 23:42:20 +02:00
Ludovic Courtès 8dc6c38785 daemon: Remove OpenSSL hash compatibility wrappers.
* nix/libutil/hash.cc (struct Ctx): Copy from gcrypt-hash.hh.
(start, update, finish): Use gcrypt functions directly instead of
OpenSSL-like wrappers.
* nix/libutil/gcrypt-hash.cc, nix/libutil/gcrypt-hash.hh,
nix/libutil/md5.h, nix/libutil/sha1.h, nix/libutil/sha256.h,
nix/libutil/sha512.h: Remove.
* nix/local.mk (libutil_a_SOURCES, libutil_headers): Adjust
accordingly.
2020-06-27 23:42:20 +02:00
Ludovic Courtès 3fb6b8f304 daemon: Map directly to gcrypt hash functions.
* nix/libutil/hash.hh (HashType): Map directly to GCRY_MD_ values.
(md5HashSize, sha1HashSize, sha256HashSize, sha512HashSize): Remove.
* nix/libutil/hash.cc (Hash::Hash): Use 'gcry_md_get_algo_dlen'.
2020-06-27 23:42:20 +02:00
Christopher Baines cd43b55da7 nix: Tweak .gitignore files.
Remove .gitignore entries where they match source files that are tracked in
Git.  This is relevant to me at least, as some code searching tools use
.gitignore files and will ignore matched files.
2020-06-24 19:55:22 +01:00
Manolis RagkousisandJan Nieuwenhuizen d0ed201e0a daemon: Avoid kill -1 bug on the Hurd.
This allows for native builds on the Hurd, doing

    sudo ./pre-inst-env guix-daemon --disable-chroot --build-users-group=guixbuild &
    ./pre-inst-env guix build hello

* nix/libutil/util.cc (killUser)[__GNU__]: Avoid kill -1 bug; kill only
current process and ignore SIGKILL status in parent.

Co-authored-by: Jan Nieuwenhuizen <janneke@gnu.org>
2020-03-26 12:59:33 +01:00
Ludovic Courtès 7033c7692c daemon: Account for deleted store files when deduplication is on.
Previously, a store item that is a regular file would not be accounted
for in the 'bytesFreed' value computed by 'deletePath' because its
'st_nlink' count would always be >= 2.  This commit fixes that.

* nix/libutil/util.hh (deletePath): Add optional 'linkThreshold' argument.
* nix/libutil/util.cc (_deletePath): Add 'linkThreshold' argument and
honor it.  Pass it down in recursive call.
(deletePath): Add 'linkThreshold' and honor it.
* nix/libstore/gc.cc (LocalStore::deleteGarbage): Pass 'linkThreshold'
argument to 'deletePath', with a value of 2 when PATH is a store item
and deduplication is on.
2020-01-12 00:19:09 +01:00
Ludovic Courtès b6b014bf42 daemon: 'pathExists' uses 'statx' when available.
* nix/libutil/util.cc (pathExists) [HAVE_STATX]: New code.
2019-11-29 15:54:20 +01:00
Ludovic Courtès fbecb5cddb daemon: 'deletePath' uses 'statx' when available.
* nix/libutil/util.cc (_deletePath) [HAVE_STATX]: Use 'statx'.
2019-11-27 00:03:03 +01:00
Eelco Dolstra 546a709f20 daemon: Don't reply on 'st_blocks'.
Ported by Ludovic Courtès <ludo@gnu.org>
from <https://github.com/NixOS/nix/commit/a2c4fcd5e9782dc8d2998773380c7171ee53b813>.

* nix/libstore/gc.cc (LocalStore::removeUnusedLinks): Use 'st.st_size'
instead of 'st.st_blocks * 512'.
* nix/libutil/util.cc (_deletePath): Likewise.
2019-08-30 01:33:45 +02:00
Ludovic Courtès 8327e73396 daemon: Use unbranded phrases in comments and messages.
* nix/libstore/build.cc, nix/libstore/globals.cc,
nix/libstore/gc.cc, nix/libstore/local-store.cc,
nix/libstore/optimise-store.cc, nix/libstore/store-api.cc,
nix/libutil/archive.cc, nix/nix-daemon/nix-daemon.cc: Replace "Nix
store" by "store", and "Nix daemon" by "build daemon".
2018-12-16 16:28:07 +01:00
Ludovic Courtès 7eca789201 daemon: Remove "case hack" for nars.
This code has never been of any use in Guix.

* nix/libutil/archive.cc (useCaseHack): Remove.
(parse): Keep only the alternate branch in "if (useCaseHack)".
2018-10-02 23:08:30 +02:00
Andy Wingo ed464f48ef nix: Remove unused function.
* nix/libutil/util.hh:
* nix/libutil/util.cc (decodeOctalEscaped): Remove unused and buggy function.
2017-08-18 11:31:28 +02:00
Ludovic Courtès 75abbd0cbe daemon: Remove unused XML output code.
* nix/local.mk (libutil_a_SOURCES): Remove libutil/xml-writer.cc.
(libutil_headers): Remove libutil/xml-writer.hh.
* nix/libutil/xml-writer.hh, nix/libutil/xml-writer.cc: Remove.
2016-05-31 18:22:14 +02:00
Eelco Dolstra 14fb686a21 daemon: Fix namespace issue. 2015-12-02 19:37:59 +02:00
Eelco DolstraandLudovic Courtès 29d3242e5c daemon: Support SHA-512 hashes.
Fixes #679.

Note: on x86_64, SHA-512 is considerably faster than SHA-256 (198 MB/s
versus 131 MB/s).

Co-authored-by: Ludovic Courtès <ludo@gnu.org>
2015-12-02 19:37:59 +02:00
Eelco Dolstra 79aa1a8305 daemon: int2String -> std::to_string. 2015-12-02 19:37:59 +02:00