nix: fix macOS sandbox escape via builtin builders

fixes
https://github.com/NixOS/nix/security/advisories/GHSA-wf4c-57rh-9pjg
This commit is contained in:
Jörg Thalheim 2024-10-23 19:16:55 +02:00
parent 30c9efeef0
commit 727b17fed0
37 changed files with 3952 additions and 0 deletions

View File

@ -16,6 +16,7 @@ let
atLeast210 = lib.versionAtLeast version "2.10pre";
atLeast213 = lib.versionAtLeast version "2.13pre";
atLeast214 = lib.versionAtLeast version "2.14pre";
atLeast218 = lib.versionAtLeast version "2.18pre";
atLeast219 = lib.versionAtLeast version "2.19pre";
atLeast220 = lib.versionAtLeast version "2.20pre";
atLeast221 = lib.versionAtLeast version "2.21pre";
@ -42,6 +43,7 @@ in
, callPackage
, coreutils
, curl
, darwin
, docbook_xsl_ns
, docbook5
, editline
@ -158,6 +160,8 @@ self = stdenv.mkDerivation {
libseccomp
] ++ lib.optionals withAWS [
aws-sdk-cpp
] ++ lib.optional (atLeast218 && stdenv.hostPlatform.isDarwin) [
darwin.apple_sdk.libs.sandbox
];

View File

@ -178,42 +178,85 @@ in lib.makeExtensible (self: ({
version = "2.18.8";
hash = "sha256-0rHRifdjzzxMh/im8pRx6XoY62irDTDUes+Pn0CR65I=";
self_attribute_name = "nix_2_18";
patches = [
./patches/2_18/0001-fix-Run-all-derivation-builders-inside-the-sandbox-o.patch
./patches/2_18/0002-local-derivation-goal-Print-sandbox-error-detail-on-.patch
./patches/2_18/0003-local-derivation-goal-Refactor.patch
./patches/2_18/0004-local-derivation-goal-Move-builder-preparation-to-no.patch
];
};
nix_2_19 = common {
version = "2.19.6";
hash = "sha256-XT5xiwOLgXf+TdyOjbJVOl992wu9mBO25WXHoyli/Tk=";
self_attribute_name = "nix_2_19";
patches = [
./patches/2_19/0001-fix-Run-all-derivation-builders-inside-the-sandbox-o.patch
./patches/2_19/0002-local-derivation-goal-Print-sandbox-error-detail-on-.patch
./patches/2_19/0003-local-derivation-goal-Refactor.patch
./patches/2_19/0004-local-derivation-goal-Move-builder-preparation-to-no.patch
];
};
nix_2_20 = common {
version = "2.20.8";
hash = "sha256-M2tkMtjKi8LDdNLsKi3IvD8oY/i3rtarjMpvhybS3WY=";
self_attribute_name = "nix_2_20";
patches = [
./patches/2_20/0001-fix-Run-all-derivation-builders-inside-the-sandbox-o.patch
./patches/2_20/0002-local-derivation-goal-Print-sandbox-error-detail-on-.patch
./patches/2_20/0003-local-derivation-goal-Refactor.patch
./patches/2_20/0004-local-derivation-goal-Move-builder-preparation-to-no.patch
];
};
nix_2_21 = common {
version = "2.21.4";
hash = "sha256-c6nVZ0pSrfhFX3eVKqayS+ioqyAGp3zG9ZPO5rkXFRQ=";
self_attribute_name = "nix_2_21";
patches = [
./patches/2_21/0001-fix-Run-all-derivation-builders-inside-the-sandbox-o.patch
./patches/2_21/0002-local-derivation-goal-Print-sandbox-error-detail-on-.patch
./patches/2_21/0003-local-derivation-goal-Refactor.patch
./patches/2_21/0004-local-derivation-goal-Move-builder-preparation-to-no.patch
];
};
nix_2_22 = common {
version = "2.22.3";
hash = "sha256-l04csH5rTWsK7eXPWVxJBUVRPMZXllFoSkYFTq/i8WU=";
self_attribute_name = "nix_2_22";
patches = [
./patches/2_22/0001-fix-Run-all-derivation-builders-inside-the-sandbox-o.patch
./patches/2_22/0002-local-derivation-goal-Print-sandbox-error-detail-on-.patch
./patches/2_22/0003-local-derivation-goal-Refactor.patch
./patches/2_22/0004-local-derivation-goal-Move-builder-preparation-to-no.patch
];
};
nix_2_23 = common {
version = "2.23.3";
hash = "sha256-lAoLGVIhRFrfgv7wcyduEkyc83QKrtsfsq4of+WrBeg=";
self_attribute_name = "nix_2_23";
patches = [
./patches/2_23/0001-fix-Run-all-derivation-builders-inside-the-sandbox-o.patch
./patches/2_23/0002-local-derivation-goal-Print-sandbox-error-detail-on-.patch
./patches/2_23/0003-local-derivation-goal-Refactor.patch
./patches/2_23/0004-local-derivation-goal-Move-builder-preparation-to-no.patch
];
};
nix_2_24 = (common {
version = "2.24.9";
hash = "sha256-OwJByTdCz1t91ysBqynK+ifszkoIGEXUn6HE2t82+c8=";
self_attribute_name = "nix_2_24";
patches = [
./patches/2_24/0001-fix-Run-all-derivation-builders-inside-the-sandbox-o.patch
./patches/2_24/0002-packaging-Add-darwin-lsandbox-in-meson.patch
./patches/2_24/0003-local-derivation-goal-Print-sandbox-error-detail-on-.patch
./patches/2_24/0004-local-derivation-goal-Refactor.patch
./patches/2_24/0005-local-derivation-goal-Move-builder-preparation-to-no.patch
];
}).override (lib.optionalAttrs (stdenv.hostPlatform.isDarwin && stdenv.hostPlatform.isx86_64) {
# Fix the following error with the default x86_64-darwin SDK:
#
@ -234,6 +277,14 @@ in lib.makeExtensible (self: ({
rev = "ca3fc1693b309ab6b8b0c09408a08d0055bf0363";
hash = "sha256-Hp7dkx7zfB9a4l5QusXUob0b1T2qdZ23LFo5dcp3xrU=";
};
patches = [
./patches/git/0001-Fix-meson-build-on-darwin.patch
./patches/git/0002-fix-Run-all-derivation-builders-inside-the-sandbox-o.patch
./patches/git/0003-packaging-Add-darwin-lsandbox-in-meson.patch
./patches/git/0004-local-derivation-goal-Print-sandbox-error-detail-on-.patch
./patches/git/0005-local-derivation-goal-Refactor.patch
./patches/git/0006-local-derivation-goal-Move-builder-preparation-to-no.patch
];
self_attribute_name = "git";
}).override (lib.optionalAttrs (stdenv.hostPlatform.isDarwin && stdenv.hostPlatform.isx86_64) {
# Fix the following error with the default x86_64-darwin SDK:

View File

@ -0,0 +1,315 @@
From 3c4bc6929eb13cf648c54931a28797bb1c289052 Mon Sep 17 00:00:00 2001
From: Puck Meerburg <puck@puckipedia.com>
Date: Fri, 1 Mar 2024 11:42:24 -0500
Subject: [PATCH 1/4] fix: Run all derivation builders inside the sandbox on
macOS
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
configure.ac | 6 +-
flake.nix | 1 +
src/libstore/build/local-derivation-goal.cc | 221 ++++++++++----------
3 files changed, 114 insertions(+), 114 deletions(-)
diff --git a/configure.ac b/configure.ac
index 4e50d0913..44852ad79 100644
--- a/configure.ac
+++ b/configure.ac
@@ -58,13 +58,17 @@ AC_CHECK_TOOL([AR], [ar])
AC_SYS_LARGEFILE
-# Solaris-specific stuff.
+# OS-specific stuff.
AC_STRUCT_DIRENT_D_TYPE
case "$host_os" in
solaris*)
# Solaris requires -lsocket -lnsl for network functions
LDFLAGS="-lsocket -lnsl $LDFLAGS"
;;
+ darwin*)
+ # Need to link to libsandbox.
+ LDFLAGS="-lsandbox $LDFLAGS"
+ ;;
esac
diff --git a/flake.nix b/flake.nix
index 6c9bef4d8..66ac1bfd8 100644
--- a/flake.nix
+++ b/flake.nix
@@ -173,6 +173,7 @@
boost
lowdown-nix
]
+ ++ lib.optionals stdenv.isDarwin [darwin.apple_sdk.libs.sandbox]
++ lib.optionals stdenv.isLinux [(libseccomp.overrideAttrs (_: rec {
version = "2.5.5";
src = fetchurl {
diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc
index 4d690beaf..fb83cfdc7 100644
--- a/src/libstore/build/local-derivation-goal.cc
+++ b/src/libstore/build/local-derivation-goal.cc
@@ -53,6 +53,10 @@
#if __APPLE__
#include <spawn.h>
#include <sys/sysctl.h>
+#include <sandbox.h>
+
+/* This definition is undocumented but depended upon by all major browsers. */
+extern "C" int sandbox_init_with_parameters(const char *profile, uint64_t flags, const char *const parameters[], char **errorbuf);
#endif
#include <pwd.h>
@@ -2034,141 +2038,132 @@ void LocalDerivationGoal::runChild()
std::string builder = "invalid";
- if (drv->isBuiltin()) {
- ;
- }
#if __APPLE__
- else {
- /* This has to appear before import statements. */
- std::string sandboxProfile = "(version 1)\n";
-
- if (useChroot) {
-
- /* Lots and lots and lots of file functions freak out if they can't stat their full ancestry */
- PathSet ancestry;
-
- /* We build the ancestry before adding all inputPaths to the store because we know they'll
- all have the same parents (the store), and there might be lots of inputs. This isn't
- particularly efficient... I doubt it'll be a bottleneck in practice */
- for (auto & i : dirsInChroot) {
- Path cur = i.first;
- while (cur.compare("/") != 0) {
- cur = dirOf(cur);
- ancestry.insert(cur);
- }
- }
+ /* This has to appear before import statements. */
+ std::string sandboxProfile = "(version 1)\n";
- /* And we want the store in there regardless of how empty dirsInChroot. We include the innermost
- path component this time, since it's typically /nix/store and we care about that. */
- Path cur = worker.store.storeDir;
+ if (useChroot) {
+
+ /* Lots and lots and lots of file functions freak out if they can't stat their full ancestry */
+ PathSet ancestry;
+
+ /* We build the ancestry before adding all inputPaths to the store because we know they'll
+ all have the same parents (the store), and there might be lots of inputs. This isn't
+ particularly efficient... I doubt it'll be a bottleneck in practice */
+ for (auto & i : dirsInChroot) {
+ Path cur = i.first;
while (cur.compare("/") != 0) {
- ancestry.insert(cur);
cur = dirOf(cur);
+ ancestry.insert(cur);
}
+ }
- /* Add all our input paths to the chroot */
- for (auto & i : inputPaths) {
- auto p = worker.store.printStorePath(i);
- dirsInChroot[p] = p;
- }
-
- /* Violations will go to the syslog if you set this. Unfortunately the destination does not appear to be configurable */
- if (settings.darwinLogSandboxViolations) {
- sandboxProfile += "(deny default)\n";
- } else {
- sandboxProfile += "(deny default (with no-log))\n";
- }
+ /* And we want the store in there regardless of how empty dirsInChroot. We include the innermost
+ path component this time, since it's typically /nix/store and we care about that. */
+ Path cur = worker.store.storeDir;
+ while (cur.compare("/") != 0) {
+ ancestry.insert(cur);
+ cur = dirOf(cur);
+ }
- sandboxProfile +=
- #include "sandbox-defaults.sb"
- ;
+ /* Add all our input paths to the chroot */
+ for (auto & i : inputPaths) {
+ auto p = worker.store.printStorePath(i);
+ dirsInChroot[p] = p;
+ }
- if (!derivationType->isSandboxed())
- sandboxProfile +=
- #include "sandbox-network.sb"
- ;
-
- /* Add the output paths we'll use at build-time to the chroot */
- sandboxProfile += "(allow file-read* file-write* process-exec\n";
- for (auto & [_, path] : scratchOutputs)
- sandboxProfile += fmt("\t(subpath \"%s\")\n", worker.store.printStorePath(path));
-
- sandboxProfile += ")\n";
-
- /* Our inputs (transitive dependencies and any impurities computed above)
-
- without file-write* allowed, access() incorrectly returns EPERM
- */
- sandboxProfile += "(allow file-read* file-write* process-exec\n";
- for (auto & i : dirsInChroot) {
- if (i.first != i.second.source)
- throw Error(
- "can't map '%1%' to '%2%': mismatched impure paths not supported on Darwin",
- i.first, i.second.source);
-
- std::string path = i.first;
- struct stat st;
- if (lstat(path.c_str(), &st)) {
- if (i.second.optional && errno == ENOENT)
- continue;
- throw SysError("getting attributes of path '%s", path);
- }
- if (S_ISDIR(st.st_mode))
- sandboxProfile += fmt("\t(subpath \"%s\")\n", path);
- else
- sandboxProfile += fmt("\t(literal \"%s\")\n", path);
- }
- sandboxProfile += ")\n";
+ /* Violations will go to the syslog if you set this. Unfortunately the destination does not appear to be configurable */
+ if (settings.darwinLogSandboxViolations) {
+ sandboxProfile += "(deny default)\n";
+ } else {
+ sandboxProfile += "(deny default (with no-log))\n";
+ }
- /* Allow file-read* on full directory hierarchy to self. Allows realpath() */
- sandboxProfile += "(allow file-read*\n";
- for (auto & i : ancestry) {
- sandboxProfile += fmt("\t(literal \"%s\")\n", i);
- }
- sandboxProfile += ")\n";
+ sandboxProfile +=
+ #include "sandbox-defaults.sb"
+ ;
- sandboxProfile += additionalSandboxProfile;
- } else
+ if (!derivationType->isSandboxed())
sandboxProfile +=
- #include "sandbox-minimal.sb"
+ #include "sandbox-network.sb"
;
- debug("Generated sandbox profile:");
- debug(sandboxProfile);
+ /* Add the output paths we'll use at build-time to the chroot */
+ sandboxProfile += "(allow file-read* file-write* process-exec\n";
+ for (auto & [_, path] : scratchOutputs)
+ sandboxProfile += fmt("\t(subpath \"%s\")\n", worker.store.printStorePath(path));
- Path sandboxFile = tmpDir + "/.sandbox.sb";
+ sandboxProfile += ")\n";
- writeFile(sandboxFile, sandboxProfile);
+ /* Our inputs (transitive dependencies and any impurities computed above)
- bool allowLocalNetworking = parsedDrv->getBoolAttr("__darwinAllowLocalNetworking");
+ without file-write* allowed, access() incorrectly returns EPERM
+ */
+ sandboxProfile += "(allow file-read* file-write* process-exec\n";
+ for (auto & i : dirsInChroot) {
+ if (i.first != i.second.source)
+ throw Error(
+ "can't map '%1%' to '%2%': mismatched impure paths not supported on Darwin",
+ i.first, i.second.source);
- /* The tmpDir in scope points at the temporary build directory for our derivation. Some packages try different mechanisms
- to find temporary directories, so we want to open up a broader place for them to dump their files, if needed. */
- Path globalTmpDir = canonPath(defaultTempDir(), true);
+ std::string path = i.first;
+ struct stat st;
+ if (lstat(path.c_str(), &st)) {
+ if (i.second.optional && errno == ENOENT)
+ continue;
+ throw SysError("getting attributes of path '%s", path);
+ }
+ if (S_ISDIR(st.st_mode))
+ sandboxProfile += fmt("\t(subpath \"%s\")\n", path);
+ else
+ sandboxProfile += fmt("\t(literal \"%s\")\n", path);
+ }
+ sandboxProfile += ")\n";
- /* They don't like trailing slashes on subpath directives */
- while (!globalTmpDir.empty() && globalTmpDir.back() == '/')
- globalTmpDir.pop_back();
+ /* Allow file-read* on full directory hierarchy to self. Allows realpath() */
+ sandboxProfile += "(allow file-read*\n";
+ for (auto & i : ancestry) {
+ sandboxProfile += fmt("\t(literal \"%s\")\n", i);
+ }
+ sandboxProfile += ")\n";
- if (getEnv("_NIX_TEST_NO_SANDBOX") != "1") {
- builder = "/usr/bin/sandbox-exec";
- args.push_back("sandbox-exec");
- args.push_back("-f");
- args.push_back(sandboxFile);
- args.push_back("-D");
- args.push_back("_GLOBAL_TMP_DIR=" + globalTmpDir);
- if (allowLocalNetworking) {
- args.push_back("-D");
- args.push_back(std::string("_ALLOW_LOCAL_NETWORKING=1"));
- }
- args.push_back(drv->builder);
- } else {
- builder = drv->builder;
- args.push_back(std::string(baseNameOf(drv->builder)));
+ sandboxProfile += additionalSandboxProfile;
+ } else
+ sandboxProfile +=
+ #include "sandbox-minimal.sb"
+ ;
+
+ debug("Generated sandbox profile:");
+ debug(sandboxProfile);
+
+ bool allowLocalNetworking = parsedDrv->getBoolAttr("__darwinAllowLocalNetworking");
+
+ /* The tmpDir in scope points at the temporary build directory for our derivation. Some packages try different mechanisms
+ to find temporary directories, so we want to open up a broader place for them to dump their files, if needed. */
+ Path globalTmpDir = canonPath(defaultTempDir(), true);
+
+ /* They don't like trailing slashes on subpath directives */
+ while (!globalTmpDir.empty() && globalTmpDir.back() == '/')
+ globalTmpDir.pop_back();
+
+ if (getEnv("_NIX_TEST_NO_SANDBOX") != "1") {
+ Strings sandboxArgs;
+ sandboxArgs.push_back("_GLOBAL_TMP_DIR");
+ sandboxArgs.push_back(globalTmpDir);
+ if (allowLocalNetworking) {
+ sandboxArgs.push_back("_ALLOW_LOCAL_NETWORKING");
+ sandboxArgs.push_back("1");
+ }
+ if (sandbox_init_with_parameters(sandboxProfile.c_str(), 0, stringsToCharPtrs(sandboxArgs).data(), NULL)) {
+ writeFull(STDERR_FILENO, "failed to configure sandbox\n");
+ _exit(1);
}
}
+
+ builder = drv->builder;
+ args.push_back(std::string(baseNameOf(drv->builder)));
#else
- else {
+ if (!drv->isBuiltin()) {
builder = drv->builder;
args.push_back(std::string(baseNameOf(drv->builder)));
}
--
2.46.1

View File

@ -0,0 +1,34 @@
From 4ac099d6ab4b6851aeb8b7a1e37f5794716d5138 Mon Sep 17 00:00:00 2001
From: Robert Hensing <robert@roberthensing.nl>
Date: Thu, 3 Oct 2024 12:44:12 +0200
Subject: [PATCH 2/4] local-derivation-goal: Print sandbox error detail on
darwin
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-Authored-By: Théophane Hufschmitt <theophane.hufschmitt@tweag.io>
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
src/libstore/build/local-derivation-goal.cc | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc
index fb83cfdc7..d41d1c1e5 100644
--- a/src/libstore/build/local-derivation-goal.cc
+++ b/src/libstore/build/local-derivation-goal.cc
@@ -2154,8 +2154,9 @@ void LocalDerivationGoal::runChild()
sandboxArgs.push_back("_ALLOW_LOCAL_NETWORKING");
sandboxArgs.push_back("1");
}
- if (sandbox_init_with_parameters(sandboxProfile.c_str(), 0, stringsToCharPtrs(sandboxArgs).data(), NULL)) {
- writeFull(STDERR_FILENO, "failed to configure sandbox\n");
+ char * sandbox_errbuf = nullptr;
+ if (sandbox_init_with_parameters(sandboxProfile.c_str(), 0, stringsToCharPtrs(sandboxArgs).data(), &sandbox_errbuf)) {
+ writeFull(STDERR_FILENO, fmt("failed to configure sandbox: %s\n", sandbox_errbuf ? sandbox_errbuf : "(null)"));
_exit(1);
}
}
--
2.46.1

View File

@ -0,0 +1,41 @@
From 6fe3a5e26def808b99856099d74aa3017ecf6d9d Mon Sep 17 00:00:00 2001
From: Robert Hensing <robert@roberthensing.nl>
Date: Thu, 3 Oct 2024 12:50:27 +0200
Subject: [PATCH 3/4] local-derivation-goal: Refactor
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This works because the `builder` and `args` variables are only used
in the non-builtin code path.
Co-Authored-By: Théophane Hufschmitt <theophane.hufschmitt@tweag.io>
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
src/libstore/build/local-derivation-goal.cc | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc
index d41d1c1e5..faecc403b 100644
--- a/src/libstore/build/local-derivation-goal.cc
+++ b/src/libstore/build/local-derivation-goal.cc
@@ -2160,15 +2160,12 @@ void LocalDerivationGoal::runChild()
_exit(1);
}
}
+#endif
- builder = drv->builder;
- args.push_back(std::string(baseNameOf(drv->builder)));
-#else
if (!drv->isBuiltin()) {
builder = drv->builder;
args.push_back(std::string(baseNameOf(drv->builder)));
}
-#endif
for (auto & i : drv->args)
args.push_back(rewriteStrings(i, inputRewrites));
--
2.46.1

View File

@ -0,0 +1,75 @@
From d219faa93badcfc8134c81ba0d2b821775eb947c Mon Sep 17 00:00:00 2001
From: Robert Hensing <robert@roberthensing.nl>
Date: Thu, 3 Oct 2024 12:57:00 +0200
Subject: [PATCH 4/4] local-derivation-goal: Move builder preparation to
non-builtin code path
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
src/libstore/build/local-derivation-goal.cc | 25 +++++++++------------
1 file changed, 10 insertions(+), 15 deletions(-)
diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc
index faecc403b..23d5d5e3f 100644
--- a/src/libstore/build/local-derivation-goal.cc
+++ b/src/libstore/build/local-derivation-goal.cc
@@ -2033,11 +2033,6 @@ void LocalDerivationGoal::runChild()
throw SysError("setuid failed");
}
- /* Fill in the arguments. */
- Strings args;
-
- std::string builder = "invalid";
-
#if __APPLE__
/* This has to appear before import statements. */
std::string sandboxProfile = "(version 1)\n";
@@ -2162,14 +2157,6 @@ void LocalDerivationGoal::runChild()
}
#endif
- if (!drv->isBuiltin()) {
- builder = drv->builder;
- args.push_back(std::string(baseNameOf(drv->builder)));
- }
-
- for (auto & i : drv->args)
- args.push_back(rewriteStrings(i, inputRewrites));
-
/* Indicate that we managed to set up the build environment. */
writeFull(STDERR_FILENO, std::string("\2\n"));
@@ -2199,6 +2186,14 @@ void LocalDerivationGoal::runChild()
}
}
+ // Now builder is not builtin
+
+ Strings args;
+ args.push_back(std::string(baseNameOf(drv->builder)));
+
+ for (auto & i : drv->args)
+ args.push_back(rewriteStrings(i, inputRewrites));
+
#if __APPLE__
posix_spawnattr_t attrp;
@@ -2220,9 +2215,9 @@ void LocalDerivationGoal::runChild()
posix_spawnattr_setbinpref_np(&attrp, 1, &cpu, NULL);
}
- posix_spawn(NULL, builder.c_str(), NULL, &attrp, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
+ posix_spawn(NULL, drv->builder.c_str(), NULL, &attrp, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
#else
- execve(builder.c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
+ execve(drv->builder.c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
#endif
throw SysError("executing '%1%'", drv->builder);
--
2.46.1

View File

@ -0,0 +1,312 @@
From 172734f47a8062285cec0055133efcc45df03e54 Mon Sep 17 00:00:00 2001
From: Puck Meerburg <puck@puckipedia.com>
Date: Fri, 1 Mar 2024 11:42:24 -0500
Subject: [PATCH 1/4] fix: Run all derivation builders inside the sandbox on
macOS
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
configure.ac | 6 +-
flake.nix | 1 +
src/libstore/build/local-derivation-goal.cc | 217 ++++++++++----------
3 files changed, 112 insertions(+), 112 deletions(-)
diff --git a/configure.ac b/configure.ac
index 281ba2c32..6d73804e2 100644
--- a/configure.ac
+++ b/configure.ac
@@ -58,13 +58,17 @@ AC_CHECK_TOOL([AR], [ar])
AC_SYS_LARGEFILE
-# Solaris-specific stuff.
+# OS-specific stuff.
AC_STRUCT_DIRENT_D_TYPE
case "$host_os" in
solaris*)
# Solaris requires -lsocket -lnsl for network functions
LDFLAGS="-lsocket -lnsl $LDFLAGS"
;;
+ darwin*)
+ # Need to link to libsandbox.
+ LDFLAGS="-lsandbox $LDFLAGS"
+ ;;
esac
diff --git a/flake.nix b/flake.nix
index 6deb09f22..98f9cc25f 100644
--- a/flake.nix
+++ b/flake.nix
@@ -202,6 +202,7 @@
libsodium
]
++ lib.optionals stdenv.isLinux [libseccomp]
+ ++ lib.optionals stdenv.isDarwin [darwin.apple_sdk.libs.sandbox]
++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid;
checkDeps = [
diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc
index b6c7666e5..effd9c613 100644
--- a/src/libstore/build/local-derivation-goal.cc
+++ b/src/libstore/build/local-derivation-goal.cc
@@ -55,6 +55,10 @@
#if __APPLE__
#include <spawn.h>
#include <sys/sysctl.h>
+#include <sandbox.h>
+
+/* This definition is undocumented but depended upon by all major browsers. */
+extern "C" int sandbox_init_with_parameters(const char *profile, uint64_t flags, const char *const parameters[], char **errorbuf);
#endif
#include <pwd.h>
@@ -2031,140 +2035,131 @@ void LocalDerivationGoal::runChild()
std::string builder = "invalid";
- if (drv->isBuiltin()) {
- ;
- }
#if __APPLE__
- else {
- /* This has to appear before import statements. */
- std::string sandboxProfile = "(version 1)\n";
-
- if (useChroot) {
-
- /* Lots and lots and lots of file functions freak out if they can't stat their full ancestry */
- PathSet ancestry;
-
- /* We build the ancestry before adding all inputPaths to the store because we know they'll
- all have the same parents (the store), and there might be lots of inputs. This isn't
- particularly efficient... I doubt it'll be a bottleneck in practice */
- for (auto & i : pathsInChroot) {
- Path cur = i.first;
- while (cur.compare("/") != 0) {
- cur = dirOf(cur);
- ancestry.insert(cur);
- }
- }
+ /* This has to appear before import statements. */
+ std::string sandboxProfile = "(version 1)\n";
- /* And we want the store in there regardless of how empty pathsInChroot. We include the innermost
- path component this time, since it's typically /nix/store and we care about that. */
- Path cur = worker.store.storeDir;
+ if (useChroot) {
+
+ /* Lots and lots and lots of file functions freak out if they can't stat their full ancestry */
+ PathSet ancestry;
+
+ /* We build the ancestry before adding all inputPaths to the store because we know they'll
+ all have the same parents (the store), and there might be lots of inputs. This isn't
+ particularly efficient... I doubt it'll be a bottleneck in practice */
+ for (auto & i : pathsInChroot) {
+ Path cur = i.first;
while (cur.compare("/") != 0) {
- ancestry.insert(cur);
cur = dirOf(cur);
+ ancestry.insert(cur);
}
+ }
- /* Add all our input paths to the chroot */
- for (auto & i : inputPaths) {
- auto p = worker.store.printStorePath(i);
- pathsInChroot[p] = p;
- }
+ /* And we want the store in there regardless of how empty pathsInChroot. We include the innermost
+ path component this time, since it's typically /nix/store and we care about that. */
+ Path cur = worker.store.storeDir;
+ while (cur.compare("/") != 0) {
+ ancestry.insert(cur);
+ cur = dirOf(cur);
+ }
- /* Violations will go to the syslog if you set this. Unfortunately the destination does not appear to be configurable */
- if (settings.darwinLogSandboxViolations) {
- sandboxProfile += "(deny default)\n";
- } else {
- sandboxProfile += "(deny default (with no-log))\n";
- }
+ /* Add all our input paths to the chroot */
+ for (auto & i : inputPaths) {
+ auto p = worker.store.printStorePath(i);
+ pathsInChroot[p] = p;
+ }
+
+ /* Violations will go to the syslog if you set this. Unfortunately the destination does not appear to be configurable */
+ if (settings.darwinLogSandboxViolations) {
+ sandboxProfile += "(deny default)\n";
+ } else {
+ sandboxProfile += "(deny default (with no-log))\n";
+ }
+ sandboxProfile +=
+ #include "sandbox-defaults.sb"
+ ;
+
+ if (!derivationType->isSandboxed())
sandboxProfile +=
- #include "sandbox-defaults.sb"
+ #include "sandbox-network.sb"
;
- if (!derivationType->isSandboxed())
- sandboxProfile +=
- #include "sandbox-network.sb"
- ;
-
- /* Add the output paths we'll use at build-time to the chroot */
- sandboxProfile += "(allow file-read* file-write* process-exec\n";
- for (auto & [_, path] : scratchOutputs)
- sandboxProfile += fmt("\t(subpath \"%s\")\n", worker.store.printStorePath(path));
-
- sandboxProfile += ")\n";
-
- /* Our inputs (transitive dependencies and any impurities computed above)
-
- without file-write* allowed, access() incorrectly returns EPERM
- */
- sandboxProfile += "(allow file-read* file-write* process-exec\n";
- for (auto & i : pathsInChroot) {
- if (i.first != i.second.source)
- throw Error(
- "can't map '%1%' to '%2%': mismatched impure paths not supported on Darwin",
- i.first, i.second.source);
-
- std::string path = i.first;
- struct stat st;
- if (lstat(path.c_str(), &st)) {
- if (i.second.optional && errno == ENOENT)
- continue;
- throw SysError("getting attributes of path '%s", path);
- }
- if (S_ISDIR(st.st_mode))
- sandboxProfile += fmt("\t(subpath \"%s\")\n", path);
- else
- sandboxProfile += fmt("\t(literal \"%s\")\n", path);
- }
- sandboxProfile += ")\n";
+ /* Add the output paths we'll use at build-time to the chroot */
+ sandboxProfile += "(allow file-read* file-write* process-exec\n";
+ for (auto & [_, path] : scratchOutputs)
+ sandboxProfile += fmt("\t(subpath \"%s\")\n", worker.store.printStorePath(path));
- /* Allow file-read* on full directory hierarchy to self. Allows realpath() */
- sandboxProfile += "(allow file-read*\n";
- for (auto & i : ancestry) {
- sandboxProfile += fmt("\t(literal \"%s\")\n", i);
- }
- sandboxProfile += ")\n";
+ sandboxProfile += ")\n";
- sandboxProfile += additionalSandboxProfile;
- } else
- sandboxProfile +=
- #include "sandbox-minimal.sb"
- ;
+ /* Our inputs (transitive dependencies and any impurities computed above)
+
+ without file-write* allowed, access() incorrectly returns EPERM
+ */
+ sandboxProfile += "(allow file-read* file-write* process-exec\n";
+ for (auto & i : pathsInChroot) {
+ if (i.first != i.second.source)
+ throw Error(
+ "can't map '%1%' to '%2%': mismatched impure paths not supported on Darwin",
+ i.first, i.second.source);
+
+ std::string path = i.first;
+ struct stat st;
+ if (lstat(path.c_str(), &st)) {
+ if (i.second.optional && errno == ENOENT)
+ continue;
+ throw SysError("getting attributes of path '%s", path);
+ }
+ if (S_ISDIR(st.st_mode))
+ sandboxProfile += fmt("\t(subpath \"%s\")\n", path);
+ else
+ sandboxProfile += fmt("\t(literal \"%s\")\n", path);
+ }
+ sandboxProfile += ")\n";
- debug("Generated sandbox profile:");
- debug(sandboxProfile);
+ /* Allow file-read* on full directory hierarchy to self. Allows realpath() */
+ sandboxProfile += "(allow file-read*\n";
+ for (auto & i : ancestry) {
+ sandboxProfile += fmt("\t(literal \"%s\")\n", i);
+ }
+ sandboxProfile += ")\n";
- Path sandboxFile = tmpDir + "/.sandbox.sb";
+ sandboxProfile += additionalSandboxProfile;
+ } else
+ sandboxProfile +=
+ #include "sandbox-minimal.sb"
+ ;
- writeFile(sandboxFile, sandboxProfile);
+ debug("Generated sandbox profile:");
+ debug(sandboxProfile);
- bool allowLocalNetworking = parsedDrv->getBoolAttr("__darwinAllowLocalNetworking");
+ bool allowLocalNetworking = parsedDrv->getBoolAttr("__darwinAllowLocalNetworking");
- /* The tmpDir in scope points at the temporary build directory for our derivation. Some packages try different mechanisms
- to find temporary directories, so we want to open up a broader place for them to dump their files, if needed. */
- Path globalTmpDir = canonPath(getEnvNonEmpty("TMPDIR").value_or("/tmp"), true);
+ /* The tmpDir in scope points at the temporary build directory for our derivation. Some packages try different mechanisms
+ to find temporary directories, so we want to open up a broader place for them to dump their files, if needed. */
+ Path globalTmpDir = canonPath(getEnvNonEmpty("TMPDIR").value_or("/tmp"), true);
- /* They don't like trailing slashes on subpath directives */
- if (globalTmpDir.back() == '/') globalTmpDir.pop_back();
+ /* They don't like trailing slashes on subpath directives */
+ if (globalTmpDir.back() == '/') globalTmpDir.pop_back();
- if (getEnv("_NIX_TEST_NO_SANDBOX") != "1") {
- builder = "/usr/bin/sandbox-exec";
- args.push_back("sandbox-exec");
- args.push_back("-f");
- args.push_back(sandboxFile);
- args.push_back("-D");
- args.push_back("_GLOBAL_TMP_DIR=" + globalTmpDir);
- if (allowLocalNetworking) {
- args.push_back("-D");
- args.push_back(std::string("_ALLOW_LOCAL_NETWORKING=1"));
- }
- args.push_back(drv->builder);
- } else {
- builder = drv->builder;
- args.push_back(std::string(baseNameOf(drv->builder)));
+ if (getEnv("_NIX_TEST_NO_SANDBOX") != "1") {
+ Strings sandboxArgs;
+ sandboxArgs.push_back("_GLOBAL_TMP_DIR");
+ sandboxArgs.push_back(globalTmpDir);
+ if (allowLocalNetworking) {
+ sandboxArgs.push_back("_ALLOW_LOCAL_NETWORKING");
+ sandboxArgs.push_back("1");
+ }
+ if (sandbox_init_with_parameters(sandboxProfile.c_str(), 0, stringsToCharPtrs(sandboxArgs).data(), NULL)) {
+ writeFull(STDERR_FILENO, "failed to configure sandbox\n");
+ _exit(1);
}
}
+
+ builder = drv->builder;
+ args.push_back(std::string(baseNameOf(drv->builder)));
#else
- else {
+ if (!drv->isBuiltin()) {
builder = drv->builder;
args.push_back(std::string(baseNameOf(drv->builder)));
}
--
2.46.1

View File

@ -0,0 +1,34 @@
From 4a5018019e969537fdba36314fe5c19fe91828af Mon Sep 17 00:00:00 2001
From: Robert Hensing <robert@roberthensing.nl>
Date: Thu, 3 Oct 2024 12:44:12 +0200
Subject: [PATCH 2/4] local-derivation-goal: Print sandbox error detail on
darwin
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-Authored-By: Théophane Hufschmitt <theophane.hufschmitt@tweag.io>
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
src/libstore/build/local-derivation-goal.cc | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc
index effd9c613..a67347b59 100644
--- a/src/libstore/build/local-derivation-goal.cc
+++ b/src/libstore/build/local-derivation-goal.cc
@@ -2150,8 +2150,9 @@ void LocalDerivationGoal::runChild()
sandboxArgs.push_back("_ALLOW_LOCAL_NETWORKING");
sandboxArgs.push_back("1");
}
- if (sandbox_init_with_parameters(sandboxProfile.c_str(), 0, stringsToCharPtrs(sandboxArgs).data(), NULL)) {
- writeFull(STDERR_FILENO, "failed to configure sandbox\n");
+ char * sandbox_errbuf = nullptr;
+ if (sandbox_init_with_parameters(sandboxProfile.c_str(), 0, stringsToCharPtrs(sandboxArgs).data(), &sandbox_errbuf)) {
+ writeFull(STDERR_FILENO, fmt("failed to configure sandbox: %s\n", sandbox_errbuf ? sandbox_errbuf : "(null)"));
_exit(1);
}
}
--
2.46.1

View File

@ -0,0 +1,41 @@
From f9e5b3b52323fdcac4e21bfec4d03bd66ea6a503 Mon Sep 17 00:00:00 2001
From: Robert Hensing <robert@roberthensing.nl>
Date: Thu, 3 Oct 2024 12:50:27 +0200
Subject: [PATCH 3/4] local-derivation-goal: Refactor
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This works because the `builder` and `args` variables are only used
in the non-builtin code path.
Co-Authored-By: Théophane Hufschmitt <theophane.hufschmitt@tweag.io>
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
src/libstore/build/local-derivation-goal.cc | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc
index a67347b59..eeb2635ee 100644
--- a/src/libstore/build/local-derivation-goal.cc
+++ b/src/libstore/build/local-derivation-goal.cc
@@ -2156,15 +2156,12 @@ void LocalDerivationGoal::runChild()
_exit(1);
}
}
+#endif
- builder = drv->builder;
- args.push_back(std::string(baseNameOf(drv->builder)));
-#else
if (!drv->isBuiltin()) {
builder = drv->builder;
args.push_back(std::string(baseNameOf(drv->builder)));
}
-#endif
for (auto & i : drv->args)
args.push_back(rewriteStrings(i, inputRewrites));
--
2.46.1

View File

@ -0,0 +1,75 @@
From 126a1fd3385175ac94ae4000a9798e0cafb3c168 Mon Sep 17 00:00:00 2001
From: Robert Hensing <robert@roberthensing.nl>
Date: Thu, 3 Oct 2024 12:57:00 +0200
Subject: [PATCH 4/4] local-derivation-goal: Move builder preparation to
non-builtin code path
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
src/libstore/build/local-derivation-goal.cc | 25 +++++++++------------
1 file changed, 10 insertions(+), 15 deletions(-)
diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc
index eeb2635ee..e29330f0e 100644
--- a/src/libstore/build/local-derivation-goal.cc
+++ b/src/libstore/build/local-derivation-goal.cc
@@ -2030,11 +2030,6 @@ void LocalDerivationGoal::runChild()
throw SysError("setuid failed");
}
- /* Fill in the arguments. */
- Strings args;
-
- std::string builder = "invalid";
-
#if __APPLE__
/* This has to appear before import statements. */
std::string sandboxProfile = "(version 1)\n";
@@ -2158,14 +2153,6 @@ void LocalDerivationGoal::runChild()
}
#endif
- if (!drv->isBuiltin()) {
- builder = drv->builder;
- args.push_back(std::string(baseNameOf(drv->builder)));
- }
-
- for (auto & i : drv->args)
- args.push_back(rewriteStrings(i, inputRewrites));
-
/* Indicate that we managed to set up the build environment. */
writeFull(STDERR_FILENO, std::string("\2\n"));
@@ -2195,6 +2182,14 @@ void LocalDerivationGoal::runChild()
}
}
+ // Now builder is not builtin
+
+ Strings args;
+ args.push_back(std::string(baseNameOf(drv->builder)));
+
+ for (auto & i : drv->args)
+ args.push_back(rewriteStrings(i, inputRewrites));
+
#if __APPLE__
posix_spawnattr_t attrp;
@@ -2216,9 +2211,9 @@ void LocalDerivationGoal::runChild()
posix_spawnattr_setbinpref_np(&attrp, 1, &cpu, NULL);
}
- posix_spawn(NULL, builder.c_str(), NULL, &attrp, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
+ posix_spawn(NULL, drv->builder.c_str(), NULL, &attrp, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
#else
- execve(builder.c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
+ execve(drv->builder.c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
#endif
throw SysError("executing '%1%'", drv->builder);
--
2.46.1

View File

@ -0,0 +1,320 @@
From aa54b01af503644a393e4e4055c4ce2a23ce9139 Mon Sep 17 00:00:00 2001
From: Puck Meerburg <puck@puckipedia.com>
Date: Fri, 1 Mar 2024 11:42:24 -0500
Subject: [PATCH 1/4] fix: Run all derivation builders inside the sandbox on
macOS
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
configure.ac | 6 +-
package.nix | 2 +
src/libstore/build/local-derivation-goal.cc | 217 ++++++++++----------
3 files changed, 113 insertions(+), 112 deletions(-)
diff --git a/configure.ac b/configure.ac
index 8c29c1e62..8c524fd93 100644
--- a/configure.ac
+++ b/configure.ac
@@ -58,13 +58,17 @@ AC_CHECK_TOOL([AR], [ar])
AC_SYS_LARGEFILE
-# Solaris-specific stuff.
+# OS-specific stuff.
AC_STRUCT_DIRENT_D_TYPE
case "$host_os" in
solaris*)
# Solaris requires -lsocket -lnsl for network functions
LDFLAGS="-lsocket -lnsl $LDFLAGS"
;;
+ darwin*)
+ # Need to link to libsandbox.
+ LDFLAGS="-lsandbox $LDFLAGS"
+ ;;
esac
diff --git a/package.nix b/package.nix
index d1d14d10e..40283ffcf 100644
--- a/package.nix
+++ b/package.nix
@@ -24,6 +24,7 @@
, libgit2
, libseccomp
, libsodium
+, darwin
, lowdown
, mdbook
, mdbook-linkcheck
@@ -233,6 +234,7 @@ in {
gtest
rapidcheck
] ++ lib.optional stdenv.isLinux libseccomp
+ ++ lib.optional stdenv.hostPlatform.isDarwin darwin.apple_sdk.libs.sandbox
++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid
# There have been issues building these dependencies
++ lib.optional (stdenv.hostPlatform == stdenv.buildPlatform && (stdenv.isLinux || stdenv.isDarwin))
diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc
index b8228bc11..9ab676429 100644
--- a/src/libstore/build/local-derivation-goal.cc
+++ b/src/libstore/build/local-derivation-goal.cc
@@ -57,6 +57,10 @@
#if __APPLE__
#include <spawn.h>
#include <sys/sysctl.h>
+#include <sandbox.h>
+
+/* This definition is undocumented but depended upon by all major browsers. */
+extern "C" int sandbox_init_with_parameters(const char *profile, uint64_t flags, const char *const parameters[], char **errorbuf);
#endif
#include <pwd.h>
@@ -2023,140 +2027,131 @@ void LocalDerivationGoal::runChild()
std::string builder = "invalid";
- if (drv->isBuiltin()) {
- ;
- }
#if __APPLE__
- else {
- /* This has to appear before import statements. */
- std::string sandboxProfile = "(version 1)\n";
-
- if (useChroot) {
-
- /* Lots and lots and lots of file functions freak out if they can't stat their full ancestry */
- PathSet ancestry;
-
- /* We build the ancestry before adding all inputPaths to the store because we know they'll
- all have the same parents (the store), and there might be lots of inputs. This isn't
- particularly efficient... I doubt it'll be a bottleneck in practice */
- for (auto & i : pathsInChroot) {
- Path cur = i.first;
- while (cur.compare("/") != 0) {
- cur = dirOf(cur);
- ancestry.insert(cur);
- }
- }
+ /* This has to appear before import statements. */
+ std::string sandboxProfile = "(version 1)\n";
- /* And we want the store in there regardless of how empty pathsInChroot. We include the innermost
- path component this time, since it's typically /nix/store and we care about that. */
- Path cur = worker.store.storeDir;
+ if (useChroot) {
+
+ /* Lots and lots and lots of file functions freak out if they can't stat their full ancestry */
+ PathSet ancestry;
+
+ /* We build the ancestry before adding all inputPaths to the store because we know they'll
+ all have the same parents (the store), and there might be lots of inputs. This isn't
+ particularly efficient... I doubt it'll be a bottleneck in practice */
+ for (auto & i : pathsInChroot) {
+ Path cur = i.first;
while (cur.compare("/") != 0) {
- ancestry.insert(cur);
cur = dirOf(cur);
+ ancestry.insert(cur);
}
+ }
- /* Add all our input paths to the chroot */
- for (auto & i : inputPaths) {
- auto p = worker.store.printStorePath(i);
- pathsInChroot[p] = p;
- }
+ /* And we want the store in there regardless of how empty pathsInChroot. We include the innermost
+ path component this time, since it's typically /nix/store and we care about that. */
+ Path cur = worker.store.storeDir;
+ while (cur.compare("/") != 0) {
+ ancestry.insert(cur);
+ cur = dirOf(cur);
+ }
- /* Violations will go to the syslog if you set this. Unfortunately the destination does not appear to be configurable */
- if (settings.darwinLogSandboxViolations) {
- sandboxProfile += "(deny default)\n";
- } else {
- sandboxProfile += "(deny default (with no-log))\n";
- }
+ /* Add all our input paths to the chroot */
+ for (auto & i : inputPaths) {
+ auto p = worker.store.printStorePath(i);
+ pathsInChroot[p] = p;
+ }
+
+ /* Violations will go to the syslog if you set this. Unfortunately the destination does not appear to be configurable */
+ if (settings.darwinLogSandboxViolations) {
+ sandboxProfile += "(deny default)\n";
+ } else {
+ sandboxProfile += "(deny default (with no-log))\n";
+ }
+ sandboxProfile +=
+ #include "sandbox-defaults.sb"
+ ;
+
+ if (!derivationType->isSandboxed())
sandboxProfile +=
- #include "sandbox-defaults.sb"
+ #include "sandbox-network.sb"
;
- if (!derivationType->isSandboxed())
- sandboxProfile +=
- #include "sandbox-network.sb"
- ;
-
- /* Add the output paths we'll use at build-time to the chroot */
- sandboxProfile += "(allow file-read* file-write* process-exec\n";
- for (auto & [_, path] : scratchOutputs)
- sandboxProfile += fmt("\t(subpath \"%s\")\n", worker.store.printStorePath(path));
-
- sandboxProfile += ")\n";
-
- /* Our inputs (transitive dependencies and any impurities computed above)
-
- without file-write* allowed, access() incorrectly returns EPERM
- */
- sandboxProfile += "(allow file-read* file-write* process-exec\n";
- for (auto & i : pathsInChroot) {
- if (i.first != i.second.source)
- throw Error(
- "can't map '%1%' to '%2%': mismatched impure paths not supported on Darwin",
- i.first, i.second.source);
-
- std::string path = i.first;
- struct stat st;
- if (lstat(path.c_str(), &st)) {
- if (i.second.optional && errno == ENOENT)
- continue;
- throw SysError("getting attributes of path '%s", path);
- }
- if (S_ISDIR(st.st_mode))
- sandboxProfile += fmt("\t(subpath \"%s\")\n", path);
- else
- sandboxProfile += fmt("\t(literal \"%s\")\n", path);
- }
- sandboxProfile += ")\n";
+ /* Add the output paths we'll use at build-time to the chroot */
+ sandboxProfile += "(allow file-read* file-write* process-exec\n";
+ for (auto & [_, path] : scratchOutputs)
+ sandboxProfile += fmt("\t(subpath \"%s\")\n", worker.store.printStorePath(path));
- /* Allow file-read* on full directory hierarchy to self. Allows realpath() */
- sandboxProfile += "(allow file-read*\n";
- for (auto & i : ancestry) {
- sandboxProfile += fmt("\t(literal \"%s\")\n", i);
- }
- sandboxProfile += ")\n";
+ sandboxProfile += ")\n";
- sandboxProfile += additionalSandboxProfile;
- } else
- sandboxProfile +=
- #include "sandbox-minimal.sb"
- ;
+ /* Our inputs (transitive dependencies and any impurities computed above)
+
+ without file-write* allowed, access() incorrectly returns EPERM
+ */
+ sandboxProfile += "(allow file-read* file-write* process-exec\n";
+ for (auto & i : pathsInChroot) {
+ if (i.first != i.second.source)
+ throw Error(
+ "can't map '%1%' to '%2%': mismatched impure paths not supported on Darwin",
+ i.first, i.second.source);
+
+ std::string path = i.first;
+ struct stat st;
+ if (lstat(path.c_str(), &st)) {
+ if (i.second.optional && errno == ENOENT)
+ continue;
+ throw SysError("getting attributes of path '%s", path);
+ }
+ if (S_ISDIR(st.st_mode))
+ sandboxProfile += fmt("\t(subpath \"%s\")\n", path);
+ else
+ sandboxProfile += fmt("\t(literal \"%s\")\n", path);
+ }
+ sandboxProfile += ")\n";
- debug("Generated sandbox profile:");
- debug(sandboxProfile);
+ /* Allow file-read* on full directory hierarchy to self. Allows realpath() */
+ sandboxProfile += "(allow file-read*\n";
+ for (auto & i : ancestry) {
+ sandboxProfile += fmt("\t(literal \"%s\")\n", i);
+ }
+ sandboxProfile += ")\n";
- Path sandboxFile = tmpDir + "/.sandbox.sb";
+ sandboxProfile += additionalSandboxProfile;
+ } else
+ sandboxProfile +=
+ #include "sandbox-minimal.sb"
+ ;
- writeFile(sandboxFile, sandboxProfile);
+ debug("Generated sandbox profile:");
+ debug(sandboxProfile);
- bool allowLocalNetworking = parsedDrv->getBoolAttr("__darwinAllowLocalNetworking");
+ bool allowLocalNetworking = parsedDrv->getBoolAttr("__darwinAllowLocalNetworking");
- /* The tmpDir in scope points at the temporary build directory for our derivation. Some packages try different mechanisms
- to find temporary directories, so we want to open up a broader place for them to dump their files, if needed. */
- Path globalTmpDir = canonPath(getEnvNonEmpty("TMPDIR").value_or("/tmp"), true);
+ /* The tmpDir in scope points at the temporary build directory for our derivation. Some packages try different mechanisms
+ to find temporary directories, so we want to open up a broader place for them to dump their files, if needed. */
+ Path globalTmpDir = canonPath(getEnvNonEmpty("TMPDIR").value_or("/tmp"), true);
- /* They don't like trailing slashes on subpath directives */
- if (globalTmpDir.back() == '/') globalTmpDir.pop_back();
+ /* They don't like trailing slashes on subpath directives */
+ if (globalTmpDir.back() == '/') globalTmpDir.pop_back();
- if (getEnv("_NIX_TEST_NO_SANDBOX") != "1") {
- builder = "/usr/bin/sandbox-exec";
- args.push_back("sandbox-exec");
- args.push_back("-f");
- args.push_back(sandboxFile);
- args.push_back("-D");
- args.push_back("_GLOBAL_TMP_DIR=" + globalTmpDir);
- if (allowLocalNetworking) {
- args.push_back("-D");
- args.push_back(std::string("_ALLOW_LOCAL_NETWORKING=1"));
- }
- args.push_back(drv->builder);
- } else {
- builder = drv->builder;
- args.push_back(std::string(baseNameOf(drv->builder)));
+ if (getEnv("_NIX_TEST_NO_SANDBOX") != "1") {
+ Strings sandboxArgs;
+ sandboxArgs.push_back("_GLOBAL_TMP_DIR");
+ sandboxArgs.push_back(globalTmpDir);
+ if (allowLocalNetworking) {
+ sandboxArgs.push_back("_ALLOW_LOCAL_NETWORKING");
+ sandboxArgs.push_back("1");
+ }
+ if (sandbox_init_with_parameters(sandboxProfile.c_str(), 0, stringsToCharPtrs(sandboxArgs).data(), NULL)) {
+ writeFull(STDERR_FILENO, "failed to configure sandbox\n");
+ _exit(1);
}
}
+
+ builder = drv->builder;
+ args.push_back(std::string(baseNameOf(drv->builder)));
#else
- else {
+ if (!drv->isBuiltin()) {
builder = drv->builder;
args.push_back(std::string(baseNameOf(drv->builder)));
}
--
2.46.1

View File

@ -0,0 +1,34 @@
From b78e489f79165457b59faa2270fd89769d0fc17d Mon Sep 17 00:00:00 2001
From: Robert Hensing <robert@roberthensing.nl>
Date: Thu, 3 Oct 2024 12:44:12 +0200
Subject: [PATCH 2/4] local-derivation-goal: Print sandbox error detail on
darwin
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-Authored-By: Théophane Hufschmitt <theophane.hufschmitt@tweag.io>
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
src/libstore/build/local-derivation-goal.cc | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc
index 9ab676429..8476e038e 100644
--- a/src/libstore/build/local-derivation-goal.cc
+++ b/src/libstore/build/local-derivation-goal.cc
@@ -2142,8 +2142,9 @@ void LocalDerivationGoal::runChild()
sandboxArgs.push_back("_ALLOW_LOCAL_NETWORKING");
sandboxArgs.push_back("1");
}
- if (sandbox_init_with_parameters(sandboxProfile.c_str(), 0, stringsToCharPtrs(sandboxArgs).data(), NULL)) {
- writeFull(STDERR_FILENO, "failed to configure sandbox\n");
+ char * sandbox_errbuf = nullptr;
+ if (sandbox_init_with_parameters(sandboxProfile.c_str(), 0, stringsToCharPtrs(sandboxArgs).data(), &sandbox_errbuf)) {
+ writeFull(STDERR_FILENO, fmt("failed to configure sandbox: %s\n", sandbox_errbuf ? sandbox_errbuf : "(null)"));
_exit(1);
}
}
--
2.46.1

View File

@ -0,0 +1,41 @@
From db6bcf3f7714929d5a21b655c5f8ccd2ddbdf7f2 Mon Sep 17 00:00:00 2001
From: Robert Hensing <robert@roberthensing.nl>
Date: Thu, 3 Oct 2024 12:50:27 +0200
Subject: [PATCH 3/4] local-derivation-goal: Refactor
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This works because the `builder` and `args` variables are only used
in the non-builtin code path.
Co-Authored-By: Théophane Hufschmitt <theophane.hufschmitt@tweag.io>
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
src/libstore/build/local-derivation-goal.cc | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc
index 8476e038e..12b67df69 100644
--- a/src/libstore/build/local-derivation-goal.cc
+++ b/src/libstore/build/local-derivation-goal.cc
@@ -2148,15 +2148,12 @@ void LocalDerivationGoal::runChild()
_exit(1);
}
}
+#endif
- builder = drv->builder;
- args.push_back(std::string(baseNameOf(drv->builder)));
-#else
if (!drv->isBuiltin()) {
builder = drv->builder;
args.push_back(std::string(baseNameOf(drv->builder)));
}
-#endif
for (auto & i : drv->args)
args.push_back(rewriteStrings(i, inputRewrites));
--
2.46.1

View File

@ -0,0 +1,75 @@
From 55be7deee1471e77e3ad408c5e23842df0d5bc28 Mon Sep 17 00:00:00 2001
From: Robert Hensing <robert@roberthensing.nl>
Date: Thu, 3 Oct 2024 12:57:00 +0200
Subject: [PATCH 4/4] local-derivation-goal: Move builder preparation to
non-builtin code path
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
src/libstore/build/local-derivation-goal.cc | 25 +++++++++------------
1 file changed, 10 insertions(+), 15 deletions(-)
diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc
index 12b67df69..ada86dbb8 100644
--- a/src/libstore/build/local-derivation-goal.cc
+++ b/src/libstore/build/local-derivation-goal.cc
@@ -2022,11 +2022,6 @@ void LocalDerivationGoal::runChild()
throw SysError("setuid failed");
}
- /* Fill in the arguments. */
- Strings args;
-
- std::string builder = "invalid";
-
#if __APPLE__
/* This has to appear before import statements. */
std::string sandboxProfile = "(version 1)\n";
@@ -2150,14 +2145,6 @@ void LocalDerivationGoal::runChild()
}
#endif
- if (!drv->isBuiltin()) {
- builder = drv->builder;
- args.push_back(std::string(baseNameOf(drv->builder)));
- }
-
- for (auto & i : drv->args)
- args.push_back(rewriteStrings(i, inputRewrites));
-
/* Indicate that we managed to set up the build environment. */
writeFull(STDERR_FILENO, std::string("\2\n"));
@@ -2187,6 +2174,14 @@ void LocalDerivationGoal::runChild()
}
}
+ // Now builder is not builtin
+
+ Strings args;
+ args.push_back(std::string(baseNameOf(drv->builder)));
+
+ for (auto & i : drv->args)
+ args.push_back(rewriteStrings(i, inputRewrites));
+
#if __APPLE__
posix_spawnattr_t attrp;
@@ -2208,9 +2203,9 @@ void LocalDerivationGoal::runChild()
posix_spawnattr_setbinpref_np(&attrp, 1, &cpu, NULL);
}
- posix_spawn(NULL, builder.c_str(), NULL, &attrp, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
+ posix_spawn(NULL, drv->builder.c_str(), NULL, &attrp, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
#else
- execve(builder.c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
+ execve(drv->builder.c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
#endif
throw SysError("executing '%1%'", drv->builder);
--
2.46.1

View File

@ -0,0 +1,320 @@
From ae8a38d29cc0fbd6394acd72fdaaa62b3798f698 Mon Sep 17 00:00:00 2001
From: Puck Meerburg <puck@puckipedia.com>
Date: Fri, 1 Mar 2024 11:42:24 -0500
Subject: [PATCH 1/4] fix: Run all derivation builders inside the sandbox on
macOS
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
configure.ac | 6 +-
package.nix | 2 +
src/libstore/build/local-derivation-goal.cc | 217 ++++++++++----------
3 files changed, 113 insertions(+), 112 deletions(-)
diff --git a/configure.ac b/configure.ac
index 676b145a5..f6fa35c81 100644
--- a/configure.ac
+++ b/configure.ac
@@ -62,13 +62,17 @@ AC_CHECK_TOOL([AR], [ar])
AC_SYS_LARGEFILE
-# Solaris-specific stuff.
+# OS-specific stuff.
AC_STRUCT_DIRENT_D_TYPE
case "$host_os" in
solaris*)
# Solaris requires -lsocket -lnsl for network functions
LDFLAGS="-lsocket -lnsl $LDFLAGS"
;;
+ darwin*)
+ # Need to link to libsandbox.
+ LDFLAGS="-lsandbox $LDFLAGS"
+ ;;
esac
diff --git a/package.nix b/package.nix
index 7d9a39771..de2e1aff1 100644
--- a/package.nix
+++ b/package.nix
@@ -25,6 +25,7 @@
, libseccomp
, libsodium
, man
+, darwin
, lowdown
, mdbook
, mdbook-linkcheck
@@ -239,6 +240,7 @@ in {
gtest
rapidcheck
] ++ lib.optional stdenv.isLinux libseccomp
+ ++ lib.optional stdenv.hostPlatform.isDarwin darwin.apple_sdk.libs.sandbox
++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid
# There have been issues building these dependencies
++ lib.optional (stdenv.hostPlatform == stdenv.buildPlatform && (stdenv.isLinux || stdenv.isDarwin))
diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc
index 710304b67..c73b30b80 100644
--- a/src/libstore/build/local-derivation-goal.cc
+++ b/src/libstore/build/local-derivation-goal.cc
@@ -58,6 +58,10 @@
#if __APPLE__
#include <spawn.h>
#include <sys/sysctl.h>
+#include <sandbox.h>
+
+/* This definition is undocumented but depended upon by all major browsers. */
+extern "C" int sandbox_init_with_parameters(const char *profile, uint64_t flags, const char *const parameters[], char **errorbuf);
#endif
#include <pwd.h>
@@ -2018,140 +2022,131 @@ void LocalDerivationGoal::runChild()
std::string builder = "invalid";
- if (drv->isBuiltin()) {
- ;
- }
#if __APPLE__
- else {
- /* This has to appear before import statements. */
- std::string sandboxProfile = "(version 1)\n";
-
- if (useChroot) {
-
- /* Lots and lots and lots of file functions freak out if they can't stat their full ancestry */
- PathSet ancestry;
-
- /* We build the ancestry before adding all inputPaths to the store because we know they'll
- all have the same parents (the store), and there might be lots of inputs. This isn't
- particularly efficient... I doubt it'll be a bottleneck in practice */
- for (auto & i : pathsInChroot) {
- Path cur = i.first;
- while (cur.compare("/") != 0) {
- cur = dirOf(cur);
- ancestry.insert(cur);
- }
- }
+ /* This has to appear before import statements. */
+ std::string sandboxProfile = "(version 1)\n";
- /* And we want the store in there regardless of how empty pathsInChroot. We include the innermost
- path component this time, since it's typically /nix/store and we care about that. */
- Path cur = worker.store.storeDir;
+ if (useChroot) {
+
+ /* Lots and lots and lots of file functions freak out if they can't stat their full ancestry */
+ PathSet ancestry;
+
+ /* We build the ancestry before adding all inputPaths to the store because we know they'll
+ all have the same parents (the store), and there might be lots of inputs. This isn't
+ particularly efficient... I doubt it'll be a bottleneck in practice */
+ for (auto & i : pathsInChroot) {
+ Path cur = i.first;
while (cur.compare("/") != 0) {
- ancestry.insert(cur);
cur = dirOf(cur);
+ ancestry.insert(cur);
}
+ }
- /* Add all our input paths to the chroot */
- for (auto & i : inputPaths) {
- auto p = worker.store.printStorePath(i);
- pathsInChroot[p] = p;
- }
+ /* And we want the store in there regardless of how empty pathsInChroot. We include the innermost
+ path component this time, since it's typically /nix/store and we care about that. */
+ Path cur = worker.store.storeDir;
+ while (cur.compare("/") != 0) {
+ ancestry.insert(cur);
+ cur = dirOf(cur);
+ }
- /* Violations will go to the syslog if you set this. Unfortunately the destination does not appear to be configurable */
- if (settings.darwinLogSandboxViolations) {
- sandboxProfile += "(deny default)\n";
- } else {
- sandboxProfile += "(deny default (with no-log))\n";
- }
+ /* Add all our input paths to the chroot */
+ for (auto & i : inputPaths) {
+ auto p = worker.store.printStorePath(i);
+ pathsInChroot[p] = p;
+ }
+
+ /* Violations will go to the syslog if you set this. Unfortunately the destination does not appear to be configurable */
+ if (settings.darwinLogSandboxViolations) {
+ sandboxProfile += "(deny default)\n";
+ } else {
+ sandboxProfile += "(deny default (with no-log))\n";
+ }
+ sandboxProfile +=
+ #include "sandbox-defaults.sb"
+ ;
+
+ if (!derivationType->isSandboxed())
sandboxProfile +=
- #include "sandbox-defaults.sb"
+ #include "sandbox-network.sb"
;
- if (!derivationType->isSandboxed())
- sandboxProfile +=
- #include "sandbox-network.sb"
- ;
-
- /* Add the output paths we'll use at build-time to the chroot */
- sandboxProfile += "(allow file-read* file-write* process-exec\n";
- for (auto & [_, path] : scratchOutputs)
- sandboxProfile += fmt("\t(subpath \"%s\")\n", worker.store.printStorePath(path));
-
- sandboxProfile += ")\n";
-
- /* Our inputs (transitive dependencies and any impurities computed above)
-
- without file-write* allowed, access() incorrectly returns EPERM
- */
- sandboxProfile += "(allow file-read* file-write* process-exec\n";
- for (auto & i : pathsInChroot) {
- if (i.first != i.second.source)
- throw Error(
- "can't map '%1%' to '%2%': mismatched impure paths not supported on Darwin",
- i.first, i.second.source);
-
- std::string path = i.first;
- struct stat st;
- if (lstat(path.c_str(), &st)) {
- if (i.second.optional && errno == ENOENT)
- continue;
- throw SysError("getting attributes of path '%s", path);
- }
- if (S_ISDIR(st.st_mode))
- sandboxProfile += fmt("\t(subpath \"%s\")\n", path);
- else
- sandboxProfile += fmt("\t(literal \"%s\")\n", path);
- }
- sandboxProfile += ")\n";
+ /* Add the output paths we'll use at build-time to the chroot */
+ sandboxProfile += "(allow file-read* file-write* process-exec\n";
+ for (auto & [_, path] : scratchOutputs)
+ sandboxProfile += fmt("\t(subpath \"%s\")\n", worker.store.printStorePath(path));
- /* Allow file-read* on full directory hierarchy to self. Allows realpath() */
- sandboxProfile += "(allow file-read*\n";
- for (auto & i : ancestry) {
- sandboxProfile += fmt("\t(literal \"%s\")\n", i);
- }
- sandboxProfile += ")\n";
+ sandboxProfile += ")\n";
- sandboxProfile += additionalSandboxProfile;
- } else
- sandboxProfile +=
- #include "sandbox-minimal.sb"
- ;
+ /* Our inputs (transitive dependencies and any impurities computed above)
+
+ without file-write* allowed, access() incorrectly returns EPERM
+ */
+ sandboxProfile += "(allow file-read* file-write* process-exec\n";
+ for (auto & i : pathsInChroot) {
+ if (i.first != i.second.source)
+ throw Error(
+ "can't map '%1%' to '%2%': mismatched impure paths not supported on Darwin",
+ i.first, i.second.source);
+
+ std::string path = i.first;
+ struct stat st;
+ if (lstat(path.c_str(), &st)) {
+ if (i.second.optional && errno == ENOENT)
+ continue;
+ throw SysError("getting attributes of path '%s", path);
+ }
+ if (S_ISDIR(st.st_mode))
+ sandboxProfile += fmt("\t(subpath \"%s\")\n", path);
+ else
+ sandboxProfile += fmt("\t(literal \"%s\")\n", path);
+ }
+ sandboxProfile += ")\n";
- debug("Generated sandbox profile:");
- debug(sandboxProfile);
+ /* Allow file-read* on full directory hierarchy to self. Allows realpath() */
+ sandboxProfile += "(allow file-read*\n";
+ for (auto & i : ancestry) {
+ sandboxProfile += fmt("\t(literal \"%s\")\n", i);
+ }
+ sandboxProfile += ")\n";
- Path sandboxFile = tmpDir + "/.sandbox.sb";
+ sandboxProfile += additionalSandboxProfile;
+ } else
+ sandboxProfile +=
+ #include "sandbox-minimal.sb"
+ ;
- writeFile(sandboxFile, sandboxProfile);
+ debug("Generated sandbox profile:");
+ debug(sandboxProfile);
- bool allowLocalNetworking = parsedDrv->getBoolAttr("__darwinAllowLocalNetworking");
+ bool allowLocalNetworking = parsedDrv->getBoolAttr("__darwinAllowLocalNetworking");
- /* The tmpDir in scope points at the temporary build directory for our derivation. Some packages try different mechanisms
- to find temporary directories, so we want to open up a broader place for them to dump their files, if needed. */
- Path globalTmpDir = canonPath(getEnvNonEmpty("TMPDIR").value_or("/tmp"), true);
+ /* The tmpDir in scope points at the temporary build directory for our derivation. Some packages try different mechanisms
+ to find temporary directories, so we want to open up a broader place for them to dump their files, if needed. */
+ Path globalTmpDir = canonPath(getEnvNonEmpty("TMPDIR").value_or("/tmp"), true);
- /* They don't like trailing slashes on subpath directives */
- if (globalTmpDir.back() == '/') globalTmpDir.pop_back();
+ /* They don't like trailing slashes on subpath directives */
+ if (globalTmpDir.back() == '/') globalTmpDir.pop_back();
- if (getEnv("_NIX_TEST_NO_SANDBOX") != "1") {
- builder = "/usr/bin/sandbox-exec";
- args.push_back("sandbox-exec");
- args.push_back("-f");
- args.push_back(sandboxFile);
- args.push_back("-D");
- args.push_back("_GLOBAL_TMP_DIR=" + globalTmpDir);
- if (allowLocalNetworking) {
- args.push_back("-D");
- args.push_back(std::string("_ALLOW_LOCAL_NETWORKING=1"));
- }
- args.push_back(drv->builder);
- } else {
- builder = drv->builder;
- args.push_back(std::string(baseNameOf(drv->builder)));
+ if (getEnv("_NIX_TEST_NO_SANDBOX") != "1") {
+ Strings sandboxArgs;
+ sandboxArgs.push_back("_GLOBAL_TMP_DIR");
+ sandboxArgs.push_back(globalTmpDir);
+ if (allowLocalNetworking) {
+ sandboxArgs.push_back("_ALLOW_LOCAL_NETWORKING");
+ sandboxArgs.push_back("1");
+ }
+ if (sandbox_init_with_parameters(sandboxProfile.c_str(), 0, stringsToCharPtrs(sandboxArgs).data(), NULL)) {
+ writeFull(STDERR_FILENO, "failed to configure sandbox\n");
+ _exit(1);
}
}
+
+ builder = drv->builder;
+ args.push_back(std::string(baseNameOf(drv->builder)));
#else
- else {
+ if (!drv->isBuiltin()) {
builder = drv->builder;
args.push_back(std::string(baseNameOf(drv->builder)));
}
--
2.46.1

View File

@ -0,0 +1,34 @@
From b429e96583e2d005c77df8c82261022397f20648 Mon Sep 17 00:00:00 2001
From: Robert Hensing <robert@roberthensing.nl>
Date: Thu, 3 Oct 2024 12:44:12 +0200
Subject: [PATCH 2/4] local-derivation-goal: Print sandbox error detail on
darwin
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-Authored-By: Théophane Hufschmitt <theophane.hufschmitt@tweag.io>
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
src/libstore/build/local-derivation-goal.cc | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc
index c73b30b80..e6f4c397d 100644
--- a/src/libstore/build/local-derivation-goal.cc
+++ b/src/libstore/build/local-derivation-goal.cc
@@ -2137,8 +2137,9 @@ void LocalDerivationGoal::runChild()
sandboxArgs.push_back("_ALLOW_LOCAL_NETWORKING");
sandboxArgs.push_back("1");
}
- if (sandbox_init_with_parameters(sandboxProfile.c_str(), 0, stringsToCharPtrs(sandboxArgs).data(), NULL)) {
- writeFull(STDERR_FILENO, "failed to configure sandbox\n");
+ char * sandbox_errbuf = nullptr;
+ if (sandbox_init_with_parameters(sandboxProfile.c_str(), 0, stringsToCharPtrs(sandboxArgs).data(), &sandbox_errbuf)) {
+ writeFull(STDERR_FILENO, fmt("failed to configure sandbox: %s\n", sandbox_errbuf ? sandbox_errbuf : "(null)"));
_exit(1);
}
}
--
2.46.1

View File

@ -0,0 +1,41 @@
From 74b93c1edba00c2601e20b8acdcc78e29bd3f092 Mon Sep 17 00:00:00 2001
From: Robert Hensing <robert@roberthensing.nl>
Date: Thu, 3 Oct 2024 12:50:27 +0200
Subject: [PATCH 3/4] local-derivation-goal: Refactor
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This works because the `builder` and `args` variables are only used
in the non-builtin code path.
Co-Authored-By: Théophane Hufschmitt <theophane.hufschmitt@tweag.io>
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
src/libstore/build/local-derivation-goal.cc | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc
index e6f4c397d..e81818fa8 100644
--- a/src/libstore/build/local-derivation-goal.cc
+++ b/src/libstore/build/local-derivation-goal.cc
@@ -2143,15 +2143,12 @@ void LocalDerivationGoal::runChild()
_exit(1);
}
}
+#endif
- builder = drv->builder;
- args.push_back(std::string(baseNameOf(drv->builder)));
-#else
if (!drv->isBuiltin()) {
builder = drv->builder;
args.push_back(std::string(baseNameOf(drv->builder)));
}
-#endif
for (auto & i : drv->args)
args.push_back(rewriteStrings(i, inputRewrites));
--
2.46.1

View File

@ -0,0 +1,75 @@
From c8de35f74cbce58651c3b64ba66061040f546b9f Mon Sep 17 00:00:00 2001
From: Robert Hensing <robert@roberthensing.nl>
Date: Thu, 3 Oct 2024 12:57:00 +0200
Subject: [PATCH 4/4] local-derivation-goal: Move builder preparation to
non-builtin code path
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
src/libstore/build/local-derivation-goal.cc | 25 +++++++++------------
1 file changed, 10 insertions(+), 15 deletions(-)
diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc
index e81818fa8..078f1a5be 100644
--- a/src/libstore/build/local-derivation-goal.cc
+++ b/src/libstore/build/local-derivation-goal.cc
@@ -2017,11 +2017,6 @@ void LocalDerivationGoal::runChild()
throw SysError("setuid failed");
}
- /* Fill in the arguments. */
- Strings args;
-
- std::string builder = "invalid";
-
#if __APPLE__
/* This has to appear before import statements. */
std::string sandboxProfile = "(version 1)\n";
@@ -2145,14 +2140,6 @@ void LocalDerivationGoal::runChild()
}
#endif
- if (!drv->isBuiltin()) {
- builder = drv->builder;
- args.push_back(std::string(baseNameOf(drv->builder)));
- }
-
- for (auto & i : drv->args)
- args.push_back(rewriteStrings(i, inputRewrites));
-
/* Indicate that we managed to set up the build environment. */
writeFull(STDERR_FILENO, std::string("\2\n"));
@@ -2183,6 +2170,14 @@ void LocalDerivationGoal::runChild()
}
}
+ // Now builder is not builtin
+
+ Strings args;
+ args.push_back(std::string(baseNameOf(drv->builder)));
+
+ for (auto & i : drv->args)
+ args.push_back(rewriteStrings(i, inputRewrites));
+
#if __APPLE__
posix_spawnattr_t attrp;
@@ -2204,9 +2199,9 @@ void LocalDerivationGoal::runChild()
posix_spawnattr_setbinpref_np(&attrp, 1, &cpu, NULL);
}
- posix_spawn(NULL, builder.c_str(), NULL, &attrp, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
+ posix_spawn(NULL, drv->builder.c_str(), NULL, &attrp, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
#else
- execve(builder.c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
+ execve(drv->builder.c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
#endif
throw SysError("executing '%1%'", drv->builder);
--
2.46.1

View File

@ -0,0 +1,327 @@
From 8217054e3554ffd376f42fb0a65087a7af2ddfab Mon Sep 17 00:00:00 2001
From: Puck Meerburg <puck@puckipedia.com>
Date: Fri, 1 Mar 2024 11:42:24 -0500
Subject: [PATCH 1/4] fix: Run all derivation builders inside the sandbox on
macOS
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
configure.ac | 6 +-
package.nix | 5 +-
.../unix/build/local-derivation-goal.cc | 223 +++++++++---------
3 files changed, 118 insertions(+), 116 deletions(-)
diff --git a/configure.ac b/configure.ac
index 8f60bf4be..5e67e04be 100644
--- a/configure.ac
+++ b/configure.ac
@@ -62,13 +62,17 @@ AC_CHECK_TOOL([AR], [ar])
AC_SYS_LARGEFILE
-# Solaris-specific stuff.
+# OS-specific stuff.
AC_STRUCT_DIRENT_D_TYPE
case "$host_os" in
solaris*)
# Solaris requires -lsocket -lnsl for network functions
LDFLAGS="-lsocket -lnsl $LDFLAGS"
;;
+ darwin*)
+ # Need to link to libsandbox.
+ LDFLAGS="-lsandbox $LDFLAGS"
+ ;;
esac
diff --git a/package.nix b/package.nix
index 59265f522..28be97400 100644
--- a/package.nix
+++ b/package.nix
@@ -27,6 +27,7 @@
, libseccomp
, libsodium
, man
+, darwin
, lowdown
, mdbook
, mdbook-linkcheck
@@ -249,7 +250,9 @@ in {
] ++ lib.optionals buildUnitTests [
gtest
rapidcheck
- ] ++ lib.optional stdenv.isLinux (libseccomp.overrideAttrs (_: rec {
+ ]
+ ++ lib.optional stdenv.hostPlatform.isDarwin darwin.apple_sdk.libs.sandbox
+ ++ lib.optional stdenv.isLinux (libseccomp.overrideAttrs (_: rec {
version = "2.5.5";
src = fetchurl {
url = "https://github.com/seccomp/libseccomp/releases/download/v${version}/libseccomp-${version}.tar.gz";
diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc
index b8ccdf834..449d4b07c 100644
--- a/src/libstore/unix/build/local-derivation-goal.cc
+++ b/src/libstore/unix/build/local-derivation-goal.cc
@@ -58,6 +58,10 @@
#if __APPLE__
#include <spawn.h>
#include <sys/sysctl.h>
+#include <sandbox.h>
+
+/* This definition is undocumented but depended upon by all major browsers. */
+extern "C" int sandbox_init_with_parameters(const char *profile, uint64_t flags, const char *const parameters[], char **errorbuf);
#endif
#include <pwd.h>
@@ -2026,141 +2030,132 @@ void LocalDerivationGoal::runChild()
std::string builder = "invalid";
- if (drv->isBuiltin()) {
- ;
- }
#if __APPLE__
- else {
- /* This has to appear before import statements. */
- std::string sandboxProfile = "(version 1)\n";
-
- if (useChroot) {
-
- /* Lots and lots and lots of file functions freak out if they can't stat their full ancestry */
- PathSet ancestry;
-
- /* We build the ancestry before adding all inputPaths to the store because we know they'll
- all have the same parents (the store), and there might be lots of inputs. This isn't
- particularly efficient... I doubt it'll be a bottleneck in practice */
- for (auto & i : pathsInChroot) {
- Path cur = i.first;
- while (cur.compare("/") != 0) {
- cur = dirOf(cur);
- ancestry.insert(cur);
- }
- }
+ /* This has to appear before import statements. */
+ std::string sandboxProfile = "(version 1)\n";
+
+ if (useChroot) {
- /* And we want the store in there regardless of how empty pathsInChroot. We include the innermost
- path component this time, since it's typically /nix/store and we care about that. */
- Path cur = worker.store.storeDir;
+ /* Lots and lots and lots of file functions freak out if they can't stat their full ancestry */
+ PathSet ancestry;
+
+ /* We build the ancestry before adding all inputPaths to the store because we know they'll
+ all have the same parents (the store), and there might be lots of inputs. This isn't
+ particularly efficient... I doubt it'll be a bottleneck in practice */
+ for (auto & i : pathsInChroot) {
+ Path cur = i.first;
while (cur.compare("/") != 0) {
- ancestry.insert(cur);
cur = dirOf(cur);
+ ancestry.insert(cur);
}
+ }
- /* Add all our input paths to the chroot */
- for (auto & i : inputPaths) {
- auto p = worker.store.printStorePath(i);
- pathsInChroot[p] = p;
- }
-
- /* Violations will go to the syslog if you set this. Unfortunately the destination does not appear to be configurable */
- if (settings.darwinLogSandboxViolations) {
- sandboxProfile += "(deny default)\n";
- } else {
- sandboxProfile += "(deny default (with no-log))\n";
- }
+ /* And we want the store in there regardless of how empty pathsInChroot. We include the innermost
+ path component this time, since it's typically /nix/store and we care about that. */
+ Path cur = worker.store.storeDir;
+ while (cur.compare("/") != 0) {
+ ancestry.insert(cur);
+ cur = dirOf(cur);
+ }
- sandboxProfile +=
- #include "sandbox-defaults.sb"
- ;
+ /* Add all our input paths to the chroot */
+ for (auto & i : inputPaths) {
+ auto p = worker.store.printStorePath(i);
+ pathsInChroot[p] = p;
+ }
- if (!derivationType->isSandboxed())
- sandboxProfile +=
- #include "sandbox-network.sb"
- ;
-
- /* Add the output paths we'll use at build-time to the chroot */
- sandboxProfile += "(allow file-read* file-write* process-exec\n";
- for (auto & [_, path] : scratchOutputs)
- sandboxProfile += fmt("\t(subpath \"%s\")\n", worker.store.printStorePath(path));
-
- sandboxProfile += ")\n";
-
- /* Our inputs (transitive dependencies and any impurities computed above)
-
- without file-write* allowed, access() incorrectly returns EPERM
- */
- sandboxProfile += "(allow file-read* file-write* process-exec\n";
- for (auto & i : pathsInChroot) {
- if (i.first != i.second.source)
- throw Error(
- "can't map '%1%' to '%2%': mismatched impure paths not supported on Darwin",
- i.first, i.second.source);
-
- std::string path = i.first;
- auto optSt = maybeLstat(path.c_str());
- if (!optSt) {
- if (i.second.optional)
- continue;
- throw SysError("getting attributes of required path '%s", path);
- }
- if (S_ISDIR(optSt->st_mode))
- sandboxProfile += fmt("\t(subpath \"%s\")\n", path);
- else
- sandboxProfile += fmt("\t(literal \"%s\")\n", path);
- }
- sandboxProfile += ")\n";
+ /* Violations will go to the syslog if you set this. Unfortunately the destination does not appear to be configurable */
+ if (settings.darwinLogSandboxViolations) {
+ sandboxProfile += "(deny default)\n";
+ } else {
+ sandboxProfile += "(deny default (with no-log))\n";
+ }
- /* Allow file-read* on full directory hierarchy to self. Allows realpath() */
- sandboxProfile += "(allow file-read*\n";
- for (auto & i : ancestry) {
- sandboxProfile += fmt("\t(literal \"%s\")\n", i);
- }
- sandboxProfile += ")\n";
+ sandboxProfile +=
+ #include "sandbox-defaults.sb"
+ ;
- sandboxProfile += additionalSandboxProfile;
- } else
+ if (!derivationType->isSandboxed())
sandboxProfile +=
- #include "sandbox-minimal.sb"
+ #include "sandbox-network.sb"
;
- debug("Generated sandbox profile:");
- debug(sandboxProfile);
-
- Path sandboxFile = tmpDir + "/.sandbox.sb";
+ /* Add the output paths we'll use at build-time to the chroot */
+ sandboxProfile += "(allow file-read* file-write* process-exec\n";
+ for (auto & [_, path] : scratchOutputs)
+ sandboxProfile += fmt("\t(subpath \"%s\")\n", worker.store.printStorePath(path));
- writeFile(sandboxFile, sandboxProfile);
+ sandboxProfile += ")\n";
- bool allowLocalNetworking = parsedDrv->getBoolAttr("__darwinAllowLocalNetworking");
+ /* Our inputs (transitive dependencies and any impurities computed above)
- /* The tmpDir in scope points at the temporary build directory for our derivation. Some packages try different mechanisms
- to find temporary directories, so we want to open up a broader place for them to put their files, if needed. */
- Path globalTmpDir = canonPath(defaultTempDir(), true);
+ without file-write* allowed, access() incorrectly returns EPERM
+ */
+ sandboxProfile += "(allow file-read* file-write* process-exec\n";
+ for (auto & i : pathsInChroot) {
+ if (i.first != i.second.source)
+ throw Error(
+ "can't map '%1%' to '%2%': mismatched impure paths not supported on Darwin",
+ i.first, i.second.source);
+
+ std::string path = i.first;
+ auto optSt = maybeLstat(path.c_str());
+ if (!optSt) {
+ if (i.second.optional)
+ continue;
+ throw SysError("getting attributes of required path '%s", path);
+ }
+ if (S_ISDIR(optSt->st_mode))
+ sandboxProfile += fmt("\t(subpath \"%s\")\n", path);
+ else
+ sandboxProfile += fmt("\t(literal \"%s\")\n", path);
+ }
+ sandboxProfile += ")\n";
- /* They don't like trailing slashes on subpath directives */
- while (!globalTmpDir.empty() && globalTmpDir.back() == '/')
- globalTmpDir.pop_back();
+ /* Allow file-read* on full directory hierarchy to self. Allows realpath() */
+ sandboxProfile += "(allow file-read*\n";
+ for (auto & i : ancestry) {
+ sandboxProfile += fmt("\t(literal \"%s\")\n", i);
+ }
+ sandboxProfile += ")\n";
- if (getEnv("_NIX_TEST_NO_SANDBOX") != "1") {
- builder = "/usr/bin/sandbox-exec";
- args.push_back("sandbox-exec");
- args.push_back("-f");
- args.push_back(sandboxFile);
- args.push_back("-D");
- args.push_back("_GLOBAL_TMP_DIR=" + globalTmpDir);
- if (allowLocalNetworking) {
- args.push_back("-D");
- args.push_back(std::string("_ALLOW_LOCAL_NETWORKING=1"));
- }
- args.push_back(drv->builder);
- } else {
- builder = drv->builder;
- args.push_back(std::string(baseNameOf(drv->builder)));
+ sandboxProfile += additionalSandboxProfile;
+ } else
+ sandboxProfile +=
+ #include "sandbox-minimal.sb"
+ ;
+
+ debug("Generated sandbox profile:");
+ debug(sandboxProfile);
+
+ bool allowLocalNetworking = parsedDrv->getBoolAttr("__darwinAllowLocalNetworking");
+
+ /* The tmpDir in scope points at the temporary build directory for our derivation. Some packages try different mechanisms
+ to find temporary directories, so we want to open up a broader place for them to put their files, if needed. */
+ Path globalTmpDir = canonPath(defaultTempDir(), true);
+
+ /* They don't like trailing slashes on subpath directives */
+ while (!globalTmpDir.empty() && globalTmpDir.back() == '/')
+ globalTmpDir.pop_back();
+
+ if (getEnv("_NIX_TEST_NO_SANDBOX") != "1") {
+ Strings sandboxArgs;
+ sandboxArgs.push_back("_GLOBAL_TMP_DIR");
+ sandboxArgs.push_back(globalTmpDir);
+ if (allowLocalNetworking) {
+ sandboxArgs.push_back("_ALLOW_LOCAL_NETWORKING");
+ sandboxArgs.push_back("1");
+ }
+ if (sandbox_init_with_parameters(sandboxProfile.c_str(), 0, stringsToCharPtrs(sandboxArgs).data(), NULL)) {
+ writeFull(STDERR_FILENO, "failed to configure sandbox\n");
+ _exit(1);
}
}
+
+ builder = drv->builder;
+ args.push_back(std::string(baseNameOf(drv->builder)));
#else
- else {
+ if (!drv->isBuiltin()) {
builder = drv->builder;
args.push_back(std::string(baseNameOf(drv->builder)));
}
--
2.46.1

View File

@ -0,0 +1,34 @@
From f0677f190d0bd042c3a864508a5307b19a2c2d26 Mon Sep 17 00:00:00 2001
From: Robert Hensing <robert@roberthensing.nl>
Date: Thu, 3 Oct 2024 12:44:12 +0200
Subject: [PATCH 2/4] local-derivation-goal: Print sandbox error detail on
darwin
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-Authored-By: Théophane Hufschmitt <theophane.hufschmitt@tweag.io>
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
src/libstore/unix/build/local-derivation-goal.cc | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc
index 449d4b07c..b74bd2e64 100644
--- a/src/libstore/unix/build/local-derivation-goal.cc
+++ b/src/libstore/unix/build/local-derivation-goal.cc
@@ -2146,8 +2146,9 @@ void LocalDerivationGoal::runChild()
sandboxArgs.push_back("_ALLOW_LOCAL_NETWORKING");
sandboxArgs.push_back("1");
}
- if (sandbox_init_with_parameters(sandboxProfile.c_str(), 0, stringsToCharPtrs(sandboxArgs).data(), NULL)) {
- writeFull(STDERR_FILENO, "failed to configure sandbox\n");
+ char * sandbox_errbuf = nullptr;
+ if (sandbox_init_with_parameters(sandboxProfile.c_str(), 0, stringsToCharPtrs(sandboxArgs).data(), &sandbox_errbuf)) {
+ writeFull(STDERR_FILENO, fmt("failed to configure sandbox: %s\n", sandbox_errbuf ? sandbox_errbuf : "(null)"));
_exit(1);
}
}
--
2.46.1

View File

@ -0,0 +1,41 @@
From 1b39753f4d63465c709d18482945ce680b6f3f1e Mon Sep 17 00:00:00 2001
From: Robert Hensing <robert@roberthensing.nl>
Date: Thu, 3 Oct 2024 12:50:27 +0200
Subject: [PATCH 3/4] local-derivation-goal: Refactor
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This works because the `builder` and `args` variables are only used
in the non-builtin code path.
Co-Authored-By: Théophane Hufschmitt <theophane.hufschmitt@tweag.io>
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
src/libstore/unix/build/local-derivation-goal.cc | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc
index b74bd2e64..9b8b3c51b 100644
--- a/src/libstore/unix/build/local-derivation-goal.cc
+++ b/src/libstore/unix/build/local-derivation-goal.cc
@@ -2152,15 +2152,12 @@ void LocalDerivationGoal::runChild()
_exit(1);
}
}
+#endif
- builder = drv->builder;
- args.push_back(std::string(baseNameOf(drv->builder)));
-#else
if (!drv->isBuiltin()) {
builder = drv->builder;
args.push_back(std::string(baseNameOf(drv->builder)));
}
-#endif
for (auto & i : drv->args)
args.push_back(rewriteStrings(i, inputRewrites));
--
2.46.1

View File

@ -0,0 +1,75 @@
From 9e198a75f76ac08f835975d4b2743e156616a219 Mon Sep 17 00:00:00 2001
From: Robert Hensing <robert@roberthensing.nl>
Date: Thu, 3 Oct 2024 12:57:00 +0200
Subject: [PATCH 4/4] local-derivation-goal: Move builder preparation to
non-builtin code path
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
.../unix/build/local-derivation-goal.cc | 25 ++++++++-----------
1 file changed, 10 insertions(+), 15 deletions(-)
diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc
index 9b8b3c51b..08366712c 100644
--- a/src/libstore/unix/build/local-derivation-goal.cc
+++ b/src/libstore/unix/build/local-derivation-goal.cc
@@ -2025,11 +2025,6 @@ void LocalDerivationGoal::runChild()
throw SysError("setuid failed");
}
- /* Fill in the arguments. */
- Strings args;
-
- std::string builder = "invalid";
-
#if __APPLE__
/* This has to appear before import statements. */
std::string sandboxProfile = "(version 1)\n";
@@ -2154,14 +2149,6 @@ void LocalDerivationGoal::runChild()
}
#endif
- if (!drv->isBuiltin()) {
- builder = drv->builder;
- args.push_back(std::string(baseNameOf(drv->builder)));
- }
-
- for (auto & i : drv->args)
- args.push_back(rewriteStrings(i, inputRewrites));
-
/* Indicate that we managed to set up the build environment. */
writeFull(STDERR_FILENO, std::string("\2\n"));
@@ -2192,6 +2179,14 @@ void LocalDerivationGoal::runChild()
}
}
+ // Now builder is not builtin
+
+ Strings args;
+ args.push_back(std::string(baseNameOf(drv->builder)));
+
+ for (auto & i : drv->args)
+ args.push_back(rewriteStrings(i, inputRewrites));
+
#if __APPLE__
posix_spawnattr_t attrp;
@@ -2213,9 +2208,9 @@ void LocalDerivationGoal::runChild()
posix_spawnattr_setbinpref_np(&attrp, 1, &cpu, NULL);
}
- posix_spawn(NULL, builder.c_str(), NULL, &attrp, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
+ posix_spawn(NULL, drv->builder.c_str(), NULL, &attrp, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
#else
- execve(builder.c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
+ execve(drv->builder.c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
#endif
throw SysError("executing '%1%'", drv->builder);
--
2.46.1

View File

@ -0,0 +1,323 @@
From 05994033d58e358ddafe51d1d04626eb76b8a192 Mon Sep 17 00:00:00 2001
From: Puck Meerburg <puck@puckipedia.com>
Date: Fri, 1 Mar 2024 11:42:24 -0500
Subject: [PATCH 1/4] fix: Run all derivation builders inside the sandbox on
macOS
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
configure.ac | 6 +-
package.nix | 2 +
.../unix/build/local-derivation-goal.cc | 223 +++++++++---------
3 files changed, 116 insertions(+), 115 deletions(-)
diff --git a/configure.ac b/configure.ac
index 90a6d45d5..f98a0a5ea 100644
--- a/configure.ac
+++ b/configure.ac
@@ -62,12 +62,16 @@ AC_CHECK_TOOL([AR], [ar])
AC_SYS_LARGEFILE
-# Solaris-specific stuff.
+# OS-specific stuff.
case "$host_os" in
solaris*)
# Solaris requires -lsocket -lnsl for network functions
LDFLAGS="-lsocket -lnsl $LDFLAGS"
;;
+ darwin*)
+ # Need to link to libsandbox.
+ LDFLAGS="-lsandbox $LDFLAGS"
+ ;;
esac
diff --git a/package.nix b/package.nix
index cf1654c6a..1dfe7ab31 100644
--- a/package.nix
+++ b/package.nix
@@ -27,6 +27,7 @@
, libseccomp
, libsodium
, man
+, darwin
, lowdown
, mdbook
, mdbook-linkcheck
@@ -250,6 +251,7 @@ in {
gtest
rapidcheck
] ++ lib.optional stdenv.isLinux libseccomp
+ ++ lib.optional stdenv.hostPlatform.isDarwin darwin.apple_sdk.libs.sandbox
++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid
# There have been issues building these dependencies
++ lib.optional (stdenv.hostPlatform == stdenv.buildPlatform && (stdenv.isLinux || stdenv.isDarwin))
diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc
index ae9c715d6..878644fa5 100644
--- a/src/libstore/unix/build/local-derivation-goal.cc
+++ b/src/libstore/unix/build/local-derivation-goal.cc
@@ -58,6 +58,10 @@
#if __APPLE__
#include <spawn.h>
#include <sys/sysctl.h>
+#include <sandbox.h>
+
+/* This definition is undocumented but depended upon by all major browsers. */
+extern "C" int sandbox_init_with_parameters(const char *profile, uint64_t flags, const char *const parameters[], char **errorbuf);
#endif
#include <pwd.h>
@@ -2017,141 +2021,132 @@ void LocalDerivationGoal::runChild()
std::string builder = "invalid";
- if (drv->isBuiltin()) {
- ;
- }
#if __APPLE__
- else {
- /* This has to appear before import statements. */
- std::string sandboxProfile = "(version 1)\n";
-
- if (useChroot) {
-
- /* Lots and lots and lots of file functions freak out if they can't stat their full ancestry */
- PathSet ancestry;
-
- /* We build the ancestry before adding all inputPaths to the store because we know they'll
- all have the same parents (the store), and there might be lots of inputs. This isn't
- particularly efficient... I doubt it'll be a bottleneck in practice */
- for (auto & i : pathsInChroot) {
- Path cur = i.first;
- while (cur.compare("/") != 0) {
- cur = dirOf(cur);
- ancestry.insert(cur);
- }
- }
+ /* This has to appear before import statements. */
+ std::string sandboxProfile = "(version 1)\n";
+
+ if (useChroot) {
- /* And we want the store in there regardless of how empty pathsInChroot. We include the innermost
- path component this time, since it's typically /nix/store and we care about that. */
- Path cur = worker.store.storeDir;
+ /* Lots and lots and lots of file functions freak out if they can't stat their full ancestry */
+ PathSet ancestry;
+
+ /* We build the ancestry before adding all inputPaths to the store because we know they'll
+ all have the same parents (the store), and there might be lots of inputs. This isn't
+ particularly efficient... I doubt it'll be a bottleneck in practice */
+ for (auto & i : pathsInChroot) {
+ Path cur = i.first;
while (cur.compare("/") != 0) {
- ancestry.insert(cur);
cur = dirOf(cur);
+ ancestry.insert(cur);
}
+ }
- /* Add all our input paths to the chroot */
- for (auto & i : inputPaths) {
- auto p = worker.store.printStorePath(i);
- pathsInChroot[p] = p;
- }
-
- /* Violations will go to the syslog if you set this. Unfortunately the destination does not appear to be configurable */
- if (settings.darwinLogSandboxViolations) {
- sandboxProfile += "(deny default)\n";
- } else {
- sandboxProfile += "(deny default (with no-log))\n";
- }
+ /* And we want the store in there regardless of how empty pathsInChroot. We include the innermost
+ path component this time, since it's typically /nix/store and we care about that. */
+ Path cur = worker.store.storeDir;
+ while (cur.compare("/") != 0) {
+ ancestry.insert(cur);
+ cur = dirOf(cur);
+ }
- sandboxProfile +=
- #include "sandbox-defaults.sb"
- ;
+ /* Add all our input paths to the chroot */
+ for (auto & i : inputPaths) {
+ auto p = worker.store.printStorePath(i);
+ pathsInChroot[p] = p;
+ }
- if (!derivationType->isSandboxed())
- sandboxProfile +=
- #include "sandbox-network.sb"
- ;
-
- /* Add the output paths we'll use at build-time to the chroot */
- sandboxProfile += "(allow file-read* file-write* process-exec\n";
- for (auto & [_, path] : scratchOutputs)
- sandboxProfile += fmt("\t(subpath \"%s\")\n", worker.store.printStorePath(path));
-
- sandboxProfile += ")\n";
-
- /* Our inputs (transitive dependencies and any impurities computed above)
-
- without file-write* allowed, access() incorrectly returns EPERM
- */
- sandboxProfile += "(allow file-read* file-write* process-exec\n";
- for (auto & i : pathsInChroot) {
- if (i.first != i.second.source)
- throw Error(
- "can't map '%1%' to '%2%': mismatched impure paths not supported on Darwin",
- i.first, i.second.source);
-
- std::string path = i.first;
- auto optSt = maybeLstat(path.c_str());
- if (!optSt) {
- if (i.second.optional)
- continue;
- throw SysError("getting attributes of required path '%s", path);
- }
- if (S_ISDIR(optSt->st_mode))
- sandboxProfile += fmt("\t(subpath \"%s\")\n", path);
- else
- sandboxProfile += fmt("\t(literal \"%s\")\n", path);
- }
- sandboxProfile += ")\n";
+ /* Violations will go to the syslog if you set this. Unfortunately the destination does not appear to be configurable */
+ if (settings.darwinLogSandboxViolations) {
+ sandboxProfile += "(deny default)\n";
+ } else {
+ sandboxProfile += "(deny default (with no-log))\n";
+ }
- /* Allow file-read* on full directory hierarchy to self. Allows realpath() */
- sandboxProfile += "(allow file-read*\n";
- for (auto & i : ancestry) {
- sandboxProfile += fmt("\t(literal \"%s\")\n", i);
- }
- sandboxProfile += ")\n";
+ sandboxProfile +=
+ #include "sandbox-defaults.sb"
+ ;
- sandboxProfile += additionalSandboxProfile;
- } else
+ if (!derivationType->isSandboxed())
sandboxProfile +=
- #include "sandbox-minimal.sb"
+ #include "sandbox-network.sb"
;
- debug("Generated sandbox profile:");
- debug(sandboxProfile);
-
- Path sandboxFile = tmpDir + "/.sandbox.sb";
+ /* Add the output paths we'll use at build-time to the chroot */
+ sandboxProfile += "(allow file-read* file-write* process-exec\n";
+ for (auto & [_, path] : scratchOutputs)
+ sandboxProfile += fmt("\t(subpath \"%s\")\n", worker.store.printStorePath(path));
- writeFile(sandboxFile, sandboxProfile);
+ sandboxProfile += ")\n";
- bool allowLocalNetworking = parsedDrv->getBoolAttr("__darwinAllowLocalNetworking");
+ /* Our inputs (transitive dependencies and any impurities computed above)
- /* The tmpDir in scope points at the temporary build directory for our derivation. Some packages try different mechanisms
- to find temporary directories, so we want to open up a broader place for them to put their files, if needed. */
- Path globalTmpDir = canonPath(defaultTempDir(), true);
+ without file-write* allowed, access() incorrectly returns EPERM
+ */
+ sandboxProfile += "(allow file-read* file-write* process-exec\n";
+ for (auto & i : pathsInChroot) {
+ if (i.first != i.second.source)
+ throw Error(
+ "can't map '%1%' to '%2%': mismatched impure paths not supported on Darwin",
+ i.first, i.second.source);
+
+ std::string path = i.first;
+ auto optSt = maybeLstat(path.c_str());
+ if (!optSt) {
+ if (i.second.optional)
+ continue;
+ throw SysError("getting attributes of required path '%s", path);
+ }
+ if (S_ISDIR(optSt->st_mode))
+ sandboxProfile += fmt("\t(subpath \"%s\")\n", path);
+ else
+ sandboxProfile += fmt("\t(literal \"%s\")\n", path);
+ }
+ sandboxProfile += ")\n";
- /* They don't like trailing slashes on subpath directives */
- while (!globalTmpDir.empty() && globalTmpDir.back() == '/')
- globalTmpDir.pop_back();
+ /* Allow file-read* on full directory hierarchy to self. Allows realpath() */
+ sandboxProfile += "(allow file-read*\n";
+ for (auto & i : ancestry) {
+ sandboxProfile += fmt("\t(literal \"%s\")\n", i);
+ }
+ sandboxProfile += ")\n";
- if (getEnv("_NIX_TEST_NO_SANDBOX") != "1") {
- builder = "/usr/bin/sandbox-exec";
- args.push_back("sandbox-exec");
- args.push_back("-f");
- args.push_back(sandboxFile);
- args.push_back("-D");
- args.push_back("_GLOBAL_TMP_DIR=" + globalTmpDir);
- if (allowLocalNetworking) {
- args.push_back("-D");
- args.push_back(std::string("_ALLOW_LOCAL_NETWORKING=1"));
- }
- args.push_back(drv->builder);
- } else {
- builder = drv->builder;
- args.push_back(std::string(baseNameOf(drv->builder)));
+ sandboxProfile += additionalSandboxProfile;
+ } else
+ sandboxProfile +=
+ #include "sandbox-minimal.sb"
+ ;
+
+ debug("Generated sandbox profile:");
+ debug(sandboxProfile);
+
+ bool allowLocalNetworking = parsedDrv->getBoolAttr("__darwinAllowLocalNetworking");
+
+ /* The tmpDir in scope points at the temporary build directory for our derivation. Some packages try different mechanisms
+ to find temporary directories, so we want to open up a broader place for them to put their files, if needed. */
+ Path globalTmpDir = canonPath(defaultTempDir(), true);
+
+ /* They don't like trailing slashes on subpath directives */
+ while (!globalTmpDir.empty() && globalTmpDir.back() == '/')
+ globalTmpDir.pop_back();
+
+ if (getEnv("_NIX_TEST_NO_SANDBOX") != "1") {
+ Strings sandboxArgs;
+ sandboxArgs.push_back("_GLOBAL_TMP_DIR");
+ sandboxArgs.push_back(globalTmpDir);
+ if (allowLocalNetworking) {
+ sandboxArgs.push_back("_ALLOW_LOCAL_NETWORKING");
+ sandboxArgs.push_back("1");
+ }
+ if (sandbox_init_with_parameters(sandboxProfile.c_str(), 0, stringsToCharPtrs(sandboxArgs).data(), NULL)) {
+ writeFull(STDERR_FILENO, "failed to configure sandbox\n");
+ _exit(1);
}
}
+
+ builder = drv->builder;
+ args.push_back(std::string(baseNameOf(drv->builder)));
#else
- else {
+ if (!drv->isBuiltin()) {
builder = drv->builder;
args.push_back(std::string(baseNameOf(drv->builder)));
}
--
2.46.1

View File

@ -0,0 +1,34 @@
From c43954ffac356b4168cbcfe2a67b4bad3f0dff5d Mon Sep 17 00:00:00 2001
From: Robert Hensing <robert@roberthensing.nl>
Date: Thu, 3 Oct 2024 12:44:12 +0200
Subject: [PATCH 2/4] local-derivation-goal: Print sandbox error detail on
darwin
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-Authored-By: Théophane Hufschmitt <theophane.hufschmitt@tweag.io>
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
src/libstore/unix/build/local-derivation-goal.cc | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc
index 878644fa5..0df1f0683 100644
--- a/src/libstore/unix/build/local-derivation-goal.cc
+++ b/src/libstore/unix/build/local-derivation-goal.cc
@@ -2137,8 +2137,9 @@ void LocalDerivationGoal::runChild()
sandboxArgs.push_back("_ALLOW_LOCAL_NETWORKING");
sandboxArgs.push_back("1");
}
- if (sandbox_init_with_parameters(sandboxProfile.c_str(), 0, stringsToCharPtrs(sandboxArgs).data(), NULL)) {
- writeFull(STDERR_FILENO, "failed to configure sandbox\n");
+ char * sandbox_errbuf = nullptr;
+ if (sandbox_init_with_parameters(sandboxProfile.c_str(), 0, stringsToCharPtrs(sandboxArgs).data(), &sandbox_errbuf)) {
+ writeFull(STDERR_FILENO, fmt("failed to configure sandbox: %s\n", sandbox_errbuf ? sandbox_errbuf : "(null)"));
_exit(1);
}
}
--
2.46.1

View File

@ -0,0 +1,41 @@
From 53b4bdcb8b0f114bea978cffbea325fd73f779b5 Mon Sep 17 00:00:00 2001
From: Robert Hensing <robert@roberthensing.nl>
Date: Thu, 3 Oct 2024 12:50:27 +0200
Subject: [PATCH 3/4] local-derivation-goal: Refactor
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This works because the `builder` and `args` variables are only used
in the non-builtin code path.
Co-Authored-By: Théophane Hufschmitt <theophane.hufschmitt@tweag.io>
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
src/libstore/unix/build/local-derivation-goal.cc | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc
index 0df1f0683..9e67283c9 100644
--- a/src/libstore/unix/build/local-derivation-goal.cc
+++ b/src/libstore/unix/build/local-derivation-goal.cc
@@ -2143,15 +2143,12 @@ void LocalDerivationGoal::runChild()
_exit(1);
}
}
+#endif
- builder = drv->builder;
- args.push_back(std::string(baseNameOf(drv->builder)));
-#else
if (!drv->isBuiltin()) {
builder = drv->builder;
args.push_back(std::string(baseNameOf(drv->builder)));
}
-#endif
for (auto & i : drv->args)
args.push_back(rewriteStrings(i, inputRewrites));
--
2.46.1

View File

@ -0,0 +1,75 @@
From 67b5c7004302cbd344f63ccd306673a9adec4520 Mon Sep 17 00:00:00 2001
From: Robert Hensing <robert@roberthensing.nl>
Date: Thu, 3 Oct 2024 12:57:00 +0200
Subject: [PATCH 4/4] local-derivation-goal: Move builder preparation to
non-builtin code path
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
.../unix/build/local-derivation-goal.cc | 25 ++++++++-----------
1 file changed, 10 insertions(+), 15 deletions(-)
diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc
index 9e67283c9..1f4bafb56 100644
--- a/src/libstore/unix/build/local-derivation-goal.cc
+++ b/src/libstore/unix/build/local-derivation-goal.cc
@@ -2016,11 +2016,6 @@ void LocalDerivationGoal::runChild()
throw SysError("setuid failed");
}
- /* Fill in the arguments. */
- Strings args;
-
- std::string builder = "invalid";
-
#if __APPLE__
/* This has to appear before import statements. */
std::string sandboxProfile = "(version 1)\n";
@@ -2145,14 +2140,6 @@ void LocalDerivationGoal::runChild()
}
#endif
- if (!drv->isBuiltin()) {
- builder = drv->builder;
- args.push_back(std::string(baseNameOf(drv->builder)));
- }
-
- for (auto & i : drv->args)
- args.push_back(rewriteStrings(i, inputRewrites));
-
/* Indicate that we managed to set up the build environment. */
writeFull(STDERR_FILENO, std::string("\2\n"));
@@ -2183,6 +2170,14 @@ void LocalDerivationGoal::runChild()
}
}
+ // Now builder is not builtin
+
+ Strings args;
+ args.push_back(std::string(baseNameOf(drv->builder)));
+
+ for (auto & i : drv->args)
+ args.push_back(rewriteStrings(i, inputRewrites));
+
#if __APPLE__
posix_spawnattr_t attrp;
@@ -2204,9 +2199,9 @@ void LocalDerivationGoal::runChild()
posix_spawnattr_setbinpref_np(&attrp, 1, &cpu, NULL);
}
- posix_spawn(NULL, builder.c_str(), NULL, &attrp, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
+ posix_spawn(NULL, drv->builder.c_str(), NULL, &attrp, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
#else
- execve(builder.c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
+ execve(drv->builder.c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
#endif
throw SysError("executing '%1%'", drv->builder);
--
2.46.1

View File

@ -0,0 +1,323 @@
From 170242cf0ca3e9fadbad2004126793634d56623e Mon Sep 17 00:00:00 2001
From: Puck Meerburg <puck@puckipedia.com>
Date: Fri, 1 Mar 2024 11:42:24 -0500
Subject: [PATCH 1/5] fix: Run all derivation builders inside the sandbox on
macOS
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
configure.ac | 6 +-
package.nix | 2 +
.../unix/build/local-derivation-goal.cc | 223 +++++++++---------
3 files changed, 116 insertions(+), 115 deletions(-)
diff --git a/configure.ac b/configure.ac
index 5c22ed176..dff35981b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -62,12 +62,16 @@ AC_CHECK_TOOL([AR], [ar])
AC_SYS_LARGEFILE
-# Solaris-specific stuff.
+# OS-specific stuff.
case "$host_os" in
solaris*)
# Solaris requires -lsocket -lnsl for network functions
LDFLAGS="-lsocket -lnsl $LDFLAGS"
;;
+ darwin*)
+ # Need to link to libsandbox.
+ LDFLAGS="-lsandbox $LDFLAGS"
+ ;;
esac
diff --git a/package.nix b/package.nix
index a7c8923e8..fcd1e1898 100644
--- a/package.nix
+++ b/package.nix
@@ -23,6 +23,7 @@
, libseccomp
, libsodium
, man
+, darwin
, lowdown
, mdbook
, mdbook-linkcheck
@@ -235,6 +236,7 @@ in {
gtest
rapidcheck
] ++ lib.optional stdenv.isLinux libseccomp
+ ++ lib.optional stdenv.hostPlatform.isDarwin darwin.apple_sdk.libs.sandbox
++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid
# There have been issues building these dependencies
++ lib.optional (stdenv.hostPlatform == stdenv.buildPlatform && (stdenv.isLinux || stdenv.isDarwin))
diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc
index 54ca69580..7ce266122 100644
--- a/src/libstore/unix/build/local-derivation-goal.cc
+++ b/src/libstore/unix/build/local-derivation-goal.cc
@@ -58,6 +58,10 @@
#if __APPLE__
#include <spawn.h>
#include <sys/sysctl.h>
+#include <sandbox.h>
+
+/* This definition is undocumented but depended upon by all major browsers. */
+extern "C" int sandbox_init_with_parameters(const char *profile, uint64_t flags, const char *const parameters[], char **errorbuf);
#endif
#include <pwd.h>
@@ -2039,141 +2043,132 @@ void LocalDerivationGoal::runChild()
std::string builder = "invalid";
- if (drv->isBuiltin()) {
- ;
- }
#if __APPLE__
- else {
- /* This has to appear before import statements. */
- std::string sandboxProfile = "(version 1)\n";
-
- if (useChroot) {
-
- /* Lots and lots and lots of file functions freak out if they can't stat their full ancestry */
- PathSet ancestry;
-
- /* We build the ancestry before adding all inputPaths to the store because we know they'll
- all have the same parents (the store), and there might be lots of inputs. This isn't
- particularly efficient... I doubt it'll be a bottleneck in practice */
- for (auto & i : pathsInChroot) {
- Path cur = i.first;
- while (cur.compare("/") != 0) {
- cur = dirOf(cur);
- ancestry.insert(cur);
- }
- }
+ /* This has to appear before import statements. */
+ std::string sandboxProfile = "(version 1)\n";
+
+ if (useChroot) {
- /* And we want the store in there regardless of how empty pathsInChroot. We include the innermost
- path component this time, since it's typically /nix/store and we care about that. */
- Path cur = worker.store.storeDir;
+ /* Lots and lots and lots of file functions freak out if they can't stat their full ancestry */
+ PathSet ancestry;
+
+ /* We build the ancestry before adding all inputPaths to the store because we know they'll
+ all have the same parents (the store), and there might be lots of inputs. This isn't
+ particularly efficient... I doubt it'll be a bottleneck in practice */
+ for (auto & i : pathsInChroot) {
+ Path cur = i.first;
while (cur.compare("/") != 0) {
- ancestry.insert(cur);
cur = dirOf(cur);
+ ancestry.insert(cur);
}
+ }
- /* Add all our input paths to the chroot */
- for (auto & i : inputPaths) {
- auto p = worker.store.printStorePath(i);
- pathsInChroot[p] = p;
- }
-
- /* Violations will go to the syslog if you set this. Unfortunately the destination does not appear to be configurable */
- if (settings.darwinLogSandboxViolations) {
- sandboxProfile += "(deny default)\n";
- } else {
- sandboxProfile += "(deny default (with no-log))\n";
- }
+ /* And we want the store in there regardless of how empty pathsInChroot. We include the innermost
+ path component this time, since it's typically /nix/store and we care about that. */
+ Path cur = worker.store.storeDir;
+ while (cur.compare("/") != 0) {
+ ancestry.insert(cur);
+ cur = dirOf(cur);
+ }
- sandboxProfile +=
- #include "sandbox-defaults.sb"
- ;
+ /* Add all our input paths to the chroot */
+ for (auto & i : inputPaths) {
+ auto p = worker.store.printStorePath(i);
+ pathsInChroot[p] = p;
+ }
- if (!derivationType->isSandboxed())
- sandboxProfile +=
- #include "sandbox-network.sb"
- ;
-
- /* Add the output paths we'll use at build-time to the chroot */
- sandboxProfile += "(allow file-read* file-write* process-exec\n";
- for (auto & [_, path] : scratchOutputs)
- sandboxProfile += fmt("\t(subpath \"%s\")\n", worker.store.printStorePath(path));
-
- sandboxProfile += ")\n";
-
- /* Our inputs (transitive dependencies and any impurities computed above)
-
- without file-write* allowed, access() incorrectly returns EPERM
- */
- sandboxProfile += "(allow file-read* file-write* process-exec\n";
- for (auto & i : pathsInChroot) {
- if (i.first != i.second.source)
- throw Error(
- "can't map '%1%' to '%2%': mismatched impure paths not supported on Darwin",
- i.first, i.second.source);
-
- std::string path = i.first;
- auto optSt = maybeLstat(path.c_str());
- if (!optSt) {
- if (i.second.optional)
- continue;
- throw SysError("getting attributes of required path '%s", path);
- }
- if (S_ISDIR(optSt->st_mode))
- sandboxProfile += fmt("\t(subpath \"%s\")\n", path);
- else
- sandboxProfile += fmt("\t(literal \"%s\")\n", path);
- }
- sandboxProfile += ")\n";
+ /* Violations will go to the syslog if you set this. Unfortunately the destination does not appear to be configurable */
+ if (settings.darwinLogSandboxViolations) {
+ sandboxProfile += "(deny default)\n";
+ } else {
+ sandboxProfile += "(deny default (with no-log))\n";
+ }
- /* Allow file-read* on full directory hierarchy to self. Allows realpath() */
- sandboxProfile += "(allow file-read*\n";
- for (auto & i : ancestry) {
- sandboxProfile += fmt("\t(literal \"%s\")\n", i);
- }
- sandboxProfile += ")\n";
+ sandboxProfile +=
+ #include "sandbox-defaults.sb"
+ ;
- sandboxProfile += additionalSandboxProfile;
- } else
+ if (!derivationType->isSandboxed())
sandboxProfile +=
- #include "sandbox-minimal.sb"
+ #include "sandbox-network.sb"
;
- debug("Generated sandbox profile:");
- debug(sandboxProfile);
-
- Path sandboxFile = tmpDir + "/.sandbox.sb";
+ /* Add the output paths we'll use at build-time to the chroot */
+ sandboxProfile += "(allow file-read* file-write* process-exec\n";
+ for (auto & [_, path] : scratchOutputs)
+ sandboxProfile += fmt("\t(subpath \"%s\")\n", worker.store.printStorePath(path));
- writeFile(sandboxFile, sandboxProfile);
+ sandboxProfile += ")\n";
- bool allowLocalNetworking = parsedDrv->getBoolAttr("__darwinAllowLocalNetworking");
+ /* Our inputs (transitive dependencies and any impurities computed above)
- /* The tmpDir in scope points at the temporary build directory for our derivation. Some packages try different mechanisms
- to find temporary directories, so we want to open up a broader place for them to put their files, if needed. */
- Path globalTmpDir = canonPath(defaultTempDir(), true);
+ without file-write* allowed, access() incorrectly returns EPERM
+ */
+ sandboxProfile += "(allow file-read* file-write* process-exec\n";
+ for (auto & i : pathsInChroot) {
+ if (i.first != i.second.source)
+ throw Error(
+ "can't map '%1%' to '%2%': mismatched impure paths not supported on Darwin",
+ i.first, i.second.source);
+
+ std::string path = i.first;
+ auto optSt = maybeLstat(path.c_str());
+ if (!optSt) {
+ if (i.second.optional)
+ continue;
+ throw SysError("getting attributes of required path '%s", path);
+ }
+ if (S_ISDIR(optSt->st_mode))
+ sandboxProfile += fmt("\t(subpath \"%s\")\n", path);
+ else
+ sandboxProfile += fmt("\t(literal \"%s\")\n", path);
+ }
+ sandboxProfile += ")\n";
- /* They don't like trailing slashes on subpath directives */
- while (!globalTmpDir.empty() && globalTmpDir.back() == '/')
- globalTmpDir.pop_back();
+ /* Allow file-read* on full directory hierarchy to self. Allows realpath() */
+ sandboxProfile += "(allow file-read*\n";
+ for (auto & i : ancestry) {
+ sandboxProfile += fmt("\t(literal \"%s\")\n", i);
+ }
+ sandboxProfile += ")\n";
- if (getEnv("_NIX_TEST_NO_SANDBOX") != "1") {
- builder = "/usr/bin/sandbox-exec";
- args.push_back("sandbox-exec");
- args.push_back("-f");
- args.push_back(sandboxFile);
- args.push_back("-D");
- args.push_back("_GLOBAL_TMP_DIR=" + globalTmpDir);
- if (allowLocalNetworking) {
- args.push_back("-D");
- args.push_back(std::string("_ALLOW_LOCAL_NETWORKING=1"));
- }
- args.push_back(drv->builder);
- } else {
- builder = drv->builder;
- args.push_back(std::string(baseNameOf(drv->builder)));
+ sandboxProfile += additionalSandboxProfile;
+ } else
+ sandboxProfile +=
+ #include "sandbox-minimal.sb"
+ ;
+
+ debug("Generated sandbox profile:");
+ debug(sandboxProfile);
+
+ bool allowLocalNetworking = parsedDrv->getBoolAttr("__darwinAllowLocalNetworking");
+
+ /* The tmpDir in scope points at the temporary build directory for our derivation. Some packages try different mechanisms
+ to find temporary directories, so we want to open up a broader place for them to put their files, if needed. */
+ Path globalTmpDir = canonPath(defaultTempDir(), true);
+
+ /* They don't like trailing slashes on subpath directives */
+ while (!globalTmpDir.empty() && globalTmpDir.back() == '/')
+ globalTmpDir.pop_back();
+
+ if (getEnv("_NIX_TEST_NO_SANDBOX") != "1") {
+ Strings sandboxArgs;
+ sandboxArgs.push_back("_GLOBAL_TMP_DIR");
+ sandboxArgs.push_back(globalTmpDir);
+ if (allowLocalNetworking) {
+ sandboxArgs.push_back("_ALLOW_LOCAL_NETWORKING");
+ sandboxArgs.push_back("1");
+ }
+ if (sandbox_init_with_parameters(sandboxProfile.c_str(), 0, stringsToCharPtrs(sandboxArgs).data(), NULL)) {
+ writeFull(STDERR_FILENO, "failed to configure sandbox\n");
+ _exit(1);
}
}
+
+ builder = drv->builder;
+ args.push_back(std::string(baseNameOf(drv->builder)));
#else
- else {
+ if (!drv->isBuiltin()) {
builder = drv->builder;
args.push_back(std::string(baseNameOf(drv->builder)));
}
--
2.46.1

View File

@ -0,0 +1,53 @@
From f8a1a149c73113e01c44b73ce9e1005575d52a9a Mon Sep 17 00:00:00 2001
From: Robert Hensing <robert@roberthensing.nl>
Date: Thu, 3 Oct 2024 12:23:17 +0200
Subject: [PATCH 2/5] packaging: Add darwin -lsandbox in meson
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
src/libstore/meson.build | 5 +++++
src/libstore/package.nix | 2 ++
2 files changed, 7 insertions(+)
diff --git a/src/libstore/meson.build b/src/libstore/meson.build
index 50b15e15d..b23c85061 100644
--- a/src/libstore/meson.build
+++ b/src/libstore/meson.build
@@ -68,6 +68,11 @@ has_acl_support = cxx.has_header('sys/xattr.h') \
and cxx.has_function('lremovexattr')
configdata.set('HAVE_ACL_SUPPORT', has_acl_support.to_int())
+if host_machine.system() == 'darwin'
+ sandbox = cxx.find_library('sandbox')
+ deps_other += [sandbox]
+endif
+
subdir('build-utils-meson/threads')
boost = dependency(
diff --git a/src/libstore/package.nix b/src/libstore/package.nix
index 4582ba0d2..d98bac16d 100644
--- a/src/libstore/package.nix
+++ b/src/libstore/package.nix
@@ -7,6 +7,7 @@
, ninja
, pkg-config
, unixtools
+, darwin
, nix-util
, boost
@@ -65,6 +66,7 @@ mkMesonDerivation (finalAttrs: {
sqlite
] ++ lib.optional stdenv.hostPlatform.isLinux libseccomp
# There have been issues building these dependencies
+ ++ lib.optional stdenv.hostPlatform.isDarwin darwin.apple_sdk.libs.sandbox
++ lib.optional (stdenv.hostPlatform == stdenv.buildPlatform && (stdenv.isLinux || stdenv.isDarwin))
aws-sdk-cpp
;
--
2.46.1

View File

@ -0,0 +1,34 @@
From ae7a2ea74136363c2f6ac6e624ea95da7abfafcc Mon Sep 17 00:00:00 2001
From: Robert Hensing <robert@roberthensing.nl>
Date: Thu, 3 Oct 2024 12:44:12 +0200
Subject: [PATCH 3/5] local-derivation-goal: Print sandbox error detail on
darwin
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-Authored-By: Théophane Hufschmitt <theophane.hufschmitt@tweag.io>
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
src/libstore/unix/build/local-derivation-goal.cc | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc
index 7ce266122..706771e8e 100644
--- a/src/libstore/unix/build/local-derivation-goal.cc
+++ b/src/libstore/unix/build/local-derivation-goal.cc
@@ -2159,8 +2159,9 @@ void LocalDerivationGoal::runChild()
sandboxArgs.push_back("_ALLOW_LOCAL_NETWORKING");
sandboxArgs.push_back("1");
}
- if (sandbox_init_with_parameters(sandboxProfile.c_str(), 0, stringsToCharPtrs(sandboxArgs).data(), NULL)) {
- writeFull(STDERR_FILENO, "failed to configure sandbox\n");
+ char * sandbox_errbuf = nullptr;
+ if (sandbox_init_with_parameters(sandboxProfile.c_str(), 0, stringsToCharPtrs(sandboxArgs).data(), &sandbox_errbuf)) {
+ writeFull(STDERR_FILENO, fmt("failed to configure sandbox: %s\n", sandbox_errbuf ? sandbox_errbuf : "(null)"));
_exit(1);
}
}
--
2.46.1

View File

@ -0,0 +1,41 @@
From 047ee50db2f660eb3f50fab8f7543ce95e814b7c Mon Sep 17 00:00:00 2001
From: Robert Hensing <robert@roberthensing.nl>
Date: Thu, 3 Oct 2024 12:50:27 +0200
Subject: [PATCH 4/5] local-derivation-goal: Refactor
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This works because the `builder` and `args` variables are only used
in the non-builtin code path.
Co-Authored-By: Théophane Hufschmitt <theophane.hufschmitt@tweag.io>
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
src/libstore/unix/build/local-derivation-goal.cc | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc
index 706771e8e..d9738a1ea 100644
--- a/src/libstore/unix/build/local-derivation-goal.cc
+++ b/src/libstore/unix/build/local-derivation-goal.cc
@@ -2165,15 +2165,12 @@ void LocalDerivationGoal::runChild()
_exit(1);
}
}
+#endif
- builder = drv->builder;
- args.push_back(std::string(baseNameOf(drv->builder)));
-#else
if (!drv->isBuiltin()) {
builder = drv->builder;
args.push_back(std::string(baseNameOf(drv->builder)));
}
-#endif
for (auto & i : drv->args)
args.push_back(rewriteStrings(i, inputRewrites));
--
2.46.1

View File

@ -0,0 +1,75 @@
From 50f83e4bbd9107576399f94449ac9cb4e80d575e Mon Sep 17 00:00:00 2001
From: Robert Hensing <robert@roberthensing.nl>
Date: Thu, 3 Oct 2024 12:57:00 +0200
Subject: [PATCH 5/5] local-derivation-goal: Move builder preparation to
non-builtin code path
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
.../unix/build/local-derivation-goal.cc | 25 ++++++++-----------
1 file changed, 10 insertions(+), 15 deletions(-)
diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc
index d9738a1ea..2a09e3dd4 100644
--- a/src/libstore/unix/build/local-derivation-goal.cc
+++ b/src/libstore/unix/build/local-derivation-goal.cc
@@ -2038,11 +2038,6 @@ void LocalDerivationGoal::runChild()
throw SysError("setuid failed");
}
- /* Fill in the arguments. */
- Strings args;
-
- std::string builder = "invalid";
-
#if __APPLE__
/* This has to appear before import statements. */
std::string sandboxProfile = "(version 1)\n";
@@ -2167,14 +2162,6 @@ void LocalDerivationGoal::runChild()
}
#endif
- if (!drv->isBuiltin()) {
- builder = drv->builder;
- args.push_back(std::string(baseNameOf(drv->builder)));
- }
-
- for (auto & i : drv->args)
- args.push_back(rewriteStrings(i, inputRewrites));
-
/* Indicate that we managed to set up the build environment. */
writeFull(STDERR_FILENO, std::string("\2\n"));
@@ -2205,6 +2192,14 @@ void LocalDerivationGoal::runChild()
}
}
+ // Now builder is not builtin
+
+ Strings args;
+ args.push_back(std::string(baseNameOf(drv->builder)));
+
+ for (auto & i : drv->args)
+ args.push_back(rewriteStrings(i, inputRewrites));
+
#if __APPLE__
posix_spawnattr_t attrp;
@@ -2226,9 +2221,9 @@ void LocalDerivationGoal::runChild()
posix_spawnattr_setbinpref_np(&attrp, 1, &cpu, NULL);
}
- posix_spawn(NULL, builder.c_str(), NULL, &attrp, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
+ posix_spawn(NULL, drv->builder.c_str(), NULL, &attrp, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
#else
- execve(builder.c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
+ execve(drv->builder.c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
#endif
throw SysError("executing '%1%'", drv->builder);
--
2.46.1

View File

@ -0,0 +1,28 @@
From 766263d53ae69d70c5915426e6e8f58abd988226 Mon Sep 17 00:00:00 2001
From: Robert Hensing <robert@roberthensing.nl>
Date: Tue, 22 Oct 2024 15:28:04 +0200
Subject: [PATCH 1/6] Fix meson build on darwin
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
std::stringbuf is defined in <sstream>
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
src/libutil/strings.cc | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/libutil/strings.cc b/src/libutil/strings.cc
index d1c9f700c..c221a43c6 100644
--- a/src/libutil/strings.cc
+++ b/src/libutil/strings.cc
@@ -1,5 +1,6 @@
#include <filesystem>
#include <string>
+#include <sstream>
#include "strings-inline.hh"
#include "os-string.hh"
--
2.46.1

View File

@ -0,0 +1,323 @@
From d2c880b03f58eb4fdd6d19eb3ffa4345a0477419 Mon Sep 17 00:00:00 2001
From: Puck Meerburg <puck@puckipedia.com>
Date: Fri, 1 Mar 2024 11:42:24 -0500
Subject: [PATCH 2/6] fix: Run all derivation builders inside the sandbox on
macOS
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
configure.ac | 6 +-
package.nix | 2 +
.../unix/build/local-derivation-goal.cc | 223 +++++++++---------
3 files changed, 116 insertions(+), 115 deletions(-)
diff --git a/configure.ac b/configure.ac
index 198198dea..c7c9b3f4b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -62,12 +62,16 @@ AC_CHECK_TOOL([AR], [ar])
AC_SYS_LARGEFILE
-# Solaris-specific stuff.
+# OS-specific stuff.
case "$host_os" in
solaris*)
# Solaris requires -lsocket -lnsl for network functions
LDFLAGS="-lsocket -lnsl $LDFLAGS"
;;
+ darwin*)
+ # Need to link to libsandbox.
+ LDFLAGS="-lsandbox $LDFLAGS"
+ ;;
esac
diff --git a/package.nix b/package.nix
index 00621d475..77f1de58c 100644
--- a/package.nix
+++ b/package.nix
@@ -23,6 +23,7 @@
, libseccomp
, libsodium
, man
+, darwin
, lowdown
, mdbook
, mdbook-linkcheck
@@ -232,6 +233,7 @@ in {
gtest
rapidcheck
] ++ lib.optional stdenv.isLinux libseccomp
+ ++ lib.optional stdenv.hostPlatform.isDarwin darwin.apple_sdk.libs.sandbox
++ lib.optional stdenv.hostPlatform.isx86_64 libcpuid
# There have been issues building these dependencies
++ lib.optional (stdenv.hostPlatform == stdenv.buildPlatform && (stdenv.isLinux || stdenv.isDarwin))
diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc
index b4685b3a7..067755c0d 100644
--- a/src/libstore/unix/build/local-derivation-goal.cc
+++ b/src/libstore/unix/build/local-derivation-goal.cc
@@ -58,6 +58,10 @@
#if __APPLE__
#include <spawn.h>
#include <sys/sysctl.h>
+#include <sandbox.h>
+
+/* This definition is undocumented but depended upon by all major browsers. */
+extern "C" int sandbox_init_with_parameters(const char *profile, uint64_t flags, const char *const parameters[], char **errorbuf);
#endif
#include <pwd.h>
@@ -2088,141 +2092,132 @@ void LocalDerivationGoal::runChild()
std::string builder = "invalid";
- if (drv->isBuiltin()) {
- ;
- }
#if __APPLE__
- else {
- /* This has to appear before import statements. */
- std::string sandboxProfile = "(version 1)\n";
-
- if (useChroot) {
-
- /* Lots and lots and lots of file functions freak out if they can't stat their full ancestry */
- PathSet ancestry;
-
- /* We build the ancestry before adding all inputPaths to the store because we know they'll
- all have the same parents (the store), and there might be lots of inputs. This isn't
- particularly efficient... I doubt it'll be a bottleneck in practice */
- for (auto & i : pathsInChroot) {
- Path cur = i.first;
- while (cur.compare("/") != 0) {
- cur = dirOf(cur);
- ancestry.insert(cur);
- }
- }
+ /* This has to appear before import statements. */
+ std::string sandboxProfile = "(version 1)\n";
+
+ if (useChroot) {
- /* And we want the store in there regardless of how empty pathsInChroot. We include the innermost
- path component this time, since it's typically /nix/store and we care about that. */
- Path cur = worker.store.storeDir;
+ /* Lots and lots and lots of file functions freak out if they can't stat their full ancestry */
+ PathSet ancestry;
+
+ /* We build the ancestry before adding all inputPaths to the store because we know they'll
+ all have the same parents (the store), and there might be lots of inputs. This isn't
+ particularly efficient... I doubt it'll be a bottleneck in practice */
+ for (auto & i : pathsInChroot) {
+ Path cur = i.first;
while (cur.compare("/") != 0) {
- ancestry.insert(cur);
cur = dirOf(cur);
+ ancestry.insert(cur);
}
+ }
- /* Add all our input paths to the chroot */
- for (auto & i : inputPaths) {
- auto p = worker.store.printStorePath(i);
- pathsInChroot[p] = p;
- }
-
- /* Violations will go to the syslog if you set this. Unfortunately the destination does not appear to be configurable */
- if (settings.darwinLogSandboxViolations) {
- sandboxProfile += "(deny default)\n";
- } else {
- sandboxProfile += "(deny default (with no-log))\n";
- }
+ /* And we want the store in there regardless of how empty pathsInChroot. We include the innermost
+ path component this time, since it's typically /nix/store and we care about that. */
+ Path cur = worker.store.storeDir;
+ while (cur.compare("/") != 0) {
+ ancestry.insert(cur);
+ cur = dirOf(cur);
+ }
- sandboxProfile +=
- #include "sandbox-defaults.sb"
- ;
+ /* Add all our input paths to the chroot */
+ for (auto & i : inputPaths) {
+ auto p = worker.store.printStorePath(i);
+ pathsInChroot[p] = p;
+ }
- if (!derivationType->isSandboxed())
- sandboxProfile +=
- #include "sandbox-network.sb"
- ;
-
- /* Add the output paths we'll use at build-time to the chroot */
- sandboxProfile += "(allow file-read* file-write* process-exec\n";
- for (auto & [_, path] : scratchOutputs)
- sandboxProfile += fmt("\t(subpath \"%s\")\n", worker.store.printStorePath(path));
-
- sandboxProfile += ")\n";
-
- /* Our inputs (transitive dependencies and any impurities computed above)
-
- without file-write* allowed, access() incorrectly returns EPERM
- */
- sandboxProfile += "(allow file-read* file-write* process-exec\n";
- for (auto & i : pathsInChroot) {
- if (i.first != i.second.source)
- throw Error(
- "can't map '%1%' to '%2%': mismatched impure paths not supported on Darwin",
- i.first, i.second.source);
-
- std::string path = i.first;
- auto optSt = maybeLstat(path.c_str());
- if (!optSt) {
- if (i.second.optional)
- continue;
- throw SysError("getting attributes of required path '%s", path);
- }
- if (S_ISDIR(optSt->st_mode))
- sandboxProfile += fmt("\t(subpath \"%s\")\n", path);
- else
- sandboxProfile += fmt("\t(literal \"%s\")\n", path);
- }
- sandboxProfile += ")\n";
+ /* Violations will go to the syslog if you set this. Unfortunately the destination does not appear to be configurable */
+ if (settings.darwinLogSandboxViolations) {
+ sandboxProfile += "(deny default)\n";
+ } else {
+ sandboxProfile += "(deny default (with no-log))\n";
+ }
- /* Allow file-read* on full directory hierarchy to self. Allows realpath() */
- sandboxProfile += "(allow file-read*\n";
- for (auto & i : ancestry) {
- sandboxProfile += fmt("\t(literal \"%s\")\n", i);
- }
- sandboxProfile += ")\n";
+ sandboxProfile +=
+ #include "sandbox-defaults.sb"
+ ;
- sandboxProfile += additionalSandboxProfile;
- } else
+ if (!derivationType->isSandboxed())
sandboxProfile +=
- #include "sandbox-minimal.sb"
+ #include "sandbox-network.sb"
;
- debug("Generated sandbox profile:");
- debug(sandboxProfile);
-
- Path sandboxFile = tmpDir + "/.sandbox.sb";
+ /* Add the output paths we'll use at build-time to the chroot */
+ sandboxProfile += "(allow file-read* file-write* process-exec\n";
+ for (auto & [_, path] : scratchOutputs)
+ sandboxProfile += fmt("\t(subpath \"%s\")\n", worker.store.printStorePath(path));
- writeFile(sandboxFile, sandboxProfile);
+ sandboxProfile += ")\n";
- bool allowLocalNetworking = parsedDrv->getBoolAttr("__darwinAllowLocalNetworking");
+ /* Our inputs (transitive dependencies and any impurities computed above)
- /* The tmpDir in scope points at the temporary build directory for our derivation. Some packages try different mechanisms
- to find temporary directories, so we want to open up a broader place for them to put their files, if needed. */
- Path globalTmpDir = canonPath(defaultTempDir(), true);
+ without file-write* allowed, access() incorrectly returns EPERM
+ */
+ sandboxProfile += "(allow file-read* file-write* process-exec\n";
+ for (auto & i : pathsInChroot) {
+ if (i.first != i.second.source)
+ throw Error(
+ "can't map '%1%' to '%2%': mismatched impure paths not supported on Darwin",
+ i.first, i.second.source);
+
+ std::string path = i.first;
+ auto optSt = maybeLstat(path.c_str());
+ if (!optSt) {
+ if (i.second.optional)
+ continue;
+ throw SysError("getting attributes of required path '%s", path);
+ }
+ if (S_ISDIR(optSt->st_mode))
+ sandboxProfile += fmt("\t(subpath \"%s\")\n", path);
+ else
+ sandboxProfile += fmt("\t(literal \"%s\")\n", path);
+ }
+ sandboxProfile += ")\n";
- /* They don't like trailing slashes on subpath directives */
- while (!globalTmpDir.empty() && globalTmpDir.back() == '/')
- globalTmpDir.pop_back();
+ /* Allow file-read* on full directory hierarchy to self. Allows realpath() */
+ sandboxProfile += "(allow file-read*\n";
+ for (auto & i : ancestry) {
+ sandboxProfile += fmt("\t(literal \"%s\")\n", i);
+ }
+ sandboxProfile += ")\n";
- if (getEnv("_NIX_TEST_NO_SANDBOX") != "1") {
- builder = "/usr/bin/sandbox-exec";
- args.push_back("sandbox-exec");
- args.push_back("-f");
- args.push_back(sandboxFile);
- args.push_back("-D");
- args.push_back("_GLOBAL_TMP_DIR=" + globalTmpDir);
- if (allowLocalNetworking) {
- args.push_back("-D");
- args.push_back(std::string("_ALLOW_LOCAL_NETWORKING=1"));
- }
- args.push_back(drv->builder);
- } else {
- builder = drv->builder;
- args.push_back(std::string(baseNameOf(drv->builder)));
+ sandboxProfile += additionalSandboxProfile;
+ } else
+ sandboxProfile +=
+ #include "sandbox-minimal.sb"
+ ;
+
+ debug("Generated sandbox profile:");
+ debug(sandboxProfile);
+
+ bool allowLocalNetworking = parsedDrv->getBoolAttr("__darwinAllowLocalNetworking");
+
+ /* The tmpDir in scope points at the temporary build directory for our derivation. Some packages try different mechanisms
+ to find temporary directories, so we want to open up a broader place for them to put their files, if needed. */
+ Path globalTmpDir = canonPath(defaultTempDir(), true);
+
+ /* They don't like trailing slashes on subpath directives */
+ while (!globalTmpDir.empty() && globalTmpDir.back() == '/')
+ globalTmpDir.pop_back();
+
+ if (getEnv("_NIX_TEST_NO_SANDBOX") != "1") {
+ Strings sandboxArgs;
+ sandboxArgs.push_back("_GLOBAL_TMP_DIR");
+ sandboxArgs.push_back(globalTmpDir);
+ if (allowLocalNetworking) {
+ sandboxArgs.push_back("_ALLOW_LOCAL_NETWORKING");
+ sandboxArgs.push_back("1");
+ }
+ if (sandbox_init_with_parameters(sandboxProfile.c_str(), 0, stringsToCharPtrs(sandboxArgs).data(), NULL)) {
+ writeFull(STDERR_FILENO, "failed to configure sandbox\n");
+ _exit(1);
}
}
+
+ builder = drv->builder;
+ args.push_back(std::string(baseNameOf(drv->builder)));
#else
- else {
+ if (!drv->isBuiltin()) {
builder = drv->builder;
args.push_back(std::string(baseNameOf(drv->builder)));
}
--
2.46.1

View File

@ -0,0 +1,53 @@
From f7335530619f9b18d6cc249a297e4dca369101a5 Mon Sep 17 00:00:00 2001
From: Robert Hensing <robert@roberthensing.nl>
Date: Thu, 3 Oct 2024 12:23:17 +0200
Subject: [PATCH 3/6] packaging: Add darwin -lsandbox in meson
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
src/libstore/meson.build | 5 +++++
src/libstore/package.nix | 2 ++
2 files changed, 7 insertions(+)
diff --git a/src/libstore/meson.build b/src/libstore/meson.build
index 6a6aabf97..c2aa5bff3 100644
--- a/src/libstore/meson.build
+++ b/src/libstore/meson.build
@@ -69,6 +69,11 @@ has_acl_support = cxx.has_header('sys/xattr.h') \
and cxx.has_function('lremovexattr')
configdata.set('HAVE_ACL_SUPPORT', has_acl_support.to_int())
+if host_machine.system() == 'darwin'
+ sandbox = cxx.find_library('sandbox')
+ deps_other += [sandbox]
+endif
+
subdir('build-utils-meson/threads')
boost = dependency(
diff --git a/src/libstore/package.nix b/src/libstore/package.nix
index 9568462b5..f04e3b95f 100644
--- a/src/libstore/package.nix
+++ b/src/libstore/package.nix
@@ -3,6 +3,7 @@
, mkMesonLibrary
, unixtools
+, darwin
, nix-util
, boost
@@ -56,6 +57,7 @@ mkMesonLibrary (finalAttrs: {
sqlite
] ++ lib.optional stdenv.hostPlatform.isLinux libseccomp
# There have been issues building these dependencies
+ ++ lib.optional stdenv.hostPlatform.isDarwin darwin.apple_sdk.libs.sandbox
++ lib.optional (stdenv.hostPlatform == stdenv.buildPlatform && (stdenv.isLinux || stdenv.isDarwin))
aws-sdk-cpp
;
--
2.46.1

View File

@ -0,0 +1,34 @@
From 14d09e0b55898ac22d4cdeade3bf6c4174052ffd Mon Sep 17 00:00:00 2001
From: Robert Hensing <robert@roberthensing.nl>
Date: Thu, 3 Oct 2024 12:44:12 +0200
Subject: [PATCH 4/6] local-derivation-goal: Print sandbox error detail on
darwin
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Co-Authored-By: Théophane Hufschmitt <theophane.hufschmitt@tweag.io>
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
src/libstore/unix/build/local-derivation-goal.cc | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc
index 067755c0d..f34d68403 100644
--- a/src/libstore/unix/build/local-derivation-goal.cc
+++ b/src/libstore/unix/build/local-derivation-goal.cc
@@ -2208,8 +2208,9 @@ void LocalDerivationGoal::runChild()
sandboxArgs.push_back("_ALLOW_LOCAL_NETWORKING");
sandboxArgs.push_back("1");
}
- if (sandbox_init_with_parameters(sandboxProfile.c_str(), 0, stringsToCharPtrs(sandboxArgs).data(), NULL)) {
- writeFull(STDERR_FILENO, "failed to configure sandbox\n");
+ char * sandbox_errbuf = nullptr;
+ if (sandbox_init_with_parameters(sandboxProfile.c_str(), 0, stringsToCharPtrs(sandboxArgs).data(), &sandbox_errbuf)) {
+ writeFull(STDERR_FILENO, fmt("failed to configure sandbox: %s\n", sandbox_errbuf ? sandbox_errbuf : "(null)"));
_exit(1);
}
}
--
2.46.1

View File

@ -0,0 +1,41 @@
From 06e27042e176b79561f50decb0fdf836b7bec3f5 Mon Sep 17 00:00:00 2001
From: Robert Hensing <robert@roberthensing.nl>
Date: Thu, 3 Oct 2024 12:50:27 +0200
Subject: [PATCH 5/6] local-derivation-goal: Refactor
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This works because the `builder` and `args` variables are only used
in the non-builtin code path.
Co-Authored-By: Théophane Hufschmitt <theophane.hufschmitt@tweag.io>
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
src/libstore/unix/build/local-derivation-goal.cc | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc
index f34d68403..f781a84c6 100644
--- a/src/libstore/unix/build/local-derivation-goal.cc
+++ b/src/libstore/unix/build/local-derivation-goal.cc
@@ -2214,15 +2214,12 @@ void LocalDerivationGoal::runChild()
_exit(1);
}
}
+#endif
- builder = drv->builder;
- args.push_back(std::string(baseNameOf(drv->builder)));
-#else
if (!drv->isBuiltin()) {
builder = drv->builder;
args.push_back(std::string(baseNameOf(drv->builder)));
}
-#endif
for (auto & i : drv->args)
args.push_back(rewriteStrings(i, inputRewrites));
--
2.46.1

View File

@ -0,0 +1,75 @@
From d1e0bae55afb3c3ef0bcad5d644b0e04da6279b3 Mon Sep 17 00:00:00 2001
From: Robert Hensing <robert@roberthensing.nl>
Date: Thu, 3 Oct 2024 12:57:00 +0200
Subject: [PATCH 6/6] local-derivation-goal: Move builder preparation to
non-builtin code path
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Jörg Thalheim <joerg@thalheim.io>
---
.../unix/build/local-derivation-goal.cc | 25 ++++++++-----------
1 file changed, 10 insertions(+), 15 deletions(-)
diff --git a/src/libstore/unix/build/local-derivation-goal.cc b/src/libstore/unix/build/local-derivation-goal.cc
index f781a84c6..dcfaadeef 100644
--- a/src/libstore/unix/build/local-derivation-goal.cc
+++ b/src/libstore/unix/build/local-derivation-goal.cc
@@ -2087,11 +2087,6 @@ void LocalDerivationGoal::runChild()
throw SysError("setuid failed");
}
- /* Fill in the arguments. */
- Strings args;
-
- std::string builder = "invalid";
-
#if __APPLE__
/* This has to appear before import statements. */
std::string sandboxProfile = "(version 1)\n";
@@ -2216,14 +2211,6 @@ void LocalDerivationGoal::runChild()
}
#endif
- if (!drv->isBuiltin()) {
- builder = drv->builder;
- args.push_back(std::string(baseNameOf(drv->builder)));
- }
-
- for (auto & i : drv->args)
- args.push_back(rewriteStrings(i, inputRewrites));
-
/* Indicate that we managed to set up the build environment. */
writeFull(STDERR_FILENO, std::string("\2\n"));
@@ -2254,6 +2241,14 @@ void LocalDerivationGoal::runChild()
}
}
+ // Now builder is not builtin
+
+ Strings args;
+ args.push_back(std::string(baseNameOf(drv->builder)));
+
+ for (auto & i : drv->args)
+ args.push_back(rewriteStrings(i, inputRewrites));
+
#if __APPLE__
posix_spawnattr_t attrp;
@@ -2275,9 +2270,9 @@ void LocalDerivationGoal::runChild()
posix_spawnattr_setbinpref_np(&attrp, 1, &cpu, NULL);
}
- posix_spawn(NULL, builder.c_str(), NULL, &attrp, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
+ posix_spawn(NULL, drv->builder.c_str(), NULL, &attrp, stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
#else
- execve(builder.c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
+ execve(drv->builder.c_str(), stringsToCharPtrs(args).data(), stringsToCharPtrs(envStrs).data());
#endif
throw SysError("executing '%1%'", drv->builder);
--
2.46.1