libstore: don't default build-dir to temp-dir

if a build directory is accessible to other users it is possible to
smuggle data in and out of build directories. usually this ins only
a build purity problem, but in combination with other issues it can
be used to break out of a build sandbox. to prevent this we default
to using a subdirectory of nixStateDir (which is more restrictive).

Fixes CVE-2025-52991.

Change-Id: Iacfc9b50534de158618c815f9fb99d7dae1be4d0
This commit is contained in:
eldritch horrors
2025-06-24 10:50:03 +00:00
committed by Raito Bezarius
parent 959f6cb084
commit 469cb4218d
9 changed files with 31 additions and 31 deletions
+12
View File
@@ -0,0 +1,12 @@
---
synopsis: "`build-dir` no longer defaults to `temp-dir`"
cls: [3443]
category: "Fixes"
credits: [horrors]
---
The directory in which temporary build directories are created no longer defaults
to the value of the `temp-dir` setting to avoid builders making their directories
world-accessible. This behavior has been used to escape the build sandbox and can
cause build impurities even when not used maliciously. We now default to `builds`
in `NIX_STATE_DIR` (which is `/nix/var/nix/builds` in the default configuration).
+3 -1
View File
@@ -483,10 +483,12 @@ try {
});
}
createDirs(settings.buildDir.get());
/* Create a temporary directory where the build will take
place. */
tmpDir = createTempDir(
settings.buildDir.get().value_or(""),
settings.buildDir.get(),
"nix-build-" + std::string(drvPath.name()),
false,
false,
+14 -4
View File
@@ -1,14 +1,24 @@
---
name: build-dir
internalName: buildDir
settingType: PathsSetting<std::optional<Path>>
default: null
settingType: PathsSetting<Path>
defaultText: "`«nixStateDir»/builds`"
defaultExpr: nixStateDir + "/builds"
---
The directory on the host, in which derivations' temporary build directories are created.
If not set, Nix will use the [`temp-dir`](#conf-temp-dir) setting if set, otherwise the system temporary directory indicated by the `TMPDIR` environment variable.
Note that builds are often performed by the Nix daemon, so its `TMPDIR` is used, and not that of the Nix command line interface.
If not set, Lix will use the `builds` subdirectory of its configured state directory.
Lix will create this directory automatically with suitable permissions if it does not
exist, otherwise its permissions must allow all users to traverse the directory (i.e.
it must have `o+x` set, in unix parlance) for non-sandboxed builds to work correctly.
This is also the location where [`--keep-failed`](@docroot@/command-ref/opt-common.md#opt-keep-failed) leaves its files.
If Nix runs without sandbox, or if the platform does not support sandboxing with bind mounts (e.g. macOS), then the [`builder`](@docroot@/language/derivations.md#attr-builder)'s environment will contain this directory, instead of the virtual location [`sandbox-build-dir`](#conf-sandbox-build-dir).
> Important:
>
> `build-dir` must not be set to a world-writable directory. Placing temporary build
> directories in a world-writable place allows other users to access or modify build
> data that is currently in use. This alone is merely an impurity, but combined with
> another factor this has allowed malicious derivations to escape the build sandbox.
+2 -1
View File
@@ -1 +1,2 @@
d @localstatedir@/nix/daemon-socket 0755 root root - -
d @localstatedir@/nix/daemon-socket 0755 root root - -
d @localstatedir@/nix/builds 0755 root root 7d -
@@ -8,7 +8,6 @@ requireSandboxSupport
[[ $busybox =~ busybox ]] || skipTest "no busybox"
unset NIX_STORE_DIR
unset NIX_STATE_DIR
# We first build a dependency of the derivation we eventually want to
# build.
@@ -2,7 +2,6 @@ requireSandboxSupport
[[ $busybox =~ busybox ]] || skipTest "no busybox"
unset NIX_STORE_DIR
unset NIX_STATE_DIR
remoteDir=$TEST_ROOT/remote
-1
View File
@@ -3,7 +3,6 @@ requireSandboxSupport
# Avoid store dir being inside sandbox build-dir
unset NIX_STORE_DIR
unset NIX_STATE_DIR
function join_by { local d=$1; shift; echo -n "$1"; shift; printf "%s" "${@/#/$d}"; }
-21
View File
@@ -49,27 +49,6 @@ test_custom_build_dir() {
}
test_custom_build_dir
test_custom_temp_dir() {
# like test_custom_build_dir(), but uses the temp-dir setting instead
# build-dir inherits from temp-dir when build-dir is unset
local customTempDir="$TEST_ROOT/custom-temp-dir"
mkdir "$customTempDir"
nix-build check.nix -A failed --argstr checkBuildId $checkBuildId \
--no-out-link --keep-failed --option temp-dir "$customTempDir" 2> $TEST_ROOT/log || status=$?
[ "$status" = "100" ]
[[ 1 == "$(count "$customTempDir/nix-build-"*)" ]]
local buildDir="$customTempDir/nix-build-"*
grep $checkBuildId $buildDir/checkBuildId
# also check a separate code path that doesn't involve build-dir
# nix-shell uses temp-dir for its rcfile path
rcpath=$(NIX_BUILD_SHELL=$SHELL nix-shell check.nix -A deterministic --option temp-dir "$customTempDir" --run 'echo $0' 2> $TEST_ROOT/log)
# rcpath is <temp-dir>/nix-shell-*/rc
[[ $rcpath = "$customTempDir"/* ]]
}
test_custom_temp_dir
test_shell_preserves_tmpdir() {
# ensure commands that spawn interactive shells don't overwrite TMPDIR with temp-dir
local envTempDir=$TEST_ROOT/shell-temp-dir-env
-1
View File
@@ -10,7 +10,6 @@ unshare --mount --map-root-user bash <<EOF
# Avoid store dir being inside sandbox build-dir
unset NIX_STORE_DIR
unset NIX_STATE_DIR
setLocalStore () {
export NIX_REMOTE=\$TEST_ROOT/\$1