| #!/bin/bash |
| # |
| # Copyright 2015 Google Inc. 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. |
| # |
| # Test sandboxing spawn strategy |
| # |
| |
| # Load test environment |
| source $(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/test-setup.sh \ |
| || { echo "test-setup.sh not found!" >&2; exit 1; } |
| |
| # namespaces which are used by the sandbox were introduced in 3.8, so |
| # test won't run on earlier kernels |
| function check_kernel_version { |
| if [ "${PLATFORM-}" = "darwin" ]; then |
| echo "Test will skip: sandbox is not yet supported on Darwin." |
| exit 0 |
| fi |
| MAJOR=$(uname -r | sed 's/^\([0-9]*\)\.\([0-9]*\)\..*/\1/') |
| MINOR=$(uname -r | sed 's/^\([0-9]*\)\.\([0-9]*\)\..*/\2/') |
| if [ $MAJOR -lt 3 ]; then |
| echo "Test will skip: sandbox requires kernel >= 3.8; got $(uname -r)" |
| exit 0 |
| fi |
| if [ $MAJOR -eq 3 ] && [ $MINOR -lt 8 ]; then |
| echo "Test will skip: sandbox requires kernel >= 3.8; got $(uname -r)" |
| exit 0 |
| fi |
| } |
| |
| # Some CI systems might deactivate sandboxing |
| function check_sandbox_allowed { |
| mkdir -p test |
| # Create a program that check if unshare(2) is allowed. |
| cat <<'EOF' > test/test.c |
| #define _GNU_SOURCE |
| #include <sched.h> |
| int main() { |
| return unshare(CLONE_NEWNS | CLONE_NEWUTS | CLONE_NEWIPC | CLONE_NEWUSER); |
| } |
| EOF |
| cat <<'EOF' >test/BUILD |
| cc_test(name = "sandbox_enabled", srcs = ["test.c"], copts = ["-std=c99"]) |
| EOF |
| bazel test //test:sandbox_enabled || { |
| echo "Sandboxing disabled, skipping..." |
| return false |
| } |
| } |
| |
| function set_up { |
| mkdir -p examples/genrule |
| cat << 'EOF' > examples/genrule/a.txt |
| foo bar bz |
| EOF |
| cat << 'EOF' > examples/genrule/b.txt |
| apples oranges bananas |
| EOF |
| |
| # Create cyclic symbolic links to check whether the strategy catches that. |
| ln -sf cyclic2 examples/genrule/cyclic1 |
| ln -sf cyclic1 examples/genrule/cyclic2 |
| |
| # Create relative symlinks. |
| mkdir -p examples/genrule/symlinks/{a,ok/sub} |
| echo OK > examples/genrule/symlinks/ok/x.txt |
| ln -s $PWD/examples/genrule/symlinks/ok/sub examples/genrule/symlinks/a/b |
| ln -s ../x.txt examples/genrule/symlinks/a/b/x.txt |
| |
| cat << 'EOF' > examples/genrule/BUILD |
| genrule( |
| name = "works", |
| srcs = [ "a.txt" ], |
| outs = [ "works.txt" ], |
| cmd = "wc $(location :a.txt) > $@", |
| ) |
| |
| sh_binary( |
| name = "tool", |
| srcs = ["tool.sh"], |
| data = ["datafile"], |
| ) |
| |
| genrule( |
| name = "tools_work", |
| srcs = [], |
| outs = ["tools.txt"], |
| cmd = "$(location :tool) $@", |
| tools = [":tool"], |
| ) |
| |
| genrule( |
| name = "tooldir", |
| srcs = [], |
| outs = ["tooldir.txt"], |
| cmd = "ls -l tools/genrule | tee $@ >&2; cat tools/genrule/genrule-setup.sh >&2", |
| ) |
| |
| genrule( |
| name = "relative_symlinks", |
| srcs = [ "symlinks/a/b/x.txt" ], |
| outs = [ "relative_symlinks.txt" ], |
| cmd = "cat $(location :symlinks/a/b/x.txt) > $@", |
| ) |
| |
| genrule( |
| name = "breaks1", |
| srcs = [ "a.txt" ], |
| outs = [ "breaks1.txt" ], |
| cmd = "wc b.txt a.txt > $@", |
| ) |
| |
| genrule( |
| name = "breaks2", |
| srcs = [ "a.txt" ], |
| outs = [ "breaks2.txt" ], |
| # the point of this test is to attempt to read something from filesystem |
| # that resides outside sandbox by using an absolute path to that file |
| # |
| # /home is an arbitrary choice of directory (we doesn't mount it in sandbox |
| # and it should exist on every linux) which could be changed in case it turns |
| # out it's necessary to put it in sandbox |
| # |
| cmd = "ls /home > $@", |
| ) |
| |
| genrule( |
| name = "breaks3", |
| srcs = [ "cyclic1", "cyclic2" ], |
| outs = [ "breaks3.txt" ], |
| cmd = "wc $(location :cyclic1) > $@", |
| ) |
| EOF |
| cat << 'EOF' >> examples/genrule/datafile |
| this is a datafile |
| EOF |
| cat << 'EOF' >> examples/genrule/tool.sh |
| #!/bin/sh |
| |
| set -e |
| cp $(dirname $0)/tool.runfiles/examples/genrule/datafile $1 |
| echo "Tools work!" |
| EOF |
| chmod +x examples/genrule/tool.sh |
| } |
| |
| function test_sandboxed_genrule() { |
| bazel build --genrule_strategy=sandboxed \ |
| examples/genrule:works \ |
| || fail "Hermetic genrule failed: examples/genrule:works" |
| [ -f "${BAZEL_GENFILES_DIR}/examples/genrule/works.txt" ] \ |
| || fail "Genrule didn't produce output: examples/genrule:works" |
| } |
| |
| function test_sandboxed_tooldir() { |
| bazel build --genrule_strategy=sandboxed \ |
| examples/genrule:tooldir \ |
| || fail "Hermetic genrule failed: examples/genrule:tooldir" |
| [ -f "${BAZEL_GENFILES_DIR}/examples/genrule/tooldir.txt" ] \ |
| || fail "Genrule didn't produce output: examples/genrule:works" |
| cat "${BAZEL_GENFILES_DIR}/examples/genrule/tooldir.txt" > $TEST_log |
| expect_log "genrule-setup.sh" |
| } |
| |
| function test_sandboxed_genrule_with_tools() { |
| bazel build --genrule_strategy=sandboxed \ |
| examples/genrule:tools_work \ |
| || fail "Hermetic genrule failed: examples/genrule:tools_work" |
| [ -f "${BAZEL_GENFILES_DIR}/examples/genrule/tools.txt" ] \ |
| || fail "Genrule didn't produce output: examples/genrule:tools_work" |
| } |
| |
| # Test for #400: Linux sandboxing and relative symbolic links. |
| # |
| # let A = examples/genrule/symlinks/a/b/x.txt -> ../x.txt |
| # where examples/genrule/symlinks/a/b -> examples/genrule/symlinks/ok/sub |
| # thus the realpath of A is example/genrule/symlinks/ok/x.txt |
| # but if the code doesn't correctly resolve intermediate symlinks and instead |
| # uses string operations to handle ".." parts, it will arrive at: |
| # examples/genrule/symlinks/a/x.txt, which is wrong. |
| # |
| function test_sandbox_relative_symlink_in_inputs() { |
| bazel build --genrule_strategy=sandboxed \ |
| examples/genrule:relative_symlinks \ |
| || fail "Hermetic genrule failed: examples/genrule:relative_symlinks" |
| [ -f "${BAZEL_GENFILES_DIR}/examples/genrule/relative_symlinks.txt" ] \ |
| || fail "Genrule didn't produce output: examples/genrule:relative_symlinks" |
| } |
| |
| function test_sandbox_undeclared_deps() { |
| bazel build --genrule_strategy=sandboxed \ |
| examples/genrule:breaks1 \ |
| && fail "Non-hermetic genrule succeeded: examples/genrule:breaks1" || true |
| [ ! -f "${BAZEL_GENFILES_DIR}/examples/genrule/breaks1.txt" ] || { |
| output=$(cat "${BAZEL_GENFILES_DIR}/examples/genrule/breaks1.txt") |
| fail "Non-hermetic genrule breaks1 suceeded with following output: $(output)" |
| } |
| } |
| |
| function test_sandbox_block_filesystem() { |
| bazel build --genrule_strategy=sandboxed \ |
| examples/genrule:breaks2 \ |
| && fail "Non-hermetic genrule succeeded: examples/genrule:breaks2" || true |
| [ ! -f "${BAZEL_GENFILES_DIR}/examples/genrule/breaks2.txt" ] || { |
| output=$(cat "${BAZEL_GENFILES_DIR}/examples/genrule/breaks2.txt") |
| fail "Non-hermetic genrule suceeded with following output: $(output)" |
| } |
| } |
| |
| function test_sandbox_cyclic_symlink_in_inputs() { |
| bazel build --genrule_strategy=sandboxed \ |
| examples/genrule:breaks3 \ |
| && fail "Genrule with cyclic symlinks succeeded: examples/genrule:breaks3" || true |
| [ ! -f "${BAZEL_GENFILES_DIR}/examples/genrule/breaks3.txt" ] || { |
| output=$(cat "${BAZEL_GENFILES_DIR}/examples/genrule/breaks3.txt") |
| fail "Genrule with cyclic symlinks breaks3 suceeded with following output: $(output)" |
| } |
| } |
| |
| check_kernel_version |
| check_sandbox_allowed || exit 0 |
| run_suite "sandbox" |