diff options
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxRunner.java | 16 | ||||
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedStrategy.java | 17 | ||||
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java | 3 | ||||
-rw-r--r-- | src/main/tools/linux-sandbox-options.cc | 24 | ||||
-rw-r--r-- | src/main/tools/linux-sandbox-options.h | 8 | ||||
-rw-r--r-- | src/main/tools/linux-sandbox-pid1.cc | 87 | ||||
-rw-r--r-- | src/main/tools/linux-sandbox.cc | 19 | ||||
-rw-r--r-- | src/main/tools/linux-sandbox.h | 1 |
8 files changed, 152 insertions, 23 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxRunner.java index cceed552ba..c3d36a615f 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxRunner.java @@ -15,6 +15,7 @@ package com.google.devtools.build.lib.sandbox; import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; import com.google.common.io.ByteStreams; import com.google.devtools.build.lib.runtime.CommandEnvironment; import com.google.devtools.build.lib.shell.Command; @@ -40,25 +41,31 @@ final class LinuxSandboxRunner extends SandboxRunner { private final Path execRoot; private final Path sandboxExecRoot; + private final Path sandboxTempDir; private final Path argumentsFilePath; private final Set<Path> writableDirs; private final Set<Path> inaccessiblePaths; + private final Set<Path> bindMounts; private final boolean sandboxDebug; LinuxSandboxRunner( Path execRoot, Path sandboxPath, Path sandboxExecRoot, + Path sandboxTempDir, Set<Path> writableDirs, Set<Path> inaccessiblePaths, + ImmutableSet<Path> bindMounts, boolean verboseFailures, boolean sandboxDebug) { super(sandboxPath, sandboxExecRoot, verboseFailures); this.execRoot = execRoot; this.sandboxExecRoot = sandboxExecRoot; + this.sandboxTempDir = sandboxTempDir; this.argumentsFilePath = sandboxPath.getRelative("linux-sandbox.params"); this.writableDirs = writableDirs; this.inaccessiblePaths = inaccessiblePaths; + this.bindMounts = bindMounts; this.sandboxDebug = sandboxDebug; } @@ -116,6 +123,10 @@ final class LinuxSandboxRunner extends SandboxRunner { fileArgs.add("-D"); } + // Temporary directory of the sandbox. + fileArgs.add("-S"); + fileArgs.add(sandboxTempDir.toString()); + // Working directory of the spawn. fileArgs.add("-W"); fileArgs.add(sandboxExecRoot.toString()); @@ -137,6 +148,11 @@ final class LinuxSandboxRunner extends SandboxRunner { fileArgs.add(inaccessiblePath.getPathString()); } + for (Path bindMount : bindMounts) { + fileArgs.add("-b"); + fileArgs.add(bindMount.getPathString()); + } + if (!allowNetwork) { // Block network access out of the namespace. fileArgs.add("-N"); diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedStrategy.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedStrategy.java index be49446a82..0e38d0d891 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedStrategy.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedStrategy.java @@ -101,6 +101,7 @@ public class LinuxSandboxedStrategy extends SandboxStrategy { // Each invocation of "exec" gets its own sandbox. Path sandboxPath = SandboxHelpers.getSandboxRoot(blazeDirs, productName, uuid, execCounter); Path sandboxExecRoot = sandboxPath.getRelative("execroot").getRelative(execRoot.getBaseName()); + Path sandboxTempDir = sandboxPath.getRelative("tmp"); try { @@ -110,6 +111,7 @@ public class LinuxSandboxedStrategy extends SandboxStrategy { Set<Path> writableDirs = getWritableDirs(sandboxExecRoot, spawn.getEnvironment(), outputs); symlinkedExecRoot.createFileSystem( getMounts(spawn, actionExecutionContext), outputs, writableDirs); + sandboxTempDir.createDirectory(); final SandboxRunner runner; if (fullySupported) { @@ -118,8 +120,10 @@ public class LinuxSandboxedStrategy extends SandboxStrategy { execRoot, sandboxPath, sandboxExecRoot, + sandboxTempDir, getWritableDirs(sandboxExecRoot, spawn.getEnvironment(), outputs), getInaccessiblePaths(), + getBindMounts(blazeDirs), verboseFailures, sandboxOptions.sandboxDebug); } else { @@ -143,4 +147,17 @@ public class LinuxSandboxedStrategy extends SandboxStrategy { } } + private ImmutableSet<Path> getBindMounts(BlazeDirectories blazeDirs) { + Path tmpPath = blazeDirs.getFileSystem().getPath("/tmp"); + ImmutableSet.Builder<Path> bindMounts = ImmutableSet.builder(); + if (blazeDirs.getWorkspace().startsWith(tmpPath)) { + + bindMounts.add(blazeDirs.getWorkspace()); + } + if (blazeDirs.getOutputBase().startsWith(tmpPath)) { + bindMounts.add(blazeDirs.getOutputBase()); + } + return bindMounts.build(); + } + } diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java index 8573ad5fa7..004c8dffbc 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/SandboxHelpers.java @@ -128,7 +128,6 @@ final class SandboxHelpers { return blazeDirs .getOutputBase() .getRelative(productName + "-sandbox") - .getRelative(uuid + "-" + execCounter.getAndIncrement()) - .getRelative(blazeDirs.getExecRoot().getBaseName()); + .getRelative(uuid + "-" + execCounter.getAndIncrement()); } } diff --git a/src/main/tools/linux-sandbox-options.cc b/src/main/tools/linux-sandbox-options.cc index 0b43a2cfb4..49e6eafeeb 100644 --- a/src/main/tools/linux-sandbox-options.cc +++ b/src/main/tools/linux-sandbox-options.cc @@ -69,6 +69,7 @@ static void Usage(char *program_name, const char *fmt, ...) { " -i <file> make a file or directory inaccessible for the " "sandboxed process\n" " -e <dir> mount an empty tmpfs on a directory\n" + " -b <dir> bind mount a file or directory inside the sandbox\n" " -N if set, a new network namespace will be created\n" " -R if set, make the uid/gid be root, otherwise use nobody\n" " -D if set, debug info will be printed\n" @@ -112,13 +113,25 @@ static void ParseCommandLine(unique_ptr<vector<char *>> args) { extern int optind, optopt; int c; - while ((c = getopt(args->size(), args->data(), ":CS:W:T:t:l:L:w:i:e:NRD")) != - -1) { + while ((c = getopt(args->size(), args->data(), + ":CS:W:T:t:l:L:w:i:e:b:NRD")) != -1) { switch (c) { case 'C': // Shortcut for the "does this system support sandboxing" check. exit(CheckNamespacesSupported()); break; + case 'S': + if (opt.sandbox_root_dir == NULL) { + if (optarg[0] != '/') { + Usage(args->front(), + "The -r option must be used with absolute paths only."); + } + opt.sandbox_root_dir = strdup(optarg); + } else { + Usage(args->front(), + "Multiple root directories (-r) specified, expected one."); + } + break; case 'W': if (opt.working_dir == NULL) { if (optarg[0] != '/') { @@ -180,6 +193,13 @@ static void ParseCommandLine(unique_ptr<vector<char *>> args) { } opt.tmpfs_dirs.push_back(strdup(optarg)); break; + case 'b': + if (optarg[0] != '/') { + Usage(args->front(), + "The -b option must be used with absolute paths only."); + } + opt.bind_mounts.push_back(strdup(optarg)); + break; case 'N': opt.create_netns = true; break; diff --git a/src/main/tools/linux-sandbox-options.h b/src/main/tools/linux-sandbox-options.h index 57004a8206..5554f9e41f 100644 --- a/src/main/tools/linux-sandbox-options.h +++ b/src/main/tools/linux-sandbox-options.h @@ -22,6 +22,8 @@ // Options parsing result. struct Options { + // Temporary root directory (-S) + const char *sandbox_root_dir; // Working directory (-W) const char *working_dir; // How long to wait before killing the child (-T) @@ -32,12 +34,14 @@ struct Options { const char *stdout_path; // Where to redirect stderr (-L) const char *stderr_path; - // Files to make writable for the sandboxed process (-w) + // Files or directories to make writable for the sandboxed process (-w) std::vector<const char *> writable_files; - // Files to make inaccessible for the sandboxed process (-i) + // Files or directories to make inaccessible for the sandboxed process (-i) std::vector<const char *> inaccessible_files; // Directories where to mount an empty tmpfs (-e) std::vector<const char *> tmpfs_dirs; + // Files or directories to explicitly bind mount into the sandbox (-b) + std::vector<const char *> bind_mounts; // Create a new network namespace (-N) bool create_netns; // Pretend to be root inside the namespace (-R) diff --git a/src/main/tools/linux-sandbox-pid1.cc b/src/main/tools/linux-sandbox-pid1.cc index 1dd049a1f4..dc6aadc84f 100644 --- a/src/main/tools/linux-sandbox-pid1.cc +++ b/src/main/tools/linux-sandbox-pid1.cc @@ -55,8 +55,8 @@ #include <unistd.h> static int global_child_pid; -static char global_inaccessible_directory[] = "/tmp/empty.XXXXXX"; -static char global_inaccessible_file[] = "/tmp/empty.XXXXXX"; +static char global_inaccessible_directory[] = "tmp/empty.XXXXXX"; +static char global_inaccessible_file[] = "tmp/empty.XXXXXX"; static void SetupSelfDestruction(int *sync_pipe) { // We could also poll() on the pipe fd to find out when the parent goes away, @@ -167,13 +167,74 @@ static void SetupHelperFiles() { } } +static bool IsDirectory(const char *path) { + struct stat sb; + if (stat(path, &sb) < 0) { + DIE("stat(%s)", path); + } + return S_ISDIR(sb.st_mode); +} + +// Recursively creates the file or directory specified in "path" and its parent +// directories. +static int CreateTarget(const char *path, bool is_directory) { + PRINT_DEBUG("CreateTarget(%s, %s)", path, is_directory ? "true" : "false"); + if (path == NULL) { + errno = EINVAL; + return -1; + } + + struct stat sb; + // If the path already exists... + if (stat(path, &sb) == 0) { + if (is_directory && S_ISDIR(sb.st_mode)) { + // and it's a directory and supposed to be a directory, we're done here. + return 0; + } else if (!is_directory && S_ISREG(sb.st_mode)) { + // and it's a regular file and supposed to be one, we're done here. + return 0; + } else { + // otherwise something is really wrong. + errno = is_directory ? ENOTDIR : EEXIST; + return -1; + } + } else { + // If stat failed because of any error other than "the path does not exist", + // this is an error. + if (errno != ENOENT) { + return -1; + } + } + + // Create the parent directory. + if (CreateTarget(dirname(strdupa(path)), true) < 0) { + DIE("CreateTarget(%s, true)", dirname(strdupa(path))); + } + + if (is_directory) { + if (mkdir(path, 0755) < 0) { + DIE("mkdir(%s, 0755)", path); + } + } else { + int handle; + if ((handle = open(path, O_CREAT | O_WRONLY | O_EXCL, 0666)) < 0) { + DIE("open(%s, O_CREAT | O_WRONLY | O_EXCL, 0666)", path); + } + if (close(handle) < 0) { + DIE("close(%d)", handle); + } + } + + return 0; +} + static void MountFilesystems() { - if (mount("/", global_sandbox_root, NULL, MS_BIND | MS_REC, NULL) < 0) { - DIE("mount(/, %s, NULL, MS_BIND | MS_REC, NULL)", global_sandbox_root); + if (mount("/", opt.sandbox_root_dir, NULL, MS_BIND | MS_REC, NULL) < 0) { + DIE("mount(/, %s, NULL, MS_BIND | MS_REC, NULL)", opt.sandbox_root_dir); } - if (chdir(global_sandbox_root) < 0) { - DIE("chdir(%s)", global_sandbox_root); + if (chdir(opt.sandbox_root_dir) < 0) { + DIE("chdir(%s)", opt.sandbox_root_dir); } for (const char *tmpfs_dir : opt.tmpfs_dirs) { @@ -187,11 +248,21 @@ static void MountFilesystems() { // Make sure that our working directory is a mount point. The easiest way to // do this is by bind-mounting it upon itself. + PRINT_DEBUG("working dir: %s", opt.working_dir); + CreateTarget(opt.working_dir + 1, true); if (mount(opt.working_dir, opt.working_dir + 1, NULL, MS_BIND, NULL) < 0) { DIE("mount(%s, %s, NULL, MS_BIND, NULL)", opt.working_dir, opt.working_dir + 1); } + for (const char *bind_mount : opt.bind_mounts) { + PRINT_DEBUG("bind mount: %s", bind_mount); + CreateTarget(bind_mount + 1, IsDirectory(bind_mount)); + if (mount(bind_mount, bind_mount + 1, NULL, MS_BIND, NULL) < 0) { + DIE("mount(%s, %s, NULL, MS_BIND, NULL)", bind_mount, bind_mount + 1); + } + } + for (const char *writable_file : opt.writable_files) { PRINT_DEBUG("writable: %s", writable_file); if (mount(writable_file, writable_file + 1, NULL, MS_BIND, NULL) < 0) { @@ -229,7 +300,7 @@ static void MountFilesystems() { // We later remount everything read-only, except the paths for which this method // returns true. static bool ShouldBeWritable(char *mnt_dir) { - mnt_dir += strlen(global_sandbox_root); + mnt_dir += strlen(opt.sandbox_root_dir); if (strcmp(mnt_dir, opt.working_dir) == 0) { return true; @@ -261,7 +332,7 @@ static void MakeFilesystemMostlyReadOnly() { struct mntent *ent; while ((ent = getmntent(mounts)) != NULL) { // Skip mounts that do not belong to our sandbox. - if (strstr(ent->mnt_dir, global_sandbox_root) != ent->mnt_dir) { + if (strstr(ent->mnt_dir, opt.sandbox_root_dir) != ent->mnt_dir) { continue; } diff --git a/src/main/tools/linux-sandbox.cc b/src/main/tools/linux-sandbox.cc index 8eeef841e6..865bf86ea2 100644 --- a/src/main/tools/linux-sandbox.cc +++ b/src/main/tools/linux-sandbox.cc @@ -72,8 +72,8 @@ int global_outer_uid; int global_outer_gid; -char global_sandbox_root[] = "/tmp/sandbox.XXXXXX"; +static char global_sandbox_root[] = "/tmp/sandbox.XXXXXX"; static int global_child_pid; // The signal that will be sent to the child when a timeout occurs. @@ -119,18 +119,22 @@ static void CloseFds() { } } -static void SetupSandboxRoot() { - if (mkdtemp(global_sandbox_root) == NULL) { - DIE("mkdtemp(%s)", global_sandbox_root); - } -} - static void RemoveSandboxRoot() { if (rmdir(global_sandbox_root) < 0) { DIE("rmdir(%s)", global_sandbox_root); } } +static void SetupSandboxRoot() { + if (opt.sandbox_root_dir == NULL) { + if (mkdtemp(global_sandbox_root) == NULL) { + DIE("mkdtemp(%s)", global_sandbox_root); + } + atexit(RemoveSandboxRoot); + opt.sandbox_root_dir = global_sandbox_root; + } +} + static void HandleSignal(int signum, void (*handler)(int)) { struct sigaction sa; memset(&sa, 0, sizeof(sa)); @@ -277,7 +281,6 @@ int main(int argc, char *argv[]) { CloseFds(); SetupSandboxRoot(); - atexit(RemoveSandboxRoot); HandleSignal(SIGALRM, OnTimeout); if (opt.timeout_secs > 0) { diff --git a/src/main/tools/linux-sandbox.h b/src/main/tools/linux-sandbox.h index 5ff778da38..df07dee39f 100644 --- a/src/main/tools/linux-sandbox.h +++ b/src/main/tools/linux-sandbox.h @@ -17,6 +17,5 @@ extern int global_outer_uid; extern int global_outer_gid; -extern char global_sandbox_root[]; #endif |