// Copyright 2019 Google LLC // // 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 // // https://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/sandbox2.h" #include #include #include #include #include #include #include // NOLINT(build/c++11) #include #include #include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/status/status.h" #include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "absl/synchronization/notification.h" #include "absl/time/clock.h" #include "absl/time/time.h" #include "sandboxed_api/config.h" #include "sandboxed_api/sandbox2/executor.h" #include "sandboxed_api/sandbox2/fork_client.h" #include "sandboxed_api/sandbox2/policy.h" #include "sandboxed_api/sandbox2/policybuilder.h" #include "sandboxed_api/sandbox2/result.h" #include "sandboxed_api/testing.h" #include "sandboxed_api/util/status_matchers.h" namespace sandbox2 { namespace { using ::sapi::CreateDefaultPermissiveTestPolicy; using ::sapi::GetTestSourcePath; using ::sapi::IsOk; using ::testing::Eq; using ::testing::IsEmpty; using ::testing::IsTrue; using ::testing::Lt; using ::testing::Ne; class Sandbox2Test : public ::testing::TestWithParam { public: PolicyBuilder CreateDefaultTestPolicy(absl::string_view path) { PolicyBuilder builder = CreateDefaultPermissiveTestPolicy(path); if (GetParam()) { builder.CollectStacktracesOnSignal(false); } return builder; } absl::Status SetUpSandbox(Sandbox2* sandbox) { return GetParam() ? sandbox->EnableUnotifyMonitor() : absl::OkStatus(); } }; // Test that aborting inside a sandbox with all userspace core dumping // disabled reports the signal. TEST_P(Sandbox2Test, AbortWithoutCoreDumpReturnsSignaled) { const std::string path = GetTestSourcePath("sandbox2/testcases/abort"); std::vector args = { path, }; auto executor = std::make_unique(path, args); SAPI_ASSERT_OK_AND_ASSIGN(auto policy, CreateDefaultTestPolicy(path) .TryBuild()); Sandbox2 sandbox(std::move(executor), std::move(policy)); ASSERT_THAT(SetUpSandbox(&sandbox), IsOk()); auto result = sandbox.Run(); ASSERT_THAT(result.final_status(), Eq(Result::SIGNALED)); EXPECT_THAT(result.reason_code(), Eq(SIGABRT)); } // Test that with TSYNC we are able to sandbox when multithreaded. TEST_P(Sandbox2Test, TsyncNoMemoryChecks) { const std::string path = GetTestSourcePath("sandbox2/testcases/tsync"); auto executor = std::make_unique(path, std::vector{path}); executor->set_enable_sandbox_before_exec(false); SAPI_ASSERT_OK_AND_ASSIGN(auto policy, CreateDefaultTestPolicy(path).TryBuild()); Sandbox2 sandbox(std::move(executor), std::move(policy)); ASSERT_THAT(SetUpSandbox(&sandbox), IsOk()); auto result = sandbox.Run(); // With TSYNC, SandboxMeHere should be able to sandbox when multithreaded. ASSERT_EQ(result.final_status(), Result::OK); ASSERT_EQ(result.reason_code(), 0); } // Tests whether Executor(fd, std::vector{path}, envp) constructor // works as expected. TEST(ExecutorTest, ExecutorFdConstructor) { const std::string path = GetTestSourcePath("sandbox2/testcases/minimal"); int fd = open(path.c_str(), O_RDONLY); ASSERT_NE(fd, -1); std::vector args = {absl::StrCat("FD:", fd)}; auto executor = std::make_unique(fd, args); SAPI_ASSERT_OK_AND_ASSIGN(auto policy, CreateDefaultPermissiveTestPolicy(path).TryBuild()); Sandbox2 sandbox(std::move(executor), std::move(policy)); auto result = sandbox.Run(); ASSERT_EQ(result.final_status(), Result::OK); } // Tests that we return the correct state when the sandboxee was killed by an // external signal. Also make sure that we do not have the stack trace. TEST_P(Sandbox2Test, SandboxeeExternalKill) { const std::string path = GetTestSourcePath("sandbox2/testcases/sleep"); std::vector args = {path}; auto executor = std::make_unique(path, args); SAPI_ASSERT_OK_AND_ASSIGN(auto policy, CreateDefaultTestPolicy(path).TryBuild()); Sandbox2 sandbox(std::move(executor), std::move(policy)); ASSERT_THAT(SetUpSandbox(&sandbox), IsOk()); ASSERT_TRUE(sandbox.RunAsync()); sleep(1); sandbox.Kill(); auto result = sandbox.AwaitResult(); EXPECT_EQ(result.final_status(), Result::EXTERNAL_KILL); EXPECT_THAT(result.stack_trace(), IsEmpty()); } // Tests that we do not collect stack traces if it was disabled (signaled). TEST_P(Sandbox2Test, SandboxeeTimeoutDisabledStacktraces) { const std::string path = GetTestSourcePath("sandbox2/testcases/sleep"); std::vector args = {path}; auto executor = std::make_unique(path, args); SAPI_ASSERT_OK_AND_ASSIGN(auto policy, CreateDefaultTestPolicy(path) .CollectStacktracesOnTimeout(false) .TryBuild()); Sandbox2 sandbox(std::move(executor), std::move(policy)); ASSERT_THAT(SetUpSandbox(&sandbox), IsOk()); ASSERT_TRUE(sandbox.RunAsync()); sandbox.set_walltime_limit(absl::Seconds(1)); auto result = sandbox.AwaitResult(); EXPECT_EQ(result.final_status(), Result::TIMEOUT); EXPECT_THAT(result.stack_trace(), IsEmpty()); } // Tests that we do not collect stack traces if it was disabled (violation). TEST(Sandbox2Test, SandboxeeViolationDisabledStacktraces) { const std::string path = GetTestSourcePath("sandbox2/testcases/sleep"); std::vector args = {path}; auto executor = std::make_unique(path, args); SAPI_ASSERT_OK_AND_ASSIGN( auto policy, PolicyBuilder() // Don't allow anything - Make sure that we'll crash. .CollectStacktracesOnViolation(false) .TryBuild()); Sandbox2 sandbox(std::move(executor), std::move(policy)); ASSERT_TRUE(sandbox.RunAsync()); auto result = sandbox.AwaitResult(); EXPECT_EQ(result.final_status(), Result::VIOLATION); EXPECT_THAT(result.stack_trace(), IsEmpty()); } TEST_P(Sandbox2Test, SandboxeeNotKilledWhenStartingThreadFinishes) { const std::string path = GetTestSourcePath("sandbox2/testcases/minimal"); std::vector args = {path}; auto executor = std::make_unique(path, args); SAPI_ASSERT_OK_AND_ASSIGN(auto policy, CreateDefaultTestPolicy(path).TryBuild()); Sandbox2 sandbox(std::move(executor), std::move(policy)); ASSERT_THAT(SetUpSandbox(&sandbox), IsOk()); std::thread sandbox_start_thread([&sandbox]() { sandbox.RunAsync(); }); sandbox_start_thread.join(); Result result = sandbox.AwaitResult(); EXPECT_EQ(result.final_status(), Result::OK); } TEST_P(Sandbox2Test, CustomForkserverWorks) { const std::string path = GetTestSourcePath("sandbox2/testcases/custom_fork"); std::vector args = {path}; auto fork_executor = std::make_unique(path, args); std::unique_ptr fork_client = fork_executor->StartForkServer(); ASSERT_THAT(fork_client.get(), Ne(nullptr)); SAPI_ASSERT_OK_AND_ASSIGN(auto policy, CreateDefaultTestPolicy(path).TryBuild()); Sandbox2 sandbox(std::make_unique(fork_client.get()), std::move(policy)); ASSERT_THAT(SetUpSandbox(&sandbox), IsOk()); Result result = sandbox.Run(); EXPECT_EQ(result.final_status(), Result::OK); } TEST(StarvationTest, MonitorIsNotStarvedByTheSandboxee) { const std::string path = GetTestSourcePath("sandbox2/testcases/starve"); std::vector args = {path}; auto executor = std::make_unique(path, args); executor->limits()->set_walltime_limit(absl::Seconds(5)); SAPI_ASSERT_OK_AND_ASSIGN(auto policy, CreateDefaultPermissiveTestPolicy(path).TryBuild()); Sandbox2 sandbox(std::move(executor), std::move(policy)); auto start = absl::Now(); ASSERT_THAT(sandbox.RunAsync(), IsTrue()); auto result = sandbox.AwaitResult(); EXPECT_THAT(result.final_status(), Eq(Result::TIMEOUT)); auto elapsed = absl::Now() - start; EXPECT_THAT(elapsed, Lt(absl::Seconds(10))); } INSTANTIATE_TEST_SUITE_P(Sandbox2, Sandbox2Test, ::testing::Values(false, true), [](const ::testing::TestParamInfo& info) { return info.param ? "UnotifyMonitor" : "PtraceMonitor"; }); } // namespace } // namespace sandbox2