diff options
Diffstat (limited to 'debuggerd/debuggerd_test.cpp')
-rw-r--r-- | debuggerd/debuggerd_test.cpp | 143 |
1 files changed, 140 insertions, 3 deletions
diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp index 939f4d257..f8b4bad6e 100644 --- a/debuggerd/debuggerd_test.cpp +++ b/debuggerd/debuggerd_test.cpp | |||
@@ -41,6 +41,9 @@ | |||
41 | #include <cutils/sockets.h> | 41 | #include <cutils/sockets.h> |
42 | #include <gtest/gtest.h> | 42 | #include <gtest/gtest.h> |
43 | 43 | ||
44 | #include <libminijail.h> | ||
45 | #include <scoped_minijail.h> | ||
46 | |||
44 | #include "debuggerd/handler.h" | 47 | #include "debuggerd/handler.h" |
45 | #include "protocol.h" | 48 | #include "protocol.h" |
46 | #include "tombstoned/tombstoned.h" | 49 | #include "tombstoned/tombstoned.h" |
@@ -76,9 +79,8 @@ constexpr char kWaitForGdbKey[] = "debug.debuggerd.wait_for_gdb"; | |||
76 | return value; \ | 79 | return value; \ |
77 | }() | 80 | }() |
78 | 81 | ||
79 | #define ASSERT_BACKTRACE_FRAME(result, frame_name) \ | 82 | #define ASSERT_BACKTRACE_FRAME(result, frame_name) \ |
80 | ASSERT_MATCH(result, R"(#\d\d pc [0-9a-f]+\s+ /system/lib)" ARCH_SUFFIX \ | 83 | ASSERT_MATCH(result, R"(#\d\d pc [0-9a-f]+\s+ \S+ \()" frame_name R"(\+)"); |
81 | R"(/libc.so \()" frame_name R"(\+)") | ||
82 | 84 | ||
83 | static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd, | 85 | static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd, |
84 | InterceptStatus* status, DebuggerdDumpType intercept_type) { | 86 | InterceptStatus* status, DebuggerdDumpType intercept_type) { |
@@ -565,6 +567,141 @@ TEST_F(CrasherTest, fake_pid) { | |||
565 | ASSERT_BACKTRACE_FRAME(result, "tgkill"); | 567 | ASSERT_BACKTRACE_FRAME(result, "tgkill"); |
566 | } | 568 | } |
567 | 569 | ||
570 | static const char* const kDebuggerdSeccompPolicy = | ||
571 | "/system/etc/seccomp_policy/crash_dump." ABI_STRING ".policy"; | ||
572 | |||
573 | pid_t seccomp_fork() { | ||
574 | unique_fd policy_fd(open(kDebuggerdSeccompPolicy, O_RDONLY | O_CLOEXEC)); | ||
575 | if (policy_fd == -1) { | ||
576 | LOG(FATAL) << "failed to open policy " << kDebuggerdSeccompPolicy; | ||
577 | } | ||
578 | |||
579 | ScopedMinijail jail{minijail_new()}; | ||
580 | if (!jail) { | ||
581 | LOG(FATAL) << "failed to create minijail"; | ||
582 | } | ||
583 | |||
584 | minijail_no_new_privs(jail.get()); | ||
585 | minijail_log_seccomp_filter_failures(jail.get()); | ||
586 | minijail_use_seccomp_filter(jail.get()); | ||
587 | minijail_parse_seccomp_filters_from_fd(jail.get(), policy_fd.release()); | ||
588 | |||
589 | pid_t result = fork(); | ||
590 | if (result == -1) { | ||
591 | return result; | ||
592 | } else if (result != 0) { | ||
593 | return result; | ||
594 | } | ||
595 | |||
596 | // Spawn and detach a thread that spins forever. | ||
597 | std::atomic<bool> thread_ready(false); | ||
598 | std::thread thread([&jail, &thread_ready]() { | ||
599 | minijail_enter(jail.get()); | ||
600 | thread_ready = true; | ||
601 | for (;;) | ||
602 | ; | ||
603 | }); | ||
604 | thread.detach(); | ||
605 | |||
606 | while (!thread_ready) { | ||
607 | continue; | ||
608 | } | ||
609 | |||
610 | minijail_enter(jail.get()); | ||
611 | return result; | ||
612 | } | ||
613 | |||
614 | TEST_F(CrasherTest, seccomp_crash) { | ||
615 | int intercept_result; | ||
616 | unique_fd output_fd; | ||
617 | |||
618 | StartProcess([]() { abort(); }, &seccomp_fork); | ||
619 | |||
620 | StartIntercept(&output_fd); | ||
621 | FinishCrasher(); | ||
622 | AssertDeath(SIGABRT); | ||
623 | FinishIntercept(&intercept_result); | ||
624 | ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; | ||
625 | |||
626 | std::string result; | ||
627 | ConsumeFd(std::move(output_fd), &result); | ||
628 | ASSERT_BACKTRACE_FRAME(result, "abort"); | ||
629 | } | ||
630 | |||
631 | __attribute__((noinline)) extern "C" bool raise_debugger_signal(DebuggerdDumpType dump_type) { | ||
632 | siginfo_t siginfo; | ||
633 | siginfo.si_code = SI_QUEUE; | ||
634 | siginfo.si_pid = getpid(); | ||
635 | siginfo.si_uid = getuid(); | ||
636 | |||
637 | if (dump_type != kDebuggerdNativeBacktrace && dump_type != kDebuggerdTombstone) { | ||
638 | PLOG(FATAL) << "invalid dump type"; | ||
639 | } | ||
640 | |||
641 | siginfo.si_value.sival_int = dump_type == kDebuggerdNativeBacktrace; | ||
642 | |||
643 | if (syscall(__NR_rt_tgsigqueueinfo, getpid(), gettid(), DEBUGGER_SIGNAL, &siginfo) != 0) { | ||
644 | PLOG(ERROR) << "libdebuggerd_client: failed to send signal to self"; | ||
645 | return false; | ||
646 | } | ||
647 | |||
648 | return true; | ||
649 | } | ||
650 | |||
651 | TEST_F(CrasherTest, seccomp_tombstone) { | ||
652 | int intercept_result; | ||
653 | unique_fd output_fd; | ||
654 | |||
655 | static const auto dump_type = kDebuggerdTombstone; | ||
656 | StartProcess( | ||
657 | []() { | ||
658 | raise_debugger_signal(dump_type); | ||
659 | _exit(0); | ||
660 | }, | ||
661 | &seccomp_fork); | ||
662 | |||
663 | StartIntercept(&output_fd, dump_type); | ||
664 | FinishCrasher(); | ||
665 | AssertDeath(0); | ||
666 | FinishIntercept(&intercept_result); | ||
667 | ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; | ||
668 | |||
669 | std::string result; | ||
670 | ConsumeFd(std::move(output_fd), &result); | ||
671 | ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal"); | ||
672 | } | ||
673 | |||
674 | TEST_F(CrasherTest, seccomp_backtrace) { | ||
675 | int intercept_result; | ||
676 | unique_fd output_fd; | ||
677 | |||
678 | static const auto dump_type = kDebuggerdNativeBacktrace; | ||
679 | StartProcess( | ||
680 | []() { | ||
681 | raise_debugger_signal(dump_type); | ||
682 | _exit(0); | ||
683 | }, | ||
684 | &seccomp_fork); | ||
685 | |||
686 | StartIntercept(&output_fd, dump_type); | ||
687 | FinishCrasher(); | ||
688 | AssertDeath(0); | ||
689 | FinishIntercept(&intercept_result); | ||
690 | ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; | ||
691 | |||
692 | std::string result; | ||
693 | ConsumeFd(std::move(output_fd), &result); | ||
694 | ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal"); | ||
695 | } | ||
696 | |||
697 | TEST_F(CrasherTest, seccomp_crash_logcat) { | ||
698 | StartProcess([]() { abort(); }, &seccomp_fork); | ||
699 | FinishCrasher(); | ||
700 | |||
701 | // Make sure we don't get SIGSYS when trying to dump a crash to logcat. | ||
702 | AssertDeath(SIGABRT); | ||
703 | } | ||
704 | |||
568 | TEST_F(CrasherTest, competing_tracer) { | 705 | TEST_F(CrasherTest, competing_tracer) { |
569 | int intercept_result; | 706 | int intercept_result; |
570 | unique_fd output_fd; | 707 | unique_fd output_fd; |