bazelbuild / bazelbuild/bazel

hermetic linux-sandbox: mount target creation can follow pre-seeded symlinks under sandbox_root and create host paths outside sandbox_root

Open
#28,515 4 comments 0 reactions 1 assignee Claimed by @meisterT View on GitHub
category: sandboxing P3 team-Local-Exec type: bug
Dominant language
Java
Stars
25.8k
Forks
4.6k
Avg merge
2d 20h
Merged PRs (30d)
72

Description

### summary
hermetic mode in `linux-sandbox` can create directories (and potentially other filesystem artifacts, depending on the target type) on the host filesystem outside `sandbox_root` if a parent path component under `sandbox_root` is a pre-seeded symlink. this is not a claim about bazel’s security threat model; it is a robustness/hardening issue because it breaks the “writes stay within the sandbox directory” expectation for hermetic sandbox setup.

### affected code (as-of)
- repo: `bazelbuild/bazel`
- observed at commit: `1d52819e40b1bcb76f8f02cff3edd4b972dd69b7`
- callsites:
- `src/main/tools/linux-sandbox-pid1.cc` `CreateTarget(...)` (uses `stat()` on paths under `opt.sandbox_root`)
- `src/main/tools/linux-sandbox-pid1.cc` mount setup path building (`full_sandbox_path = opt.sandbox_root + target`), then `CreateTarget(full_sandbox_path, ...)`

### why this matters (robustness, not “security boundary”)
- hermetic setup code runs before `pivot_root`/`chroot` and performs host filesystem operations to prepare mount targets under `sandbox_root`.
- if `sandbox_root/` is (unexpectedly) a symlink, `stat()` will follow it and subsequent `mkdir()` / `link()` / etc will operate outside `sandbox_root`.
- this can cause surprising host writes outside the intended sandbox directory in cases like sandbox directory reuse, misconfigured sandbox roots, or other accidental pre-seeding.

### minimal reproduction (conceptual)
1) pick a writable host directory outside the sandbox root, e.g. `outside_root=/tmp/outside_root`.
2) create a sandbox root and pre-seed a symlink inside it:
- `sandbox_root=/tmp/sandbox_root`
- `mkdir -p "$sandbox_root"`
- `ln -s "$outside_root" "$sandbox_root/mnt"`
3) run hermetic `linux-sandbox` with a mount target that requires creating `sandbox_root/mnt/` (exact flags depend on the invocation; the key is that the target path is under `/mnt/...` in the sandbox and is realized as `sandbox_root + "/mnt/..."` during setup).
4) observe that `"$outside_root/"` gets created on the host, i.e. outside `sandbox_root`.

expected: mount target creation should not traverse symlinks under `sandbox_root` (either fail fast or do secure no-follow traversal).
actual: symlink traversal occurs via `stat()`-based checks, enabling setup to write outside `sandbox_root`.

### suggested fix direction
any of these would address the class:
- fail fast: when creating/validating any path under `sandbox_root`, reject if any parent component is a symlink (use `lstat()` per component).
- secure traversal: walk the path with `openat()` + `O_NOFOLLOW` (and/or equivalent) and create components relative to an fd anchored at `sandbox_root`, never by concatenating strings and calling `stat()`/`mkdir()` on the full path.
- add a small regression test (unit/integration) asserting that a symlink under `sandbox_root` does not allow creating mount targets outside `sandbox_root`.

### question
would you accept a pr that implements “no symlink traversal under `sandbox_root`” (either fail-fast or `openat`-based traversal) and adds a regression test for this scenario?

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.