From a613dda7f2a291b5791ae40d19ab1d4766aa6bf3 Mon Sep 17 00:00:00 2001 From: Wiktor Garbacz Date: Thu, 2 Mar 2023 05:09:05 -0800 Subject: [PATCH] Test stack unwinding more thoroughly Check unwinding recursive calls. Verify we can unwind in absence of unwind tables. PiperOrigin-RevId: 513506498 Change-Id: Ib87240b7481dae3a4513c944e17a7924a54926e9 --- sandboxed_api/sandbox2/stack_trace_test.cc | 57 +++++++++++++++---- sandboxed_api/sandbox2/testcases/BUILD.bazel | 17 ++++++ .../sandbox2/testcases/CMakeLists.txt | 18 ++++++ sandboxed_api/sandbox2/testcases/symbolize.cc | 49 ++++++++++++++-- .../sandbox2/testcases/symbolize_lib.cc | 25 ++++++++ .../sandbox2/testcases/symbolize_lib.h | 10 ++++ 6 files changed, 160 insertions(+), 16 deletions(-) create mode 100644 sandboxed_api/sandbox2/testcases/symbolize_lib.cc create mode 100644 sandboxed_api/sandbox2/testcases/symbolize_lib.h diff --git a/sandboxed_api/sandbox2/stack_trace_test.cc b/sandboxed_api/sandbox2/stack_trace_test.cc index 48b56eb..6a15b31 100644 --- a/sandboxed_api/sandbox2/stack_trace_test.cc +++ b/sandboxed_api/sandbox2/stack_trace_test.cc @@ -54,9 +54,11 @@ using ::testing::IsEmpty; using ::testing::StartsWith; struct TestCase { - std::string arg = "1"; + std::string testname = "CrashMe"; + int testno = 1; + int testmode = 1; int final_status = Result::SIGNALED; - std::string function_name = "CrashMe"; + std::string function_name = testname; std::string full_function_description = "CrashMe(char)"; std::function modify_policy; absl::Duration wall_time_limit = absl::ZeroDuration(); @@ -67,7 +69,8 @@ class StackTraceTest : public ::testing::TestWithParam {}; // Test that symbolization of stack traces works. void SymbolizationWorksCommon(TestCase param) { const std::string path = GetTestSourcePath("sandbox2/testcases/symbolize"); - std::vector args = {path, param.arg}; + std::vector args = {path, absl::StrCat(param.testno), + absl::StrCat(param.testmode)}; auto policybuilder = PolicyBuilder() // Don't restrict the syscalls at all. @@ -87,6 +90,20 @@ void SymbolizationWorksCommon(TestCase param) { // Check that demangling works as well. EXPECT_THAT(result.stack_trace(), Contains(StartsWith(param.full_function_description))); + EXPECT_THAT(result.stack_trace(), Contains(StartsWith("RunTest"))); + EXPECT_THAT(result.stack_trace(), Contains(StartsWith("main"))); + if (param.testmode == 2) { + EXPECT_THAT(result.stack_trace(), + Contains(StartsWith("RecurseA")).Times(5)); + EXPECT_THAT(result.stack_trace(), + Contains(StartsWith("RecurseB")).Times(5)); + } else if (param.testmode == 3) { + EXPECT_THAT(result.stack_trace(), Contains(StartsWith("LibRecurse"))); + EXPECT_THAT(result.stack_trace(), + Contains(StartsWith("LibRecurseA")).Times(5)); + EXPECT_THAT(result.stack_trace(), + Contains(StartsWith("LibRecurseB")).Times(5)); + } } void SymbolizationWorksWithModifiedPolicy( @@ -206,21 +223,21 @@ INSTANTIATE_TEST_SUITE_P( Instantiation, StackTraceTest, ::testing::Values( TestCase{ - .arg = "1", + .testname = "CrashMe", + .testno = 1, .final_status = Result::SIGNALED, - .function_name = "CrashMe", .full_function_description = "CrashMe(char)", }, TestCase{ - .arg = "2", + .testname = "ViolatePolicy", + .testno = 2, .final_status = Result::VIOLATION, - .function_name = "ViolatePolicy", .full_function_description = "ViolatePolicy(int)", }, TestCase{ - .arg = "3", + .testname = "ExitNormally", + .testno = 3, .final_status = Result::OK, - .function_name = "ExitNormally", .full_function_description = "ExitNormally(int)", .modify_policy = [](PolicyBuilder* builder) { @@ -228,14 +245,30 @@ INSTANTIATE_TEST_SUITE_P( }, }, TestCase{ - .arg = "4", + .testname = "SleepForXSeconds", + .testno = 4, .final_status = Result::TIMEOUT, - .function_name = "SleepForXSeconds", .full_function_description = "SleepForXSeconds(int)", .wall_time_limit = absl::Seconds(1), + }, + TestCase{ + .testname = "ViolatePolicyRecursive", + .testno = 2, + .testmode = 2, + .final_status = Result::VIOLATION, + .function_name = "ViolatePolicy", + .full_function_description = "ViolatePolicy(int)", + }, + TestCase{ + .testname = "ViolatePolicyRecursiveLib", + .testno = 2, + .testmode = 3, + .final_status = Result::VIOLATION, + .function_name = "ViolatePolicy", + .full_function_description = "ViolatePolicy(int)", }), [](const ::testing::TestParamInfo& info) { - return info.param.function_name; + return info.param.testname; }); } // namespace diff --git a/sandboxed_api/sandbox2/testcases/BUILD.bazel b/sandboxed_api/sandbox2/testcases/BUILD.bazel index 017a6ba..b3ca0b0 100644 --- a/sandboxed_api/sandbox2/testcases/BUILD.bazel +++ b/sandboxed_api/sandbox2/testcases/BUILD.bazel @@ -163,6 +163,22 @@ cc_binary( features = ["fully_static_link"], ) +cc_library( + name = "symbolize_lib", + testonly = True, + srcs = ["symbolize_lib.cc"], + hdrs = ["symbolize_lib.h"], + copts = sapi_platform_copts([ + "-fno-omit-frame-pointer", + "-fno-unwind-tables", + "-fno-asynchronous-unwind-tables", + ]), + features = ["fully_static_link"], + deps = [ + "@com_google_absl//absl/base:core_headers", + ], +) + cc_binary( name = "symbolize", testonly = True, @@ -170,6 +186,7 @@ cc_binary( copts = sapi_platform_copts(), features = ["fully_static_link"], deps = [ + ":symbolize_lib", "//sandboxed_api/util:raw_logging", "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/strings", diff --git a/sandboxed_api/sandbox2/testcases/CMakeLists.txt b/sandboxed_api/sandbox2/testcases/CMakeLists.txt index ca6f610..a810421 100644 --- a/sandboxed_api/sandbox2/testcases/CMakeLists.txt +++ b/sandboxed_api/sandbox2/testcases/CMakeLists.txt @@ -213,6 +213,24 @@ target_link_libraries(sandbox2_testcase_symbolize PRIVATE absl::strings sapi::base sapi::raw_logging + sandbox2::testcase_symbolize_lib +) + +# sandboxed_api/sandbox2/testcases:symbolize_lib +add_library(sandbox2_testcase_symbolize_lib + symbolize_lib.cc + symbolize_lib.h +) +add_library(sandbox2::testcase_symbolize_lib ALIAS sandbox2_testcase_symbolize_lib) +target_link_libraries(sandbox2_testcase_symbolize_lib PRIVATE + -static + absl::core_headers + sapi::base +) +target_compile_options(sandbox2_testcase_symbolize_lib PRIVATE + -fno-omit-frame-pointer + -fno-unwind-tables + -fno-asynchronous-unwind-tables ) # sandboxed_api/sandbox2/testcases:starve diff --git a/sandboxed_api/sandbox2/testcases/symbolize.cc b/sandboxed_api/sandbox2/testcases/symbolize.cc index 9a5d998..0b20c69 100644 --- a/sandboxed_api/sandbox2/testcases/symbolize.cc +++ b/sandboxed_api/sandbox2/testcases/symbolize.cc @@ -22,6 +22,7 @@ #include "absl/base/attributes.h" #include "absl/strings/numbers.h" +#include "sandboxed_api/sandbox2/testcases/symbolize_lib.h" #include "sandboxed_api/util/raw_logging.h" // Sometimes we don't have debug info to properly unwind through libc (a frame @@ -63,10 +64,9 @@ void SleepForXSeconds(int x = 0) { IndirectLibcCall([x]() { sleep(x); }); } -int main(int argc, char* argv[]) { - SAPI_RAW_CHECK(argc >= 2, "Not enough arguments"); - int testno; - SAPI_RAW_CHECK(absl::SimpleAtoi(argv[1], &testno), "testno not a number"); +ABSL_ATTRIBUTE_NOINLINE +ABSL_ATTRIBUTE_NO_TAIL_CALL +void RunTest(int testno) { switch (testno) { case 1: CrashMe(); @@ -83,5 +83,46 @@ int main(int argc, char* argv[]) { default: SAPI_RAW_LOG(FATAL, "Unknown test case: %d", testno); } +} + +ABSL_ATTRIBUTE_NOINLINE +ABSL_ATTRIBUTE_NO_TAIL_CALL +void RecurseA(int testno, int n); + +ABSL_ATTRIBUTE_NOINLINE +ABSL_ATTRIBUTE_NO_TAIL_CALL +void RecurseB(int testno, int n) { + if (n > 1) { + return RecurseA(testno, n - 1); + } + return RunTest(testno); +} + +void RecurseA(int testno, int n) { + if (n > 1) { + return RecurseB(testno, n - 1); + } + return RunTest(testno); +} + +int main(int argc, char* argv[]) { + SAPI_RAW_CHECK(argc >= 3, "Not enough arguments"); + int testno; + int testmode; + SAPI_RAW_CHECK(absl::SimpleAtoi(argv[1], &testno), "testno not a number"); + SAPI_RAW_CHECK(absl::SimpleAtoi(argv[2], &testmode), "testmode not a number"); + switch (testmode) { + case 1: + RunTest(testno); + break; + case 2: + RecurseA(testno, 10); + break; + case 3: + LibRecurse(&RunTest, testno, 10); + break; + default: + SAPI_RAW_LOG(FATAL, "Unknown test mode: %d", testmode); + } return EXIT_SUCCESS; } diff --git a/sandboxed_api/sandbox2/testcases/symbolize_lib.cc b/sandboxed_api/sandbox2/testcases/symbolize_lib.cc new file mode 100644 index 0000000..8d6ce6b --- /dev/null +++ b/sandboxed_api/sandbox2/testcases/symbolize_lib.cc @@ -0,0 +1,25 @@ +#include "sandboxed_api/sandbox2/testcases/symbolize_lib.h" + +#include "absl/base/attributes.h" + +ABSL_ATTRIBUTE_NOINLINE +ABSL_ATTRIBUTE_NO_TAIL_CALL +void LibRecurseA(void (*cb)(int), int data, int n); + +ABSL_ATTRIBUTE_NOINLINE +ABSL_ATTRIBUTE_NO_TAIL_CALL +void LibRecurseB(void (*cb)(int), int data, int n) { + if (n > 1) { + return LibRecurseA(cb, data, n - 1); + } + return cb(data); +} + +void LibRecurseA(void (*cb)(int), int data, int n) { + if (n > 1) { + return LibRecurseB(cb, data, n - 1); + } + return cb(data); +} + +void LibRecurse(void (*cb)(int), int data, int n) { LibRecurseA(cb, data, n); } diff --git a/sandboxed_api/sandbox2/testcases/symbolize_lib.h b/sandboxed_api/sandbox2/testcases/symbolize_lib.h new file mode 100644 index 0000000..7e934f6 --- /dev/null +++ b/sandboxed_api/sandbox2/testcases/symbolize_lib.h @@ -0,0 +1,10 @@ +#ifndef SANDBOXED_API_SANDBOX2_TESTCASES_SYMBOLIZE_LIB_H_ +#define SANDBOXED_API_SANDBOX2_TESTCASES_SYMBOLIZE_LIB_H_ + +#include "absl/base/attributes.h" + +ABSL_ATTRIBUTE_NOINLINE +ABSL_ATTRIBUTE_NO_TAIL_CALL +void LibRecurse(void (*cb)(int), int data, int n); + +#endif // SANDBOXED_API_SANDBOX2_TESTCASES_SYMBOLIZE_LIB_H_