diff --git a/sandboxed_api/sandbox2/BUILD.bazel b/sandboxed_api/sandbox2/BUILD.bazel index 2ad2195..c9ced23 100644 --- a/sandboxed_api/sandbox2/BUILD.bazel +++ b/sandboxed_api/sandbox2/BUILD.bazel @@ -671,6 +671,7 @@ cc_test( "//sandboxed_api/sandbox2/testcases:abort", "//sandboxed_api/sandbox2/testcases:minimal", "//sandboxed_api/sandbox2/testcases:sleep", + "//sandboxed_api/sandbox2/testcases:starve", "//sandboxed_api/sandbox2/testcases:tsync", ], tags = ["local"], diff --git a/sandboxed_api/sandbox2/sandbox2_test.cc b/sandboxed_api/sandbox2/sandbox2_test.cc index e9e648e..6d29e8f 100644 --- a/sandboxed_api/sandbox2/sandbox2_test.cc +++ b/sandboxed_api/sandbox2/sandbox2_test.cc @@ -37,6 +37,8 @@ using ::testing::Eq; using ::testing::HasSubstr; using ::testing::IsEmpty; +using ::testing::IsTrue; +using ::testing::Lt; namespace sandbox2 { namespace { @@ -196,5 +198,25 @@ TEST(RunAsyncTest, SandboxeeViolationDisabledStacktraces) { EXPECT_THAT(result.GetStackTrace(), IsEmpty()); } +TEST(StarvationTest, MonitorIsNotStarvedByTheSandboxee) { + const std::string path = GetTestSourcePath("sandbox2/testcases/starve"); + + std::vector args = {path}; + std::vector envs; + auto executor = absl::make_unique(path, args, envs); + + SAPI_ASSERT_OK_AND_ASSIGN(auto policy, + PolicyBuilder().DangerDefaultAllowAll().TryBuild()); + executor->limits()->set_walltime_limit(absl::Seconds(5)); + 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 end = absl::Now(); + auto elapsed = end - start; + EXPECT_THAT(elapsed, Lt(absl::Seconds(10))); +} + } // namespace } // namespace sandbox2 diff --git a/sandboxed_api/sandbox2/testcases/BUILD.bazel b/sandboxed_api/sandbox2/testcases/BUILD.bazel index fa68f02..4cd91e7 100644 --- a/sandboxed_api/sandbox2/testcases/BUILD.bazel +++ b/sandboxed_api/sandbox2/testcases/BUILD.bazel @@ -240,6 +240,13 @@ cc_binary( ], ) +cc_binary( + name = "starve", + testonly = 1, + srcs = ["starve.cc"], + copts = sapi_platform_copts(), +) + cc_binary( name = "hostname", testonly = 1, diff --git a/sandboxed_api/sandbox2/testcases/CMakeLists.txt b/sandboxed_api/sandbox2/testcases/CMakeLists.txt index c7da1a0..c3ee7c4 100644 --- a/sandboxed_api/sandbox2/testcases/CMakeLists.txt +++ b/sandboxed_api/sandbox2/testcases/CMakeLists.txt @@ -212,6 +212,19 @@ target_link_libraries(symbolize PRIVATE ${_sandbox2_fully_static_linkopts} ) +# sandboxed_api/sandbox2/testcases:starve +add_executable(starve + starve.cc +) +add_executable(sandbox2::testcase_starve ALIAS starve) +set_target_properties(starve PROPERTIES + ${_sandbox2_testcase_properties} +) +target_link_libraries(starve PRIVATE + sapi::base +) + + # sandboxed_api/sandbox2/testcases:tsync add_executable(tsync tsync.cc diff --git a/sandboxed_api/sandbox2/testcases/starve.cc b/sandboxed_api/sandbox2/testcases/starve.cc new file mode 100644 index 0000000..1be64ef --- /dev/null +++ b/sandboxed_api/sandbox2/testcases/starve.cc @@ -0,0 +1,78 @@ +// 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 +#include +#include + +#include + +constexpr int kProcesses = 512; +constexpr int kThreads = 1; +constexpr int kSenders = 1; +constexpr int kStackSize = 4096; + +pid_t g_pids[kProcesses]; + +uint8_t g_stacks[kThreads][kStackSize] __attribute__((aligned(4096))); +constexpr int kSignals[] = { + SIGPROF, +}; + +void SignalHandler(int) {} + +int ChildFunc(void*) { + for (;;) { + sleep(10); + } +} + +int main() { + for (int i = 0; i < kProcesses; ++i) { + int p[2]; + char c; + pipe(p); + g_pids[i] = fork(); + if (g_pids[i] == 0) { + for (int sig : kSignals) { + signal(sig, SignalHandler); + } + for (int j = 0; j < kThreads; ++j) { + int flags = CLONE_FILES | CLONE_FS | CLONE_IO | CLONE_PARENT | + CLONE_SIGHAND | CLONE_THREAD | CLONE_VM; + clone(&ChildFunc, g_stacks[j + 1], flags, nullptr, nullptr, nullptr, + nullptr); + } + close(p[0]); + write(p[1], &c, 1); + close(p[1]); + for (;;) { + sleep(10); + } + } + read(p[0], &c, 1); + } + for (int i = 0; i < kSenders; ++i) { + if (fork() == 0) { + break; + } + } + for (;;) { + for (int sig : kSignals) { + for (int pid : g_pids) { + kill(pid, sig); + } + } + } +}