Test stack unwinding more thoroughly

Check unwinding recursive calls.
Verify we can unwind in absence of unwind tables.

PiperOrigin-RevId: 513506498
Change-Id: Ib87240b7481dae3a4513c944e17a7924a54926e9
This commit is contained in:
Wiktor Garbacz 2023-03-02 05:09:05 -08:00 committed by Copybara-Service
parent 0033c4563f
commit a613dda7f2
6 changed files with 160 additions and 16 deletions

View File

@ -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<void(PolicyBuilder*)> modify_policy;
absl::Duration wall_time_limit = absl::ZeroDuration();
@ -67,7 +69,8 @@ class StackTraceTest : public ::testing::TestWithParam<TestCase> {};
// Test that symbolization of stack traces works.
void SymbolizationWorksCommon(TestCase param) {
const std::string path = GetTestSourcePath("sandbox2/testcases/symbolize");
std::vector<std::string> args = {path, param.arg};
std::vector<std::string> 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<TestCase>& info) {
return info.param.function_name;
return info.param.testname;
});
} // namespace

View File

@ -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",

View File

@ -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

View File

@ -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;
}

View File

@ -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); }

View File

@ -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_