diff --git a/sandboxed_api/sandbox2/BUILD.bazel b/sandboxed_api/sandbox2/BUILD.bazel index e6eef73..7e58d36 100644 --- a/sandboxed_api/sandbox2/BUILD.bazel +++ b/sandboxed_api/sandbox2/BUILD.bazel @@ -261,13 +261,13 @@ cc_library( "monitor.h", "policybuilder.cc", "sandbox2.cc", - "stack-trace.cc", + "stack_trace.cc", ], hdrs = [ "executor.h", "policybuilder.h", "sandbox2.h", - "stack-trace.h", + "stack_trace.h", # Workaround: reexporting the header files of our dependencies, see b/15420638 "ipc.h", "limits.h", @@ -717,8 +717,8 @@ cc_test( ) cc_test( - name = "stack-trace_test", - srcs = ["stack-trace_test.cc"], + name = "stack_trace_test", + srcs = ["stack_trace_test.cc"], copts = sapi_platform_copts(), data = ["//sandboxed_api/sandbox2/testcases:symbolize"], deps = [ diff --git a/sandboxed_api/sandbox2/CMakeLists.txt b/sandboxed_api/sandbox2/CMakeLists.txt index 1e879cb..277a03a 100644 --- a/sandboxed_api/sandbox2/CMakeLists.txt +++ b/sandboxed_api/sandbox2/CMakeLists.txt @@ -278,8 +278,8 @@ add_library(sandbox2_sandbox2 STATIC policybuilder.h sandbox2.cc sandbox2.h - stack-trace.cc - stack-trace.h + stack_trace.cc + stack_trace.h ) add_library(sandbox2::sandbox2 ALIAS sandbox2_sandbox2) target_link_libraries(sandbox2_sandbox2 @@ -773,13 +773,13 @@ target_link_libraries(util_test PRIVATE gtest_discover_tests(util_test) # sandboxed_api/sandbox2:stack_trace_test -add_executable(stack-trace_test - stack-trace_test.cc +add_executable(stack_trace_test + stack_trace_test.cc ) -add_dependencies(stack-trace_test +add_dependencies(stack_trace_test sandbox2::testcase_symbolize ) -target_link_libraries(stack-trace_test PRIVATE +target_link_libraries(stack_trace_test PRIVATE absl::memory absl::strings sandbox2::bpf_helper @@ -793,7 +793,7 @@ target_link_libraries(stack-trace_test PRIVATE sapi::status_matchers sapi::test_main ) -gtest_discover_tests(stack-trace_test PROPERTIES +gtest_discover_tests(stack_trace_test PROPERTIES ENVIRONMENT "TEST_TMPDIR=/tmp" ENVIRONMENT "TEST_SRCDIR=${PROJECT_BINARY_DIR}" ) diff --git a/sandboxed_api/sandbox2/monitor.cc b/sandboxed_api/sandbox2/monitor.cc index c18b657..7a16fa3 100644 --- a/sandboxed_api/sandbox2/monitor.cc +++ b/sandboxed_api/sandbox2/monitor.cc @@ -57,7 +57,7 @@ #include "sandboxed_api/sandbox2/regs.h" #include "sandboxed_api/sandbox2/result.h" #include "sandboxed_api/sandbox2/sanitizer.h" -#include "sandboxed_api/sandbox2/stack-trace.h" +#include "sandboxed_api/sandbox2/stack_trace.h" #include "sandboxed_api/sandbox2/syscall.h" #include "sandboxed_api/sandbox2/util.h" #include "sandboxed_api/util/raw_logging.h" diff --git a/sandboxed_api/sandbox2/policybuilder.h b/sandboxed_api/sandbox2/policybuilder.h index 628f8ea..1fc46d2 100644 --- a/sandboxed_api/sandbox2/policybuilder.h +++ b/sandboxed_api/sandbox2/policybuilder.h @@ -32,7 +32,6 @@ #include "absl/strings/string_view.h" #include "sandboxed_api/sandbox2/mounts.h" #include "sandboxed_api/sandbox2/policy.h" -#include "sandboxed_api/sandbox2/stack-trace.h" #include "sandboxed_api/util/statusor.h" struct bpf_labels; diff --git a/sandboxed_api/sandbox2/stack_trace.cc b/sandboxed_api/sandbox2/stack_trace.cc new file mode 100644 index 0000000..bfcc3e5 --- /dev/null +++ b/sandboxed_api/sandbox2/stack_trace.cc @@ -0,0 +1,322 @@ +// Copyright 2019 Google LLC. 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. + +// Implementation of the sandbox2::StackTrace class. + +#include "sandboxed_api/sandbox2/stack_trace.h" + +#include +#include +#include +#include +#include +#include + +#include +#include "sandboxed_api/util/flag.h" +#include "absl/memory/memory.h" +#include "absl/strings/numbers.h" +#include "absl/strings/str_cat.h" +#include "absl/strings/strip.h" +#include "libcap/include/sys/capability.h" +#include "sandboxed_api/sandbox2/comms.h" +#include "sandboxed_api/sandbox2/executor.h" +#include "sandboxed_api/sandbox2/ipc.h" +#include "sandboxed_api/sandbox2/limits.h" +#include "sandboxed_api/sandbox2/policy.h" +#include "sandboxed_api/sandbox2/policybuilder.h" +#include "sandboxed_api/sandbox2/regs.h" +#include "sandboxed_api/sandbox2/result.h" +#include "sandboxed_api/sandbox2/sandbox2.h" +#include "sandboxed_api/sandbox2/unwind/unwind.h" +#include "sandboxed_api/sandbox2/unwind/unwind.pb.h" +#include "sandboxed_api/sandbox2/util/bpf_helper.h" +#include "sandboxed_api/sandbox2/util/fileops.h" +#include "sandboxed_api/sandbox2/util/path.h" + +ABSL_FLAG(bool, sandbox_disable_all_stack_traces, false, + "Completely disable stack trace collection for sandboxees"); + +ABSL_FLAG(bool, sandbox_libunwind_crash_handler, true, + "Sandbox libunwind when handling violations (preferred)"); + +namespace sandbox2 { + +class StackTracePeer { + public: + static std::unique_ptr GetPolicy(pid_t target_pid, + const std::string& maps_file, + const std::string& app_path, + const std::string& exe_path, + const Mounts& mounts); + + static bool LaunchLibunwindSandbox(const Regs* regs, const Mounts& mounts, + UnwindResult* result, + const std::string& delim); +}; + +std::unique_ptr StackTracePeer::GetPolicy(pid_t target_pid, + const std::string& maps_file, + const std::string& app_path, + const std::string& exe_path, + const Mounts& mounts) { + PolicyBuilder builder; + builder + // Use the mounttree of the original executable as starting point. + .SetMounts(mounts) + .AllowOpen() + .AllowRead() + .AllowWrite() + .AllowSyscall(__NR_close) + .AllowMmap() + .AllowExit() + .AllowHandleSignals() + + // libunwind + .AllowSyscall(__NR_fstat) + .AllowSyscall(__NR_lseek) + .AllowSyscall(__NR_mincore) + .AllowSyscall(__NR_mprotect) + .AllowSyscall(__NR_munmap) + .AllowSyscall(__NR_pipe2) + + // Symbolizer + .AllowSyscall(__NR_brk) + .AllowSyscall(__NR_clock_gettime) + + // Other + .AllowSyscall(__NR_dup) + .AllowSyscall(__NR_fcntl) + .AllowSyscall(__NR_getpid) + .AllowSyscall(__NR_gettid) + .AllowSyscall(__NR_madvise) + + // Required for our ptrace replacement. + .AddPolicyOnSyscall( + __NR_process_vm_readv, + { + // The pid technically is a 64bit int, however + // Linux usually uses max 16 bit, so we are fine + // with comparing only 32 bits here. + ARG_32(0), + JEQ32(static_cast(target_pid), ALLOW), + JEQ32(static_cast(1), ALLOW), + }) + + .EnableNamespaces() + + // Add proc maps. + .AddFileAt(maps_file, + file::JoinPath("/proc", absl::StrCat(target_pid), "maps")) + .AddFileAt(maps_file, + file::JoinPath("/proc", absl::StrCat(target_pid), "task", + absl::StrCat(target_pid), "maps")) + + // Add the binary itself. + .AddFileAt(exe_path, app_path); + + // Add all possible libraries without the need of parsing the binary + // or /proc/pid/maps. + for (const auto& library_path : { + "/usr/lib", + "/lib", + }) { + if (access(library_path, F_OK) != -1) { + VLOG(1) << "Adding library folder '" << library_path << "'"; + builder.AddDirectory(library_path); + } else { + VLOG(1) << "Could not add library folder '" << library_path + << "' as it does not exist"; + } + } + + auto policy_or = builder.TryBuild(); + if (!policy_or.ok()) { + LOG(ERROR) << "Creating stack unwinder sandbox policy failed"; + return nullptr; + } + std::unique_ptr policy = std::move(policy_or).ValueOrDie(); + auto keep_capabilities = absl::make_unique>(); + keep_capabilities->push_back(CAP_SYS_PTRACE); + policy->AllowUnsafeKeepCapabilities(std::move(keep_capabilities)); + // Use no special namespace flags when cloning. We will join an existing + // user namespace and will unshare() afterwards (See forkserver.cc). + policy->GetNamespace()->clone_flags_ = 0; + return policy; +} + +bool StackTracePeer::LaunchLibunwindSandbox(const Regs* regs, + const Mounts& mounts, + sandbox2::UnwindResult* result, + const std::string& delim) { + const pid_t pid = regs->pid(); + + // Tell executor to use this special internal mode. + std::vector argv; + std::vector envp; + + // We're not using absl::make_unique here as we're a friend of this specific + // constructor and using make_unique won't work. + auto executor = absl::WrapUnique(new Executor(pid)); + + executor->limits() + ->set_rlimit_as(RLIM64_INFINITY) + .set_rlimit_cpu(10) + .set_walltime_limit(absl::Seconds(5)); + + // Temporary directory used to provide files from /proc to the unwind sandbox. + char unwind_temp_directory_template[] = "/tmp/.sandbox2_unwind_XXXXXX"; + char* unwind_temp_directory = mkdtemp(unwind_temp_directory_template); + if (!unwind_temp_directory) { + LOG(WARNING) << "Could not create temporary directory for unwinding"; + return false; + } + struct UnwindTempDirectoryCleanup { + ~UnwindTempDirectoryCleanup() { + file_util::fileops::DeleteRecursively(capture); + } + char* capture; + } cleanup{unwind_temp_directory}; + + // Copy over important files from the /proc directory as we can't mount them. + const std::string unwind_temp_maps_path = + file::JoinPath(unwind_temp_directory, "maps"); + + if (!file_util::fileops::CopyFile( + file::JoinPath("/proc", absl::StrCat(pid), "maps"), + unwind_temp_maps_path, 0400)) { + LOG(WARNING) << "Could not copy maps file"; + return false; + } + + // Get path to the binary. + // app_path contains the path like it is also in /proc/pid/maps. This is + // important when the file was removed, it will have a ' (deleted)' suffix. + std::string app_path; + // The exe_path will have a mountable path of the application, even if it was + // removed. + std::string exe_path; + std::string proc_pid_exe = file::JoinPath("/proc", absl::StrCat(pid), "exe"); + if (!file_util::fileops::ReadLinkAbsolute(proc_pid_exe, &app_path)) { + LOG(WARNING) << "Could not obtain absolute path to the binary"; + return false; + } + + // Check whether the file still exists or not (SAPI). + if (access(app_path.c_str(), F_OK) == -1) { + LOG(WARNING) << "File was removed, using /proc/pid/exe."; + app_path = std::string(absl::StripSuffix(app_path, " (deleted)")); + // Create a copy of /proc/pid/exe, mount that one. + exe_path = file::JoinPath(unwind_temp_directory, "exe"); + if (!file_util::fileops::CopyFile(proc_pid_exe, exe_path, 0700)) { + LOG(WARNING) << "Could not copy /proc/pid/exe"; + return false; + } + } else { + exe_path = app_path; + } + + VLOG(1) << "Resolved binary: " << app_path << " / " << exe_path; + + // Add mappings for the binary (as they might not have been added due to the + // forkserver). + auto policy = StackTracePeer::GetPolicy(pid, unwind_temp_maps_path, app_path, + exe_path, mounts); + if (!policy) { + return false; + } + auto comms = executor->ipc()->comms(); + Sandbox2 s2(std::move(executor), std::move(policy)); + + VLOG(1) << "Running libunwind sandbox"; + s2.RunAsync(); + UnwindSetup msg; + msg.set_pid(pid); + msg.set_regs(reinterpret_cast(®s->user_regs_), + sizeof(regs->user_regs_)); + msg.set_default_max_frames(kDefaultMaxFrames); + msg.set_delim(delim.c_str(), delim.size()); + + bool success = true; + if (!comms->SendProtoBuf(msg)) { + LOG(ERROR) << "Sending libunwind setup message failed"; + success = false; + } + + if (success && !comms->RecvProtoBuf(result)) { + LOG(ERROR) << "Receiving libunwind result failed"; + success = false; + } + + if (!success) { + s2.Kill(); + } + auto s2_result = s2.AwaitResult(); + + LOG(INFO) << "Libunwind execution status: " << s2_result.ToString(); + + return success && s2_result.final_status() == Result::OK; +} + +std::string GetStackTrace(const Regs* regs, const Mounts& mounts, + const std::string& delim) { + if (absl::GetFlag(FLAGS_sandbox_disable_all_stack_traces)) { + return ""; + } + if (!regs) { + LOG(WARNING) << "Could not obtain stacktrace, regs == nullptr"; + return "[ERROR (noregs)]"; + } + +#if defined(THREAD_SANITIZER) || defined(ADDRESS_SANITIZER) || \ + defined(MEMORY_SANITIZER) + constexpr bool kSanitizerEnabled = true; +#else + constexpr bool kSanitizerEnabled = false; +#endif + + const bool coverage_enabled = + getenv("COVERAGE"); + + // Show a warning if sandboxed libunwind is requested but we're running in + // an ASAN / coverage build (= we can't use sandboxed libunwind). + if (absl::GetFlag(FLAGS_sandbox_libunwind_crash_handler) && + (kSanitizerEnabled || coverage_enabled)) { + LOG_IF(WARNING, kSanitizerEnabled) + << "Sanitizer build, using non-sandboxed libunwind"; + LOG_IF(WARNING, coverage_enabled) + << "Coverage build, using non-sandboxed libunwind"; + return UnsafeGetStackTrace(regs->pid(), delim); + } + + if (!absl::GetFlag(FLAGS_sandbox_libunwind_crash_handler)) { + return UnsafeGetStackTrace(regs->pid(), delim); + } + UnwindResult res; + + if (!StackTracePeer::LaunchLibunwindSandbox(regs, mounts, &res, delim)) { + return ""; + } + return res.stacktrace(); +} + +std::string UnsafeGetStackTrace(pid_t pid, const std::string& delim) { + LOG(WARNING) << "Using non-sandboxed libunwind"; + std::string stack_trace; + std::vector ips; + RunLibUnwindAndSymbolizer(pid, &stack_trace, &ips, kDefaultMaxFrames, delim); + return stack_trace; +} + +} // namespace sandbox2 diff --git a/sandboxed_api/sandbox2/stack_trace.h b/sandboxed_api/sandbox2/stack_trace.h new file mode 100644 index 0000000..67dc2fb --- /dev/null +++ b/sandboxed_api/sandbox2/stack_trace.h @@ -0,0 +1,47 @@ +// Copyright 2019 Google LLC. 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. + +// The sandbox2::StackTrace class provides static methods useful when analyzing +// call-stack of the process. It uses libunwind-ptrace, so the process must be +// in a stopped state to call those methods. + +#ifndef SANDBOXED_API_SANDBOX2_STACK_TRACE_H_ +#define SANDBOXED_API_SANDBOX2_STACK_TRACE_H_ + +#include +#include +#include +#include + +#include "sandboxed_api/sandbox2/mounts.h" +#include "sandboxed_api/sandbox2/policy.h" +#include "sandboxed_api/sandbox2/regs.h" +#include "sandboxed_api/sandbox2/unwind/unwind.pb.h" + +namespace sandbox2 { + +// Maximum depth of analyzed call stack. +constexpr size_t kDefaultMaxFrames = 200; + +// Returns the stack-trace of the PID=pid, delimited by the delim argument. +std::string GetStackTrace(const Regs* regs, const Mounts& mounts, + const std::string& delim = " "); + +// Similar to GetStackTrace() but without using the sandbox to isolate +// libunwind. +std::string UnsafeGetStackTrace(pid_t pid, const std::string& delim = " "); + +} // namespace sandbox2 + +#endif // SANDBOXED_API_SANDBOX2_STACK_TRACE_H_ diff --git a/sandboxed_api/sandbox2/stack_trace_test.cc b/sandboxed_api/sandbox2/stack_trace_test.cc new file mode 100644 index 0000000..e5921b2 --- /dev/null +++ b/sandboxed_api/sandbox2/stack_trace_test.cc @@ -0,0 +1,196 @@ +// Copyright 2019 Google LLC. 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. + +#include "sandboxed_api/sandbox2/stack_trace.h" + +#include + +#include +#include + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "sandboxed_api/util/flag.h" +#include "absl/memory/memory.h" +#include "absl/strings/match.h" +#include "absl/strings/str_cat.h" +#include "sandboxed_api/sandbox2/executor.h" +#include "sandboxed_api/sandbox2/global_forkclient.h" +#include "sandboxed_api/sandbox2/policy.h" +#include "sandboxed_api/sandbox2/policybuilder.h" +#include "sandboxed_api/sandbox2/result.h" +#include "sandboxed_api/sandbox2/sandbox2.h" +#include "sandboxed_api/sandbox2/testing.h" +#include "sandboxed_api/sandbox2/util/bpf_helper.h" +#include "sandboxed_api/sandbox2/util/fileops.h" +#include "sandboxed_api/sandbox2/util/temp_file.h" +#include "sandboxed_api/util/status_matchers.h" + +ABSL_DECLARE_FLAG(bool, sandbox_libunwind_crash_handler); + +using ::testing::Eq; +using ::testing::HasSubstr; +using ::testing::Not; + +namespace sandbox2 { +namespace { + +// Temporarily overrides a flag, restores the original flag value when it goes +// out of scope. +template +class TemporaryFlagOverride { + public: + using Flag = T; + TemporaryFlagOverride(Flag* flag, T value) + : flag_(flag), original_value_(absl::GetFlag(*flag)) { + absl::SetFlag(flag, value); + } + + ~TemporaryFlagOverride() { absl::SetFlag(flag_, original_value_); } + + private: + Flag* flag_; + T original_value_; +}; + +// Test that symbolization of stack traces works. +void SymbolizationWorksCommon( + const std::function& modify_policy) { + const std::string path = GetTestSourcePath("sandbox2/testcases/symbolize"); + std::vector args = {path, "1"}; + auto executor = absl::make_unique(path, args); + + std::string temp_filename = CreateNamedTempFileAndClose("/tmp/").ValueOrDie(); + file_util::fileops::CopyFile("/proc/cpuinfo", temp_filename, 0444); + struct TempCleanup { + ~TempCleanup() { remove(capture->c_str()); } + std::string* capture; + } temp_cleanup{&temp_filename}; + + PolicyBuilder policybuilder; + policybuilder + // Don't restrict the syscalls at all. + .DangerDefaultAllowAll() + .EnableNamespaces() + .AddFile(path) + .AddLibrariesForBinary(path) + .AddFileAt(temp_filename, "/proc/cpuinfo"); + + modify_policy(&policybuilder); + SAPI_ASSERT_OK_AND_ASSIGN(auto policy, policybuilder.TryBuild()); + + Sandbox2 s2(std::move(executor), std::move(policy)); + auto result = s2.Run(); + + ASSERT_THAT(result.final_status(), Eq(Result::SIGNALED)); + ASSERT_THAT(result.GetStackTrace(), HasSubstr("CrashMe")); + // Check that demangling works as well. + ASSERT_THAT(result.GetStackTrace(), HasSubstr("CrashMe()")); +} + +TEST(StackTraceTest, SymbolizationWorksNonSandboxedLibunwind) { + SKIP_SANITIZERS_AND_COVERAGE; + TemporaryFlagOverride temp_override( + &FLAGS_sandbox_libunwind_crash_handler, false); + SymbolizationWorksCommon([](PolicyBuilder*) {}); +} + +TEST(StackTraceTest, SymbolizationWorksSandboxedLibunwind) { + SKIP_SANITIZERS_AND_COVERAGE; + TemporaryFlagOverride temp_override( + &FLAGS_sandbox_libunwind_crash_handler, true); + SymbolizationWorksCommon([](PolicyBuilder*) {}); +} + +TEST(StackTraceTest, SymbolizationWorksSandboxedLibunwindProcDirMounted) { + SKIP_SANITIZERS_AND_COVERAGE; + TemporaryFlagOverride temp_override( + &FLAGS_sandbox_libunwind_crash_handler, true); + SymbolizationWorksCommon( + [](PolicyBuilder* builder) { builder->AddDirectory("/proc"); }); +} + +TEST(StackTraceTest, SymbolizationWorksSandboxedLibunwindProcFileMounted) { + SKIP_SANITIZERS_AND_COVERAGE; + TemporaryFlagOverride temp_override( + &FLAGS_sandbox_libunwind_crash_handler, true); + SymbolizationWorksCommon([](PolicyBuilder* builder) { + builder->AddFile("/proc/sys/vm/overcommit_memory"); + }); +} + +TEST(StackTraceTest, SymbolizationWorksSandboxedLibunwindSysDirMounted) { + SKIP_SANITIZERS_AND_COVERAGE; + TemporaryFlagOverride temp_override( + &FLAGS_sandbox_libunwind_crash_handler, true); + SymbolizationWorksCommon( + [](PolicyBuilder* builder) { builder->AddDirectory("/sys"); }); +} + +TEST(StackTraceTest, SymbolizationWorksSandboxedLibunwindSysFileMounted) { + SKIP_SANITIZERS_AND_COVERAGE; + TemporaryFlagOverride temp_override( + &FLAGS_sandbox_libunwind_crash_handler, true); + SymbolizationWorksCommon([](PolicyBuilder* builder) { + builder->AddFile("/sys/devices/system/cpu/online"); + }); +} + +static size_t FileCountInDirectory(const std::string& path) { + std::vector fds; + std::string error; + CHECK(file_util::fileops::ListDirectoryEntries(path, &fds, &error)); + return fds.size(); +} + +TEST(StackTraceTest, ForkEnterNsLibunwindDoesNotLeakFDs) { + SKIP_SANITIZERS_AND_COVERAGE; + // Get list of open FDs in the global forkserver. + pid_t forkserver_pid = GetGlobalForkServerPid(); + std::string forkserver_fd_path = + absl::StrCat("/proc/", forkserver_pid, "/fd"); + size_t filecount_before = FileCountInDirectory(forkserver_fd_path); + + TemporaryFlagOverride temp_override( + &FLAGS_sandbox_libunwind_crash_handler, true); + SymbolizationWorksCommon([](PolicyBuilder* builder) { + builder->AddFile("/sys/devices/system/cpu/online"); + }); + + EXPECT_THAT(filecount_before, Eq(FileCountInDirectory(forkserver_fd_path))); +} + +// Test that symbolization skips writeable files (attack vector). +TEST(StackTraceTest, SymbolizationTrustedFilesOnly) { + SKIP_SANITIZERS_AND_COVERAGE; + const std::string path = GetTestSourcePath("sandbox2/testcases/symbolize"); + std::vector args = {path, "2"}; + auto executor = absl::make_unique(path, args); + SAPI_ASSERT_OK_AND_ASSIGN(auto policy, PolicyBuilder{} + // Don't restrict the syscalls at all. + .DangerDefaultAllowAll() + .EnableNamespaces() + .AddFile(path) + .AddLibrariesForBinary(path) + .TryBuild()); + + Sandbox2 s2(std::move(executor), std::move(policy)); + auto result = s2.Run(); + + ASSERT_THAT(result.final_status(), Eq(Result::SIGNALED)); + ASSERT_THAT(result.GetStackTrace(), Not(HasSubstr("CrashMe"))); +} + +} // namespace +} // namespace sandbox2