mirror of
https://github.com/google/sandboxed-api.git
synced 2024-03-22 13:11:30 +08:00
Rework stack_trace_test
PiperOrigin-RevId: 513467290 Change-Id: Iab630412052fa5e7333514f3864ebdfb7f10e1ef
This commit is contained in:
parent
dfe0b9aa6d
commit
d74dac096a
|
@ -970,15 +970,13 @@ cc_test(
|
||||||
":stack_trace",
|
":stack_trace",
|
||||||
":testonly_allow_all_syscalls",
|
":testonly_allow_all_syscalls",
|
||||||
"//sandboxed_api:testing",
|
"//sandboxed_api:testing",
|
||||||
"//sandboxed_api/sandbox2/util:bpf_helper",
|
|
||||||
"//sandboxed_api/util:fileops",
|
"//sandboxed_api/util:fileops",
|
||||||
"//sandboxed_api/util:status_matchers",
|
"//sandboxed_api/util:status_matchers",
|
||||||
"//sandboxed_api/util:temp_file",
|
|
||||||
"@com_google_absl//absl/cleanup",
|
|
||||||
"@com_google_absl//absl/flags:flag",
|
"@com_google_absl//absl/flags:flag",
|
||||||
"@com_google_absl//absl/flags:reflection",
|
"@com_google_absl//absl/flags:reflection",
|
||||||
"@com_google_absl//absl/status:statusor",
|
"@com_google_absl//absl/status:statusor",
|
||||||
"@com_google_absl//absl/strings",
|
"@com_google_absl//absl/strings",
|
||||||
|
"@com_google_absl//absl/time",
|
||||||
"@com_google_googletest//:gtest_main",
|
"@com_google_googletest//:gtest_main",
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
@ -1063,19 +1063,17 @@ if(BUILD_TESTING AND SAPI_BUILD_TESTING)
|
||||||
sandbox2::testcase_symbolize
|
sandbox2::testcase_symbolize
|
||||||
)
|
)
|
||||||
target_link_libraries(sandbox2_stack_trace_test PRIVATE
|
target_link_libraries(sandbox2_stack_trace_test PRIVATE
|
||||||
absl::cleanup
|
|
||||||
absl::flags
|
absl::flags
|
||||||
absl::status
|
absl::status
|
||||||
absl::strings
|
absl::strings
|
||||||
|
absl::time
|
||||||
sandbox2::allow_all_syscalls
|
sandbox2::allow_all_syscalls
|
||||||
sandbox2::bpf_helper
|
|
||||||
sandbox2::global_forkserver
|
sandbox2::global_forkserver
|
||||||
sandbox2::namespace
|
sandbox2::namespace
|
||||||
sandbox2::sandbox2
|
sandbox2::sandbox2
|
||||||
sandbox2::stack_trace
|
sandbox2::stack_trace
|
||||||
sandbox2::util
|
sandbox2::util
|
||||||
sapi::fileops
|
sapi::fileops
|
||||||
sapi::temp_file
|
|
||||||
sapi::testing
|
sapi::testing
|
||||||
sapi::status_matchers
|
sapi::status_matchers
|
||||||
sapi::test_main
|
sapi::test_main
|
||||||
|
|
|
@ -53,7 +53,6 @@ PolicyBuilder CreateDefaultPolicyBuilder(absl::string_view path) {
|
||||||
|
|
||||||
using ::sapi::GetTestSourcePath;
|
using ::sapi::GetTestSourcePath;
|
||||||
using ::testing::Eq;
|
using ::testing::Eq;
|
||||||
using ::testing::HasSubstr;
|
|
||||||
using ::testing::IsEmpty;
|
using ::testing::IsEmpty;
|
||||||
using ::testing::IsTrue;
|
using ::testing::IsTrue;
|
||||||
using ::testing::Lt;
|
using ::testing::Lt;
|
||||||
|
@ -118,28 +117,6 @@ TEST(ExecutorTest, ExecutorFdConstructor) {
|
||||||
ASSERT_EQ(result.final_status(), Result::OK);
|
ASSERT_EQ(result.final_status(), Result::OK);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that we return the correct state when the sandboxee timed out.
|
|
||||||
TEST(StackTraceTest, StackTraceOnTimeoutWorks) {
|
|
||||||
SKIP_ANDROID;
|
|
||||||
const std::string path = GetTestSourcePath("sandbox2/testcases/sleep");
|
|
||||||
|
|
||||||
std::vector<std::string> args = {path};
|
|
||||||
std::vector<std::string> envs;
|
|
||||||
auto executor = std::make_unique<Executor>(path, args, envs);
|
|
||||||
|
|
||||||
SAPI_ASSERT_OK_AND_ASSIGN(auto policy,
|
|
||||||
PolicyBuilder()
|
|
||||||
// Don't restrict the syscalls at all.
|
|
||||||
.DangerDefaultAllowAll()
|
|
||||||
.TryBuild());
|
|
||||||
Sandbox2 sandbox(std::move(executor), std::move(policy));
|
|
||||||
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.GetStackTrace(), HasSubstr("sleep"));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests that we return the correct state when the sandboxee was killed by an
|
// 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.
|
// external signal. Also make sure that we do not have the stack trace.
|
||||||
TEST(RunAsyncTest, SandboxeeExternalKill) {
|
TEST(RunAsyncTest, SandboxeeExternalKill) {
|
||||||
|
@ -157,7 +134,7 @@ TEST(RunAsyncTest, SandboxeeExternalKill) {
|
||||||
sandbox.Kill();
|
sandbox.Kill();
|
||||||
auto result = sandbox.AwaitResult();
|
auto result = sandbox.AwaitResult();
|
||||||
EXPECT_EQ(result.final_status(), Result::EXTERNAL_KILL);
|
EXPECT_EQ(result.final_status(), Result::EXTERNAL_KILL);
|
||||||
EXPECT_THAT(result.GetStackTrace(), IsEmpty());
|
EXPECT_THAT(result.stack_trace(), IsEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that we do not collect stack traces if it was disabled (signaled).
|
// Tests that we do not collect stack traces if it was disabled (signaled).
|
||||||
|
@ -176,7 +153,7 @@ TEST(RunAsyncTest, SandboxeeTimeoutDisabledStacktraces) {
|
||||||
sandbox.set_walltime_limit(absl::Seconds(1));
|
sandbox.set_walltime_limit(absl::Seconds(1));
|
||||||
auto result = sandbox.AwaitResult();
|
auto result = sandbox.AwaitResult();
|
||||||
EXPECT_EQ(result.final_status(), Result::TIMEOUT);
|
EXPECT_EQ(result.final_status(), Result::TIMEOUT);
|
||||||
EXPECT_THAT(result.GetStackTrace(), IsEmpty());
|
EXPECT_THAT(result.stack_trace(), IsEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests that we do not collect stack traces if it was disabled (violation).
|
// Tests that we do not collect stack traces if it was disabled (violation).
|
||||||
|
@ -196,7 +173,7 @@ TEST(RunAsyncTest, SandboxeeViolationDisabledStacktraces) {
|
||||||
ASSERT_TRUE(sandbox.RunAsync());
|
ASSERT_TRUE(sandbox.RunAsync());
|
||||||
auto result = sandbox.AwaitResult();
|
auto result = sandbox.AwaitResult();
|
||||||
EXPECT_EQ(result.final_status(), Result::VIOLATION);
|
EXPECT_EQ(result.final_status(), Result::VIOLATION);
|
||||||
EXPECT_THAT(result.GetStackTrace(), IsEmpty());
|
EXPECT_THAT(result.stack_trace(), IsEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(RunAsyncTest, SandboxeeNotKilledWhenStartingThreadFinishes) {
|
TEST(RunAsyncTest, SandboxeeNotKilledWhenStartingThreadFinishes) {
|
||||||
|
@ -205,7 +182,6 @@ TEST(RunAsyncTest, SandboxeeNotKilledWhenStartingThreadFinishes) {
|
||||||
auto executor = std::make_unique<Executor>(path, args);
|
auto executor = std::make_unique<Executor>(path, args);
|
||||||
|
|
||||||
SAPI_ASSERT_OK_AND_ASSIGN(auto policy, CreateDefaultPolicyBuilder(path)
|
SAPI_ASSERT_OK_AND_ASSIGN(auto policy, CreateDefaultPolicyBuilder(path)
|
||||||
.CollectStacktracesOnExit(true)
|
|
||||||
.TryBuild());
|
.TryBuild());
|
||||||
Sandbox2 sandbox(std::move(executor), std::move(policy));
|
Sandbox2 sandbox(std::move(executor), std::move(policy));
|
||||||
std::thread sandbox_start_thread([&sandbox]() { sandbox.RunAsync(); });
|
std::thread sandbox_start_thread([&sandbox]() { sandbox.RunAsync(); });
|
||||||
|
|
|
@ -24,12 +24,11 @@
|
||||||
|
|
||||||
#include "gmock/gmock.h"
|
#include "gmock/gmock.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
#include "absl/cleanup/cleanup.h"
|
|
||||||
#include "absl/flags/declare.h"
|
#include "absl/flags/declare.h"
|
||||||
#include "absl/flags/flag.h"
|
#include "absl/flags/flag.h"
|
||||||
#include "absl/flags/reflection.h"
|
#include "absl/flags/reflection.h"
|
||||||
#include "absl/strings/match.h"
|
|
||||||
#include "absl/strings/str_cat.h"
|
#include "absl/strings/str_cat.h"
|
||||||
|
#include "absl/time/time.h"
|
||||||
#include "sandboxed_api/sandbox2/allow_all_syscalls.h"
|
#include "sandboxed_api/sandbox2/allow_all_syscalls.h"
|
||||||
#include "sandboxed_api/sandbox2/executor.h"
|
#include "sandboxed_api/sandbox2/executor.h"
|
||||||
#include "sandboxed_api/sandbox2/global_forkclient.h"
|
#include "sandboxed_api/sandbox2/global_forkclient.h"
|
||||||
|
@ -37,11 +36,9 @@
|
||||||
#include "sandboxed_api/sandbox2/policybuilder.h"
|
#include "sandboxed_api/sandbox2/policybuilder.h"
|
||||||
#include "sandboxed_api/sandbox2/result.h"
|
#include "sandboxed_api/sandbox2/result.h"
|
||||||
#include "sandboxed_api/sandbox2/sandbox2.h"
|
#include "sandboxed_api/sandbox2/sandbox2.h"
|
||||||
#include "sandboxed_api/sandbox2/util/bpf_helper.h"
|
|
||||||
#include "sandboxed_api/testing.h"
|
#include "sandboxed_api/testing.h"
|
||||||
#include "sandboxed_api/util/fileops.h"
|
#include "sandboxed_api/util/fileops.h"
|
||||||
#include "sandboxed_api/util/status_matchers.h"
|
#include "sandboxed_api/util/status_matchers.h"
|
||||||
#include "sandboxed_api/util/temp_file.h"
|
|
||||||
|
|
||||||
ABSL_DECLARE_FLAG(bool, sandbox_libunwind_crash_handler);
|
ABSL_DECLARE_FLAG(bool, sandbox_libunwind_crash_handler);
|
||||||
|
|
||||||
|
@ -49,64 +46,70 @@ namespace sandbox2 {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
namespace file_util = ::sapi::file_util;
|
namespace file_util = ::sapi::file_util;
|
||||||
using ::sapi::CreateNamedTempFileAndClose;
|
|
||||||
using ::sapi::GetTestSourcePath;
|
using ::sapi::GetTestSourcePath;
|
||||||
|
using ::testing::Contains;
|
||||||
using ::testing::ElementsAre;
|
using ::testing::ElementsAre;
|
||||||
using ::testing::Eq;
|
using ::testing::Eq;
|
||||||
using ::testing::HasSubstr;
|
|
||||||
using ::testing::IsEmpty;
|
using ::testing::IsEmpty;
|
||||||
using ::testing::IsTrue;
|
using ::testing::StartsWith;
|
||||||
using ::testing::Not;
|
|
||||||
|
struct TestCase {
|
||||||
|
std::string arg = "1";
|
||||||
|
int final_status = Result::SIGNALED;
|
||||||
|
std::string function_name = "CrashMe";
|
||||||
|
std::string full_function_description = "CrashMe(char)";
|
||||||
|
std::function<void(PolicyBuilder*)> modify_policy;
|
||||||
|
absl::Duration wall_time_limit = absl::ZeroDuration();
|
||||||
|
};
|
||||||
|
|
||||||
|
class StackTraceTest : public ::testing::TestWithParam<TestCase> {};
|
||||||
|
|
||||||
// Test that symbolization of stack traces works.
|
// Test that symbolization of stack traces works.
|
||||||
void SymbolizationWorksCommon(
|
void SymbolizationWorksCommon(TestCase param) {
|
||||||
std::function<void(PolicyBuilder*)> modify_policy = {}) {
|
|
||||||
const std::string path = GetTestSourcePath("sandbox2/testcases/symbolize");
|
const std::string path = GetTestSourcePath("sandbox2/testcases/symbolize");
|
||||||
std::vector<std::string> args = {path, "1"};
|
std::vector<std::string> args = {path, param.arg};
|
||||||
|
|
||||||
SAPI_ASSERT_OK_AND_ASSIGN(std::string temp_filename,
|
|
||||||
CreateNamedTempFileAndClose("/tmp/"));
|
|
||||||
absl::Cleanup temp_cleanup = [&temp_filename] {
|
|
||||||
remove(temp_filename.c_str());
|
|
||||||
};
|
|
||||||
ASSERT_THAT(
|
|
||||||
file_util::fileops::CopyFile("/proc/cpuinfo", temp_filename, 0444),
|
|
||||||
IsTrue());
|
|
||||||
|
|
||||||
auto policybuilder = PolicyBuilder()
|
auto policybuilder = PolicyBuilder()
|
||||||
// Don't restrict the syscalls at all.
|
// Don't restrict the syscalls at all.
|
||||||
.DefaultAction(AllowAllSyscalls())
|
.DefaultAction(AllowAllSyscalls());
|
||||||
.AddFile(path)
|
if (param.modify_policy) {
|
||||||
.AddLibrariesForBinary(path)
|
param.modify_policy(&policybuilder);
|
||||||
.AddFileAt(temp_filename, "/proc/cpuinfo");
|
|
||||||
if (modify_policy) {
|
|
||||||
modify_policy(&policybuilder);
|
|
||||||
}
|
}
|
||||||
SAPI_ASSERT_OK_AND_ASSIGN(auto policy, policybuilder.TryBuild());
|
SAPI_ASSERT_OK_AND_ASSIGN(auto policy, policybuilder.TryBuild());
|
||||||
|
|
||||||
Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
|
Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
|
||||||
auto result = s2.Run();
|
ASSERT_TRUE(s2.RunAsync());
|
||||||
|
s2.set_walltime_limit(param.wall_time_limit);
|
||||||
|
auto result = s2.AwaitResult();
|
||||||
|
|
||||||
ASSERT_THAT(result.final_status(), Eq(Result::SIGNALED));
|
EXPECT_THAT(result.final_status(), Eq(param.final_status));
|
||||||
ASSERT_THAT(result.GetStackTrace(), HasSubstr("CrashMe"));
|
EXPECT_THAT(result.stack_trace(), Contains(StartsWith(param.function_name)));
|
||||||
// Check that demangling works as well.
|
// Check that demangling works as well.
|
||||||
ASSERT_THAT(result.GetStackTrace(), HasSubstr("CrashMe()"));
|
EXPECT_THAT(result.stack_trace(),
|
||||||
|
Contains(StartsWith(param.full_function_description)));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(StackTraceTest, SymbolizationWorksNonSandboxedLibunwind) {
|
void SymbolizationWorksWithModifiedPolicy(
|
||||||
|
std::function<void(PolicyBuilder*)> modify_policy) {
|
||||||
|
TestCase test_case;
|
||||||
|
test_case.modify_policy = std::move(modify_policy);
|
||||||
|
SymbolizationWorksCommon(test_case);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(StackTraceTest, SymbolizationWorksNonSandboxedLibunwind) {
|
||||||
SKIP_SANITIZERS_AND_COVERAGE;
|
SKIP_SANITIZERS_AND_COVERAGE;
|
||||||
absl::FlagSaver fs;
|
absl::FlagSaver fs;
|
||||||
absl::SetFlag(&FLAGS_sandbox_libunwind_crash_handler, false);
|
absl::SetFlag(&FLAGS_sandbox_libunwind_crash_handler, false);
|
||||||
|
|
||||||
SymbolizationWorksCommon();
|
SymbolizationWorksCommon(GetParam());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(StackTraceTest, SymbolizationWorksSandboxedLibunwind) {
|
TEST_P(StackTraceTest, SymbolizationWorksSandboxedLibunwind) {
|
||||||
SKIP_SANITIZERS_AND_COVERAGE;
|
SKIP_SANITIZERS_AND_COVERAGE;
|
||||||
absl::FlagSaver fs;
|
absl::FlagSaver fs;
|
||||||
absl::SetFlag(&FLAGS_sandbox_libunwind_crash_handler, true);
|
absl::SetFlag(&FLAGS_sandbox_libunwind_crash_handler, true);
|
||||||
|
|
||||||
SymbolizationWorksCommon();
|
SymbolizationWorksCommon(GetParam());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(StackTraceTest, SymbolizationWorksSandboxedLibunwindProcDirMounted) {
|
TEST(StackTraceTest, SymbolizationWorksSandboxedLibunwindProcDirMounted) {
|
||||||
|
@ -114,7 +117,7 @@ TEST(StackTraceTest, SymbolizationWorksSandboxedLibunwindProcDirMounted) {
|
||||||
absl::FlagSaver fs;
|
absl::FlagSaver fs;
|
||||||
absl::SetFlag(&FLAGS_sandbox_libunwind_crash_handler, true);
|
absl::SetFlag(&FLAGS_sandbox_libunwind_crash_handler, true);
|
||||||
|
|
||||||
SymbolizationWorksCommon(
|
SymbolizationWorksWithModifiedPolicy(
|
||||||
[](PolicyBuilder* builder) { builder->AddDirectory("/proc"); });
|
[](PolicyBuilder* builder) { builder->AddDirectory("/proc"); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,7 +126,7 @@ TEST(StackTraceTest, SymbolizationWorksSandboxedLibunwindProcFileMounted) {
|
||||||
absl::FlagSaver fs;
|
absl::FlagSaver fs;
|
||||||
absl::SetFlag(&FLAGS_sandbox_libunwind_crash_handler, true);
|
absl::SetFlag(&FLAGS_sandbox_libunwind_crash_handler, true);
|
||||||
|
|
||||||
SymbolizationWorksCommon([](PolicyBuilder* builder) {
|
SymbolizationWorksWithModifiedPolicy([](PolicyBuilder* builder) {
|
||||||
builder->AddFile("/proc/sys/vm/overcommit_memory");
|
builder->AddFile("/proc/sys/vm/overcommit_memory");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -133,7 +136,7 @@ TEST(StackTraceTest, SymbolizationWorksSandboxedLibunwindSysDirMounted) {
|
||||||
absl::FlagSaver fs;
|
absl::FlagSaver fs;
|
||||||
absl::SetFlag(&FLAGS_sandbox_libunwind_crash_handler, true);
|
absl::SetFlag(&FLAGS_sandbox_libunwind_crash_handler, true);
|
||||||
|
|
||||||
SymbolizationWorksCommon(
|
SymbolizationWorksWithModifiedPolicy(
|
||||||
[](PolicyBuilder* builder) { builder->AddDirectory("/sys"); });
|
[](PolicyBuilder* builder) { builder->AddDirectory("/sys"); });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -142,12 +145,12 @@ TEST(StackTraceTest, SymbolizationWorksSandboxedLibunwindSysFileMounted) {
|
||||||
absl::FlagSaver fs;
|
absl::FlagSaver fs;
|
||||||
absl::SetFlag(&FLAGS_sandbox_libunwind_crash_handler, true);
|
absl::SetFlag(&FLAGS_sandbox_libunwind_crash_handler, true);
|
||||||
|
|
||||||
SymbolizationWorksCommon([](PolicyBuilder* builder) {
|
SymbolizationWorksWithModifiedPolicy([](PolicyBuilder* builder) {
|
||||||
builder->AddFile("/sys/devices/system/cpu/online");
|
builder->AddFile("/sys/devices/system/cpu/online");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
static size_t FileCountInDirectory(const std::string& path) {
|
size_t FileCountInDirectory(const std::string& path) {
|
||||||
std::vector<std::string> fds;
|
std::vector<std::string> fds;
|
||||||
std::string error;
|
std::string error;
|
||||||
CHECK(file_util::fileops::ListDirectoryEntries(path, &fds, &error));
|
CHECK(file_util::fileops::ListDirectoryEntries(path, &fds, &error));
|
||||||
|
@ -161,9 +164,7 @@ TEST(StackTraceTest, ForkEnterNsLibunwindDoesNotLeakFDs) {
|
||||||
|
|
||||||
// Very first sanitization might create some fds (e.g. for initial
|
// Very first sanitization might create some fds (e.g. for initial
|
||||||
// namespaces).
|
// namespaces).
|
||||||
SymbolizationWorksCommon([](PolicyBuilder* builder) {
|
SymbolizationWorksCommon({});
|
||||||
builder->AddFile("/sys/devices/system/cpu/online");
|
|
||||||
});
|
|
||||||
|
|
||||||
// Get list of open FDs in the global forkserver.
|
// Get list of open FDs in the global forkserver.
|
||||||
pid_t forkserver_pid = GlobalForkClient::GetPid();
|
pid_t forkserver_pid = GlobalForkClient::GetPid();
|
||||||
|
@ -171,33 +172,11 @@ TEST(StackTraceTest, ForkEnterNsLibunwindDoesNotLeakFDs) {
|
||||||
absl::StrCat("/proc/", forkserver_pid, "/fd");
|
absl::StrCat("/proc/", forkserver_pid, "/fd");
|
||||||
size_t filecount_before = FileCountInDirectory(forkserver_fd_path);
|
size_t filecount_before = FileCountInDirectory(forkserver_fd_path);
|
||||||
|
|
||||||
SymbolizationWorksCommon([](PolicyBuilder* builder) {
|
SymbolizationWorksCommon({});
|
||||||
builder->AddFile("/sys/devices/system/cpu/online");
|
|
||||||
});
|
|
||||||
|
|
||||||
EXPECT_THAT(filecount_before, Eq(FileCountInDirectory(forkserver_fd_path)));
|
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<std::string> args = {path, "2"};
|
|
||||||
|
|
||||||
SAPI_ASSERT_OK_AND_ASSIGN(auto policy,
|
|
||||||
PolicyBuilder()
|
|
||||||
// Don't restrict the syscalls at all.
|
|
||||||
.DefaultAction(AllowAllSyscalls())
|
|
||||||
.AddFile(path)
|
|
||||||
.AddLibrariesForBinary(path)
|
|
||||||
.TryBuild());
|
|
||||||
Sandbox2 s2(std::make_unique<Executor>(path, args), std::move(policy));
|
|
||||||
auto result = s2.Run();
|
|
||||||
|
|
||||||
ASSERT_THAT(result.final_status(), Eq(Result::SIGNALED));
|
|
||||||
ASSERT_THAT(result.GetStackTrace(), Not(HasSubstr("CrashMe")));
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST(StackTraceTest, CompactStackTrace) {
|
TEST(StackTraceTest, CompactStackTrace) {
|
||||||
EXPECT_THAT(CompactStackTrace({}), IsEmpty());
|
EXPECT_THAT(CompactStackTrace({}), IsEmpty());
|
||||||
EXPECT_THAT(CompactStackTrace({"_start"}), ElementsAre("_start"));
|
EXPECT_THAT(CompactStackTrace({"_start"}), ElementsAre("_start"));
|
||||||
|
@ -223,5 +202,41 @@ TEST(StackTraceTest, CompactStackTrace) {
|
||||||
"(previous frame repeated 3 times)"));
|
"(previous frame repeated 3 times)"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
INSTANTIATE_TEST_SUITE_P(
|
||||||
|
Instantiation, StackTraceTest,
|
||||||
|
::testing::Values(
|
||||||
|
TestCase{
|
||||||
|
.arg = "1",
|
||||||
|
.final_status = Result::SIGNALED,
|
||||||
|
.function_name = "CrashMe",
|
||||||
|
.full_function_description = "CrashMe(char)",
|
||||||
|
},
|
||||||
|
TestCase{
|
||||||
|
.arg = "2",
|
||||||
|
.final_status = Result::VIOLATION,
|
||||||
|
.function_name = "ViolatePolicy",
|
||||||
|
.full_function_description = "ViolatePolicy(int)",
|
||||||
|
},
|
||||||
|
TestCase{
|
||||||
|
.arg = "3",
|
||||||
|
.final_status = Result::OK,
|
||||||
|
.function_name = "ExitNormally",
|
||||||
|
.full_function_description = "ExitNormally(int)",
|
||||||
|
.modify_policy =
|
||||||
|
[](PolicyBuilder* builder) {
|
||||||
|
builder->CollectStacktracesOnExit(true);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TestCase{
|
||||||
|
.arg = "4",
|
||||||
|
.final_status = Result::TIMEOUT,
|
||||||
|
.function_name = "SleepForXSeconds",
|
||||||
|
.full_function_description = "SleepForXSeconds(int)",
|
||||||
|
.wall_time_limit = absl::Seconds(1),
|
||||||
|
}),
|
||||||
|
[](const ::testing::TestParamInfo<TestCase>& info) {
|
||||||
|
return info.param.function_name;
|
||||||
|
});
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace sandbox2
|
} // namespace sandbox2
|
||||||
|
|
|
@ -168,9 +168,9 @@ cc_binary(
|
||||||
testonly = True,
|
testonly = True,
|
||||||
srcs = ["symbolize.cc"],
|
srcs = ["symbolize.cc"],
|
||||||
copts = sapi_platform_copts(),
|
copts = sapi_platform_copts(),
|
||||||
|
features = ["fully_static_link"],
|
||||||
deps = [
|
deps = [
|
||||||
"//sandboxed_api/util:raw_logging",
|
"//sandboxed_api/util:raw_logging",
|
||||||
"//sandboxed_api/util:temp_file",
|
|
||||||
"@com_google_absl//absl/base:core_headers",
|
"@com_google_absl//absl/base:core_headers",
|
||||||
"@com_google_absl//absl/strings",
|
"@com_google_absl//absl/strings",
|
||||||
],
|
],
|
||||||
|
|
|
@ -208,10 +208,9 @@ set_target_properties(sandbox2_testcase_symbolize PROPERTIES
|
||||||
OUTPUT_NAME symbolize
|
OUTPUT_NAME symbolize
|
||||||
)
|
)
|
||||||
target_link_libraries(sandbox2_testcase_symbolize PRIVATE
|
target_link_libraries(sandbox2_testcase_symbolize PRIVATE
|
||||||
|
-static
|
||||||
absl::core_headers
|
absl::core_headers
|
||||||
absl::strings
|
absl::strings
|
||||||
sapi::temp_file
|
|
||||||
sapi::strerror
|
|
||||||
sapi::base
|
sapi::base
|
||||||
sapi::raw_logging
|
sapi::raw_logging
|
||||||
)
|
)
|
||||||
|
|
|
@ -12,65 +12,59 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
// A binary that crashes, either directly or by copying and re-executing,
|
// A binary that exits via different modes: crashes, causes violation, exits
|
||||||
// to test the stack tracing symbolizer.
|
// normally or times out, to test the stack tracing symbolizer.
|
||||||
|
|
||||||
#include <fcntl.h>
|
#include <sys/syscall.h>
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <sys/stat.h>
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include <string>
|
#include <cstdlib>
|
||||||
|
|
||||||
#include "absl/base/attributes.h"
|
#include "absl/base/attributes.h"
|
||||||
#include "absl/strings/numbers.h"
|
#include "absl/strings/numbers.h"
|
||||||
#include "sandboxed_api/util/raw_logging.h"
|
#include "sandboxed_api/util/raw_logging.h"
|
||||||
#include "sandboxed_api/util/temp_file.h"
|
|
||||||
|
// Sometimes we don't have debug info to properly unwind through libc (a frame
|
||||||
|
// is skipped).
|
||||||
|
// Workaround by putting another frame on the call stack.
|
||||||
|
template <typename F>
|
||||||
|
ABSL_ATTRIBUTE_NOINLINE ABSL_ATTRIBUTE_NO_TAIL_CALL void IndirectLibcCall(
|
||||||
|
F func) {
|
||||||
|
func();
|
||||||
|
}
|
||||||
|
|
||||||
ABSL_ATTRIBUTE_NOINLINE
|
ABSL_ATTRIBUTE_NOINLINE
|
||||||
void CrashMe() {
|
void CrashMe(char x = 0) {
|
||||||
volatile char* null = nullptr;
|
volatile char* null = nullptr;
|
||||||
*null = 0;
|
*null = x;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RunWritable() {
|
ABSL_ATTRIBUTE_NOINLINE
|
||||||
int exe_fd = open("/proc/self/exe", O_RDONLY);
|
ABSL_ATTRIBUTE_NO_TAIL_CALL
|
||||||
SAPI_RAW_PCHECK(exe_fd >= 0, "Opening /proc/self/exe");
|
void ViolatePolicy(int x = 0) {
|
||||||
|
IndirectLibcCall([x]() { syscall(__NR_ptrace, x); });
|
||||||
std::string tmpname;
|
|
||||||
int tmp_fd;
|
|
||||||
std::tie(tmpname, tmp_fd) = sapi::CreateNamedTempFile("tmp").value();
|
|
||||||
SAPI_RAW_PCHECK(fchmod(tmp_fd, S_IRWXU) == 0, "Fchmod on temporary file");
|
|
||||||
|
|
||||||
char buf[4096];
|
|
||||||
while (true) {
|
|
||||||
ssize_t read_cnt = read(exe_fd, buf, sizeof(buf));
|
|
||||||
SAPI_RAW_PCHECK(read_cnt >= 0, "Reading /proc/self/exe");
|
|
||||||
if (read_cnt == 0) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
SAPI_RAW_PCHECK(write(tmp_fd, buf, read_cnt) == read_cnt,
|
|
||||||
"Writing temporary file");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
SAPI_RAW_PCHECK(close(tmp_fd) == 0, "Closing temporary file");
|
ABSL_ATTRIBUTE_NOINLINE
|
||||||
tmp_fd = open(tmpname.c_str(), O_RDONLY);
|
ABSL_ATTRIBUTE_NO_TAIL_CALL
|
||||||
SAPI_RAW_PCHECK(tmp_fd >= 0, "Reopening temporary file");
|
void ExitNormally(int x = 0) {
|
||||||
|
IndirectLibcCall([x]() {
|
||||||
|
// _exit is marked noreturn, which makes stack traces a bit trickier -
|
||||||
|
// work around by using a volatile read
|
||||||
|
if (volatile int y = 1) {
|
||||||
|
_exit(x);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
char prog_name[] = "crashme";
|
ABSL_ATTRIBUTE_NOINLINE
|
||||||
char testno[] = "1";
|
ABSL_ATTRIBUTE_NO_TAIL_CALL
|
||||||
char* argv[] = {prog_name, testno, nullptr};
|
void SleepForXSeconds(int x = 0) {
|
||||||
|
IndirectLibcCall([x]() { sleep(x); });
|
||||||
SAPI_RAW_PCHECK(execv(tmpname.c_str(), argv) == 0, "Executing copied binary");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
if (argc < 2) {
|
SAPI_RAW_CHECK(argc >= 2, "Not enough arguments");
|
||||||
printf("argc < 3\n");
|
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
int testno;
|
int testno;
|
||||||
SAPI_RAW_CHECK(absl::SimpleAtoi(argv[1], &testno), "testno not a number");
|
SAPI_RAW_CHECK(absl::SimpleAtoi(argv[1], &testno), "testno not a number");
|
||||||
switch (testno) {
|
switch (testno) {
|
||||||
|
@ -78,13 +72,16 @@ int main(int argc, char* argv[]) {
|
||||||
CrashMe();
|
CrashMe();
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
RunWritable();
|
ViolatePolicy();
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
ExitNormally();
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
SleepForXSeconds(10);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
printf("Unknown test: %d\n", testno);
|
SAPI_RAW_LOG(FATAL, "Unknown test case: %d", testno);
|
||||||
return EXIT_FAILURE;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("OK: All tests went OK\n");
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user