Skip to content

Commit 8e32f44

Browse files
lberkicopybara-github
authored andcommitted
This change makes it possible to use the Linux sandbox when either the execroot, some package path entries, or both are under /tmp.
This is achieved by a reshuffling of the sandbox directory layout in the following way: * The exec root base is `/tmp/bazel-working-directory` (cwd is under it) * Source roots are mapped under `/tmp/bazel-source-roots/$NUMBER` * The "real" exec root is mapped to `/tmp/bazel-execroot`. * All this is achieved with subtle manipulation of bind mounts: 1. The real exec root (bazel info execution_root) under `$SANDBOX/_tmp/bazel-execroot` 2. The sandbox exec root (the symlink tree the sandbox creates) under `$SANDBOX/_tmp/bazel-working-directory` 3. Each source root under `$SANDBOX/_tmp/bazel-source-roots/$NUMBER` 4. `$SANDBOX/_tmp` under `/tmp` This makes the directories in (1), (2) and (3) available as `/tmp/$NAME` even if they were originally under `/tmp` (which gets clobbered in step (4)) The functionality is gated under `--incompatible_sandbox_hermetic_tmp` since it requires `/tmp` to be in a known state, which only that flag can guarantee. Notably, putting these three directories under `/` does not work, because the non-hermetic sandbox uses the real file system and the root directory is not writable. We could conceivably get around that by bind mounting every first child of the "real" root directory in the sandbox root directory and using a writable directory as the sandbox root, but why bother if this one works. Progress towards #3236 (the flag still needs to be flipped) RELNOTES: None. PiperOrigin-RevId: 494650851 Change-Id: I0b3d1baf748a357a5bb4dee799cf807c2d75ef15
1 parent 0a2c4ed commit 8e32f44

12 files changed

+338
-110
lines changed

src/main/java/com/google/devtools/build/lib/sandbox/AbstractSandboxSpawnRunner.java

+6-2
Original file line numberDiff line numberDiff line change
@@ -310,17 +310,21 @@ private boolean wasTimeout(Duration timeout, Duration wallTime) {
310310
/**
311311
* Gets the list of directories that the spawn will assume to be writable.
312312
*
313+
* @param sandboxExecRoot the exec root of the sandbox from the point of view of the Bazel process
314+
* @param withinSandboxExecRoot the exec root from the point of view of the sandboxed processes
315+
* @param env the environment of the sandboxed processes
313316
* @throws IOException because we might resolve symlinks, which throws {@link IOException}.
314317
*/
315-
protected ImmutableSet<Path> getWritableDirs(Path sandboxExecRoot, Map<String, String> env)
318+
protected ImmutableSet<Path> getWritableDirs(
319+
Path sandboxExecRoot, Path withinSandboxExecRoot, Map<String, String> env)
316320
throws IOException {
317321
// We have to make the TEST_TMPDIR directory writable if it is specified.
318322
ImmutableSet.Builder<Path> writablePaths = ImmutableSet.builder();
319323

320324
// On Windows, sandboxExecRoot is actually the main execroot. We will specify
321325
// exactly which output path is writable.
322326
if (OS.getCurrent() != OS.WINDOWS) {
323-
writablePaths.add(sandboxExecRoot);
327+
writablePaths.add(withinSandboxExecRoot);
324328
}
325329

326330
String testTmpdir = env.get("TEST_TMPDIR");

src/main/java/com/google/devtools/build/lib/sandbox/BUILD

+1
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ java_library(
116116
"//src/main/java/com/google/devtools/build/lib/actions:execution_requirements",
117117
"//src/main/java/com/google/devtools/build/lib/vfs",
118118
"//src/main/java/com/google/devtools/build/lib/vfs:pathfragment",
119+
"//third_party:auto_value",
119120
"//third_party:flogger",
120121
"//third_party:guava",
121122
],

src/main/java/com/google/devtools/build/lib/sandbox/DarwinSandboxedSpawnRunner.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,8 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context
227227
localEnvProvider.rewriteLocalEnv(spawn.getEnvironment(), binTools, "/tmp");
228228

229229
final HashSet<Path> writableDirs = new HashSet<>(alwaysWritableDirs);
230-
ImmutableSet<Path> extraWritableDirs = getWritableDirs(sandboxExecRoot, environment);
230+
ImmutableSet<Path> extraWritableDirs =
231+
getWritableDirs(sandboxExecRoot, sandboxExecRoot, environment);
231232
writableDirs.addAll(extraWritableDirs);
232233

233234
SandboxInputs inputs =

src/main/java/com/google/devtools/build/lib/sandbox/LinuxSandboxCommandLineBuilder.java

+21-8
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@
1414

1515
package com.google.devtools.build.lib.sandbox;
1616

17+
import com.google.auto.value.AutoValue;
1718
import com.google.common.base.Preconditions;
1819
import com.google.common.collect.ImmutableList;
19-
import com.google.common.collect.ImmutableMap;
2020
import com.google.common.collect.ImmutableSet;
2121
import com.google.devtools.build.lib.actions.ExecutionRequirements;
2222
import com.google.devtools.build.lib.vfs.Path;
@@ -32,6 +32,20 @@
3232
* linux-sandbox} tool.
3333
*/
3434
public class LinuxSandboxCommandLineBuilder {
35+
/** A bind mount that needs to be present when the sandboxed command runs. */
36+
@AutoValue
37+
public abstract static class BindMount {
38+
public static BindMount of(Path mountPoint, Path source) {
39+
return new AutoValue_LinuxSandboxCommandLineBuilder_BindMount(mountPoint, source);
40+
}
41+
42+
/** "target" in mount(2) */
43+
public abstract Path getMountPoint();
44+
45+
/** "source" in mount(2) */
46+
public abstract Path getContent();
47+
}
48+
3549
private final Path linuxSandboxPath;
3650
private final List<String> commandArguments;
3751
private Path hermeticSandboxPath;
@@ -43,7 +57,7 @@ public class LinuxSandboxCommandLineBuilder {
4357
private Path stderrPath;
4458
private Set<Path> writableFilesAndDirectories = ImmutableSet.of();
4559
private ImmutableSet<PathFragment> tmpfsDirectories = ImmutableSet.of();
46-
private Map<Path, Path> bindMounts = ImmutableMap.of();
60+
private List<BindMount> bindMounts = ImmutableList.of();
4761
private Path statisticsPath;
4862
private boolean useFakeHostname = false;
4963
private boolean createNetworkNamespace = false;
@@ -140,7 +154,7 @@ public LinuxSandboxCommandLineBuilder setTmpfsDirectories(
140154
* if any.
141155
*/
142156
@CanIgnoreReturnValue
143-
public LinuxSandboxCommandLineBuilder setBindMounts(Map<Path, Path> bindMounts) {
157+
public LinuxSandboxCommandLineBuilder setBindMounts(List<BindMount> bindMounts) {
144158
this.bindMounts = bindMounts;
145159
return this;
146160
}
@@ -248,12 +262,11 @@ public ImmutableList<String> build() {
248262
for (PathFragment tmpfsPath : tmpfsDirectories) {
249263
commandLineBuilder.add("-e", tmpfsPath.getPathString());
250264
}
251-
for (Path bindMountTarget : bindMounts.keySet()) {
252-
Path bindMountSource = bindMounts.get(bindMountTarget);
253-
commandLineBuilder.add("-M", bindMountSource.getPathString());
265+
for (BindMount bindMount : bindMounts) {
266+
commandLineBuilder.add("-M", bindMount.getContent().getPathString());
254267
// The file is mounted in a custom location inside the sandbox.
255-
if (!bindMountSource.equals(bindMountTarget)) {
256-
commandLineBuilder.add("-m", bindMountTarget.getPathString());
268+
if (!bindMount.getContent().equals(bindMount.getMountPoint())) {
269+
commandLineBuilder.add("-m", bindMount.getMountPoint().getPathString());
257270
}
258271
}
259272
if (statisticsPath != null) {

0 commit comments

Comments
 (0)