-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add --incompatible_sandbox_hermetic_tmp #16336
Conversation
c870104
to
d984b32
Compare
@larsrc-google @philwo Would you be able to take a look? This realizes #3236 (comment) as an incompatible flag as I believe these kind of bugs should not occur with default settings. Happy to discuss alternative ways to ship this though. |
@@ -283,9 +289,15 @@ protected ImmutableSet<Path> getWritableDirs(Path sandboxExecRoot, Map<String, S | |||
} | |||
|
|||
private SortedMap<Path, Path> getReadOnlyBindMounts( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it just me, or is this very poorly named?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not just you, it was very poorly named after my changes. Fixed it.
// host filesystem's /tmp. User-specified bind mounts can override this and use the host's | ||
// /tmp instead by mounting /tmp to /tmp, if desired. | ||
bindMounts.put(tmpPath, sandboxTmp); | ||
} | ||
if (blazeDirs.getWorkspace().startsWith(tmpPath)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do these two following remounts work with hermetic tmp?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not as far as I can tell. I used the commands from test_write_hermetic_tmp
to run it within /tmp
and elsewhere, and it works elsewhere but fails within /tmp
:
$ /tmp/bazel-hermeticsandbox --bazelrc=/dev/null test pkg:tmp_test --spawn_strategy=sandboxed --incompatible_sandbox_hermetic_tmp --sandbox_debug --verbose_failures
INFO: Analyzed target //pkg:tmp_test (1 packages loaded, 2 targets configured).
INFO: Found 1 test target...
ERROR: /tmp/hermetictmptest/pkg/BUILD:1:8: Testing //pkg:tmp_test failed: (Exit 1): linux-sandbox failed: error executing command
(cd /usr/local/google/home/larsrc/.cache/bazel/_bazel_larsrc/5aee95154b6da9eb556a8fb39d46f41c/sandbox/linux-sandbox/8/execroot/__main__ && \
exec env - \
EXPERIMENTAL_SPLIT_XML_GENERATION=1 \
JAVA_RUNFILES=bazel-out/k8-fastbuild/bin/pkg/tmp_test.runfiles \
PATH=/usr/local/google/home/larsrc/.local/bin:/usr/local/google/home/larsrc/bin:/usr/lib/google-golang/bin:/usr/local/buildtools/java/jdk/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/intellij-idea/bin:/usr/local/google/home/larsrc/bin \
PYTHON_RUNFILES=bazel-out/k8-fastbuild/bin/pkg/tmp_test.runfiles \
RUNFILES_DIR=bazel-out/k8-fastbuild/bin/pkg/tmp_test.runfiles \
RUN_UNDER_RUNFILES=1 \
TEST_BINARY=pkg/tmp_test \
TEST_INFRASTRUCTURE_FAILURE_FILE=bazel-out/k8-fastbuild/testlogs/pkg/tmp_test/test.infrastructure_failure \
TEST_LOGSPLITTER_OUTPUT_FILE=bazel-out/k8-fastbuild/testlogs/pkg/tmp_test/test.raw_splitlogs/test.splitlogs \
TEST_NAME=//pkg:tmp_test \
TEST_PREMATURE_EXIT_FILE=bazel-out/k8-fastbuild/testlogs/pkg/tmp_test/test.exited_prematurely \
TEST_SHARD_INDEX=0 \
TEST_SIZE=medium \
TEST_SRCDIR=bazel-out/k8-fastbuild/bin/pkg/tmp_test.runfiles \
TEST_TARGET=//pkg:tmp_test \
TEST_TIMEOUT=300 \
TEST_TMPDIR=_tmp/dce7d97fa01edd02eaaa2ea9bd7d11f5 \
TEST_TOTAL_SHARDS=0 \
TEST_UNDECLARED_OUTPUTS_ANNOTATIONS=bazel-out/k8-fastbuild/testlogs/pkg/tmp_test/test.outputs_manifest/ANNOTATIONS \
TEST_UNDECLARED_OUTPUTS_ANNOTATIONS_DIR=bazel-out/k8-fastbuild/testlogs/pkg/tmp_test/test.outputs_manifest \
TEST_UNDECLARED_OUTPUTS_DIR=bazel-out/k8-fastbuild/testlogs/pkg/tmp_test/test.outputs \
TEST_UNDECLARED_OUTPUTS_MANIFEST=bazel-out/k8-fastbuild/testlogs/pkg/tmp_test/test.outputs_manifest/MANIFEST \
TEST_UNDECLARED_OUTPUTS_ZIP=bazel-out/k8-fastbuild/testlogs/pkg/tmp_test/test.outputs/outputs.zip \
TEST_UNUSED_RUNFILES_LOG_FILE=bazel-out/k8-fastbuild/testlogs/pkg/tmp_test/test.unused_runfiles_log \
TEST_WARNINGS_OUTPUT_FILE=bazel-out/k8-fastbuild/testlogs/pkg/tmp_test/test.warnings \
TEST_WORKSPACE=__main__ \
TMPDIR=/tmp \
TZ=UTC \
XML_OUTPUT_FILE=bazel-out/k8-fastbuild/testlogs/pkg/tmp_test/test.xml \
/usr/local/google/home/larsrc/.cache/bazel/_bazel_larsrc/install/cd380c01deb6ae3b758a4940bc515c99/linux-sandbox -t 15 -w /usr/local/google/home/larsrc/.cache/bazel/_bazel_larsrc/5aee95154b6da9eb556a8fb39d46f41c/sandbox/linux-sandbox/8/execroot/__main__ -w /usr/local/google/home/larsrc/.cache/bazel/_bazel_larsrc/5aee95154b6da9eb556a8fb39d46f41c/sandbox/linux-sandbox/8/execroot/__main__/_tmp/dce7d97fa01edd02eaaa2ea9bd7d11f5 -w /tmp -w /dev/shm -e /tmp -M /usr/local/google/home/larsrc/.cache/bazel/_bazel_larsrc/5aee95154b6da9eb556a8fb39d46f41c/sandbox/linux-sandbox/8/_tmp -m /tmp -M /tmp/hermetictmptest -S /usr/local/google/home/larsrc/.cache/bazel/_bazel_larsrc/5aee95154b6da9eb556a8fb39d46f41c/sandbox/linux-sandbox/8/stats.out -D -- external/bazel_tools/tools/test/generate-xml.sh bazel-out/k8-fastbuild/testlogs/pkg/tmp_test/test.log bazel-out/k8-fastbuild/testlogs/pkg/tmp_test/test.xml 0 1)
Target //pkg:tmp_test up-to-date:
bazel-bin/pkg/tmp_test
INFO: Elapsed time: 0.259s, Critical Path: 0.02s
INFO: 3 processes: 3 internal.
FAILED: Build did NOT complete successfully
//pkg:tmp_test NO STATUS
FAILED: Build did NOT complete successfully
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, that doesn't seem to work. I think that getting this to work would be possible but slightly involved: It requires mounting the workspace/output base somewhere else first, then mounting /tmp
, then mounting that "somewhere else" into the expected paths. That "somewhere else" may require cleanup though.
Do you think that this would be worth the effort? Alternatively, I could disable hermetic /tmp
in this case and emit a warning once.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
At least for now, let's disallow hermetic /tmp
when inside /tmp
, whether by disabling it with a warning or maybe downright failing. I would expect it to be rare.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I added a warning and am now also taking --sandbox_tmpfs_path=/tmp
into account.
21c5265
to
5094411
Compare
@@ -178,6 +184,12 @@ protected SandboxedSpawn prepareSpawn(Spawn spawn, SpawnExecutionContext context | |||
sandboxExecRoot.getParentDirectory().createDirectory(); | |||
sandboxExecRoot.createDirectory(); | |||
|
|||
Path sandboxTmp = null; | |||
if (getSandboxOptions().sandboxHermeticTmp && !getSandboxOptions().sandboxTmpfsPath.contains(PathFragment.create("/tmp"))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this check for starting with /tmp
(or rather either being /tmp
or starting with /tmp/
) instead of containing /tmp
? Not sure what would happen if --sandbox_tmpfs_path
is /tmp/foobar
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a check for this as well as a descriptive warning. I think we should be able to support it by sorting the tmpfs paths into the bind mounts, but would prefer to ignore this edge case for now.
5094411
to
0ba0349
Compare
.findFirst(); | ||
// Mounting a tmpfs strictly below the hermetic /tmp isn't supported. Mounting a tmpfs on /tmp | ||
// makes mounting the disk-based hermetic /tmp unnecessary. | ||
if (tmpfsPathUnderTmp.isPresent() && !getSandboxOptions().sandboxTmpfsPath.contains(tmpRoot) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The ifs for the warning and the actual logic should not be separate. It would be better to have one if with the logic and an inner if with just the warnedAboutNonHermeticTmp
check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I rearranged the control flow and also moved the comments around. PTAL.
4f3852b
to
65105dd
Compare
Looks nice now. With this much new logic, could you add some unit tests in |
With the new flag, each Linux sandbox will have its own dedicated empty directory mounted as /tmp rather than sharing /tmp with the host filesystem. This is necessary since the Linux sandbox uses a PID namespace, but many tools (e.g. the JVM) create files at well-known locations such as `/tmp/.javapid${PID}`, which leads to collisions between different sandboxes and the host. These collisions can result in crashes or surprising situations such as Java agents being attached to a JVM in a different sandbox. With the flag enabled, `--sandbox_add_mount_pair=/tmp` can be used to restore the old behavior of a non-hermetic /tmp directory. This is made possible by a small change to linux-sandbox which allows mount pair source directories to also be marked as writable directories.
65105dd
to
bece15b
Compare
I added unit tests for three scenarios. I wasn't able to figure out how to:
@larsrc-google In case you want me to add coverage for any of these cases, I would need some hlep. |
With the new flag, each Linux sandbox will have its own dedicated empty directory mounted as `/tmp` rather than sharing `/tmp` with the host filesystem. This is necessary since the Linux sandbox uses a PID namespace, but many tools (e.g. the JVM) create files at well-known locations such as `/tmp/.javapid${PID}`, which leads to collisions between different sandboxes and the host. These collisions can result in crashes or surprising situations such as Java agents being attached to a JVM in a different sandbox. With the flag enabled, `--sandbox_add_mount_pair=/tmp` can be used to restore the old behavior of a non-hermetic `/tmp` directory. This is made possible by a small change to linux-sandbox which allows mount pair source directories to also be marked as writable directories. Work towards bazelbuild#3236 Closes bazelbuild#16336. PiperOrigin-RevId: 481570131 Change-Id: I01b654a1f4b0223a8f272cf644e23a7d0572ea09
@fmeum you may be interested in my recent findings, I was trying this feature for some yarn stuff that otherwise fails to build and is hard to migrate to rules_js, so we call yarn directly, only that we do it inside a sandbox, thing is when I started enabling this flag I started getting a weird bug about files not existing:
I managed to create a repro repository https://github.com/bookingcom/test-sandbox-hermetic-tmp, turns out it's a weird interaction with |
With the new flag, each Linux sandbox will have its own dedicated empty
directory mounted as
/tmp
rather than sharing/tmp
with the hostfilesystem.
This is necessary since the Linux sandbox uses a PID namespace, but many
tools (e.g. the JVM) create files at well-known locations such as
/tmp/.javapid${PID}
, which leads to collisions between differentsandboxes and the host. These collisions can result in crashes or
surprising situations such as Java agents being attached to a JVM in a
different sandbox.
With the flag enabled,
--sandbox_add_mount_pair=/tmp
can be used torestore the old behavior of a non-hermetic
/tmp
directory.This is made possible by a small change to linux-sandbox which allows
mount pair source directories to also be marked as writable directories.
Work towards #3236