Skip to content

Commit 620d617

Browse files
fmeumcopybara-github
authored andcommitted
Add support for tmpfs mounts under /tmp with hermetic tmp
This is achieved by mounting tmpfs after regular mounts in the sandbox binary as well as creating the directories at which tmpfs are mounted under the sandbox tmp directory. Closes #20658. PiperOrigin-RevId: 595500822 Change-Id: Icf3d51bffdd004f668ba4fbbdbd5f833c20db3d9
1 parent 9b027c8 commit 620d617

4 files changed

Lines changed: 60 additions & 48 deletions

File tree

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

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,6 @@
5959
import java.time.Duration;
6060
import java.util.HashMap;
6161
import java.util.Map;
62-
import java.util.Optional;
6362
import java.util.SortedMap;
6463
import java.util.concurrent.atomic.AtomicBoolean;
6564
import javax.annotation.Nullable;
@@ -74,7 +73,6 @@ final class LinuxSandboxedSpawnRunner extends AbstractSandboxSpawnRunner {
7473

7574
// Since checking if sandbox is supported is expensive, we remember what we've checked.
7675
private static final Map<Path, Boolean> isSupportedMap = new HashMap<>();
77-
private static final AtomicBoolean warnedAboutNonHermeticTmp = new AtomicBoolean();
7876

7977
private static final AtomicBoolean warnedAboutUnsupportedModificationCheck = new AtomicBoolean();
8078

@@ -205,22 +203,6 @@ private boolean useHermeticTmp() {
205203
return false;
206204
}
207205

208-
Optional<PathFragment> tmpfsPathUnderTmp =
209-
getSandboxOptions().sandboxTmpfsPath.stream()
210-
.filter(path -> path.startsWith(SLASH_TMP))
211-
.findFirst();
212-
if (tmpfsPathUnderTmp.isPresent()) {
213-
if (warnedAboutNonHermeticTmp.compareAndSet(false, true)) {
214-
reporter.handle(
215-
Event.warn(
216-
String.format(
217-
"Falling back to non-hermetic '/tmp' in sandbox due to '%s' being a tmpfs path",
218-
tmpfsPathUnderTmp.get())));
219-
}
220-
221-
return false;
222-
}
223-
224206
return true;
225207
}
226208

@@ -295,6 +277,16 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context
295277

296278
createDirectoryWithinSandboxTmp(sandboxTmp, withinSandboxExecRoot);
297279
createDirectoryWithinSandboxTmp(sandboxTmp, withinSandboxWorkingDirectory);
280+
281+
for (PathFragment pathFragment : getSandboxOptions().sandboxTmpfsPath) {
282+
Path path = fileSystem.getPath(pathFragment);
283+
if (path.startsWith(SLASH_TMP)) {
284+
// tmpfs mount points must exist, which is usually the user's responsibility. But if the
285+
// user requests a tmpfs mount under /tmp, we have to create it under the sandbox tmp
286+
// directory.
287+
createDirectoryWithinSandboxTmp(sandboxTmp, path);
288+
}
289+
}
298290
}
299291

300292
SandboxOutputs outputs = helpers.getOutputs(spawn);

src/main/tools/linux-sandbox-pid1.cc

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -271,15 +271,6 @@ static void SetupUtsNamespace() {
271271
}
272272

273273
static void MountFilesystems() {
274-
for (const std::string &tmpfs_dir : opt.tmpfs_dirs) {
275-
PRINT_DEBUG("tmpfs: %s", tmpfs_dir.c_str());
276-
if (mount("tmpfs", tmpfs_dir.c_str(), "tmpfs",
277-
MS_NOSUID | MS_NODEV | MS_NOATIME, nullptr) < 0) {
278-
DIE("mount(tmpfs, %s, tmpfs, MS_NOSUID | MS_NODEV | MS_NOATIME, nullptr)",
279-
tmpfs_dir.c_str());
280-
}
281-
}
282-
283274
// An attempt to mount the sandbox in tmpfs will always fail, so this block is
284275
// slightly redundant with the next mount() check, but dumping the mount()
285276
// syscall is incredibly cryptic, so we explicitly check against and warn
@@ -307,6 +298,15 @@ static void MountFilesystems() {
307298
}
308299
}
309300

301+
for (const std::string &tmpfs_dir : opt.tmpfs_dirs) {
302+
PRINT_DEBUG("tmpfs: %s", tmpfs_dir.c_str());
303+
if (mount("tmpfs", tmpfs_dir.c_str(), "tmpfs",
304+
MS_NOSUID | MS_NODEV | MS_NOATIME, nullptr) < 0) {
305+
DIE("mount(tmpfs, %s, tmpfs, MS_NOSUID | MS_NODEV | MS_NOATIME, nullptr)",
306+
tmpfs_dir.c_str());
307+
}
308+
}
309+
310310
for (const std::string &writable_file : opt.writable_files) {
311311
PRINT_DEBUG("writable: %s", writable_file.c_str());
312312
if (bind_mount_sources.find(writable_file) != bind_mount_sources.end()) {

src/test/java/com/google/devtools/build/lib/sandbox/LinuxSandboxedSpawnRunnerTest.java

Lines changed: 0 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -203,27 +203,6 @@ public void hermeticTmp_sandboxTmpfsOnTmp_tmpNotCreatedOrMounted() throws Except
203203
assertThat(args).doesNotContain("-m /tmp");
204204
}
205205

206-
@Test
207-
public void hermeticTmp_sandboxTmpfsUnderTmp_tmpNotCreatedOrMounted() throws Exception {
208-
runtimeWrapper.addOptions(
209-
"--incompatible_sandbox_hermetic_tmp", "--sandbox_tmpfs_path=/tmp/subdir");
210-
CommandEnvironment commandEnvironment = createCommandEnvironment();
211-
LinuxSandboxedSpawnRunner runner = setupSandboxAndCreateRunner(commandEnvironment);
212-
Spawn spawn = new SpawnBuilder().build();
213-
SandboxedSpawn sandboxedSpawn = runner.prepareSpawn(spawn, createSpawnExecutionContext(spawn));
214-
215-
Path sandboxPath =
216-
sandboxedSpawn.getSandboxExecRoot().getParentDirectory().getParentDirectory();
217-
Path hermeticTmpPath = sandboxPath.getRelative("_hermetic_tmp");
218-
assertThat(hermeticTmpPath.isDirectory()).isFalse();
219-
220-
assertThat(sandboxedSpawn).isInstanceOf(SymlinkedSandboxedSpawn.class);
221-
String args = String.join(" ", sandboxedSpawn.getArguments());
222-
assertThat(args).contains("-w /tmp");
223-
assertThat(args).contains("-e /tmp");
224-
assertThat(args).doesNotContain("-m /tmp");
225-
}
226-
227206
private static LinuxSandboxedSpawnRunner setupSandboxAndCreateRunner(
228207
CommandEnvironment commandEnvironment) throws IOException {
229208
Path execRoot = commandEnvironment.getExecRoot();

src/test/shell/bazel/bazel_sandboxing_test.sh

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -423,6 +423,47 @@ EOF
423423
bazel --output_base="$tmp_output_base" shutdown
424424
}
425425

426+
function test_tmpfs_path_under_tmp() {
427+
if [[ "$PLATFORM" == "darwin" ]]; then
428+
# Tests Linux-specific functionality
429+
return 0
430+
fi
431+
432+
create_workspace_with_default_repos WORKSPACE
433+
434+
sed -i.bak '/sandbox_tmpfs_path/d' $TEST_TMPDIR/bazelrc
435+
436+
local tmp_file=$(mktemp "/tmp/bazel_tmp.XXXXXXXX")
437+
trap "rm $tmp_file" EXIT
438+
echo BAD > "$tmp_file"
439+
440+
local tmpfs=$(mktemp -d "/tmp/bazel_tmpfs.XXXXXXXX")
441+
trap "rm -fr $tmpfs" EXIT
442+
echo BAD > "$tmpfs/data.txt"
443+
444+
mkdir -p pkg
445+
cat > pkg/BUILD <<EOF
446+
genrule(
447+
name = "gen",
448+
outs = ["gen.txt"],
449+
cmd = """
450+
# Verify that /tmp is still hermetic and that the tmpfs under /tmp exists, but is empty.
451+
[[ ! -e "${tmp_file}" ]]
452+
[[ -d /tmp/tmpfs ]]
453+
[[ ! -e /tmp/tmpfs/data.txt ]]
454+
# Verify that the tmpfs on /etc exists and is empty.
455+
[[ -d /etc ]]
456+
[[ -z "\$\$(ls -A /etc)" ]]
457+
touch \$@
458+
""",
459+
)
460+
EOF
461+
462+
# This assumes the existence of /etc on the host system
463+
bazel build --sandbox_tmpfs_path=/tmp/tmpfs --sandbox_tmpfs_path=/etc \
464+
//pkg:gen || fail "build failed"
465+
}
466+
426467
# The test shouldn't fail if the environment doesn't support running it.
427468
check_sandbox_allowed || exit 0
428469

0 commit comments

Comments
 (0)