// 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. // A binary that exits via different modes: crashes, causes violation, exits // normally or times out, to test the stack tracing symbolizer. #include #include #include #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 // is skipped). // Workaround by putting another frame on the call stack. template ABSL_ATTRIBUTE_NOINLINE ABSL_ATTRIBUTE_NO_TAIL_CALL void IndirectLibcCall( F func) { func(); } ABSL_ATTRIBUTE_NOINLINE void CrashMe(char x = 0) { volatile char* null = nullptr; *null = x; } ABSL_ATTRIBUTE_NOINLINE ABSL_ATTRIBUTE_NO_TAIL_CALL void ViolatePolicy(int x = 0) { IndirectLibcCall([x]() { syscall(__NR_ptrace, x); }); } ABSL_ATTRIBUTE_NOINLINE ABSL_ATTRIBUTE_NO_TAIL_CALL 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); } }); } ABSL_ATTRIBUTE_NOINLINE ABSL_ATTRIBUTE_NO_TAIL_CALL void SleepForXSeconds(int x = 0) { IndirectLibcCall([x]() { sleep(x); }); } ABSL_ATTRIBUTE_NOINLINE ABSL_ATTRIBUTE_NO_TAIL_CALL void RunTest(int testno) { switch (testno) { case 1: CrashMe(); break; case 2: ViolatePolicy(); break; case 3: ExitNormally(); break; case 4: SleepForXSeconds(10); break; case 5: { constexpr int kMaxForks = 16; for (int i = 0; i < kMaxForks; ++i) { if (fork() == 0) { if (i == kMaxForks - 1) { ViolatePolicy(); } break; } } SleepForXSeconds(10); break; } 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; }