diff options
Diffstat (limited to 'src/main/java')
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/runtime/LinuxSandboxUtil.java | 264 | ||||
-rw-r--r-- | src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java | 101 |
2 files changed, 77 insertions, 288 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/runtime/LinuxSandboxUtil.java b/src/main/java/com/google/devtools/build/lib/runtime/LinuxSandboxUtil.java deleted file mode 100644 index 97d6427400..0000000000 --- a/src/main/java/com/google/devtools/build/lib/runtime/LinuxSandboxUtil.java +++ /dev/null @@ -1,264 +0,0 @@ -// Copyright 2017 The Bazel Authors. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.devtools.build.lib.runtime; - -import com.google.common.base.Preconditions; -import com.google.common.collect.ImmutableList; -import com.google.devtools.build.lib.util.OsUtils; -import com.google.devtools.build.lib.vfs.Path; -import com.google.devtools.build.lib.vfs.PathFragment; -import java.time.Duration; -import java.util.Collection; -import java.util.List; -import java.util.Map; -import java.util.Optional; - -/** Utility functions for the {@code linux-sandbox} embedded tool. */ -public final class LinuxSandboxUtil { - private static final String LINUX_SANDBOX = "linux-sandbox" + OsUtils.executableExtension(); - - /** Returns whether using the {@code linux-sandbox} is supported in the command environment. */ - public static boolean isSupported(CommandEnvironment cmdEnv) { - // We can only use the linux-sandbox if the linux-sandbox exists in the embedded tools. - // This might not always be the case, e.g. while bootstrapping. - return getLinuxSandbox(cmdEnv) != null; - } - - /** Returns the path of the {@code linux-sandbox} binary, or null if it doesn't exist. */ - public static Path getLinuxSandbox(CommandEnvironment cmdEnv) { - PathFragment execPath = cmdEnv.getBlazeWorkspace().getBinTools().getExecPath(LINUX_SANDBOX); - return execPath != null ? cmdEnv.getExecRoot().getRelative(execPath) : null; - } - - /** Returns a new command line builder for the {@code linux-sandbox} tool. */ - public static CommandLineBuilder commandLineBuilder( - String linuxSandboxPath, Collection<String> commandArguments) { - return new CommandLineBuilder() - .setLinuxSandboxPath(linuxSandboxPath) - .setCommandArguments(commandArguments); - } - - /** - * A builder class for constructing the full command line to run a command using the {@code - * linux-sandbox} tool. - */ - public static class CommandLineBuilder { - // TODO(b/62588075): Reconsider using Path/PathFragment instead of Strings for file paths. - private Optional<String> linuxSandboxPath = Optional.empty(); - private Optional<String> workingDirectory = Optional.empty(); - private Optional<Duration> timeout = Optional.empty(); - private Optional<Duration> killDelay = Optional.empty(); - private Optional<String> stdoutPath = Optional.empty(); - private Optional<String> stderrPath = Optional.empty(); - private Optional<Iterable<Path>> writableFilesAndDirectories = Optional.empty(); - private Optional<Iterable<Path>> tmpfsDirectories = Optional.empty(); - private Optional<Map<Path, Path>> bindMounts = Optional.empty(); - private Optional<String> statisticsPath = Optional.empty(); - private boolean useFakeHostname = false; - private boolean createNetworkNamespace = false; - private boolean useFakeRoot = false; - private boolean useFakeUsername = false; - private boolean useDebugMode = false; - private Optional<Collection<String>> commandArguments = Optional.empty(); - - private CommandLineBuilder() { - // Prevent external construction via "new". - } - - /** Sets the path of the {@code linux-sandbox} tool. */ - public CommandLineBuilder setLinuxSandboxPath(String linuxSandboxPath) { - this.linuxSandboxPath = Optional.of(linuxSandboxPath); - return this; - } - - /** Sets the working directory to use, if any. */ - public CommandLineBuilder setWorkingDirectory(String workingDirectory) { - this.workingDirectory = Optional.of(workingDirectory); - return this; - } - - /** Sets the timeout for the command run using the {@code linux-sandbox} tool. */ - public CommandLineBuilder setTimeout(Duration timeout) { - this.timeout = Optional.of(timeout); - return this; - } - - /** - * Sets the kill delay for commands run using the {@code linux-sandbox} tool that exceed their - * timeout. - */ - public CommandLineBuilder setKillDelay(Duration killDelay) { - this.killDelay = Optional.of(killDelay); - return this; - } - - /** Sets the path to use for redirecting stdout, if any. */ - public CommandLineBuilder setStdoutPath(String stdoutPath) { - this.stdoutPath = Optional.of(stdoutPath); - return this; - } - - /** Sets the path to use for redirecting stderr, if any. */ - public CommandLineBuilder setStderrPath(String stderrPath) { - this.stderrPath = Optional.of(stderrPath); - return this; - } - - /** Sets the files or directories to make writable for the sandboxed process, if any. */ - public CommandLineBuilder setWritableFilesAndDirectories( - Iterable<Path> writableFilesAndDirectories) { - this.writableFilesAndDirectories = Optional.of(writableFilesAndDirectories); - return this; - } - - /** Sets the directories where to mount an empty tmpfs, if any. */ - public CommandLineBuilder setTmpfsDirectories(Iterable<Path> tmpfsDirectories) { - this.tmpfsDirectories = Optional.of(tmpfsDirectories); - return this; - } - - /** - * Sets the sources and targets of files or directories to explicitly bind-mount in the sandbox, - * if any. - */ - public CommandLineBuilder setBindMounts(Map<Path, Path> bindMounts) { - this.bindMounts = Optional.of(bindMounts); - return this; - } - - /** Sets the path for writing execution statistics (e.g. resource usage). */ - public CommandLineBuilder setStatisticsPath(String statisticsPath) { - this.statisticsPath = Optional.of(statisticsPath); - return this; - } - - /** Sets whether to use a fake 'localhost' hostname inside the sandbox. */ - public CommandLineBuilder setUseFakeHostname(boolean useFakeHostname) { - this.useFakeHostname = useFakeHostname; - return this; - } - - /** Sets whether to create a new network namespace. */ - public CommandLineBuilder setCreateNetworkNamespace(boolean createNetworkNamespace) { - this.createNetworkNamespace = createNetworkNamespace; - return this; - } - - /** Sets whether to pretend to be 'root' inside the namespace. */ - public CommandLineBuilder setUseFakeRoot(boolean useFakeRoot) { - this.useFakeRoot = useFakeRoot; - return this; - } - - /** Sets whether to use a fake 'nobody' username inside the sandbox. */ - public CommandLineBuilder setUseFakeUsername(boolean useFakeUsername) { - this.useFakeUsername = useFakeUsername; - return this; - } - - /** Sets whether to enable debug mode (e.g. to print debugging messages). */ - public CommandLineBuilder setUseDebugMode(boolean useDebugMode) { - this.useDebugMode = useDebugMode; - return this; - } - - /** Sets the command (and its arguments) to run using the {@code linux-sandbox} tool. */ - public CommandLineBuilder setCommandArguments(Collection<String> commandArguments) { - this.commandArguments = Optional.of(commandArguments); - return this; - } - - /** - * Builds the command line to invoke a specific command using the {@code linux-sandbox} tool. - */ - public List<String> build() { - Preconditions.checkState(this.linuxSandboxPath.isPresent(), "linuxSandboxPath is required"); - Preconditions.checkState(this.commandArguments.isPresent(), "commandArguments are required"); - Preconditions.checkState( - !(this.useFakeUsername && this.useFakeRoot), - "useFakeUsername and useFakeRoot are exclusive"); - - ImmutableList.Builder<String> commandLineBuilder = ImmutableList.<String>builder(); - - commandLineBuilder.add(linuxSandboxPath.get()); - if (workingDirectory.isPresent()) { - commandLineBuilder.add("-W", workingDirectory.get()); - } - if (timeout.isPresent()) { - commandLineBuilder.add("-T", Long.toString(timeout.get().getSeconds())); - } - if (killDelay.isPresent()) { - commandLineBuilder.add("-t", Long.toString(killDelay.get().getSeconds())); - } - if (stdoutPath.isPresent()) { - commandLineBuilder.add("-l", stdoutPath.get()); - } - if (stderrPath.isPresent()) { - commandLineBuilder.add("-L", stderrPath.get()); - } - if (writableFilesAndDirectories.isPresent()) { - for (Path writablePath : writableFilesAndDirectories.get()) { - commandLineBuilder.add("-w", writablePath.getPathString()); - } - } - if (tmpfsDirectories.isPresent()) { - for (Path tmpfsPath : tmpfsDirectories.get()) { - commandLineBuilder.add("-e", tmpfsPath.getPathString()); - } - } - if (bindMounts.isPresent()) { - for (Path bindMountTarget : bindMounts.get().keySet()) { - Path bindMountSource = bindMounts.get().get(bindMountTarget); - commandLineBuilder.add("-M", bindMountSource.getPathString()); - // The file is mounted in a custom location inside the sandbox. - if (!bindMountSource.equals(bindMountTarget)) { - commandLineBuilder.add("-m", bindMountTarget.getPathString()); - } - } - } - if (statisticsPath.isPresent()) { - commandLineBuilder.add("-S", statisticsPath.get()); - } - if (useFakeHostname) { - commandLineBuilder.add("-H"); - } - if (createNetworkNamespace) { - commandLineBuilder.add("-N"); - } - if (useFakeRoot) { - commandLineBuilder.add("-R"); - } - if (useFakeUsername) { - commandLineBuilder.add("-U"); - } - if (useDebugMode) { - commandLineBuilder.add("-D"); - } - commandLineBuilder.add("--"); - commandLineBuilder.addAll(commandArguments.get()); - - return commandLineBuilder.build(); - } - - /** - * Builds the command line to invoke a specific command using the {@code linux-sandbox} tool. - * - * @return the command line as an array of strings - */ - public String[] buildAsArray() { - return build().toArray(new String[0]); - } - } -} diff --git a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java index 00caa70d64..a16497febc 100644 --- a/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java +++ b/src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunner.java @@ -14,7 +14,6 @@ package com.google.devtools.build.lib.sandbox; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Maps; @@ -28,7 +27,6 @@ import com.google.devtools.build.lib.actions.UserExecException; import com.google.devtools.build.lib.analysis.BlazeDirectories; import com.google.devtools.build.lib.exec.local.LocalEnvProvider; import com.google.devtools.build.lib.runtime.CommandEnvironment; -import com.google.devtools.build.lib.runtime.LinuxSandboxUtil; import com.google.devtools.build.lib.shell.Command; import com.google.devtools.build.lib.shell.CommandException; import com.google.devtools.build.lib.util.OS; @@ -39,6 +37,7 @@ import com.google.devtools.build.lib.vfs.Symlinks; import java.io.File; import java.io.IOException; import java.time.Duration; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.Set; @@ -46,25 +45,30 @@ import java.util.SortedMap; /** Spawn runner that uses linux sandboxing APIs to execute a local subprocess. */ final class LinuxSandboxedSpawnRunner extends AbstractSandboxSpawnRunner { + private static final String LINUX_SANDBOX = "linux-sandbox"; public static boolean isSupported(CommandEnvironment cmdEnv) { if (OS.getCurrent() != OS.LINUX) { return false; } - if (!LinuxSandboxUtil.isSupported(cmdEnv)) { + Path embeddedTool = getLinuxSandbox(cmdEnv); + if (embeddedTool == null) { + // The embedded tool does not exist, meaning that we don't support sandboxing (e.g., while + // bootstrapping). return false; } - List<String> linuxSandboxArgv = - LinuxSandboxUtil.commandLineBuilder( - LinuxSandboxUtil.getLinuxSandbox(cmdEnv).getPathString(), - ImmutableList.of("/bin/true")) - .build(); - ImmutableMap<String, String> env = ImmutableMap.of(); Path execRoot = cmdEnv.getExecRoot(); + + List<String> args = new ArrayList<>(); + args.add(embeddedTool.getPathString()); + args.add("--"); + args.add("/bin/true"); + + ImmutableMap<String, String> env = ImmutableMap.of(); File cwd = execRoot.getPathFile(); - Command cmd = new Command(linuxSandboxArgv.toArray(new String[0]), env, cwd); + Command cmd = new Command(args.toArray(new String[0]), env, cwd); try { cmd.execute(ByteStreams.nullOutputStream(), ByteStreams.nullOutputStream()); } catch (CommandException e) { @@ -74,6 +78,11 @@ final class LinuxSandboxedSpawnRunner extends AbstractSandboxSpawnRunner { return true; } + private static Path getLinuxSandbox(CommandEnvironment cmdEnv) { + PathFragment execPath = cmdEnv.getBlazeWorkspace().getBinTools().getExecPath(LINUX_SANDBOX); + return execPath != null ? cmdEnv.getExecRoot().getRelative(execPath) : null; + } + private final FileSystem fileSystem; private final BlazeDirectories blazeDirs; private final Path execRoot; @@ -98,7 +107,7 @@ final class LinuxSandboxedSpawnRunner extends AbstractSandboxSpawnRunner { this.execRoot = cmdEnv.getExecRoot(); this.productName = productName; this.allowNetwork = SandboxHelpers.shouldAllowNetwork(cmdEnv.getOptions()); - this.linuxSandbox = LinuxSandboxUtil.getLinuxSandbox(cmdEnv); + this.linuxSandbox = getLinuxSandbox(cmdEnv); this.inaccessibleHelperFile = inaccessibleHelperFile; this.inaccessibleHelperDir = inaccessibleHelperDir; this.timeoutGraceSeconds = timeoutGraceSeconds; @@ -153,23 +162,67 @@ final class LinuxSandboxedSpawnRunner extends AbstractSandboxSpawnRunner { Map<Path, Path> bindMounts, boolean allowNetwork, boolean requiresFakeRoot) { - LinuxSandboxUtil.CommandLineBuilder commandLineBuilder = - LinuxSandboxUtil.commandLineBuilder(linuxSandbox.getPathString(), spawn.getArguments()) - .setWritableFilesAndDirectories(writableDirs) - .setTmpfsDirectories(tmpfsPaths) - .setBindMounts(bindMounts) - .setUseFakeHostname(getSandboxOptions().sandboxFakeHostname) - .setCreateNetworkNamespace(!allowNetwork) - .setUseFakeRoot(requiresFakeRoot) - .setUseFakeUsername(getSandboxOptions().sandboxFakeUsername) - .setUseDebugMode(getSandboxOptions().sandboxDebug); + List<String> commandLineArgs = new ArrayList<>(); + commandLineArgs.add(linuxSandbox.getPathString()); + + if (getSandboxOptions().sandboxDebug) { + commandLineArgs.add("-D"); + } + + // Kill the process after a timeout. if (!timeout.isZero()) { - commandLineBuilder.setTimeout(timeout); + commandLineArgs.add("-T"); + commandLineArgs.add(Long.toString(timeout.getSeconds())); } + if (timeoutGraceSeconds != -1) { - commandLineBuilder.setKillDelay(Duration.ofSeconds(timeoutGraceSeconds)); + commandLineArgs.add("-t"); + commandLineArgs.add(Integer.toString(timeoutGraceSeconds)); } - return commandLineBuilder.build(); + + // Create all needed directories. + for (Path writablePath : writableDirs) { + commandLineArgs.add("-w"); + commandLineArgs.add(writablePath.getPathString()); + } + + for (Path tmpfsPath : tmpfsPaths) { + commandLineArgs.add("-e"); + commandLineArgs.add(tmpfsPath.getPathString()); + } + + for (ImmutableMap.Entry<Path, Path> bindMount : bindMounts.entrySet()) { + commandLineArgs.add("-M"); + commandLineArgs.add(bindMount.getValue().getPathString()); + + // The file is mounted in a custom location inside the sandbox. + if (!bindMount.getKey().equals(bindMount.getValue())) { + commandLineArgs.add("-m"); + commandLineArgs.add(bindMount.getKey().getPathString()); + } + } + + if (!allowNetwork) { + // Block network access out of the namespace. + commandLineArgs.add("-N"); + } + + if (getSandboxOptions().sandboxFakeHostname) { + // Use a fake hostname ("localhost") inside the sandbox. + commandLineArgs.add("-H"); + } + + if (requiresFakeRoot) { + // Use fake root. + commandLineArgs.add("-R"); + } else if (getSandboxOptions().sandboxFakeUsername) { + // Use a fake username ("nobody") inside the sandbox. + commandLineArgs.add("-U"); + } + + commandLineArgs.add("--"); + commandLineArgs.addAll(spawn.getArguments()); + return commandLineArgs; } @Override |