diff --git a/Android.bp b/Android.bp new file mode 100644 index 000000000000..c77a8033723e --- /dev/null +++ b/Android.bp @@ -0,0 +1,5 @@ +dirgroup { + name: "trusty_dirgroup_system_core", + dirs: ["."], + visibility: ["//trusty/vendor/google/aosp/scripts"], +} diff --git a/METADATA b/METADATA deleted file mode 100644 index d97975ca3b99..000000000000 --- a/METADATA +++ /dev/null @@ -1,3 +0,0 @@ -third_party { - license_type: NOTICE -} diff --git a/OWNERS b/OWNERS index 682a067b3a98..96b4f54ec0b5 100644 --- a/OWNERS +++ b/OWNERS @@ -1 +1,2 @@ +# Bug component: 128577 enh@google.com diff --git a/PREUPLOAD.cfg b/PREUPLOAD.cfg index 9b96f3691b55..f47c3171c9a2 100644 --- a/PREUPLOAD.cfg +++ b/PREUPLOAD.cfg @@ -1,6 +1,7 @@ [Builtin Hooks] clang_format = true rustfmt = true +bpfmt = true [Builtin Hooks Options] clang_format = --commit ${PREUPLOAD_COMMIT} --style file --extensions c,h,cc,cpp diff --git a/bootstat/Android.bp b/bootstat/Android.bp index ca59ef39bb95..0c8760c2d1b4 100644 --- a/bootstat/Android.bp +++ b/bootstat/Android.bp @@ -72,9 +72,6 @@ cc_binary { ], init_rc: ["bootstat.rc"], product_variables: { - pdk: { - enabled: false, - }, debuggable: { init_rc: ["bootstat-debug.rc"], }, diff --git a/bootstat/OWNERS b/bootstat/OWNERS index f66b309bb24b..71b4e0b1cfa8 100644 --- a/bootstat/OWNERS +++ b/bootstat/OWNERS @@ -1,2 +1,3 @@ -jhawkins@google.com dvander@google.com +achant@google.com +markcheng@google.com diff --git a/bootstat/bootstat.cpp b/bootstat/bootstat.cpp index 844357cfcb63..96c5b8146291 100644 --- a/bootstat/bootstat.cpp +++ b/bootstat/bootstat.cpp @@ -459,6 +459,17 @@ const std::map kBootReasonMap = { {"reboot,sys_ldo_ok,pmic,main", 227}, {"reboot,sys_ldo_ok,pmic,sub", 228}, {"reboot,smpl_timeout,pmic,main", 229}, + {"reboot,ota,.*", 230}, + {"reboot,periodic,.*", 231}, + {"reboot,early,abl", 232}, + {"reboot,early,bl2", 233}, + {"reboot,longkey,pmic_cold", 234}, + {"reboot,longkey,master_dc", 235}, + {"reboot,ocp2,pmic,if", 236}, + {"reboot,ocp,pmic,if", 237}, + {"reboot,fship.*", 238}, + {"reboot,ocp,.*", 239}, + {"reboot,ntc,pmic,sub", 240}, }; // Converts a string value representing the reason the system booted to an @@ -489,7 +500,7 @@ int32_t BootReasonStrToEnum(const std::string& boot_reason) { } // Canonical list of supported primary reboot reasons. -const std::vector knownReasons = { +const std::vector knownReasons = { // clang-format off // kernel "watchdog", @@ -820,7 +831,7 @@ std::string getSubreason(const std::string& content, size_t pos, bool quoted) { return subReason; } -bool addKernelPanicSubReason(const pstoreConsole& console, std::string& ret) { +void addKernelPanicSubReason(const pstoreConsole& console, std::string& ret) { // Check for kernel panic types to refine information if ((console.rfind("SysRq : Trigger a crash") != std::string::npos) || (console.rfind("PC is at sysrq_handle_crash+") != std::string::npos)) { @@ -832,63 +843,61 @@ bool addKernelPanicSubReason(const pstoreConsole& console, std::string& ret) { if (pos != std::string::npos) { ret += "," + getSubreason(console, pos + strlen(sysrqSubreason), /* quoted */ true); } - return true; + return; } if (console.rfind("Unable to handle kernel NULL pointer dereference at virtual address") != std::string::npos) { ret = "kernel_panic,null"; - return true; + return; } if (console.rfind("Kernel BUG at ") != std::string::npos) { ret = "kernel_panic,bug"; - return true; + return; } std::string panic("Kernel panic - not syncing: "); auto pos = console.rfind(panic); - if (pos != std::string::npos) { - static const std::vector> panicReasons = { - {"Out of memory", "oom"}, - {"out of memory", "oom"}, - {"Oh boy, that early out of memory", "oom"}, // omg - {"BUG!", "bug"}, - {"hung_task: blocked tasks", "hung"}, - {"audit: ", "audit"}, - {"scheduling while atomic", "atomic"}, - {"Attempted to kill init!", "init"}, - {"Requested init", "init"}, - {"No working init", "init"}, - {"Could not decompress init", "init"}, - {"RCU Stall", "hung,rcu"}, - {"stack-protector", "stack"}, - {"kernel stack overflow", "stack"}, - {"Corrupt kernel stack", "stack"}, - {"low stack detected", "stack"}, - {"corrupted stack end", "stack"}, - {"subsys-restart: Resetting the SoC - modem crashed.", "modem"}, - {"subsys-restart: Resetting the SoC - adsp crashed.", "adsp"}, - {"subsys-restart: Resetting the SoC - dsps crashed.", "dsps"}, - {"subsys-restart: Resetting the SoC - wcnss crashed.", "wcnss"}, - }; + if (pos == std::string::npos) return; + + static const std::vector> panicReasons = { + {"Out of memory", "oom"}, + {"out of memory", "oom"}, + {"Oh boy, that early out of memory", "oom"}, // omg + {"BUG!", "bug"}, + {"hung_task: blocked tasks", "hung"}, + {"audit: ", "audit"}, + {"scheduling while atomic", "atomic"}, + {"Attempted to kill init!", "init"}, + {"Requested init", "init"}, + {"No working init", "init"}, + {"Could not decompress init", "init"}, + {"RCU Stall", "hung,rcu"}, + {"stack-protector", "stack"}, + {"kernel stack overflow", "stack"}, + {"Corrupt kernel stack", "stack"}, + {"low stack detected", "stack"}, + {"corrupted stack end", "stack"}, + {"subsys-restart: Resetting the SoC - modem crashed.", "modem"}, + {"subsys-restart: Resetting the SoC - adsp crashed.", "adsp"}, + {"subsys-restart: Resetting the SoC - dsps crashed.", "dsps"}, + {"subsys-restart: Resetting the SoC - wcnss crashed.", "wcnss"}, + }; - ret = "kernel_panic"; - for (auto& s : panicReasons) { - if (console.find(panic + s.first, pos) != std::string::npos) { - ret += "," + s.second; - return true; - } + ret = "kernel_panic"; + for (auto& s : panicReasons) { + if (console.find(panic + s.first, pos) != std::string::npos) { + ret += "," + s.second; + return; } - auto reason = getSubreason(console, pos + panic.length(), /* newline */ false); - if (reason.length() > 3) { - ret += "," + reason; - } - return true; } - return false; + auto reason = getSubreason(console, pos + panic.length(), /* newline */ false); + if (reason.length() > 3) { + ret += "," + reason; + } } -bool addKernelPanicSubReason(const std::string& content, std::string& ret) { - return addKernelPanicSubReason(pstoreConsole(content), ret); +void addKernelPanicSubReason(const std::string& content, std::string& ret) { + addKernelPanicSubReason(pstoreConsole(content), ret); } const char system_reboot_reason_property[] = "sys.boot.reason"; @@ -904,6 +913,19 @@ const char bootloader_reboot_reason_property[] = "ro.boot.bootreason"; void BootReasonAddToHistory(const std::string& system_boot_reason) { if (system_boot_reason.empty()) return; LOG(INFO) << "Canonical boot reason: " << system_boot_reason; + + // skip system_boot_reason(factory_reset, ota) shift since device boot up from shipmode + const auto bootloader_boot_reason = + android::base::GetProperty(bootloader_reboot_reason_property, ""); + const char reg_fship[] = ".*fship.*"; + if (std::regex_search(bootloader_boot_reason, std::regex(reg_fship)) != 0) { + if (system_boot_reason == "reboot,factory_reset" || system_boot_reason == "reboot,ota") { + LOG(INFO) << "skip boot reason (" << system_boot_reason + << ") shift since device boot up from shipmode."; + return; + } + } + auto old_system_boot_reason = android::base::GetProperty(system_reboot_reason_property, ""); if (!android::base::SetProperty(system_reboot_reason_property, system_boot_reason)) { android::base::SetProperty(system_reboot_reason_property, @@ -945,6 +967,14 @@ void BootReasonAddToHistory(const std::string& system_boot_reason) { std::string BootReasonStrToReason(const std::string& boot_reason) { auto ret = android::base::GetProperty(system_reboot_reason_property, ""); std::string reason(boot_reason); + + // skip BootReasonStrToReason() if device boot up from shipmode + const char reg_fship[] = ".*fship.*"; + if (reason == ret && std::regex_search(reason, std::regex(reg_fship)) != 0) { + LOG(INFO) << "skip boot reason enhancement if device boot up from shipmode"; + return ret; + } + // If sys.boot.reason == ro.boot.bootreason, let's re-evaluate if (reason == ret) ret = ""; @@ -1069,12 +1099,7 @@ std::string BootReasonStrToReason(const std::string& boot_reason) { } // Check for kernel panics, allowed to override reboot command. - if (!addKernelPanicSubReason(console, ret) && - // check for long-press power down - ((console.rfind("Power held for ") != std::string::npos) || - (console.rfind("charger: [") != std::string::npos))) { - ret = "cold"; - } + (void)addKernelPanicSubReason(console, ret); } // TODO: use the HAL to get battery level (http://b/77725702). diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp index d20de6bbf724..0e62ceb8f61b 100644 --- a/debuggerd/Android.bp +++ b/debuggerd/Android.bp @@ -12,6 +12,7 @@ cc_defaults { "-Wno-unused-argument", "-Wno-unused-function", "-Wno-nullability-completeness", + "-Wno-reorder-init-list", "-Os", "-fno-finite-loops", "-DANDROID_DEBUGGABLE=0", @@ -20,8 +21,11 @@ cc_defaults { local_include_dirs: ["include"], product_variables: { debuggable: { - cflags: ["-UANDROID_DEBUGGABLE", "-DANDROID_DEBUGGABLE=1"], - } + cflags: [ + "-UANDROID_DEBUGGABLE", + "-DANDROID_DEBUGGABLE=1", + ], + }, }, } @@ -31,9 +35,10 @@ cc_library_headers { recovery_available: true, vendor_ramdisk_available: true, apex_available: [ + "com.android.runtime", "com.android.virt", "//apex_available:platform", - ], + ], } cc_library_shared { @@ -84,6 +89,7 @@ cc_library_static { export_header_lib_headers: ["libdebuggerd_common_headers"], export_include_dirs: ["tombstoned/include"], + apex_available: ["com.android.runtime"], } // Core implementation, linked into libdebuggerd_handler and the dynamic linker. @@ -109,6 +115,9 @@ cc_library_static { export_header_lib_headers: ["libdebuggerd_common_headers"], export_include_dirs: ["include"], + apex_available: [ + "com.android.runtime", + ], } // Implementation with a no-op fallback. @@ -185,9 +194,45 @@ cc_library { export_include_dirs: ["include"], } +cc_library { + name: "libdebuggerd_tombstone_proto_to_text", + defaults: ["debuggerd_defaults"], + ramdisk_available: true, + recovery_available: true, + vendor_ramdisk_available: true, + host_supported: true, + + local_include_dirs: ["libdebuggerd/include"], + export_include_dirs: ["libdebuggerd/include"], + + srcs: [ + "libdebuggerd/tombstone_proto_to_text.cpp", + "libdebuggerd/utility_host.cpp", + ], + + static_libs: [ + "libbase", + ], + + whole_static_libs: [ + "libtombstone_proto", + "libprotobuf-cpp-lite", + ], + + shared_libs: [ + "liblog", + ], + + apex_available: [ + "//apex_available:platform", + "com.android.runtime", + ], +} + cc_library_static { name: "libdebuggerd", defaults: ["debuggerd_defaults"], + ramdisk_available: true, recovery_available: true, vendor_ramdisk_available: true, @@ -195,12 +240,16 @@ cc_library_static { "libdebuggerd/backtrace.cpp", "libdebuggerd/gwp_asan.cpp", "libdebuggerd/open_files_list.cpp", + "libdebuggerd/scudo.cpp", "libdebuggerd/tombstone.cpp", "libdebuggerd/tombstone_proto.cpp", - "libdebuggerd/tombstone_proto_to_text.cpp", "libdebuggerd/utility.cpp", ], + cflags: [ + "-DUSE_SCUDO", + ], + local_include_dirs: ["libdebuggerd/include"], export_include_dirs: ["libdebuggerd/include"], @@ -212,25 +261,25 @@ cc_library_static { "bionic_libc_platform_headers", "gwp_asan_headers", "liblog_headers", + "scudo_headers", ], static_libs: [ - "libdexfile_support", // libunwindstack dependency + "libdexfile_support", // libunwindstack dependency "libunwindstack", "liblzma", "libbase", "libcutils", ], - runtime_libs: [ - "libdexfile", // libdexfile_support dependency - ], whole_static_libs: [ + "libdebuggerd_tombstone_proto_to_text", "libasync_safe", "gwp_asan_crash_handler", "libtombstone_proto", "libprocinfo", "libprotobuf-cpp-lite", + "libscudo", ], target: { @@ -250,6 +299,19 @@ cc_library_static { "libdexfile", ], }, + ramdisk: { + exclude_static_libs: [ + "libdexfile_support", + ], + exclude_runtime_libs: [ + "libdexfile", + ], + }, + android: { + runtime_libs: [ + "libdexfile", // libdexfile_support dependency + ], + }, }, product_variables: { @@ -257,26 +319,30 @@ cc_library_static { cflags: ["-DROOT_POSSIBLE"], }, - malloc_not_svelte: { - cflags: ["-DUSE_SCUDO"], - whole_static_libs: ["libscudo"], - srcs: ["libdebuggerd/scudo.cpp"], - header_libs: ["scudo_headers"], + malloc_low_memory: { + cflags: ["-UUSE_SCUDO"], + exclude_static_libs: ["libscudo"], }, }, + apex_available: [ + "com.android.runtime", + ], } cc_binary { name: "pbtombstone", + host_supported: true, defaults: ["debuggerd_defaults"], - srcs: ["pbtombstone.cpp"], + srcs: [ + "pbtombstone.cpp", + "tombstone_symbolize.cpp", + ], static_libs: [ "libbase", - "libdebuggerd", + "libdebuggerd_tombstone_proto_to_text", "liblog", "libprotobuf-cpp-lite", "libtombstone_proto", - "libunwindstack", ], } @@ -296,8 +362,9 @@ cc_test { "libdebuggerd/test/dump_memory_test.cpp", "libdebuggerd/test/elf_fake.cpp", "libdebuggerd/test/log_fake.cpp", + "libdebuggerd/test/mte_stack_record_test.cpp", "libdebuggerd/test/open_files_list_test.cpp", - "libdebuggerd/test/utility_test.cpp", + "libdebuggerd/test/tombstone_proto_to_text_test.cpp", ], target: { @@ -313,6 +380,10 @@ cc_test { }, }, + sanitize: { + memtag_heap: true, + }, + shared_libs: [ "libbase", "libcutils", @@ -385,6 +456,7 @@ cc_binary { header_libs: [ "bionic_libc_platform_headers", + "libnative_bridge_support_accessor_headers", ], static_libs: [ @@ -394,6 +466,8 @@ cc_binary { "libtombstone_proto", "libprotobuf-cpp-lite", + + "libnative_bridge_guest_state_accessor", ], shared_libs: [ @@ -409,6 +483,15 @@ cc_binary { // Required for tests. required: ["crash_dump.policy"], + + target: { + android: { + header_libs: [ + "libnative_bridge_support_accessor_headers", // For dlext_namespaces.h + ], + shared_libs: ["libdl_android"], // For android_get_exported_namespace implementation + }, + }, } cc_binary { @@ -422,6 +505,7 @@ cc_binary { "libbase", "libdebuggerd_client", "liblog", + "libprocessgroup", "libprocinfo", ], @@ -439,7 +523,7 @@ cc_defaults { header_libs: [ "bionic_libc_platform_headers", - "libdebuggerd_common_headers" + "libdebuggerd_common_headers", ], static_libs: [ @@ -497,7 +581,6 @@ prebuilt_etc { }, } - // This installs the "other" architecture (so 32-bit on 64-bit device). prebuilt_etc { name: "crash_dump.policy_other", diff --git a/debuggerd/TEST_MAPPING b/debuggerd/TEST_MAPPING index 8633cb866dbe..824d20a5a8b8 100644 --- a/debuggerd/TEST_MAPPING +++ b/debuggerd/TEST_MAPPING @@ -3,6 +3,10 @@ { "name": "debuggerd_test" }, + { + "name": "debuggerd_test", + "keywords": ["primary-device"] + }, { "name": "libtombstoned_client_rust_test" }, @@ -14,5 +18,10 @@ { "name": "debuggerd_test" } + ], + "postsubmit": [ + { + "name": "CtsCrashDetailHostTestCases" + } ] } diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp index c9e097ea35b2..632f6f1c9024 100644 --- a/debuggerd/client/debuggerd_client.cpp +++ b/debuggerd/client/debuggerd_client.cpp @@ -116,7 +116,6 @@ static std::string get_wchan_data(int fd, pid_t pid) { bool debuggerd_trigger_dump(pid_t tid, DebuggerdDumpType dump_type, unsigned int timeout_ms, unique_fd output_fd) { - pid_t pid = tid; if (dump_type == kDebuggerdJavaBacktrace) { // Java dumps always get sent to the tgid, so we need to resolve our tid to a tgid. android::procinfo::ProcessInfo procinfo; @@ -125,10 +124,10 @@ bool debuggerd_trigger_dump(pid_t tid, DebuggerdDumpType dump_type, unsigned int log_error(output_fd, 0, "failed to get process info: %s", error.c_str()); return false; } - pid = procinfo.pid; + tid = procinfo.pid; } - LOG(INFO) << TAG "started dumping process " << pid; + LOG(INFO) << TAG "started dumping process " << tid; // Rather than try to deal with poll() all the way through the flow, we update // the socket timeout between each step (and only use poll() during the final @@ -139,7 +138,7 @@ bool debuggerd_trigger_dump(pid_t tid, DebuggerdDumpType dump_type, unsigned int auto remaining = end - std::chrono::steady_clock::now(); if (remaining < decltype(remaining)::zero()) { - log_error(output_fd, 0, "timeout expired"); + log_error(output_fd, 0, "timeout expired (update_timeout)"); return false; } @@ -172,7 +171,7 @@ bool debuggerd_trigger_dump(pid_t tid, DebuggerdDumpType dump_type, unsigned int InterceptRequest req = { .dump_type = dump_type, - .pid = pid, + .pid = tid, }; // Create an intermediate pipe to pass to the other end. @@ -235,8 +234,8 @@ bool debuggerd_trigger_dump(pid_t tid, DebuggerdDumpType dump_type, unsigned int // Send the signal. const int signal = (dump_type == kDebuggerdJavaBacktrace) ? SIGQUIT : BIONIC_SIGNAL_DEBUGGER; sigval val = {.sival_int = (dump_type == kDebuggerdNativeBacktrace) ? 1 : 0}; - if (sigqueue(pid, signal, val) != 0) { - log_error(output_fd, errno, "failed to send signal to pid %d", pid); + if (sigqueue(tid, signal, val) != 0) { + log_error(output_fd, errno, "failed to send signal to pid %d", tid); return false; } @@ -255,7 +254,7 @@ bool debuggerd_trigger_dump(pid_t tid, DebuggerdDumpType dump_type, unsigned int if (timeout_ms <= 0) { remaining_ms = -1; } else if (remaining_ms < 0) { - log_error(output_fd, 0, "timeout expired"); + log_error(output_fd, 0, "timeout expired before poll"); return false; } @@ -272,10 +271,17 @@ bool debuggerd_trigger_dump(pid_t tid, DebuggerdDumpType dump_type, unsigned int return false; } } else if (rc == 0) { - log_error(output_fd, 0, "timeout expired"); + log_error(output_fd, 0, "poll timeout expired"); return false; } + // WARNING: It's not possible to replace the below with a splice call. + // Due to the way debuggerd does many small writes across the pipe, + // this would cause splice to copy a page for each write. The second + // pipe fills up based on the number of pages being copied, even + // though there is not much data being transferred per page. When + // the second pipe is full, everything stops since there is nothing + // reading the second pipe to clear it. char buf[1024]; rc = TEMP_FAILURE_RETRY(read(pipe_read.get(), buf, sizeof(buf))); if (rc == 0) { @@ -292,7 +298,7 @@ bool debuggerd_trigger_dump(pid_t tid, DebuggerdDumpType dump_type, unsigned int } } - LOG(INFO) << TAG "done dumping process " << pid; + LOG(INFO) << TAG "done dumping process " << tid; return true; } diff --git a/debuggerd/client/debuggerd_client_test.cpp b/debuggerd/client/debuggerd_client_test.cpp index ebb8d86e0bce..33ff05fd6ace 100644 --- a/debuggerd/client/debuggerd_client_test.cpp +++ b/debuggerd/client/debuggerd_client_test.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -51,23 +52,35 @@ static int getThreadCount() { TEST(debuggerd_client, race) { static int THREAD_COUNT = getThreadCount(); - pid_t forkpid = fork(); - ASSERT_NE(-1, forkpid); + // Semaphore incremented once per thread started. + unique_fd barrier(eventfd(0, EFD_SEMAPHORE)); + ASSERT_NE(-1, barrier.get()); + pid_t forkpid = fork(); + ASSERT_NE(-1, forkpid); if (forkpid == 0) { // Spawn a bunch of threads, to make crash_dump take longer. std::vector threads; + threads.reserve(THREAD_COUNT); for (int i = 0; i < THREAD_COUNT; ++i) { - threads.emplace_back([]() { - while (true) { - std::this_thread::sleep_for(60s); + threads.emplace_back([&barrier]() { + uint64_t count = 1; + ASSERT_NE(-1, write(barrier.get(), &count, sizeof(count))); + for (;;) { + pause(); } }); } + for (;;) { + pause(); + } + } - std::this_thread::sleep_for(60s); - exit(0); + // Wait for the child to spawn all of its threads. + for (int i = 0; i < THREAD_COUNT; ++i) { + uint64_t count; + ASSERT_NE(-1, read(barrier.get(), &count, sizeof(count))); } unique_fd pipe_read, pipe_write; @@ -77,9 +90,6 @@ TEST(debuggerd_client, race) { constexpr int PIPE_SIZE = 16 * 1024 * 1024; ASSERT_EQ(PIPE_SIZE, fcntl(pipe_read.get(), F_SETPIPE_SZ, PIPE_SIZE)); - // Wait for a bit to let the child spawn all of its threads. - std::this_thread::sleep_for(1s); - ASSERT_TRUE( debuggerd_trigger_dump(forkpid, kDebuggerdNativeBacktrace, 60000, std::move(pipe_write))); // Immediately kill the forked child, to make sure that the dump didn't return early. diff --git a/debuggerd/common/include/dump_type.h b/debuggerd/common/include/dump_type.h index a3e171b25162..82ef7b67ca02 100644 --- a/debuggerd/common/include/dump_type.h +++ b/debuggerd/common/include/dump_type.h @@ -28,26 +28,24 @@ enum DebuggerdDumpType : uint8_t { kDebuggerdTombstoneProto, }; -inline std::ostream& operator<<(std::ostream& stream, const DebuggerdDumpType& rhs) { - switch (rhs) { +inline const char* get_dump_type_name(const DebuggerdDumpType& dump_type) { + switch (dump_type) { case kDebuggerdNativeBacktrace: - stream << "kDebuggerdNativeBacktrace"; - break; + return "kDebuggerdNativeBacktrace"; case kDebuggerdTombstone: - stream << "kDebuggerdTombstone"; - break; + return "kDebuggerdTombstone"; case kDebuggerdJavaBacktrace: - stream << "kDebuggerdJavaBacktrace"; - break; + return "kDebuggerdJavaBacktrace"; case kDebuggerdAnyIntercept: - stream << "kDebuggerdAnyIntercept"; - break; + return "kDebuggerdAnyIntercept"; case kDebuggerdTombstoneProto: - stream << "kDebuggerdTombstoneProto"; - break; + return "kDebuggerdTombstoneProto"; default: - stream << "[unknown]"; + return "[unknown]"; } +} +inline std::ostream& operator<<(std::ostream& stream, const DebuggerdDumpType& rhs) { + stream << get_dump_type_name(rhs); return stream; } diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp index 3563436ac41d..92d81b326db0 100644 --- a/debuggerd/crash_dump.cpp +++ b/debuggerd/crash_dump.cpp @@ -22,9 +22,15 @@ #include #include #include +#include #include #include +#if defined(__i386__) +#include +#endif + +#include #include #include #include @@ -42,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -52,7 +59,18 @@ #include #include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include + +#include #include "libdebuggerd/backtrace.h" #include "libdebuggerd/tombstone.h" @@ -68,6 +86,10 @@ using android::base::ErrnoRestorer; using android::base::StringPrintf; using android::base::unique_fd; +// This stores guest architecture. When the architecture is supported, tombstone file will output +// guest state information. +static Architecture g_guest_arch = Architecture::NONE; + static bool pid_contains_tid(int pid_proc_fd, pid_t tid) { struct stat st; std::string task_path = StringPrintf("task/%d", tid); @@ -143,7 +165,7 @@ static bool ptrace_interrupt(pid_t tid, int* received_signal) { } static bool activity_manager_notify(pid_t pid, int signal, const std::string& amfd_data, - bool recoverable_gwp_asan_crash) { + bool recoverable_crash) { ATRACE_CALL(); android::base::unique_fd amfd(socket_local_client( "/data/system/ndebugsocket", ANDROID_SOCKET_NAMESPACE_FILESYSTEM, SOCK_STREAM)); @@ -169,7 +191,7 @@ static bool activity_manager_notify(pid_t pid, int signal, const std::string& am // Activity Manager protocol: // - 32-bit network-byte-order: pid // - 32-bit network-byte-order: signal number - // - byte: recoverable_gwp_asan_crash + // - byte: recoverable_crash // - bytes: raw text of the dump // - null terminator @@ -185,10 +207,9 @@ static bool activity_manager_notify(pid_t pid, int signal, const std::string& am return false; } - uint8_t recoverable_gwp_asan_crash_byte = recoverable_gwp_asan_crash ? 1 : 0; - if (!android::base::WriteFully(amfd, &recoverable_gwp_asan_crash_byte, - sizeof(recoverable_gwp_asan_crash_byte))) { - PLOG(ERROR) << "AM recoverable_gwp_asan_crash_byte write failed"; + uint8_t recoverable_crash_byte = recoverable_crash ? 1 : 0; + if (!android::base::WriteFully(amfd, &recoverable_crash_byte, sizeof(recoverable_crash_byte))) { + PLOG(ERROR) << "AM recoverable_crash_byte write failed"; return false; } @@ -280,35 +301,34 @@ static void ParseArgs(int argc, char** argv, pid_t* pseudothread_tid, DebuggerdD static void ReadCrashInfo(unique_fd& fd, siginfo_t* siginfo, std::unique_ptr* regs, ProcessInfo* process_info, - bool* recoverable_gwp_asan_crash) { + bool* recoverable_crash) { std::aligned_storage::type buf; CrashInfo* crash_info = reinterpret_cast(&buf); ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), &buf, sizeof(buf))); - *recoverable_gwp_asan_crash = false; + *recoverable_crash = false; if (rc == -1) { PLOG(FATAL) << "failed to read target ucontext"; - } else { - ssize_t expected_size = 0; - switch (crash_info->header.version) { - case 1: - case 2: - case 3: - expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataStatic); - break; - - case 4: - expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataDynamic); - break; - - default: - LOG(FATAL) << "unexpected CrashInfo version: " << crash_info->header.version; - break; - }; - - if (rc < expected_size) { - LOG(FATAL) << "read " << rc << " bytes when reading target crash information, expected " - << expected_size; - } + } + ssize_t expected_size = 0; + switch (crash_info->header.version) { + case 1: + case 2: + case 3: + expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataStatic); + break; + + case 4: + expected_size = sizeof(CrashInfoHeader) + sizeof(CrashInfoDataDynamic); + break; + + default: + LOG(FATAL) << "unexpected CrashInfo version: " << crash_info->header.version; + break; + } + + if (rc < expected_size) { + LOG(FATAL) << "read " << rc << " bytes when reading target crash information, expected " + << expected_size; } switch (crash_info->header.version) { @@ -317,10 +337,12 @@ static void ReadCrashInfo(unique_fd& fd, siginfo_t* siginfo, process_info->gwp_asan_state = crash_info->data.d.gwp_asan_state; process_info->gwp_asan_metadata = crash_info->data.d.gwp_asan_metadata; process_info->scudo_stack_depot = crash_info->data.d.scudo_stack_depot; + process_info->scudo_stack_depot_size = crash_info->data.d.scudo_stack_depot_size; process_info->scudo_region_info = crash_info->data.d.scudo_region_info; process_info->scudo_ring_buffer = crash_info->data.d.scudo_ring_buffer; process_info->scudo_ring_buffer_size = crash_info->data.d.scudo_ring_buffer_size; - *recoverable_gwp_asan_crash = crash_info->data.d.recoverable_gwp_asan_crash; + *recoverable_crash = crash_info->data.d.recoverable_crash; + process_info->crash_detail_page = crash_info->data.d.crash_detail_page; FALLTHROUGH_INTENDED; case 1: case 2: @@ -402,6 +424,140 @@ static void InstallSigPipeHandler() { sigaction(SIGPIPE, &action, nullptr); } +static bool PtracePeek(int request, pid_t tid, uintptr_t addr, void* data, std::string_view err_msg, + uintptr_t* result) { + errno = 0; + *result = ptrace(request, tid, addr, data); + if (errno != 0) { + PLOG(ERROR) << err_msg; + return false; + } + return true; +} + +static bool GetGuestRegistersFromCrashedProcess(pid_t tid, NativeBridgeGuestRegs* guest_regs) { + auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(tid); + + uintptr_t header_ptr = 0; + uintptr_t base = 0; +#if defined(__aarch64__) + // base is implicitly casted to uint64_t. + struct iovec pt_iov { + .iov_base = &base, .iov_len = sizeof(base), + }; + + if (ptrace(PTRACE_GETREGSET, tid, NT_ARM_TLS, &pt_iov) != 0) { + PLOG(ERROR) << "failed to read thread register for thread " << tid; + return false; + } +#elif defined(__arm__) + // Arm doesn't support any guest architectures yet. + return false; +#elif defined(__i386__) + struct user_regs_struct regs; + struct iovec pt_iov = {.iov_base = ®s, .iov_len = sizeof(regs)}; + if (ptrace(PTRACE_GETREGSET, tid, NT_PRSTATUS, &pt_iov) != 0) { + PLOG(ERROR) << "failed to get registers for thread " << tid; + return false; + } + + struct user_desc desc; + desc.entry_number = regs.xgs >> 3; + if (ptrace(PTRACE_GET_THREAD_AREA, tid, desc.entry_number, &desc) != 0) { + PLOG(ERROR) << "failed to get thread area for thread " << tid; + return false; + } + base = desc.base_addr; +#elif defined(__riscv) + struct user_regs_struct regs; + struct iovec pt_iov = {.iov_base = ®s, .iov_len = sizeof(regs)}; + if (ptrace(PTRACE_GETREGSET, tid, NT_PRSTATUS, &pt_iov) != 0) { + PLOG(ERROR) << "failed to read thread register for thread " << tid; + return false; + } + base = reinterpret_cast(regs.tp); +#elif defined(__x86_64__) + if (!PtracePeek(PTRACE_PEEKUSER, tid, offsetof(user_regs_struct, fs_base), nullptr, + "failed to read thread register for thread " + std::to_string(tid), &base)) { + return false; + } +#else + // TODO(b/339287219): Add case for Riscv host. + return false; +#endif + auto ptr_to_guest_slot = base + TLS_SLOT_NATIVE_BRIDGE_GUEST_STATE * sizeof(uintptr_t); + if (!process_memory->ReadFully(ptr_to_guest_slot, &header_ptr, sizeof(uintptr_t))) { + PLOG(ERROR) << "failed to get guest state TLS slot content for thread " << tid; + return false; + } + + NativeBridgeGuestStateHeader header; + if (!process_memory->ReadFully(header_ptr, &header, sizeof(NativeBridgeGuestStateHeader)) || + header.signature != NATIVE_BRIDGE_GUEST_STATE_SIGNATURE) { + // Return when ptr points to unmapped memory or no valid guest state. + return false; + } + + auto guest_state_data_copy = std::make_unique(header.guest_state_data_size); + if (!process_memory->ReadFully(reinterpret_cast(header.guest_state_data), + guest_state_data_copy.get(), header.guest_state_data_size)) { + PLOG(ERROR) << "failed to read the guest state data for thread " << tid; + return false; + } + + LoadGuestStateRegisters(guest_state_data_copy.get(), header.guest_state_data_size, guest_regs); + return true; +} + +static void ReadGuestRegisters(std::unique_ptr* regs, pid_t tid) { + NativeBridgeGuestRegs guest_regs; + if (!GetGuestRegistersFromCrashedProcess(tid, &guest_regs)) { + return; + } + + switch (guest_regs.guest_arch) { +#if defined(__LP64__) + case NATIVE_BRIDGE_ARCH_ARM64: { + unwindstack::arm64_user_regs arm64_user_regs = {}; + for (size_t i = 0; i < unwindstack::ARM64_REG_R31; i++) { + arm64_user_regs.regs[i] = guest_regs.regs_arm64.x[i]; + } + arm64_user_regs.sp = guest_regs.regs_arm64.sp; + arm64_user_regs.pc = guest_regs.regs_arm64.ip; + regs->reset(unwindstack::RegsArm64::Read(&arm64_user_regs)); + + g_guest_arch = Architecture::ARM64; + break; + } + case NATIVE_BRIDGE_ARCH_RISCV64: { + unwindstack::riscv64_user_regs riscv64_user_regs = {}; + // RISCV64_REG_PC is at the first position. + riscv64_user_regs.regs[0] = guest_regs.regs_riscv64.ip; + for (size_t i = 1; i < unwindstack::RISCV64_REG_REAL_COUNT; i++) { + riscv64_user_regs.regs[i] = guest_regs.regs_riscv64.x[i]; + } + regs->reset(unwindstack::RegsRiscv64::Read(&riscv64_user_regs, tid)); + + g_guest_arch = Architecture::RISCV64; + break; + } +#else + case NATIVE_BRIDGE_ARCH_ARM: { + unwindstack::arm_user_regs arm_user_regs = {}; + for (size_t i = 0; i < unwindstack::ARM_REG_LAST; i++) { + arm_user_regs.regs[i] = guest_regs.regs_arm.r[i]; + } + regs->reset(unwindstack::RegsArm::Read(&arm_user_regs)); + + g_guest_arch = Architecture::ARM32; + break; + } +#endif + default: + break; + } +} + int main(int argc, char** argv) { DefuseSignalHandlers(); InstallSigPipeHandler(); @@ -485,7 +641,7 @@ int main(int argc, char** argv) { std::map thread_info; siginfo_t siginfo; std::string error; - bool recoverable_gwp_asan_crash = false; + bool recoverable_crash = false; { ATRACE_NAME("ptrace"); @@ -535,10 +691,18 @@ int main(int argc, char** argv) { info.pac_enabled_keys = -1; } +#if defined(__aarch64__) + struct iovec tls_iov = { + &info.tls, + sizeof(info.tls), + }; + if (ptrace(PTRACE_GETREGSET, thread, NT_ARM_TLS, reinterpret_cast(&tls_iov)) == -1) { + info.tls = 0; + } +#endif if (thread == g_target_thread) { // Read the thread's registers along with the rest of the crash info out of the pipe. - ReadCrashInfo(input_pipe, &siginfo, &info.registers, &process_info, - &recoverable_gwp_asan_crash); + ReadCrashInfo(input_pipe, &siginfo, &info.registers, &process_info, &recoverable_crash); info.siginfo = &siginfo; info.signo = info.siginfo->si_signo; @@ -551,6 +715,7 @@ int main(int argc, char** argv) { continue; } } + ReadGuestRegisters(&info.guest_registers, thread); thread_info[thread] = std::move(info); } @@ -660,15 +825,36 @@ int main(int argc, char** argv) { { ATRACE_NAME("engrave_tombstone"); - engrave_tombstone(std::move(g_output_fd), std::move(g_proto_fd), &unwinder, thread_info, - g_target_thread, process_info, &open_files, &amfd_data); + unwindstack::ArchEnum regs_arch = unwindstack::ARCH_UNKNOWN; + switch (g_guest_arch) { + case Architecture::ARM32: + regs_arch = unwindstack::ARCH_ARM; + break; + case Architecture::ARM64: + regs_arch = unwindstack::ARCH_ARM64; + break; + case Architecture::RISCV64: + regs_arch = unwindstack::ARCH_RISCV64; + break; + default: + break; + } + if (regs_arch == unwindstack::ARCH_UNKNOWN) { + engrave_tombstone(std::move(g_output_fd), std::move(g_proto_fd), &unwinder, thread_info, + g_target_thread, process_info, &open_files, &amfd_data); + } else { + unwindstack::AndroidRemoteUnwinder guest_unwinder(vm_pid, regs_arch); + engrave_tombstone(std::move(g_output_fd), std::move(g_proto_fd), &unwinder, thread_info, + g_target_thread, process_info, &open_files, &amfd_data, &g_guest_arch, + &guest_unwinder); + } } } if (fatal_signal) { // Don't try to notify ActivityManager if it just crashed, or we might hang until timeout. if (thread_info[target_process].thread_name != "system_server") { - activity_manager_notify(target_process, signo, amfd_data, recoverable_gwp_asan_crash); + activity_manager_notify(target_process, signo, amfd_data, recoverable_crash); } } diff --git a/debuggerd/crasher/Android.bp b/debuggerd/crasher/Android.bp index fe1689c71763..3af806b43151 100644 --- a/debuggerd/crasher/Android.bp +++ b/debuggerd/crasher/Android.bp @@ -15,7 +15,6 @@ cc_defaults { "-fstack-protector-all", "-Wno-date-time", ], - tidy: false, // crasher.cpp tests many memory access errors srcs: ["crasher.cpp"], arch: { arm: { diff --git a/debuggerd/crasher/arm/crashglue.S b/debuggerd/crasher/arm/crashglue.S index e4adf403cd85..3001ca19e9b7 100644 --- a/debuggerd/crasher/arm/crashglue.S +++ b/debuggerd/crasher/arm/crashglue.S @@ -1,3 +1,19 @@ +/* + * Copyright 2006, The Android Open Source Project + * + * 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 + * + * http://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. + */ + .globl crash1 .type crash1, %function crash1: @@ -23,10 +39,11 @@ crash1: ldr lr, [lr] b . .cfi_endproc + .size crash1, .-crash1 -.globl crashnostack -.type crashnostack, %function -crashnostack: +.globl crash_no_stack +.type crash_no_stack, %function +crash_no_stack: .cfi_startproc mov r1, sp .cfi_def_cfa_register r1 @@ -35,3 +52,4 @@ crashnostack: ldr r0, [r0] b . .cfi_endproc + .size crash_no_stack, .-crash_no_stack diff --git a/debuggerd/crasher/arm64/crashglue.S b/debuggerd/crasher/arm64/crashglue.S index 97c824efb2d2..90ba9a1eb2b0 100644 --- a/debuggerd/crasher/arm64/crashglue.S +++ b/debuggerd/crasher/arm64/crashglue.S @@ -1,3 +1,19 @@ +/* + * Copyright 2013, The Android Open Source Project + * + * 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 + * + * http://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. + */ + .globl crash1 .type crash1, %function crash1: @@ -41,11 +57,12 @@ crash1: ldr x30, [x30] b . .cfi_endproc + .size crash1, .-crash1 -.globl crashnostack -.type crashnostack, %function -crashnostack: +.globl crash_no_stack +.type crash_no_stack, %function +crash_no_stack: .cfi_startproc mov x1, sp .cfi_def_cfa_register x1 @@ -54,3 +71,41 @@ crashnostack: ldr x0, [x0] b . .cfi_endproc + .size crash_no_stack, .-crash_no_stack + + +.globl crash_bti +.type crash_bti, %function +crash_bti: + .cfi_startproc + adr x16, 1f + br x16 +1: // Deliberatly not a bti instruction so we crash here. + b . + .cfi_endproc + .size crash_bti, .-crash_bti + + +.globl crash_pac +.type crash_pac, %function +crash_pac: + .cfi_startproc + paciasp + // Since sp is a pac input, this ensures a mismatch. + sub sp, sp, #16 + autiasp + b . + .cfi_endproc + .size crash_pac, .-crash_pac + +// Set the PAC and BTI bits for this object file. +.section .note.gnu.property, "a" +.balign 8 +.long 4 +.long 0x10 +.long 0x5 +.asciz "GNU" +.long 0xc0000000 +.long 4 +.long 0x3 +.long 0 diff --git a/debuggerd/crasher/crasher.cpp b/debuggerd/crasher/crasher.cpp index 6a19878827f6..c3dd92b43c2f 100644 --- a/debuggerd/crasher/crasher.cpp +++ b/debuggerd/crasher/crasher.cpp @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -29,6 +30,9 @@ #include #include +#include +#include + // We test both kinds of logging. #include #include @@ -59,8 +63,10 @@ typedef int (__kuser_cmpxchg64_t)(const int64_t*, const int64_t*, volatile int64 // Avoid name mangling so that stacks are more readable. extern "C" { -void crash1(void); -void crashnostack(void); +void crash1(); +void crash_no_stack(); +void crash_bti(); +void crash_pac(); int do_action(const char* arg); @@ -148,7 +154,7 @@ noinline void abuse_heap() { noinline void leak() { while (true) { void* mapping = - mmap(nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); + mmap(nullptr, getpagesize(), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); static_cast(mapping)[0] = 'a'; } } @@ -196,14 +202,9 @@ static int usage() { fprintf(stderr, " fdsan_file close a file descriptor that's owned by a FILE*\n"); fprintf(stderr, " fdsan_dir close a file descriptor that's owned by a DIR*\n"); fprintf(stderr, " seccomp fail a seccomp check\n"); -#if defined(__arm__) - fprintf(stderr, " kuser_helper_version call kuser_helper_version\n"); - fprintf(stderr, " kuser_get_tls call kuser_get_tls\n"); - fprintf(stderr, " kuser_cmpxchg call kuser_cmpxchg\n"); - fprintf(stderr, " kuser_memory_barrier call kuser_memory_barrier\n"); - fprintf(stderr, " kuser_cmpxchg64 call kuser_cmpxchg64\n"); -#endif +#if defined(__LP64__) fprintf(stderr, " xom read execute-only memory\n"); +#endif fprintf(stderr, "\n"); fprintf(stderr, " LOG_ALWAYS_FATAL call liblog LOG_ALWAYS_FATAL\n"); fprintf(stderr, " LOG_ALWAYS_FATAL_IF call liblog LOG_ALWAYS_FATAL_IF\n"); @@ -223,6 +224,20 @@ static int usage() { fprintf(stderr, "\n"); fprintf(stderr, " no_new_privs set PR_SET_NO_NEW_PRIVS and then abort\n"); fprintf(stderr, "\n"); +#if defined(__arm__) + fprintf(stderr, "Also, since this is an arm32 binary:\n"); + fprintf(stderr, " kuser_helper_version call kuser_helper_version\n"); + fprintf(stderr, " kuser_get_tls call kuser_get_tls\n"); + fprintf(stderr, " kuser_cmpxchg call kuser_cmpxchg\n"); + fprintf(stderr, " kuser_memory_barrier call kuser_memory_barrier\n"); + fprintf(stderr, " kuser_cmpxchg64 call kuser_cmpxchg64\n"); +#endif +#if defined(__aarch64__) + fprintf(stderr, "Also, since this is an arm64 binary:\n"); + fprintf(stderr, " bti fail a branch target identification (BTI) check\n"); + fprintf(stderr, " pac fail a pointer authentication (PAC) check\n"); +#endif + fprintf(stderr, "\n"); fprintf(stderr, "prefix any of the above with 'thread-' to run on a new thread\n"); fprintf(stderr, "prefix any of the above with 'exhaustfd-' to exhaust\n"); fprintf(stderr, "all available file descriptors before crashing.\n"); @@ -231,6 +246,21 @@ static int usage() { return EXIT_FAILURE; } +[[maybe_unused]] static void CheckCpuFeature(const std::string& name) { + std::string cpuinfo; + if (!android::base::ReadFileToString("/proc/cpuinfo", &cpuinfo)) { + error(1, errno, "couldn't read /proc/cpuinfo"); + } + std::vector lines = android::base::Split(cpuinfo, "\n"); + for (std::string_view line : lines) { + if (!android::base::ConsumePrefix(&line, "Features\t:")) continue; + std::vector features = android::base::Split(std::string(line), " "); + if (std::find(features.begin(), features.end(), name) == features.end()) { + error(1, 0, "/proc/cpuinfo does not report feature '%s'", name.c_str()); + } + } +} + noinline int do_action(const char* arg) { // Prefixes. if (!strncmp(arg, "wait-", strlen("wait-"))) { @@ -256,7 +286,7 @@ noinline int do_action(const char* arg) { } else if (!strcasecmp(arg, "stack-overflow")) { overflow_stack(nullptr); } else if (!strcasecmp(arg, "nostack")) { - crashnostack(); + crash_no_stack(); } else if (!strcasecmp(arg, "exit")) { exit(1); } else if (!strcasecmp(arg, "call-null")) { @@ -349,6 +379,14 @@ noinline int do_action(const char* arg) { __kuser_dmb(); } else if (!strcasecmp(arg, "kuser_cmpxchg64")) { return __kuser_cmpxchg64(0, 0, 0); +#endif +#if defined(__aarch64__) + } else if (!strcasecmp(arg, "bti")) { + CheckCpuFeature("bti"); + crash_bti(); + } else if (!strcasecmp(arg, "pac")) { + CheckCpuFeature("paca"); + crash_pac(); #endif } else if (!strcasecmp(arg, "no_new_privs")) { if (prctl(PR_SET_NO_NEW_PRIVS, 1) != 0) { @@ -364,6 +402,8 @@ noinline int do_action(const char* arg) { return EXIT_SUCCESS; } +} // extern "C" + int main(int argc, char** argv) { #if defined(STATIC_CRASHER) debuggerd_callbacks_t callbacks = { @@ -389,5 +429,3 @@ int main(int argc, char** argv) { return usage(); } - -}; diff --git a/debuggerd/crasher/riscv64/crashglue.S b/debuggerd/crasher/riscv64/crashglue.S index 42f59b345723..804a511b917f 100644 --- a/debuggerd/crasher/riscv64/crashglue.S +++ b/debuggerd/crasher/riscv64/crashglue.S @@ -1,3 +1,19 @@ +/* + * Copyright 2022, The Android Open Source Project + * + * 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 + * + * http://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. + */ + .globl crash1 crash1: .cfi_startproc @@ -43,10 +59,11 @@ crash1: ld t2, 0(zero) j . .cfi_endproc + .size crash1, .-crash1 -.globl crashnostack -crashnostack: +.globl crash_no_stack +crash_no_stack: .cfi_startproc mv t1, sp .cfi_def_cfa_register t1 @@ -54,3 +71,4 @@ crashnostack: ld t2, 0(zero) j . .cfi_endproc + .size crash_no_stack, .-crash_no_stack diff --git a/debuggerd/crasher/x86/crashglue.S b/debuggerd/crasher/x86/crashglue.S index e8eb3a7cab5c..fe7c6480a448 100644 --- a/debuggerd/crasher/x86/crashglue.S +++ b/debuggerd/crasher/x86/crashglue.S @@ -1,3 +1,19 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * 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 + * + * http://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. + */ + .globl crash1 crash1: movl $0xa5a50000, %eax @@ -6,13 +22,15 @@ crash1: movl $0, %edx jmp *%edx + .size crash1, .-crash1 -.globl crashnostack -crashnostack: +.globl crash_no_stack +crash_no_stack: .cfi_startproc movl %esp, %eax .cfi_def_cfa_register %eax movl $0, %esp movl (%esp), %ebx .cfi_endproc + .size crash_no_stack, .-crash_no_stack diff --git a/debuggerd/crasher/x86_64/crashglue.S b/debuggerd/crasher/x86_64/crashglue.S index 8f6721478a0b..ae13aa7e61eb 100644 --- a/debuggerd/crasher/x86_64/crashglue.S +++ b/debuggerd/crasher/x86_64/crashglue.S @@ -1,3 +1,19 @@ +/* + * Copyright 2010, The Android Open Source Project + * + * 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 + * + * http://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. + */ + .globl crash1 crash1: movl $0xa5a50000, %eax @@ -6,13 +22,15 @@ crash1: movl $0, %edx jmp *%rdx + .size crash1, .-crash1 -.globl crashnostack -crashnostack: +.globl crash_no_stack +crash_no_stack: .cfi_startproc movq %rsp, %rax .cfi_def_cfa_register %rax movq $0, %rsp movq (%rsp), %rbx .cfi_endproc + .size crash_no_stack, .-crash_no_stack diff --git a/debuggerd/debuggerd.cpp b/debuggerd/debuggerd.cpp index e20e8d9e1613..7a2500c60afc 100644 --- a/debuggerd/debuggerd.cpp +++ b/debuggerd/debuggerd.cpp @@ -23,11 +23,11 @@ #include #include -#include #include #include #include #include +#include #include #include "util.h" @@ -41,22 +41,6 @@ static void usage(int exit_code) { _exit(exit_code); } -static std::thread spawn_redirect_thread(unique_fd fd) { - return std::thread([fd{ std::move(fd) }]() { - while (true) { - char buf[BUFSIZ]; - ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), buf, sizeof(buf))); - if (rc <= 0) { - return; - } - - if (!android::base::WriteFully(STDOUT_FILENO, buf, rc)) { - return; - } - } - }); -} - int main(int argc, char* argv[]) { if (argc <= 1) usage(0); if (argc > 3) usage(1); @@ -107,14 +91,15 @@ int main(int argc, char* argv[]) { } } - unique_fd piperead, pipewrite; - if (!Pipe(&piperead, &pipewrite)) { - err(1, "failed to create pipe"); - } + // unfreeze if pid is frozen. + SetProcessProfiles(proc_info.uid, proc_info.pid, {"Unfrozen"}); + // we don't restore the frozen state as this is considered a benign change. - std::thread redirect_thread = spawn_redirect_thread(std::move(piperead)); - if (!debuggerd_trigger_dump(proc_info.pid, dump_type, 0, std::move(pipewrite))) { - redirect_thread.join(); + unique_fd output_fd(fcntl(STDOUT_FILENO, F_DUPFD_CLOEXEC, 0)); + if (output_fd.get() == -1) { + err(1, "failed to fcntl dup stdout"); + } + if (!debuggerd_trigger_dump(proc_info.pid, dump_type, 0, std::move(output_fd))) { if (pid == proc_info.pid) { errx(1, "failed to dump process %d", pid); } else { @@ -122,6 +107,5 @@ int main(int argc, char* argv[]) { } } - redirect_thread.join(); return 0; } diff --git a/debuggerd/debuggerd_test.cpp b/debuggerd/debuggerd_test.cpp index 39d7fff1ca86..34f2c450c60b 100644 --- a/debuggerd/debuggerd_test.cpp +++ b/debuggerd/debuggerd_test.cpp @@ -18,9 +18,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -37,6 +39,7 @@ #include #include +#include #include #include #include @@ -67,7 +70,7 @@ #include "crash_test.h" #include "debuggerd/handler.h" #include "gtest/gtest.h" -#include "libdebuggerd/utility.h" +#include "libdebuggerd/utility_host.h" #include "protocol.h" #include "tombstoned/tombstoned.h" #include "util.h" @@ -94,7 +97,7 @@ constexpr char kWaitForDebuggerKey[] = "debug.debuggerd.wait_for_debugger"; if (sigaction(SIGALRM, &new_sigaction, &old_sigaction) != 0) { \ err(1, "sigaction failed"); \ } \ - alarm(seconds); \ + alarm(seconds * android::base::HwTimeoutMultiplier()); \ auto value = expr; \ int saved_errno = errno; \ if (sigaction(SIGALRM, &old_sigaction, nullptr) != 0) { \ @@ -114,7 +117,7 @@ constexpr char kWaitForDebuggerKey[] = "debug.debuggerd.wait_for_debugger"; R"(#\d\d pc [0-9a-f]+\s+ \S+ (\(offset 0x[0-9a-f]+\) )?\()" frame_name R"(\+)"); static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, unique_fd* output_fd, - InterceptStatus* status, DebuggerdDumpType intercept_type) { + InterceptResponse* response, DebuggerdDumpType intercept_type) { intercept_fd->reset(socket_local_client(kTombstonedInterceptSocketName, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_SEQPACKET)); if (intercept_fd->get() == -1) { @@ -155,18 +158,15 @@ static void tombstoned_intercept(pid_t target_pid, unique_fd* intercept_fd, uniq FAIL() << "failed to send output fd to tombstoned: " << strerror(errno); } - InterceptResponse response; - rc = TEMP_FAILURE_RETRY(read(intercept_fd->get(), &response, sizeof(response))); + rc = TEMP_FAILURE_RETRY(read(intercept_fd->get(), response, sizeof(*response))); if (rc == -1) { FAIL() << "failed to read response from tombstoned: " << strerror(errno); } else if (rc == 0) { FAIL() << "failed to read response from tombstoned (EOF)"; - } else if (rc != sizeof(response)) { - FAIL() << "received packet of unexpected length from tombstoned: expected " << sizeof(response) + } else if (rc != sizeof(*response)) { + FAIL() << "received packet of unexpected length from tombstoned: expected " << sizeof(*response) << ", received " << rc; } - - *status = response.status; } static bool pac_supported() { @@ -225,9 +225,10 @@ void CrasherTest::StartIntercept(unique_fd* output_fd, DebuggerdDumpType interce FAIL() << "crasher hasn't been started"; } - InterceptStatus status; - tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd, &status, intercept_type); - ASSERT_EQ(InterceptStatus::kRegistered, status); + InterceptResponse response = {}; + tombstoned_intercept(crasher_pid, &this->intercept_fd, output_fd, &response, intercept_type); + ASSERT_EQ(InterceptStatus::kRegistered, response.status) + << "Error message: " << response.error_message; } void CrasherTest::FinishIntercept(int* result) { @@ -300,24 +301,7 @@ void CrasherTest::AssertDeath(int signo) { } static void ConsumeFd(unique_fd fd, std::string* output) { - constexpr size_t read_length = PAGE_SIZE; - std::string result; - - while (true) { - size_t offset = result.size(); - result.resize(result.size() + PAGE_SIZE); - ssize_t rc = TEMP_FAILURE_RETRY(read(fd.get(), &result[offset], read_length)); - if (rc == -1) { - FAIL() << "read failed: " << strerror(errno); - } else if (rc == 0) { - result.resize(result.size() - PAGE_SIZE); - break; - } - - result.resize(result.size() - PAGE_SIZE + rc); - } - - *output = std::move(result); + ASSERT_TRUE(android::base::ReadFdToString(fd, output)); } class LogcatCollector { @@ -349,14 +333,9 @@ TEST_F(CrasherTest, smoke) { std::string result; ConsumeFd(std::move(output_fd), &result); -#ifdef __LP64__ - ASSERT_MATCH(result, - R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0x000000000000dead)"); -#else - ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0x0000dead)"); -#endif + ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0x0+dead)"); - if (mte_supported()) { + if (mte_supported() && mte_enabled()) { // Test that the default TAGGED_ADDR_CTRL value is set. ASSERT_MATCH(result, R"(tagged_addr_ctrl: 000000000007fff3)" R"( \(PR_TAGGED_ADDR_ENABLE, PR_MTE_TCF_SYNC, mask 0xfffe\))"); @@ -464,7 +443,7 @@ INSTANTIATE_TEST_SUITE_P(Sizes, SizeParamCrasherTest, testing::Values(0, 16, 131 TEST_P(SizeParamCrasherTest, mte_uaf) { #if defined(__aarch64__) - if (!mte_supported()) { + if (!mte_supported() || !mte_enabled()) { GTEST_SKIP() << "Requires MTE"; } @@ -511,7 +490,7 @@ TEST_P(SizeParamCrasherTest, mte_uaf) { TEST_P(SizeParamCrasherTest, mte_oob_uaf) { #if defined(__aarch64__) - if (!mte_supported()) { + if (!mte_supported() || !mte_enabled()) { GTEST_SKIP() << "Requires MTE"; } @@ -543,7 +522,7 @@ TEST_P(SizeParamCrasherTest, mte_oob_uaf) { TEST_P(SizeParamCrasherTest, mte_overflow) { #if defined(__aarch64__) - if (!mte_supported()) { + if (!mte_supported() || !mte_enabled()) { GTEST_SKIP() << "Requires MTE"; } @@ -586,7 +565,7 @@ TEST_P(SizeParamCrasherTest, mte_overflow) { TEST_P(SizeParamCrasherTest, mte_underflow) { #if defined(__aarch64__) - if (!mte_supported()) { + if (!mte_supported() || !mte_enabled()) { GTEST_SKIP() << "Requires MTE"; } @@ -619,9 +598,57 @@ TEST_P(SizeParamCrasherTest, mte_underflow) { #endif } +__attribute__((noinline)) void mte_illegal_setjmp_helper(jmp_buf& jump_buf) { + // This frame is at least 8 bytes for storing and restoring the LR before the + // setjmp below. So this can never get an empty stack frame, even if we omit + // the frame pointer. So, the SP of this is always less (numerically) than the + // calling function frame. + setjmp(jump_buf); +} + +TEST_F(CrasherTest, DISABLED_mte_illegal_setjmp) { + // This setjmp is illegal because it jumps back into a function that already returned. + // Quoting man 3 setjmp: + // If the function which called setjmp() returns before longjmp() is + // called, the behavior is undefined. Some kind of subtle or + // unsubtle chaos is sure to result. + // https://man7.org/linux/man-pages/man3/longjmp.3.html +#if defined(__aarch64__) + if (!mte_supported() || !mte_enabled()) { + GTEST_SKIP() << "Requires MTE"; + } + + int intercept_result; + unique_fd output_fd; + StartProcess([&]() { + SetTagCheckingLevelSync(); + jmp_buf jump_buf; + mte_illegal_setjmp_helper(jump_buf); + longjmp(jump_buf, 1); + }); + + StartIntercept(&output_fd); + FinishCrasher(); + AssertDeath(SIGABRT); + FinishIntercept(&intercept_result); + + ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; + + std::string result; + ConsumeFd(std::move(output_fd), &result); + + // In our test-case, we have a NEGATIVE stack adjustment, which is being + // interpreted as unsigned integer, and thus is "too large". + // TODO(fmayer): fix the error message for this + ASSERT_MATCH(result, R"(memtag_handle_longjmp: stack adjustment too large)"); +#else + GTEST_SKIP() << "Requires aarch64"; +#endif +} + TEST_F(CrasherTest, mte_async) { #if defined(__aarch64__) - if (!mte_supported()) { + if (!mte_supported() || !mte_enabled()) { GTEST_SKIP() << "Requires MTE"; } @@ -651,7 +678,7 @@ TEST_F(CrasherTest, mte_async) { TEST_F(CrasherTest, mte_multiple_causes) { #if defined(__aarch64__) - if (!mte_supported()) { + if (!mte_supported() || !mte_enabled()) { GTEST_SKIP() << "Requires MTE"; } @@ -737,7 +764,7 @@ static uintptr_t CreateTagMapping() { TEST_F(CrasherTest, mte_register_tag_dump) { #if defined(__aarch64__) - if (!mte_supported()) { + if (!mte_supported() || !mte_enabled()) { GTEST_SKIP() << "Requires MTE"; } @@ -770,7 +797,7 @@ TEST_F(CrasherTest, mte_register_tag_dump) { TEST_F(CrasherTest, mte_fault_tag_dump_front_truncated) { #if defined(__aarch64__) - if (!mte_supported()) { + if (!mte_supported() || !mte_enabled()) { GTEST_SKIP() << "Requires MTE"; } @@ -801,7 +828,7 @@ TEST_F(CrasherTest, mte_fault_tag_dump_front_truncated) { TEST_F(CrasherTest, mte_fault_tag_dump) { #if defined(__aarch64__) - if (!mte_supported()) { + if (!mte_supported() || !mte_enabled()) { GTEST_SKIP() << "Requires MTE"; } @@ -835,7 +862,7 @@ TEST_F(CrasherTest, mte_fault_tag_dump) { TEST_F(CrasherTest, mte_fault_tag_dump_rear_truncated) { #if defined(__aarch64__) - if (!mte_supported()) { + if (!mte_supported() || !mte_enabled()) { GTEST_SKIP() << "Requires MTE"; } @@ -958,6 +985,233 @@ TEST_F(CrasherTest, abort_message) { ASSERT_MATCH(result, R"(Abort message: 'x{4045}')"); } +static char g_crash_detail_value_changes[] = "crash_detail_value"; +static char g_crash_detail_value[] = "crash_detail_value"; +static char g_crash_detail_value2[] = "crash_detail_value2"; + +inline crash_detail_t* _Nullable android_register_crash_detail_strs(const char* _Nonnull name, + const char* _Nonnull data) { + return android_crash_detail_register(name, strlen(name), data, strlen(data)); +} + +TEST_F(CrasherTest, crash_detail_single) { + int intercept_result; + unique_fd output_fd; + StartProcess([]() { + android_register_crash_detail_strs("CRASH_DETAIL_NAME", g_crash_detail_value); + abort(); + }); + StartIntercept(&output_fd); + FinishCrasher(); + AssertDeath(SIGABRT); + FinishIntercept(&intercept_result); + + ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; + + std::string result; + ConsumeFd(std::move(output_fd), &result); + ASSERT_MATCH(result, R"(CRASH_DETAIL_NAME: 'crash_detail_value')"); +} + +TEST_F(CrasherTest, crash_detail_replace_data) { + int intercept_result; + unique_fd output_fd; + StartProcess([]() { + auto *cd = android_register_crash_detail_strs("CRASH_DETAIL_NAME", "original_data"); + android_crash_detail_replace_data(cd, "new_data", strlen("new_data")); + abort(); + }); + StartIntercept(&output_fd); + FinishCrasher(); + AssertDeath(SIGABRT); + FinishIntercept(&intercept_result); + + ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; + + std::string result; + ConsumeFd(std::move(output_fd), &result); + ASSERT_MATCH(result, R"(CRASH_DETAIL_NAME: 'new_data')"); + // Ensure the old one no longer shows up, i.e. that we actually replaced + // it, not added a new one. + ASSERT_NOT_MATCH(result, R"(CRASH_DETAIL_NAME: 'original_data')"); +} + +TEST_F(CrasherTest, crash_detail_replace_name) { + int intercept_result; + unique_fd output_fd; + StartProcess([]() { + auto *cd = android_register_crash_detail_strs("old_name", g_crash_detail_value); + android_crash_detail_replace_name(cd, "new_name", strlen("new_name")); + abort(); + }); + StartIntercept(&output_fd); + FinishCrasher(); + AssertDeath(SIGABRT); + FinishIntercept(&intercept_result); + + ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; + + std::string result; + ConsumeFd(std::move(output_fd), &result); + ASSERT_MATCH(result, R"(new_name: 'crash_detail_value')"); + // Ensure the old one no longer shows up, i.e. that we actually replaced + // it, not added a new one. + ASSERT_NOT_MATCH(result, R"(old_name: 'crash_detail_value')"); +} + +TEST_F(CrasherTest, crash_detail_single_byte_name) { + int intercept_result; + unique_fd output_fd; + StartProcess([]() { + android_register_crash_detail_strs("CRASH_DETAIL_NAME\1", g_crash_detail_value); + abort(); + }); + StartIntercept(&output_fd); + FinishCrasher(); + AssertDeath(SIGABRT); + FinishIntercept(&intercept_result); + + ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; + + std::string result; + ConsumeFd(std::move(output_fd), &result); + ASSERT_MATCH(result, R"(CRASH_DETAIL_NAME\\1: 'crash_detail_value')"); +} + + +TEST_F(CrasherTest, crash_detail_single_bytes) { + int intercept_result; + unique_fd output_fd; + StartProcess([]() { + android_crash_detail_register("CRASH_DETAIL_NAME", strlen("CRASH_DETAIL_NAME"), "\1", + sizeof("\1")); + abort(); + }); + StartIntercept(&output_fd); + FinishCrasher(); + AssertDeath(SIGABRT); + FinishIntercept(&intercept_result); + + ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; + + std::string result; + ConsumeFd(std::move(output_fd), &result); + ASSERT_MATCH(result, R"(CRASH_DETAIL_NAME: '\\1\\0')"); +} + +TEST_F(CrasherTest, crash_detail_mixed) { + int intercept_result; + unique_fd output_fd; + StartProcess([]() { + const char data[] = "helloworld\1\255\3"; + android_register_crash_detail_strs("CRASH_DETAIL_NAME", data); + abort(); + }); + StartIntercept(&output_fd); + FinishCrasher(); + AssertDeath(SIGABRT); + FinishIntercept(&intercept_result); + + ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; + + std::string result; + ConsumeFd(std::move(output_fd), &result); + ASSERT_MATCH(result, R"(CRASH_DETAIL_NAME: 'helloworld\\1\\255\\3')"); +} + +TEST_F(CrasherTest, crash_detail_many) { + int intercept_result; + unique_fd output_fd; + StartProcess([]() { + for (int i = 0; i < 1000; ++i) { + std::string name = "CRASH_DETAIL_NAME" + std::to_string(i); + std::string value = "CRASH_DETAIL_VALUE" + std::to_string(i); + auto* h = android_register_crash_detail_strs(name.data(), value.data()); + android_crash_detail_unregister(h); + } + + android_register_crash_detail_strs("FINAL_NAME", "FINAL_VALUE"); + android_register_crash_detail_strs("FINAL_NAME2", "FINAL_VALUE2"); + abort(); + }); + StartIntercept(&output_fd); + FinishCrasher(); + AssertDeath(SIGABRT); + FinishIntercept(&intercept_result); + + ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; + + std::string result; + ConsumeFd(std::move(output_fd), &result); + ASSERT_NOT_MATCH(result, "CRASH_DETAIL_NAME"); + ASSERT_NOT_MATCH(result, "CRASH_DETAIL_VALUE"); + ASSERT_MATCH(result, R"(FINAL_NAME: 'FINAL_VALUE')"); + ASSERT_MATCH(result, R"(FINAL_NAME2: 'FINAL_VALUE2')"); +} + +TEST_F(CrasherTest, crash_detail_single_changes) { + int intercept_result; + unique_fd output_fd; + StartProcess([]() { + android_register_crash_detail_strs("CRASH_DETAIL_NAME", g_crash_detail_value_changes); + g_crash_detail_value_changes[0] = 'C'; + abort(); + }); + StartIntercept(&output_fd); + FinishCrasher(); + AssertDeath(SIGABRT); + FinishIntercept(&intercept_result); + + ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; + + std::string result; + ConsumeFd(std::move(output_fd), &result); + ASSERT_MATCH(result, R"(CRASH_DETAIL_NAME: 'Crash_detail_value')"); +} + +TEST_F(CrasherTest, crash_detail_multiple) { + int intercept_result; + unique_fd output_fd; + StartProcess([]() { + android_register_crash_detail_strs("CRASH_DETAIL_NAME", g_crash_detail_value); + android_register_crash_detail_strs("CRASH_DETAIL_NAME2", g_crash_detail_value2); + abort(); + }); + StartIntercept(&output_fd); + FinishCrasher(); + AssertDeath(SIGABRT); + FinishIntercept(&intercept_result); + + ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; + + std::string result; + ConsumeFd(std::move(output_fd), &result); + ASSERT_MATCH(result, R"(CRASH_DETAIL_NAME: 'crash_detail_value')"); + ASSERT_MATCH(result, R"(CRASH_DETAIL_NAME2: 'crash_detail_value2')"); +} + +TEST_F(CrasherTest, crash_detail_remove) { + int intercept_result; + unique_fd output_fd; + StartProcess([]() { + auto* detail1 = android_register_crash_detail_strs("CRASH_DETAIL_NAME", g_crash_detail_value); + android_crash_detail_unregister(detail1); + android_register_crash_detail_strs("CRASH_DETAIL_NAME2", g_crash_detail_value2); + abort(); + }); + StartIntercept(&output_fd); + FinishCrasher(); + AssertDeath(SIGABRT); + FinishIntercept(&intercept_result); + + ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; + + std::string result; + ConsumeFd(std::move(output_fd), &result); + ASSERT_NOT_MATCH(result, R"(CRASH_DETAIL_NAME: 'crash_detail_value')"); + ASSERT_MATCH(result, R"(CRASH_DETAIL_NAME2: 'crash_detail_value2')"); +} + TEST_F(CrasherTest, abort_message_newline_trimmed) { int intercept_result; unique_fd output_fd; @@ -1409,6 +1663,9 @@ TEST_F(CrasherTest, seccomp_tombstone_thread_abort) { std::string result; ConsumeFd(std::move(output_fd), &result); + ASSERT_MATCH( + result, + R"(signal 6 \(SIGABRT\))"); ASSERT_BACKTRACE_FRAME(result, "abort"); } @@ -1515,6 +1772,75 @@ TEST_F(CrasherTest, seccomp_crash_logcat) { AssertDeath(SIGABRT); } +extern "C" void malloc_enable(); +extern "C" void malloc_disable(); + +TEST_F(CrasherTest, seccomp_tombstone_no_allocation) { + int intercept_result; + unique_fd output_fd; + + static const auto dump_type = kDebuggerdTombstone; + StartProcess( + []() { + std::thread a(foo); + std::thread b(bar); + + std::this_thread::sleep_for(100ms); + + // Disable allocations to verify that nothing in the fallback + // signal handler does an allocation. + malloc_disable(); + raise_debugger_signal(dump_type); + _exit(0); + }, + &seccomp_fork); + + StartIntercept(&output_fd, dump_type); + FinishCrasher(); + AssertDeath(0); + FinishIntercept(&intercept_result); + ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; + + std::string result; + ConsumeFd(std::move(output_fd), &result); + ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal"); + ASSERT_BACKTRACE_FRAME(result, "foo"); + ASSERT_BACKTRACE_FRAME(result, "bar"); +} + +TEST_F(CrasherTest, seccomp_backtrace_no_allocation) { + int intercept_result; + unique_fd output_fd; + + static const auto dump_type = kDebuggerdNativeBacktrace; + StartProcess( + []() { + std::thread a(foo); + std::thread b(bar); + + std::this_thread::sleep_for(100ms); + + // Disable allocations to verify that nothing in the fallback + // signal handler does an allocation. + malloc_disable(); + raise_debugger_signal(dump_type); + _exit(0); + }, + &seccomp_fork); + + StartIntercept(&output_fd, dump_type); + FinishCrasher(); + AssertDeath(0); + FinishIntercept(&intercept_result); + ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; + + std::string result; + ConsumeFd(std::move(output_fd), &result); + ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal"); + ASSERT_BACKTRACE_FRAME(result, "foo"); + ASSERT_BACKTRACE_FRAME(result, "bar"); +} + TEST_F(CrasherTest, competing_tracer) { int intercept_result; unique_fd output_fd; @@ -1568,10 +1894,14 @@ GwpAsanTestParameters gwp_asan_tests[] = { "Use After Free, 0 bytes into a 7-byte allocation"}, {/* alloc_size */ 15, /* free_before_access */ true, /* access_offset */ 1, "Use After Free, 1 byte into a 15-byte allocation"}, - {/* alloc_size */ 4096, /* free_before_access */ false, /* access_offset */ 4098, - "Buffer Overflow, 2 bytes right of a 4096-byte allocation"}, - {/* alloc_size */ 4096, /* free_before_access */ false, /* access_offset */ -1, - "Buffer Underflow, 1 byte left of a 4096-byte allocation"}, + {/* alloc_size */ static_cast(getpagesize()), /* free_before_access */ false, + /* access_offset */ getpagesize() + 2, + android::base::StringPrintf("Buffer Overflow, 2 bytes right of a %d-byte allocation", + getpagesize())}, + {/* alloc_size */ static_cast(getpagesize()), /* free_before_access */ false, + /* access_offset */ -1, + android::base::StringPrintf("Buffer Underflow, 1 byte left of a %d-byte allocation", + getpagesize())}, }; INSTANTIATE_TEST_SUITE_P( @@ -1616,8 +1946,8 @@ TEST_P(GwpAsanCrasherTest, run_gwp_asan_test) { StartProcess([&recoverable]() { const char* env[] = {"GWP_ASAN_SAMPLE_RATE=1", "GWP_ASAN_PROCESS_SAMPLING=1", "GWP_ASAN_MAX_ALLOCS=40000", nullptr, nullptr}; - if (recoverable) { - env[3] = "GWP_ASAN_RECOVERABLE=true"; + if (!recoverable) { + env[3] = "GWP_ASAN_RECOVERABLE=false"; } std::string test_name = ::testing::UnitTest::GetInstance()->current_test_info()->name(); test_name = std::regex_replace(test_name, std::regex("run_gwp_asan_test"), @@ -1761,9 +2091,10 @@ TEST(tombstoned, no_notify) { pid_t pid = 123'456'789 + i; unique_fd intercept_fd, output_fd; - InterceptStatus status; - tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone); - ASSERT_EQ(InterceptStatus::kRegistered, status); + InterceptResponse response = {}; + tombstoned_intercept(pid, &intercept_fd, &output_fd, &response, kDebuggerdTombstone); + ASSERT_EQ(InterceptStatus::kRegistered, response.status) + << "Error message: " << response.error_message; { unique_fd tombstoned_socket, input_fd; @@ -1795,9 +2126,10 @@ TEST(tombstoned, stress) { pid_t pid = pid_base + dump; unique_fd intercept_fd, output_fd; - InterceptStatus status; - tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone); - ASSERT_EQ(InterceptStatus::kRegistered, status); + InterceptResponse response = {}; + tombstoned_intercept(pid, &intercept_fd, &output_fd, &response, kDebuggerdTombstone); + ASSERT_EQ(InterceptStatus::kRegistered, response.status) + << "Error messeage: " << response.error_message; // Pretend to crash, and then immediately close the socket. unique_fd sockfd(socket_local_client(kTombstonedCrashSocketName, @@ -1828,9 +2160,10 @@ TEST(tombstoned, stress) { pid_t pid = pid_base + dump; unique_fd intercept_fd, output_fd; - InterceptStatus status; - tombstoned_intercept(pid, &intercept_fd, &output_fd, &status, kDebuggerdTombstone); - ASSERT_EQ(InterceptStatus::kRegistered, status); + InterceptResponse response = {}; + tombstoned_intercept(pid, &intercept_fd, &output_fd, &response, kDebuggerdTombstone); + ASSERT_EQ(InterceptStatus::kRegistered, response.status) + << "Error message: " << response.error_message; { unique_fd tombstoned_socket, input_fd; @@ -1855,16 +2188,17 @@ TEST(tombstoned, stress) { } } -TEST(tombstoned, java_trace_intercept_smoke) { +TEST(tombstoned, intercept_java_trace_smoke) { // Using a "real" PID is a little dangerous here - if the test fails // or crashes, we might end up getting a bogus / unreliable stack // trace. const pid_t self = getpid(); unique_fd intercept_fd, output_fd; - InterceptStatus status; - tombstoned_intercept(self, &intercept_fd, &output_fd, &status, kDebuggerdJavaBacktrace); - ASSERT_EQ(InterceptStatus::kRegistered, status); + InterceptResponse response = {}; + tombstoned_intercept(self, &intercept_fd, &output_fd, &response, kDebuggerdJavaBacktrace); + ASSERT_EQ(InterceptStatus::kRegistered, response.status) + << "Error message: " << response.error_message; // First connect to tombstoned requesting a native tombstone. This // should result in a "regular" FD and not the installed intercept. @@ -1886,25 +2220,96 @@ TEST(tombstoned, java_trace_intercept_smoke) { ASSERT_STREQ("java", outbuf); } -TEST(tombstoned, multiple_intercepts) { +TEST(tombstoned, intercept_multiple_dump_types) { const pid_t fake_pid = 1'234'567; unique_fd intercept_fd, output_fd; - InterceptStatus status; - tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &status, kDebuggerdJavaBacktrace); - ASSERT_EQ(InterceptStatus::kRegistered, status); + InterceptResponse response = {}; + tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &response, kDebuggerdJavaBacktrace); + ASSERT_EQ(InterceptStatus::kRegistered, response.status) + << "Error message: " << response.error_message; unique_fd intercept_fd_2, output_fd_2; - tombstoned_intercept(fake_pid, &intercept_fd_2, &output_fd_2, &status, kDebuggerdNativeBacktrace); - ASSERT_EQ(InterceptStatus::kFailedAlreadyRegistered, status); + tombstoned_intercept(fake_pid, &intercept_fd_2, &output_fd_2, &response, + kDebuggerdNativeBacktrace); + ASSERT_EQ(InterceptStatus::kRegistered, response.status) + << "Error message: " << response.error_message; +} + +TEST(tombstoned, intercept_bad_pid) { + const pid_t fake_pid = -1; + unique_fd intercept_fd, output_fd; + InterceptResponse response = {}; + tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &response, kDebuggerdNativeBacktrace); + ASSERT_EQ(InterceptStatus::kFailed, response.status) + << "Error message: " << response.error_message; + ASSERT_MATCH(response.error_message, "bad pid"); +} + +TEST(tombstoned, intercept_bad_dump_types) { + const pid_t fake_pid = 1'234'567; + unique_fd intercept_fd, output_fd; + InterceptResponse response = {}; + tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &response, + static_cast(20)); + ASSERT_EQ(InterceptStatus::kFailed, response.status) + << "Error message: " << response.error_message; + ASSERT_MATCH(response.error_message, "bad dump type \\[unknown\\]"); + + tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &response, kDebuggerdAnyIntercept); + ASSERT_EQ(InterceptStatus::kFailed, response.status) + << "Error message: " << response.error_message; + ASSERT_MATCH(response.error_message, "bad dump type kDebuggerdAnyIntercept"); + + tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &response, kDebuggerdTombstoneProto); + ASSERT_EQ(InterceptStatus::kFailed, response.status) + << "Error message: " << response.error_message; + ASSERT_MATCH(response.error_message, "bad dump type kDebuggerdTombstoneProto"); +} + +TEST(tombstoned, intercept_already_registered) { + const pid_t fake_pid = 1'234'567; + unique_fd intercept_fd1, output_fd1; + InterceptResponse response = {}; + tombstoned_intercept(fake_pid, &intercept_fd1, &output_fd1, &response, kDebuggerdTombstone); + ASSERT_EQ(InterceptStatus::kRegistered, response.status) + << "Error message: " << response.error_message; + + unique_fd intercept_fd2, output_fd2; + tombstoned_intercept(fake_pid, &intercept_fd2, &output_fd2, &response, kDebuggerdTombstone); + ASSERT_EQ(InterceptStatus::kFailedAlreadyRegistered, response.status) + << "Error message: " << response.error_message; + ASSERT_MATCH(response.error_message, "already registered, type kDebuggerdTombstone"); +} + +TEST(tombstoned, intercept_tombstone_proto_matched_to_tombstone) { + const pid_t fake_pid = 1'234'567; + + unique_fd intercept_fd, output_fd; + InterceptResponse response = {}; + tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &response, kDebuggerdTombstone); + ASSERT_EQ(InterceptStatus::kRegistered, response.status) + << "Error message: " << response.error_message; + + const char data[] = "tombstone_proto"; + unique_fd tombstoned_socket, input_fd; + ASSERT_TRUE( + tombstoned_connect(fake_pid, &tombstoned_socket, &input_fd, kDebuggerdTombstoneProto)); + ASSERT_TRUE(android::base::WriteFully(input_fd.get(), data, sizeof(data))); + tombstoned_notify_completion(tombstoned_socket.get()); + + char outbuf[sizeof(data)]; + ASSERT_TRUE(android::base::ReadFully(output_fd.get(), outbuf, sizeof(outbuf))); + ASSERT_STREQ("tombstone_proto", outbuf); } TEST(tombstoned, intercept_any) { const pid_t fake_pid = 1'234'567; unique_fd intercept_fd, output_fd; - InterceptStatus status; - tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &status, kDebuggerdNativeBacktrace); - ASSERT_EQ(InterceptStatus::kRegistered, status); + InterceptResponse response = {}; + tombstoned_intercept(fake_pid, &intercept_fd, &output_fd, &response, kDebuggerdNativeBacktrace); + ASSERT_EQ(InterceptStatus::kRegistered, response.status) + << "Error message: " << response.error_message; const char any[] = "any"; unique_fd tombstoned_socket, input_fd; @@ -1917,6 +2322,77 @@ TEST(tombstoned, intercept_any) { ASSERT_STREQ("any", outbuf); } +TEST(tombstoned, intercept_any_failed_with_multiple_intercepts) { + const pid_t fake_pid = 1'234'567; + + InterceptResponse response = {}; + unique_fd intercept_fd1, output_fd1; + tombstoned_intercept(fake_pid, &intercept_fd1, &output_fd1, &response, kDebuggerdNativeBacktrace); + ASSERT_EQ(InterceptStatus::kRegistered, response.status) + << "Error message: " << response.error_message; + + unique_fd intercept_fd2, output_fd2; + tombstoned_intercept(fake_pid, &intercept_fd2, &output_fd2, &response, kDebuggerdJavaBacktrace); + ASSERT_EQ(InterceptStatus::kRegistered, response.status) + << "Error message: " << response.error_message; + + unique_fd tombstoned_socket, input_fd; + ASSERT_FALSE(tombstoned_connect(fake_pid, &tombstoned_socket, &input_fd, kDebuggerdAnyIntercept)); +} + +TEST(tombstoned, intercept_multiple_verify_intercept) { + // Need to use our pid for java since that will verify the pid. + const pid_t fake_pid = getpid(); + + InterceptResponse response = {}; + unique_fd intercept_fd1, output_fd1; + tombstoned_intercept(fake_pid, &intercept_fd1, &output_fd1, &response, kDebuggerdNativeBacktrace); + ASSERT_EQ(InterceptStatus::kRegistered, response.status) + << "Error message: " << response.error_message; + + unique_fd intercept_fd2, output_fd2; + tombstoned_intercept(fake_pid, &intercept_fd2, &output_fd2, &response, kDebuggerdJavaBacktrace); + ASSERT_EQ(InterceptStatus::kRegistered, response.status) + << "Error message: " << response.error_message; + + unique_fd intercept_fd3, output_fd3; + tombstoned_intercept(fake_pid, &intercept_fd3, &output_fd3, &response, kDebuggerdTombstone); + ASSERT_EQ(InterceptStatus::kRegistered, response.status) + << "Error message: " << response.error_message; + + const char native_data[] = "native"; + unique_fd tombstoned_socket1, input_fd1; + ASSERT_TRUE( + tombstoned_connect(fake_pid, &tombstoned_socket1, &input_fd1, kDebuggerdNativeBacktrace)); + ASSERT_TRUE(android::base::WriteFully(input_fd1.get(), native_data, sizeof(native_data))); + tombstoned_notify_completion(tombstoned_socket1.get()); + + char native_outbuf[sizeof(native_data)]; + ASSERT_TRUE(android::base::ReadFully(output_fd1.get(), native_outbuf, sizeof(native_outbuf))); + ASSERT_STREQ("native", native_outbuf); + + const char java_data[] = "java"; + unique_fd tombstoned_socket2, input_fd2; + ASSERT_TRUE( + tombstoned_connect(fake_pid, &tombstoned_socket2, &input_fd2, kDebuggerdJavaBacktrace)); + ASSERT_TRUE(android::base::WriteFully(input_fd2.get(), java_data, sizeof(java_data))); + tombstoned_notify_completion(tombstoned_socket2.get()); + + char java_outbuf[sizeof(java_data)]; + ASSERT_TRUE(android::base::ReadFully(output_fd2.get(), java_outbuf, sizeof(java_outbuf))); + ASSERT_STREQ("java", java_outbuf); + + const char tomb_data[] = "tombstone"; + unique_fd tombstoned_socket3, input_fd3; + ASSERT_TRUE(tombstoned_connect(fake_pid, &tombstoned_socket3, &input_fd3, kDebuggerdTombstone)); + ASSERT_TRUE(android::base::WriteFully(input_fd3.get(), tomb_data, sizeof(tomb_data))); + tombstoned_notify_completion(tombstoned_socket3.get()); + + char tomb_outbuf[sizeof(tomb_data)]; + ASSERT_TRUE(android::base::ReadFully(output_fd3.get(), tomb_outbuf, sizeof(tomb_outbuf))); + ASSERT_STREQ("tombstone", tomb_outbuf); +} + TEST(tombstoned, interceptless_backtrace) { // Generate 50 backtraces, and then check to see that we haven't created 50 new tombstones. auto get_tombstone_timestamps = []() -> std::map { @@ -2094,28 +2570,10 @@ TEST_F(CrasherTest, unreadable_elf) { ASSERT_MATCH(result, match_str); } -TEST(tombstoned, proto) { - const pid_t self = getpid(); - unique_fd tombstoned_socket, text_fd, proto_fd; - ASSERT_TRUE( - tombstoned_connect(self, &tombstoned_socket, &text_fd, &proto_fd, kDebuggerdTombstoneProto)); - - tombstoned_notify_completion(tombstoned_socket.get()); - - ASSERT_NE(-1, text_fd.get()); - ASSERT_NE(-1, proto_fd.get()); - - struct stat text_st; - ASSERT_EQ(0, fstat(text_fd.get(), &text_st)); - - // Give tombstoned some time to link the files into place. - std::this_thread::sleep_for(100ms); - - // Find the tombstone. - std::optional tombstone_file; +void CheckForTombstone(const struct stat& text_st, std::optional& tombstone_file) { + static std::regex tombstone_re("tombstone_\\d+"); std::unique_ptr dir_h(opendir("/data/tombstones"), closedir); ASSERT_TRUE(dir_h != nullptr); - std::regex tombstone_re("tombstone_\\d+"); dirent* entry; while ((entry = readdir(dir_h.get())) != nullptr) { if (!std::regex_match(entry->d_name, tombstone_re)) { @@ -2133,8 +2591,38 @@ TEST(tombstoned, proto) { break; } } +} + +TEST(tombstoned, proto) { + const pid_t self = getpid(); + unique_fd tombstoned_socket, text_fd, proto_fd; + ASSERT_TRUE( + tombstoned_connect(self, &tombstoned_socket, &text_fd, &proto_fd, kDebuggerdTombstoneProto)); + + tombstoned_notify_completion(tombstoned_socket.get()); + + ASSERT_NE(-1, text_fd.get()); + ASSERT_NE(-1, proto_fd.get()); + + struct stat text_st; + ASSERT_EQ(0, fstat(text_fd.get(), &text_st)); + + std::optional tombstone_file; + // Allow up to 5 seconds for the tombstone to be written to the system. + const auto max_wait_time = std::chrono::seconds(5) * android::base::HwTimeoutMultiplier(); + const auto start = std::chrono::high_resolution_clock::now(); + while (true) { + std::this_thread::sleep_for(100ms); + CheckForTombstone(text_st, tombstone_file); + if (tombstone_file) { + break; + } + if (std::chrono::high_resolution_clock::now() - start > max_wait_time) { + break; + } + } - ASSERT_TRUE(tombstone_file); + ASSERT_TRUE(tombstone_file) << "Timed out trying to find tombstone file."; std::string proto_path = tombstone_file.value() + ".pb"; struct stat proto_fd_st; @@ -2149,10 +2637,11 @@ TEST(tombstoned, proto) { TEST(tombstoned, proto_intercept) { const pid_t self = getpid(); unique_fd intercept_fd, output_fd; - InterceptStatus status; - tombstoned_intercept(self, &intercept_fd, &output_fd, &status, kDebuggerdTombstone); - ASSERT_EQ(InterceptStatus::kRegistered, status); + InterceptResponse response = {}; + tombstoned_intercept(self, &intercept_fd, &output_fd, &response, kDebuggerdTombstone); + ASSERT_EQ(InterceptStatus::kRegistered, response.status) + << "Error message: " << response.error_message; unique_fd tombstoned_socket, text_fd, proto_fd; ASSERT_TRUE( @@ -2279,12 +2768,16 @@ TEST_F(CrasherTest, fault_address_after_last_map) { match_str += format_full_pointer(crash_uptr); ASSERT_MATCH(result, match_str); - ASSERT_MATCH(result, R"(\nmemory map \(.*\): \(fault address prefixed with --->)\n)"); + ASSERT_MATCH(result, R"(\nmemory map \(.*\): \(fault address prefixed with --->\)\n)"); - // Assumes that the open files section comes after the map section. - // If that assumption changes, the regex below needs to change. + // Verifies that the fault address error message is at the end of the + // maps section. To do this, the check below looks for the start of the + // open files section or the start of the log file section. It's possible + // for either of these sections to be present after the maps section right + // now. + // If the sections move around, this check might need to be modified. match_str = android::base::StringPrintf( - R"(\n--->Fault address falls at %s after any mapped regions\n\nopen files:)", + R"(\n--->Fault address falls at %s after any mapped regions\n(---------|\nopen files:))", format_pointer(crash_uptr).c_str()); ASSERT_MATCH(result, match_str); } @@ -2295,6 +2788,10 @@ TEST_F(CrasherTest, fault_address_between_maps) { void* start_ptr = mmap(nullptr, 3 * getpagesize(), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); ASSERT_NE(MAP_FAILED, start_ptr); + // Add a name to guarantee that this map is distinct and not combined in the map listing. + EXPECT_EQ( + prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, start_ptr, 3 * getpagesize(), "debuggerd map start"), + 0); // Unmap the page in the middle. void* middle_ptr = reinterpret_cast(reinterpret_cast(start_ptr) + getpagesize()); @@ -2327,7 +2824,7 @@ TEST_F(CrasherTest, fault_address_between_maps) { match_str += format_full_pointer(reinterpret_cast(middle_ptr)); ASSERT_MATCH(result, match_str); - ASSERT_MATCH(result, R"(\nmemory map \(.*\): \(fault address prefixed with --->)\n)"); + ASSERT_MATCH(result, R"(\nmemory map \(.*\): \(fault address prefixed with --->\)\n)"); match_str = android::base::StringPrintf( R"( %s.*\n--->Fault address falls at %s between mapped regions\n %s)", @@ -2341,6 +2838,8 @@ TEST_F(CrasherTest, fault_address_in_map) { // Create a map before the fork so it will be present in the child. void* ptr = mmap(nullptr, getpagesize(), 0, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); ASSERT_NE(MAP_FAILED, ptr); + // Add a name to guarantee that this map is distinct and not combined in the map listing. + EXPECT_EQ(prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, ptr, getpagesize(), "debuggerd map"), 0); StartProcess([ptr]() { ASSERT_EQ(0, crash_call(reinterpret_cast(ptr))); @@ -2365,7 +2864,7 @@ TEST_F(CrasherTest, fault_address_in_map) { match_str += format_full_pointer(reinterpret_cast(ptr)); ASSERT_MATCH(result, match_str); - ASSERT_MATCH(result, R"(\nmemory map \(.*\): \(fault address prefixed with --->)\n)"); + ASSERT_MATCH(result, R"(\nmemory map \(.*\): \(fault address prefixed with --->\)\n)"); match_str = android::base::StringPrintf(R"(\n--->%s.*\n)", format_pointer(ptr).c_str()); ASSERT_MATCH(result, match_str); @@ -2412,7 +2911,7 @@ TEST_F(CrasherTest, verify_dex_pc_with_function_name) { mmap(nullptr, sizeof(kDexData), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); ASSERT_TRUE(ptr != MAP_FAILED); memcpy(ptr, kDexData, sizeof(kDexData)); - prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, ptr, sizeof(kDexData), "dex"); + EXPECT_EQ(prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, ptr, sizeof(kDexData), "dex"), 0); JITCodeEntry dex_entry = {.symfile_addr = reinterpret_cast(ptr), .symfile_size = sizeof(kDexData)}; @@ -2513,12 +3012,18 @@ TEST_F(CrasherTest, verify_map_format) { // Create multiple maps to make sure that the map data is formatted properly. void* none_map = mmap(nullptr, getpagesize(), 0, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); ASSERT_NE(MAP_FAILED, none_map); + // Add names to guarantee that the maps are distinct and not combined in the map listing. + EXPECT_EQ(prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, none_map, getpagesize(), "debuggerd map none"), + 0); void* r_map = mmap(nullptr, getpagesize(), PROT_READ, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); ASSERT_NE(MAP_FAILED, r_map); + EXPECT_EQ(prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, r_map, getpagesize(), "debuggerd map r"), 0); void* w_map = mmap(nullptr, getpagesize(), PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); + EXPECT_EQ(prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, w_map, getpagesize(), "debuggerd map w"), 0); ASSERT_NE(MAP_FAILED, w_map); void* x_map = mmap(nullptr, getpagesize(), PROT_EXEC, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); ASSERT_NE(MAP_FAILED, x_map); + EXPECT_EQ(prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, x_map, getpagesize(), "debuggerd map x"), 0); TemporaryFile tf; ASSERT_EQ(0x2000, lseek(tf.fd, 0x2000, SEEK_SET)); @@ -2553,30 +3058,34 @@ TEST_F(CrasherTest, verify_map_format) { std::string match_str; // Verify none. match_str = android::base::StringPrintf( - " %s-%s --- 0 1000\\n", + " %s-%s --- 0 %x \\[anon:debuggerd map none\\]\\n", format_map_pointer(reinterpret_cast(none_map)).c_str(), - format_map_pointer(reinterpret_cast(none_map) + getpagesize() - 1).c_str()); + format_map_pointer(reinterpret_cast(none_map) + getpagesize() - 1).c_str(), + getpagesize()); ASSERT_MATCH(result, match_str); // Verify read-only. match_str = android::base::StringPrintf( - " %s-%s r-- 0 1000\\n", + " %s-%s r-- 0 %x \\[anon:debuggerd map r\\]\\n", format_map_pointer(reinterpret_cast(r_map)).c_str(), - format_map_pointer(reinterpret_cast(r_map) + getpagesize() - 1).c_str()); + format_map_pointer(reinterpret_cast(r_map) + getpagesize() - 1).c_str(), + getpagesize()); ASSERT_MATCH(result, match_str); // Verify write-only. match_str = android::base::StringPrintf( - " %s-%s -w- 0 1000\\n", + " %s-%s -w- 0 %x \\[anon:debuggerd map w\\]\\n", format_map_pointer(reinterpret_cast(w_map)).c_str(), - format_map_pointer(reinterpret_cast(w_map) + getpagesize() - 1).c_str()); + format_map_pointer(reinterpret_cast(w_map) + getpagesize() - 1).c_str(), + getpagesize()); ASSERT_MATCH(result, match_str); // Verify exec-only. match_str = android::base::StringPrintf( - " %s-%s --x 0 1000\\n", + " %s-%s --x 0 %x \\[anon:debuggerd map x\\]\\n", format_map_pointer(reinterpret_cast(x_map)).c_str(), - format_map_pointer(reinterpret_cast(x_map) + getpagesize() - 1).c_str()); + format_map_pointer(reinterpret_cast(x_map) + getpagesize() - 1).c_str(), + getpagesize()); ASSERT_MATCH(result, match_str); // Verify file map with non-zero offset and a name. @@ -2693,7 +3202,8 @@ TEST_F(CrasherTest, verify_build_id) { } prev_file = match[1]; - unwindstack::Elf elf(unwindstack::Memory::CreateFileMemory(prev_file, 0).release()); + auto elf_memory = unwindstack::Memory::CreateFileMemory(prev_file, 0); + unwindstack::Elf elf(elf_memory); if (!elf.Init() || !elf.valid()) { // Skipping invalid elf files. continue; @@ -2759,3 +3269,134 @@ TEST_F(CrasherTest, logd_skips_reading_logs_not_main_thread) { ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal"); ASSERT_NOT_MATCH(result, kLogMessage); } + +// Disable this test since there is a high liklihood that this would +// be flaky since it requires 500 messages being in the log. +TEST_F(CrasherTest, DISABLED_max_log_messages) { + StartProcess([]() { + for (size_t i = 0; i < 600; i++) { + LOG(INFO) << "Message number " << i; + } + abort(); + }); + + unique_fd output_fd; + StartIntercept(&output_fd); + FinishCrasher(); + AssertDeath(SIGABRT); + int intercept_result; + FinishIntercept(&intercept_result); + ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; + + std::string result; + ConsumeFd(std::move(output_fd), &result); + ASSERT_NOT_MATCH(result, "Message number 99"); + ASSERT_MATCH(result, "Message number 100"); + ASSERT_MATCH(result, "Message number 599"); +} + +TEST_F(CrasherTest, log_with_newline) { + StartProcess([]() { + LOG(INFO) << "This line has a newline.\nThis is on the next line."; + abort(); + }); + + unique_fd output_fd; + StartIntercept(&output_fd); + FinishCrasher(); + AssertDeath(SIGABRT); + int intercept_result; + FinishIntercept(&intercept_result); + ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; + + std::string result; + ConsumeFd(std::move(output_fd), &result); + ASSERT_MATCH(result, ":\\s*This line has a newline."); + ASSERT_MATCH(result, ":\\s*This is on the next line."); +} + +TEST_F(CrasherTest, log_with_non_printable_ascii_verify_encoded) { + static const std::string kEncodedStr = + "\x5C\x31" + "\x5C\x32" + "\x5C\x33" + "\x5C\x34" + "\x5C\x35" + "\x5C\x36" + "\x5C\x37" + "\x5C\x31\x30" + "\x5C\x31\x36" + "\x5C\x31\x37" + "\x5C\x32\x30" + "\x5C\x32\x31" + "\x5C\x32\x32" + "\x5C\x32\x33" + "\x5C\x32\x34" + "\x5C\x32\x35" + "\x5C\x32\x36" + "\x5C\x32\x37" + "\x5C\x33\x30" + "\x5C\x33\x31" + "\x5C\x33\x32" + "\x5C\x33\x33" + "\x5C\x33\x34" + "\x5C\x33\x35" + "\x5C\x33\x36" + "\x5C\x33\x37" + "\x5C\x31\x37\x37" + "\x5C\x32\x34\x30" + "\x5C\x32\x36\x30" + "\x5C\x33\x30\x30" + "\x5C\x33\x32\x30"; + StartProcess([]() { + LOG(FATAL) << "Encoded: " + "\x1\x2\x3\x4\x5\x6\x7\x8\xe\xf\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b" + "\x1c\x1d\x1e\x1f\x7f\xA0\xB0\xC0\xD0 after"; + }); + + unique_fd output_fd; + StartIntercept(&output_fd); + FinishCrasher(); + AssertDeath(SIGABRT); + int intercept_result; + FinishIntercept(&intercept_result); + ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; + + std::string result; + ConsumeFd(std::move(output_fd), &result); + // Verify the abort message is sanitized properly. + size_t pos = result.find(std::string("Abort message: 'Encoded: ") + kEncodedStr + " after'"); + EXPECT_TRUE(pos != std::string::npos) << "Couldn't find sanitized abort message: " << result; + + // Make sure that the log message is sanitized properly too. + EXPECT_TRUE(result.find(std::string("Encoded: ") + kEncodedStr + " after", pos + 1) != + std::string::npos) + << "Couldn't find sanitized log message: " << result; +} + +TEST_F(CrasherTest, log_with_with_special_printable_ascii) { + static const std::string kMsg = "Not encoded: \t\v\f\r\n after"; + StartProcess([]() { LOG(FATAL) << kMsg; }); + + unique_fd output_fd; + StartIntercept(&output_fd); + FinishCrasher(); + AssertDeath(SIGABRT); + int intercept_result; + FinishIntercept(&intercept_result); + ASSERT_EQ(1, intercept_result) << "tombstoned reported failure"; + + std::string result; + ConsumeFd(std::move(output_fd), &result); + // Verify the abort message does not remove characters that are UTF8 but + // are, technically, not printable. + size_t pos = result.find(std::string("Abort message: '") + kMsg + "'"); + EXPECT_TRUE(pos != std::string::npos) << "Couldn't find abort message: " << result; + + // Make sure that the log message is handled properly too. + // The logger automatically splits a newline message into two pieces. + pos = result.find("Not encoded: \t\v\f\r", pos + kMsg.size()); + EXPECT_TRUE(pos != std::string::npos) << "Couldn't find log message: " << result; + EXPECT_TRUE(result.find(" after", pos + 1) != std::string::npos) + << "Couldn't find sanitized log message: " << result; +} diff --git a/debuggerd/handler/debuggerd_fallback.cpp b/debuggerd/handler/debuggerd_fallback.cpp index 4721391ef58b..8ab5f253fcc7 100644 --- a/debuggerd/handler/debuggerd_fallback.cpp +++ b/debuggerd/handler/debuggerd_fallback.cpp @@ -48,50 +48,69 @@ using android::base::unique_fd; extern "C" bool __linker_enable_fallback_allocator(); extern "C" void __linker_disable_fallback_allocator(); -// This is incredibly sketchy to do inside of a signal handler, especially when libbacktrace -// uses the C++ standard library throughout, but this code runs in the linker, so we'll be using -// the linker's malloc instead of the libc one. Switch it out for a replacement, just in case. -// -// This isn't the default method of dumping because it can fail in cases such as address space -// exhaustion. -static void debuggerd_fallback_trace(int output_fd, ucontext_t* ucontext) { - if (!__linker_enable_fallback_allocator()) { - async_safe_format_log(ANDROID_LOG_ERROR, "libc", "fallback allocator already in use"); - return; +// This file implements a fallback path for processes that do not allow the +// normal fork and exec of crash_dump to handle crashes/unwinds. +// The issue is that all of this happens from within a signal handler, which +// can cause problems since this code uses the linker allocator which is not +// thread safe. In order to avoid any problems allocating, the code calls +// a function to switch to use a fallback allocator in the linker that will +// only be used for the current thread. All of the libunwindstack code does +// allocations using C++ stl, but should be fine since the code runs in the +// linker and should use the fallback handler. + +// This method can still fail if the virtual space is exhausted on a 32 bit +// process or mmap failing due to hitting the maximum number of maps (65535 +// total maps) on a 64 bit process. + +// Class to handle automatically turning on and off the fallback allocator. +class ScopedUseFallbackAllocator { + public: + ScopedUseFallbackAllocator() { Enable(); } + + ~ScopedUseFallbackAllocator() { Disable(); } + + bool Enable() { + if (!enabled_) { + enabled_ = __linker_enable_fallback_allocator(); + if (!enabled_) { + async_safe_format_log(ANDROID_LOG_ERROR, "libc", + "Unable to enable fallback allocator, already in use."); + } + } + return enabled_; } - { - std::unique_ptr regs; - - ThreadInfo thread; - thread.pid = getpid(); - thread.tid = gettid(); - thread.thread_name = get_thread_name(gettid()); - thread.registers.reset( - unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext)); - - // Do not use the thread cache here because it will call pthread_key_create - // which doesn't work in linker code. See b/189803009. - // Use a normal cached object because the thread is stopped, and there - // is no chance of data changing between reads. - auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid()); - // TODO: Create this once and store it in a global? - unwindstack::AndroidLocalUnwinder unwinder(process_memory); - dump_backtrace_thread(output_fd, &unwinder, thread); + void Disable() { + if (enabled_) { + __linker_disable_fallback_allocator(); + enabled_ = false; + } } - __linker_disable_fallback_allocator(); -} -static void debuggerd_fallback_tombstone(int output_fd, int proto_fd, ucontext_t* ucontext, - siginfo_t* siginfo, void* abort_message) { - if (!__linker_enable_fallback_allocator()) { - async_safe_format_log(ANDROID_LOG_ERROR, "libc", "fallback allocator already in use"); - return; - } + bool enabled() { return enabled_; } - engrave_tombstone_ucontext(output_fd, proto_fd, reinterpret_cast(abort_message), - siginfo, ucontext); - __linker_disable_fallback_allocator(); + private: + bool enabled_ = false; +}; + +static void debuggerd_fallback_trace(int output_fd, ucontext_t* ucontext) { + std::unique_ptr regs; + + ThreadInfo thread; + thread.pid = getpid(); + thread.tid = gettid(); + thread.thread_name = get_thread_name(gettid()); + thread.registers.reset( + unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(), ucontext)); + + // Do not use the thread cache here because it will call pthread_key_create + // which doesn't work in linker code. See b/189803009. + // Use a normal cached object because the thread is stopped, and there + // is no chance of data changing between reads. + auto process_memory = unwindstack::Memory::CreateProcessMemoryCached(getpid()); + // TODO: Create this once and store it in a global? + unwindstack::AndroidLocalUnwinder unwinder(process_memory); + dump_backtrace_thread(output_fd, &unwinder, thread); } static bool forward_output(int src_fd, int dst_fd, pid_t expected_tid) { @@ -154,6 +173,11 @@ static std::pair unpack_thread_fd(uint64_t value) { } static void trace_handler(siginfo_t* info, ucontext_t* ucontext) { + ScopedUseFallbackAllocator allocator; + if (!allocator.enabled()) { + return; + } + static std::atomic trace_output(pack_thread_fd(-1, -1)); if (info->si_value.sival_ptr == kDebuggerdFallbackSivalPtrRequestDump) { @@ -181,6 +205,11 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) { async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to write to output fd"); } + // Stop using the fallback allocator before the close. This will prevent + // a race condition where the thread backtracing all of the threads tries + // to re-acquire the fallback allocator. + allocator.Disable(); + close(fd); return; } @@ -210,10 +239,15 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) { // Send a signal to all of our siblings, asking them to dump their stack. pid_t current_tid = gettid(); - if (!iterate_tids(current_tid, [&output_fd, ¤t_tid](pid_t tid) { + if (!iterate_tids(current_tid, [&allocator, &output_fd, ¤t_tid](pid_t tid) { if (current_tid == tid) { return; } + + if (!allocator.enabled()) { + return; + } + // Use a pipe, to be able to detect situations where the thread gracefully exits before // receiving our signal. unique_fd pipe_read, pipe_write; @@ -233,22 +267,29 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) { return; } + // Disable our use of the fallback allocator while the target thread + // is getting the backtrace. + allocator.Disable(); + siginfo_t siginfo = {}; siginfo.si_code = SI_QUEUE; siginfo.si_value.sival_ptr = kDebuggerdFallbackSivalPtrRequestDump; siginfo.si_pid = getpid(); siginfo.si_uid = getuid(); - if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, BIONIC_SIGNAL_DEBUGGER, &siginfo) != 0) { + if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, BIONIC_SIGNAL_DEBUGGER, &siginfo) == 0) { + if (!forward_output(pipe_read.get(), output_fd.get(), tid)) { + async_safe_format_log(ANDROID_LOG_ERROR, "libc", + "timeout expired while waiting for thread %d to dump", tid); + } + } else { async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s", tid, strerror(errno)); - return; } - bool success = forward_output(pipe_read.get(), output_fd.get(), tid); - if (!success) { - async_safe_format_log(ANDROID_LOG_ERROR, "libc", - "timeout expired while waiting for thread %d to dump", tid); + // The thread should be finished now, so try and re-enable the fallback allocator. + if (!allocator.Enable()) { + return; } // Regardless of whether the poll succeeds, check to see if the thread took fd ownership. @@ -260,14 +301,15 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) { close(fd); } } - - return; })) { async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to open /proc/%d/task: %s", current_tid, strerror(errno)); } - dump_backtrace_footer(output_fd.get()); + if (allocator.enabled()) { + dump_backtrace_footer(output_fd.get()); + } + tombstoned_notify_completion(tombstone_socket.get()); } @@ -295,7 +337,13 @@ static void crash_handler(siginfo_t* info, ucontext_t* ucontext, void* abort_mes unique_fd tombstone_socket, output_fd, proto_fd; bool tombstoned_connected = tombstoned_connect(getpid(), &tombstone_socket, &output_fd, &proto_fd, kDebuggerdTombstoneProto); - debuggerd_fallback_tombstone(output_fd.get(), proto_fd.get(), ucontext, info, abort_message); + { + ScopedUseFallbackAllocator allocator; + if (allocator.enabled()) { + engrave_tombstone_ucontext(output_fd.get(), proto_fd.get(), + reinterpret_cast(abort_message), info, ucontext); + } + } if (tombstoned_connected) { tombstoned_notify_completion(tombstone_socket.get()); } diff --git a/debuggerd/handler/debuggerd_handler.cpp b/debuggerd/handler/debuggerd_handler.cpp index baa5bfb50677..88278ca667e8 100644 --- a/debuggerd/handler/debuggerd_handler.cpp +++ b/debuggerd/handler/debuggerd_handler.cpp @@ -36,10 +36,12 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -108,13 +110,66 @@ static bool is_permissive_mte() { "persist.device_config.memory_safety_native.permissive.process.%s", getprogname()); // DO NOT REPLACE this with GetBoolProperty. That uses std::string which allocates, so it is - // not async-safe (and this functiong gets used in a signal handler). + // not async-safe, and this function gets used in a signal handler. return property_parse_bool("persist.sys.mte.permissive") || property_parse_bool("persist.device_config.memory_safety_native.permissive.default") || property_parse_bool(process_sysprop_name) || (permissive_env && ParseBool(permissive_env) == ParseBoolResult::kTrue); } +static bool parse_uint_with_error_reporting(const char* s, const char* name, int* v) { + if (android::base::ParseInt(s, v) && *v >= 0) { + return true; + } + async_safe_format_log(ANDROID_LOG_ERROR, "libc", "invalid %s: %s", name, s); + return false; +} + +// We cannot use base::GetIntProperty, because that internally uses +// std::string, which allocates. +static bool property_parse_int(const char* name, int* out) { + const prop_info* pi = __system_property_find(name); + if (!pi) return false; + struct cookie_t { + int* out; + bool empty; + } cookie{out, true}; + __system_property_read_callback( + pi, + [](void* raw_cookie, const char* name, const char* value, uint32_t) { + // Property is set to empty value, ignoring. + if (!*value) return; + cookie_t* cookie = reinterpret_cast(raw_cookie); + if (parse_uint_with_error_reporting(value, name, cookie->out)) cookie->empty = false; + }, + &cookie); + return !cookie.empty; +} + +static int permissive_mte_renable_timer() { + if (char* env = getenv("MTE_PERMISSIVE_REENABLE_TIME_CPUMS")) { + int v; + if (parse_uint_with_error_reporting(env, "MTE_PERMISSIVE_REENABLE_TIME_CPUMS", &v)) return v; + } + + char process_sysprop_name[512]; + async_safe_format_buffer(process_sysprop_name, sizeof(process_sysprop_name), + "persist.sys.mte.permissive_reenable_timer.process.%s", getprogname()); + int v; + if (property_parse_int(process_sysprop_name, &v)) return v; + if (property_parse_int("persist.sys.mte.permissive_reenable_timer.default", &v)) return v; + char process_deviceconf_sysprop_name[512]; + async_safe_format_buffer( + process_deviceconf_sysprop_name, sizeof(process_deviceconf_sysprop_name), + "persist.device_config.memory_safety_native.permissive_reenable_timer.process.%s", + getprogname()); + if (property_parse_int(process_deviceconf_sysprop_name, &v)) return v; + if (property_parse_int( + "persist.device_config.memory_safety_native.permissive_reenable_timer.default", &v)) + return v; + return 0; +} + static inline void futex_wait(volatile void* ftx, int value) { syscall(__NR_futex, ftx, FUTEX_WAIT, value, nullptr, nullptr, 0); } @@ -275,10 +330,6 @@ static void raise_caps() { } } -static pid_t __fork() { - return clone(nullptr, nullptr, 0, nullptr); -} - // Double-clone, with CLONE_FILES to share the file descriptor table for kcmp validation. // Returns 0 in the orphaned child, the pid of the orphan in the original process, or -1 on failure. static void create_vm_process() { @@ -338,6 +389,13 @@ static DebuggerdDumpType get_dump_type(const debugger_thread_info* thread_info) return kDebuggerdTombstoneProto; } +static const char* get_unwind_type(const debugger_thread_info* thread_info) { + if (thread_info->siginfo->si_signo == BIONIC_SIGNAL_DEBUGGER) { + return "Unwind request"; + } + return "Crash due to signal"; +} + static int debuggerd_dispatch_pseudothread(void* arg) { debugger_thread_info* thread_info = static_cast(arg); @@ -395,7 +453,9 @@ static int debuggerd_dispatch_pseudothread(void* arg) { ASSERT_SAME_OFFSET(scudo_region_info, scudo_region_info); ASSERT_SAME_OFFSET(scudo_ring_buffer, scudo_ring_buffer); ASSERT_SAME_OFFSET(scudo_ring_buffer_size, scudo_ring_buffer_size); - ASSERT_SAME_OFFSET(recoverable_gwp_asan_crash, recoverable_gwp_asan_crash); + ASSERT_SAME_OFFSET(scudo_stack_depot_size, scudo_stack_depot_size); + ASSERT_SAME_OFFSET(recoverable_crash, recoverable_crash); + ASSERT_SAME_OFFSET(crash_detail_page, crash_detail_page); #undef ASSERT_SAME_OFFSET iovs[3] = {.iov_base = &thread_info->process_info, @@ -424,7 +484,7 @@ static int debuggerd_dispatch_pseudothread(void* arg) { } // Don't use fork(2) to avoid calling pthread_atfork handlers. - pid_t crash_dump_pid = __fork(); + pid_t crash_dump_pid = _Fork(); if (crash_dump_pid == -1) { async_safe_format_log(ANDROID_LOG_FATAL, "libc", "failed to fork in debuggerd signal handler: %s", strerror(errno)); @@ -449,8 +509,8 @@ static int debuggerd_dispatch_pseudothread(void* arg) { execle(CRASH_DUMP_PATH, CRASH_DUMP_NAME, main_tid, pseudothread_tid, debuggerd_dump_type, nullptr, nullptr); - async_safe_format_log(ANDROID_LOG_FATAL, "libc", "failed to exec crash_dump helper: %s", - strerror(errno)); + async_safe_format_log(ANDROID_LOG_FATAL, "libc", "%s: failed to exec crash_dump helper: %s", + get_unwind_type(thread_info), strerror(errno)); return 1; } @@ -471,26 +531,30 @@ static int debuggerd_dispatch_pseudothread(void* arg) { } else { // Something went wrong, log it. if (rc == -1) { - async_safe_format_log(ANDROID_LOG_FATAL, "libc", "read of IPC pipe failed: %s", - strerror(errno)); + async_safe_format_log(ANDROID_LOG_FATAL, "libc", "%s: read of IPC pipe failed: %s", + get_unwind_type(thread_info), strerror(errno)); } else if (rc == 0) { async_safe_format_log(ANDROID_LOG_FATAL, "libc", - "crash_dump helper failed to exec, or was killed"); + "%s: crash_dump helper failed to exec, or was killed", + get_unwind_type(thread_info)); } else if (rc != 1) { async_safe_format_log(ANDROID_LOG_FATAL, "libc", - "read of IPC pipe returned unexpected value: %zd", rc); + "%s: read of IPC pipe returned unexpected value: %zd", + get_unwind_type(thread_info), rc); } else if (buf[0] != '\1') { - async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper reported failure"); + async_safe_format_log(ANDROID_LOG_FATAL, "libc", "%s: crash_dump helper reported failure", + get_unwind_type(thread_info)); } } // Don't leave a zombie child. int status; if (TEMP_FAILURE_RETRY(waitpid(crash_dump_pid, &status, 0)) == -1) { - async_safe_format_log(ANDROID_LOG_FATAL, "libc", "failed to wait for crash_dump helper: %s", - strerror(errno)); + async_safe_format_log(ANDROID_LOG_FATAL, "libc", "%s: failed to wait for crash_dump helper: %s", + get_unwind_type(thread_info), strerror(errno)); } else if (WIFSTOPPED(status) || WIFSIGNALED(status)) { - async_safe_format_log(ANDROID_LOG_FATAL, "libc", "crash_dump helper crashed or stopped"); + async_safe_format_log(ANDROID_LOG_FATAL, "libc", "%s: crash_dump helper crashed or stopped", + get_unwind_type(thread_info)); } if (success) { @@ -552,8 +616,14 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c } debugger_process_info process_info = {}; + if (g_callbacks.get_process_info) { + process_info = g_callbacks.get_process_info(); + } uintptr_t si_val = reinterpret_cast(info->si_ptr); if (signal_number == BIONIC_SIGNAL_DEBUGGER) { + // Applications can set abort messages via android_set_abort_message without + // actually aborting; ignore those messages in non-fatal dumps. + process_info.abort_msg = nullptr; if (info->si_code == SI_QUEUE && info->si_pid == __getpid()) { // Allow for the abort message to be explicitly specified via the sigqueue value. // Keep the bottom bit intact for representing whether we want a backtrace or a tombstone. @@ -562,24 +632,74 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c info->si_ptr = reinterpret_cast(si_val & 1); } } - } else if (g_callbacks.get_process_info) { - process_info = g_callbacks.get_process_info(); } - // GWP-ASan catches use-after-free and heap-buffer-overflow by using PROT_NONE - // guard pages, which lead to SEGV. Normally, debuggerd prints a bug report - // and the process terminates, but in some cases, we actually want to print - // the bug report and let the signal handler return, and restart the process. - // In order to do that, we need to disable GWP-ASan's guard pages. The - // following callbacks handle this case. - gwp_asan_callbacks_t gwp_asan_callbacks = g_callbacks.get_gwp_asan_callbacks(); - if (signal_number == SIGSEGV && signal_has_si_addr(info) && - gwp_asan_callbacks.debuggerd_needs_gwp_asan_recovery && - gwp_asan_callbacks.debuggerd_gwp_asan_pre_crash_report && - gwp_asan_callbacks.debuggerd_gwp_asan_post_crash_report && - gwp_asan_callbacks.debuggerd_needs_gwp_asan_recovery(info->si_addr)) { - gwp_asan_callbacks.debuggerd_gwp_asan_pre_crash_report(info->si_addr); - process_info.recoverable_gwp_asan_crash = true; + gwp_asan_callbacks_t gwp_asan_callbacks = {}; + bool recoverable_gwp_asan_crash = false; + if (g_callbacks.get_gwp_asan_callbacks != nullptr) { + // GWP-ASan catches use-after-free and heap-buffer-overflow by using PROT_NONE + // guard pages, which lead to SEGV. Normally, debuggerd prints a bug report + // and the process terminates, but in some cases, we actually want to print + // the bug report and let the signal handler return, and restart the process. + // In order to do that, we need to disable GWP-ASan's guard pages. The + // following callbacks handle this case. + gwp_asan_callbacks = g_callbacks.get_gwp_asan_callbacks(); + if (signal_number == SIGSEGV && signal_has_si_addr(info) && + gwp_asan_callbacks.debuggerd_needs_gwp_asan_recovery && + gwp_asan_callbacks.debuggerd_gwp_asan_pre_crash_report && + gwp_asan_callbacks.debuggerd_gwp_asan_post_crash_report && + gwp_asan_callbacks.debuggerd_needs_gwp_asan_recovery(info->si_addr)) { + gwp_asan_callbacks.debuggerd_gwp_asan_pre_crash_report(info->si_addr); + recoverable_gwp_asan_crash = true; + process_info.recoverable_crash = true; + } + } + + if (info->si_signo == SIGSEGV && + (info->si_code == SEGV_MTESERR || info->si_code == SEGV_MTEAERR) && is_permissive_mte()) { + process_info.recoverable_crash = true; + // If we are in permissive MTE mode, we do not crash, but instead disable MTE on this thread, + // and then let the failing instruction be retried. The second time should work (except + // if there is another non-MTE fault). + int tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0); + if (tagged_addr_ctrl < 0) { + fatal_errno("failed to PR_GET_TAGGED_ADDR_CTRL"); + } + int previous = tagged_addr_ctrl & PR_MTE_TCF_MASK; + tagged_addr_ctrl = (tagged_addr_ctrl & ~PR_MTE_TCF_MASK) | PR_MTE_TCF_NONE; + if (prctl(PR_SET_TAGGED_ADDR_CTRL, tagged_addr_ctrl, 0, 0, 0) < 0) { + fatal_errno("failed to PR_SET_TAGGED_ADDR_CTRL"); + } + if (int reenable_timer = permissive_mte_renable_timer()) { + async_safe_format_log(ANDROID_LOG_ERROR, "libc", + "MTE ERROR DETECTED BUT RUNNING IN PERMISSIVE MODE. CONTINUING WITH " + "MTE DISABLED FOR %d MS OF CPU TIME.", + reenable_timer); + timer_t timerid{}; + struct sigevent sev {}; + sev.sigev_signo = BIONIC_ENABLE_MTE; + sev.sigev_notify = SIGEV_THREAD_ID; + sev.sigev_value.sival_int = previous; + sev.sigev_notify_thread_id = __gettid(); + // This MUST be CLOCK_THREAD_CPUTIME_ID. If we used CLOCK_MONOTONIC we could get stuck + // in an endless loop of re-running the same instruction, calling this signal handler, + // and re-enabling MTE before we had a chance to re-run the instruction. + if (timer_create(CLOCK_THREAD_CPUTIME_ID, &sev, &timerid) == -1) { + fatal_errno("timer_create() failed"); + } + struct itimerspec its {}; + its.it_value.tv_sec = reenable_timer / 1000; + its.it_value.tv_nsec = (reenable_timer % 1000) * 1000000; + + if (timer_settime(timerid, 0, &its, nullptr) == -1) { + fatal_errno("timer_settime() failed"); + } + } else { + async_safe_format_log( + ANDROID_LOG_ERROR, "libc", + "MTE ERROR DETECTED BUT RUNNING IN PERMISSIVE MODE. CONTINUING WITH MTE DISABLED."); + } + pthread_mutex_unlock(&crash_mutex); } // If sival_int is ~0, it means that the fallback handler has been called @@ -593,7 +713,7 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c // you can only set NO_NEW_PRIVS to 1, and the effect should be at worst a single missing // ANR trace. debuggerd_fallback_handler(info, ucontext, process_info.abort_msg); - if (no_new_privs && process_info.recoverable_gwp_asan_crash) { + if (no_new_privs && recoverable_gwp_asan_crash) { gwp_asan_callbacks.debuggerd_gwp_asan_post_crash_report(info->si_addr); return; } @@ -670,29 +790,14 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c // If the signal is fatal, don't unlock the mutex to prevent other crashing threads from // starting to dump right before our death. pthread_mutex_unlock(&crash_mutex); - } else if (process_info.recoverable_gwp_asan_crash) { - gwp_asan_callbacks.debuggerd_gwp_asan_post_crash_report(info->si_addr); + } else if (process_info.recoverable_crash) { + if (recoverable_gwp_asan_crash) { + gwp_asan_callbacks.debuggerd_gwp_asan_post_crash_report(info->si_addr); + } pthread_mutex_unlock(&crash_mutex); } #ifdef __aarch64__ - else if (info->si_signo == SIGSEGV && - (info->si_code == SEGV_MTESERR || info->si_code == SEGV_MTEAERR) && - is_permissive_mte()) { - // If we are in permissive MTE mode, we do not crash, but instead disable MTE on this thread, - // and then let the failing instruction be retried. The second time should work (except - // if there is another non-MTE fault). - int tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0); - if (tagged_addr_ctrl < 0) { - fatal_errno("failed to PR_GET_TAGGED_ADDR_CTRL"); - } - tagged_addr_ctrl = (tagged_addr_ctrl & ~PR_MTE_TCF_MASK) | PR_MTE_TCF_NONE; - if (prctl(PR_SET_TAGGED_ADDR_CTRL, tagged_addr_ctrl, 0, 0, 0) < 0) { - fatal_errno("failed to PR_SET_TAGGED_ADDR_CTRL"); - } - async_safe_format_log(ANDROID_LOG_ERROR, "libc", - "MTE ERROR DETECTED BUT RUNNING IN PERMISSIVE MODE. CONTINUING."); - pthread_mutex_unlock(&crash_mutex); - } else if (info->si_signo == SIGSEGV && info->si_code == SEGV_MTEAERR && getppid() == 1) { + else if (info->si_signo == SIGSEGV && info->si_code == SEGV_MTEAERR && getppid() == 1) { // Back channel to init (see system/core/init/service.cpp) to signal that // this process crashed due to an ASYNC MTE fault and should be considered // for upgrade to SYNC mode. We are re-using the ART profiler signal, which @@ -718,19 +823,19 @@ void debuggerd_init(debuggerd_callbacks_t* callbacks) { } size_t thread_stack_pages = 8; - void* thread_stack_allocation = mmap(nullptr, PAGE_SIZE * (thread_stack_pages + 2), PROT_NONE, + void* thread_stack_allocation = mmap(nullptr, getpagesize() * (thread_stack_pages + 2), PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); if (thread_stack_allocation == MAP_FAILED) { fatal_errno("failed to allocate debuggerd thread stack"); } - char* stack = static_cast(thread_stack_allocation) + PAGE_SIZE; - if (mprotect(stack, PAGE_SIZE * thread_stack_pages, PROT_READ | PROT_WRITE) != 0) { + char* stack = static_cast(thread_stack_allocation) + getpagesize(); + if (mprotect(stack, getpagesize() * thread_stack_pages, PROT_READ | PROT_WRITE) != 0) { fatal_errno("failed to mprotect debuggerd thread stack"); } // Stack grows negatively, set it to the last byte in the page... - stack = (stack + thread_stack_pages * PAGE_SIZE - 1); + stack = (stack + thread_stack_pages * getpagesize() - 1); // and align it. stack -= 15; pseudothread_stack = stack; @@ -744,7 +849,6 @@ void debuggerd_init(debuggerd_callbacks_t* callbacks) { // Use the alternate signal stack if available so we can catch stack overflows. action.sa_flags |= SA_ONSTACK; -#define SA_EXPOSE_TAGBITS 0x00000800 // Request that the kernel set tag bits in the fault address. This is necessary for diagnosing MTE // faults. action.sa_flags |= SA_EXPOSE_TAGBITS; @@ -752,18 +856,8 @@ void debuggerd_init(debuggerd_callbacks_t* callbacks) { debuggerd_register_handlers(&action); } -// When debuggerd's signal handler is the first handler called, it's great at -// handling the recoverable GWP-ASan mode. For apps, sigchain (from libart) is -// always the first signal handler, and so the following function is what -// sigchain must call before processing the signal. This allows for processing -// of a potentially recoverable GWP-ASan crash. If the signal requires GWP-ASan -// recovery, then dump a report (via the regular debuggerd hanndler), and patch -// up the allocator, and allow the process to continue (indicated by returning -// 'true'). If the crash has nothing to do with GWP-ASan, or recovery isn't -// possible, return 'false'. -bool debuggerd_handle_signal(int signal_number, siginfo_t* info, void* context) { - if (signal_number != SIGSEGV || !signal_has_si_addr(info)) return false; - +bool debuggerd_handle_gwp_asan_signal(int signal_number, siginfo_t* info, void* context) { + if (g_callbacks.get_gwp_asan_callbacks == nullptr) return false; gwp_asan_callbacks_t gwp_asan_callbacks = g_callbacks.get_gwp_asan_callbacks(); if (gwp_asan_callbacks.debuggerd_needs_gwp_asan_recovery == nullptr || gwp_asan_callbacks.debuggerd_gwp_asan_pre_crash_report == nullptr || @@ -800,3 +894,33 @@ bool debuggerd_handle_signal(int signal_number, siginfo_t* info, void* context) pthread_mutex_unlock(&first_crash_mutex); return true; } + +// When debuggerd's signal handler is the first handler called, it's great at +// handling the recoverable GWP-ASan and permissive MTE modes. For apps, +// sigchain (from libart) is always the first signal handler, and so the +// following function is what sigchain must call before processing the signal. +// This allows for processing of a potentially recoverable GWP-ASan or MTE +// crash. If the signal requires recovery, then dump a report (via the regular +// debuggerd hanndler), and patch up the allocator (in the case of GWP-ASan) or +// disable MTE on the thread, and allow the process to continue (indicated by +// returning 'true'). If the crash has nothing to do with GWP-ASan/MTE, or +// recovery isn't possible, return 'false'. +bool debuggerd_handle_signal(int signal_number, siginfo_t* info, void* context) { + if (signal_number != SIGSEGV) return false; + if (info->si_code == SEGV_MTEAERR || info->si_code == SEGV_MTESERR) { + if (!is_permissive_mte()) return false; + // Because permissive MTE disables MTE for the entire thread, we're less + // worried about getting a whole bunch of crashes in a row. ActivityManager + // doesn't like multiple native crashes for an app in a short period of time + // (see the comment about recoverable GWP-ASan in + // `debuggerd_handle_gwp_asan_signal`), but that shouldn't happen if MTE is + // disabled for the entire thread. This might need to be changed if there's + // some low-hanging bug that happens across multiple threads in quick + // succession. + debuggerd_signal_handler(signal_number, info, context); + return true; + } + + if (!signal_has_si_addr(info)) return false; + return debuggerd_handle_gwp_asan_signal(signal_number, info, context); +} diff --git a/debuggerd/include/debuggerd/client.h b/debuggerd/include/debuggerd/client.h index b7284b08ecff..e7401cc4e2b3 100644 --- a/debuggerd/include/debuggerd/client.h +++ b/debuggerd/include/debuggerd/client.h @@ -26,7 +26,7 @@ // Trigger a dump of specified process to output_fd. // output_fd is consumed, timeout of 0 will wait forever. -bool debuggerd_trigger_dump(pid_t pid, enum DebuggerdDumpType dump_type, unsigned int timeout_ms, +bool debuggerd_trigger_dump(pid_t tid, enum DebuggerdDumpType dump_type, unsigned int timeout_ms, android::base::unique_fd output_fd); int dump_backtrace_to_file(pid_t tid, enum DebuggerdDumpType dump_type, int output_fd); diff --git a/debuggerd/include/debuggerd/handler.h b/debuggerd/include/debuggerd/handler.h index ebb537236d83..954f049a6f55 100644 --- a/debuggerd/include/debuggerd/handler.h +++ b/debuggerd/include/debuggerd/handler.h @@ -33,6 +33,8 @@ struct AllocatorState; struct AllocationMetadata; }; // namespace gwp_asan +struct crash_detail_page_t; + // When updating this data structure, CrashInfoDataDynamic and the code in // ReadCrashInfo() must also be updated. struct __attribute__((packed)) debugger_process_info { @@ -44,7 +46,9 @@ struct __attribute__((packed)) debugger_process_info { const char* scudo_region_info; const char* scudo_ring_buffer; size_t scudo_ring_buffer_size; - bool recoverable_gwp_asan_crash; + size_t scudo_stack_depot_size; + bool recoverable_crash; + struct crash_detail_page_t* crash_detail_page; }; // GWP-ASan calbacks to support the recoverable mode. Separate from the diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h b/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h index d47f2ddf6d6b..12a425e95bf2 100644 --- a/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h +++ b/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h @@ -20,6 +20,7 @@ #include #include +#include #include #include #include diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h index a506859a4211..89bf5a96da74 100644 --- a/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h +++ b/debuggerd/libdebuggerd/include/libdebuggerd/scudo.h @@ -16,6 +16,8 @@ #pragma once +#if defined(USE_SCUDO) + #include "types.h" #include "utility.h" @@ -49,3 +51,5 @@ class ScudoCrashData { void FillInCause(Cause* cause, const scudo_error_report* report, unwindstack::AndroidUnwinder* unwinder) const; }; + +#endif // USE_SCUDO diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h index be999e04e6a0..39989c3a3c82 100644 --- a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h +++ b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone.h @@ -28,6 +28,7 @@ #include #include "open_files_list.h" +#include "tombstone.pb.h" #include "types.h" // Forward declarations @@ -54,20 +55,22 @@ void engrave_tombstone(android::base::unique_fd output_fd, android::base::unique unwindstack::AndroidUnwinder* unwinder, const std::map& thread_info, pid_t target_thread, const ProcessInfo& process_info, OpenFilesList* open_files, - std::string* amfd_data); + std::string* amfd_data, const Architecture* guest_arch = nullptr, + unwindstack::AndroidUnwinder* guest_unwinder = nullptr); void engrave_tombstone_ucontext(int tombstone_fd, int proto_fd, uint64_t abort_msg_address, siginfo_t* siginfo, ucontext_t* ucontext); void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder, const std::map& threads, pid_t target_thread, - const ProcessInfo& process_info, const OpenFilesList* open_files); - -bool tombstone_proto_to_text( - const Tombstone& tombstone, - std::function callback); + const ProcessInfo& process_info, const OpenFilesList* open_files, + const Architecture* guest_arch, + unwindstack::AndroidUnwinder* guest_unwinder); void fill_in_backtrace_frame(BacktraceFrame* f, const unwindstack::FrameData& frame); void set_human_readable_cause(Cause* cause, uint64_t fault_addr); - +#if defined(__aarch64__) +void dump_stack_history(unwindstack::AndroidUnwinder* unwinder, uintptr_t target_tls, + StackHistoryBuffer& shb_ob, bool nounwind = false); +#endif #endif // _DEBUGGERD_TOMBSTONE_H diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/tombstone_proto_to_text.h b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone_proto_to_text.h new file mode 100644 index 000000000000..2de9723447a4 --- /dev/null +++ b/debuggerd/libdebuggerd/include/libdebuggerd/tombstone_proto_to_text.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * 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 + * + * http://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. + */ + +#pragma once + +#include +#include + +class BacktraceFrame; +class Tombstone; + +bool tombstone_proto_to_text( + const Tombstone& tombstone, + std::function callback, + std::function symbolize); diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/types.h b/debuggerd/libdebuggerd/include/libdebuggerd/types.h index 5a2a7abad43f..f7fc2a3b7df3 100644 --- a/debuggerd/libdebuggerd/include/libdebuggerd/types.h +++ b/debuggerd/libdebuggerd/include/libdebuggerd/types.h @@ -39,6 +39,11 @@ struct ThreadInfo { int signo = 0; siginfo_t* siginfo = nullptr; + + std::unique_ptr guest_registers; +#if defined(__aarch64__) + uintptr_t tls; // This is currently used for MTE stack history buffer. +#endif }; // This struct is written into a pipe from inside the crashing process. @@ -51,8 +56,10 @@ struct ProcessInfo { uintptr_t scudo_region_info = 0; uintptr_t scudo_ring_buffer = 0; size_t scudo_ring_buffer_size = 0; + size_t scudo_stack_depot_size = 0; bool has_fault_address = false; uintptr_t untagged_fault_address = 0; uintptr_t maybe_tagged_fault_address = 0; + uintptr_t crash_detail_page = 0; }; diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h index 198de37a6263..b86c13d0806a 100644 --- a/debuggerd/libdebuggerd/include/libdebuggerd/utility.h +++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility.h @@ -91,12 +91,3 @@ bool signal_has_si_addr(const siginfo_t*); void get_signal_sender(char* buf, size_t n, const siginfo_t*); const char* get_signame(const siginfo_t*); const char* get_sigcode(const siginfo_t*); -std::string describe_tagged_addr_ctrl(long ctrl); -std::string describe_pac_enabled_keys(long keys); - -// Number of bytes per MTE granule. -constexpr size_t kTagGranuleSize = 16; - -// Number of rows and columns to display in an MTE tag dump. -constexpr size_t kNumTagColumns = 16; -constexpr size_t kNumTagRows = 16; diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/utility_host.h b/debuggerd/libdebuggerd/include/libdebuggerd/utility_host.h new file mode 100644 index 000000000000..819a99d2d942 --- /dev/null +++ b/debuggerd/libdebuggerd/include/libdebuggerd/utility_host.h @@ -0,0 +1,36 @@ +/* + * Copyright 2024, The Android Open Source Project + * + * 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 + * + * http://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. + */ + +#pragma once + +#include + +#include + +std::string describe_tagged_addr_ctrl(long ctrl); +std::string describe_pac_enabled_keys(long keys); + +// Number of bytes per MTE granule. +constexpr size_t kTagGranuleSize = 16; + +// Number of rows and columns to display in an MTE tag dump. +constexpr size_t kNumTagColumns = 16; +constexpr size_t kNumTagRows = 16; + +// Encode all non-ascii values and also ascii values that are not printable. +std::string oct_encode_non_ascii_printable(const std::string& data); +// Encode any value that fails isprint(), includes encoding chars like '\n' and '\t'. +std::string oct_encode_non_printable(const std::string& data); diff --git a/debuggerd/libdebuggerd/scudo.cpp b/debuggerd/libdebuggerd/scudo.cpp index 5a62fe1e762c..71d5fcfa73ba 100644 --- a/debuggerd/libdebuggerd/scudo.cpp +++ b/debuggerd/libdebuggerd/scudo.cpp @@ -14,14 +14,18 @@ * limitations under the License. */ +#if defined(USE_SCUDO) + #include "libdebuggerd/scudo.h" #include "libdebuggerd/tombstone.h" +#include "libdebuggerd/utility_host.h" #include "unwindstack/AndroidUnwinder.h" #include "unwindstack/Memory.h" #include #include +#include #include "tombstone.pb.h" @@ -40,8 +44,6 @@ ScudoCrashData::ScudoCrashData(unwindstack::Memory* process_memory, return; } - auto stack_depot = AllocAndReadFully(process_memory, process_info.scudo_stack_depot, - __scudo_get_stack_depot_size()); auto region_info = AllocAndReadFully(process_memory, process_info.scudo_region_info, __scudo_get_region_info_size()); std::unique_ptr ring_buffer; @@ -49,26 +51,31 @@ ScudoCrashData::ScudoCrashData(unwindstack::Memory* process_memory, ring_buffer = AllocAndReadFully(process_memory, process_info.scudo_ring_buffer, process_info.scudo_ring_buffer_size); } - if (!stack_depot || !region_info) { + std::unique_ptr stack_depot; + if (process_info.scudo_stack_depot_size != 0) { + stack_depot = AllocAndReadFully(process_memory, process_info.scudo_stack_depot, + process_info.scudo_stack_depot_size); + } + if (!region_info) { return; } untagged_fault_addr_ = process_info.untagged_fault_address; - uintptr_t fault_page = untagged_fault_addr_ & ~(PAGE_SIZE - 1); + uintptr_t fault_page = untagged_fault_addr_ & ~(getpagesize() - 1); - uintptr_t memory_begin = fault_page - PAGE_SIZE * 16; + uintptr_t memory_begin = fault_page - getpagesize() * 16; if (memory_begin > fault_page) { return; } - uintptr_t memory_end = fault_page + PAGE_SIZE * 16; + uintptr_t memory_end = fault_page + getpagesize() * 16; if (memory_end < fault_page) { return; } auto memory = std::make_unique(memory_end - memory_begin); - for (auto i = memory_begin; i != memory_end; i += PAGE_SIZE) { - process_memory->ReadFully(i, memory.get() + i - memory_begin, PAGE_SIZE); + for (auto i = memory_begin; i != memory_end; i += getpagesize()) { + process_memory->ReadFully(i, memory.get() + i - memory_begin, getpagesize()); } auto memory_tags = std::make_unique((memory_end - memory_begin) / kTagGranuleSize); @@ -77,7 +84,8 @@ ScudoCrashData::ScudoCrashData(unwindstack::Memory* process_memory, } __scudo_get_error_info(&error_info_, process_info.maybe_tagged_fault_address, stack_depot.get(), - region_info.get(), ring_buffer.get(), memory.get(), memory_tags.get(), + process_info.scudo_stack_depot_size, region_info.get(), ring_buffer.get(), + process_info.scudo_ring_buffer_size, memory.get(), memory_tags.get(), memory_begin, memory_end - memory_begin); } @@ -136,3 +144,5 @@ void ScudoCrashData::AddCauseProtos(Tombstone* tombstone, FillInCause(tombstone->add_causes(), &error_info_.reports[report_num++], unwinder); } } + +#endif // USE_SCUDO diff --git a/debuggerd/libdebuggerd/test/dump_memory_test.cpp b/debuggerd/libdebuggerd/test/dump_memory_test.cpp index 5be145aad128..dee7b4827af7 100644 --- a/debuggerd/libdebuggerd/test/dump_memory_test.cpp +++ b/debuggerd/libdebuggerd/test/dump_memory_test.cpp @@ -20,6 +20,8 @@ #include #include +#include +#include #include #include @@ -27,61 +29,64 @@ #include "log_fake.h" -const char g_expected_full_dump[] = -"\nmemory near r1:\n" +std::string GetMemoryString(uintptr_t addr, const std::vector& data) { + // Must be even number of data values. + CHECK((data.size() & 1) == 0); + + std::string str; + for (size_t i = 0; i < data.size(); i += 2) { + str += " "; + std::string ascii_str = ""; + for (size_t j = 0; j < 2; j++) { + for (size_t k = 0; k < 8; k++) { + uint8_t c = (data[i + j] >> (k * 8)) & 0xff; + if (c >= 0x20 && c < 0x7f) { + ascii_str += c; + } else { + ascii_str += '.'; + } + } + } #if defined(__LP64__) -" 0000000012345650 0706050403020100 0f0e0d0c0b0a0908 ................\n" -" 0000000012345660 1716151413121110 1f1e1d1c1b1a1918 ................\n" -" 0000000012345670 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n" -" 0000000012345680 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n" -" 0000000012345690 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n" -" 00000000123456a0 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n" -" 00000000123456b0 6766656463626160 6f6e6d6c6b6a6968 `abcdefghijklmno\n" -" 00000000123456c0 7776757473727170 7f7e7d7c7b7a7978 pqrstuvwxyz{|}~.\n" -" 00000000123456d0 8786858483828180 8f8e8d8c8b8a8988 ................\n" -" 00000000123456e0 9796959493929190 9f9e9d9c9b9a9998 ................\n" -" 00000000123456f0 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n" -" 0000000012345700 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n" -" 0000000012345710 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n" -" 0000000012345720 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n" -" 0000000012345730 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n" -" 0000000012345740 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n"; + str += android::base::StringPrintf("%016zx %016zx %016zx ", addr, data[i], data[i + 1]); #else -" 12345650 03020100 07060504 0b0a0908 0f0e0d0c ................\n" -" 12345660 13121110 17161514 1b1a1918 1f1e1d1c ................\n" -" 12345670 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n" -" 12345680 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n" -" 12345690 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n" -" 123456a0 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n" -" 123456b0 63626160 67666564 6b6a6968 6f6e6d6c `abcdefghijklmno\n" -" 123456c0 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~.\n" -" 123456d0 83828180 87868584 8b8a8988 8f8e8d8c ................\n" -" 123456e0 93929190 97969594 9b9a9998 9f9e9d9c ................\n" -" 123456f0 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................\n" -" 12345700 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................\n" -" 12345710 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n" -" 12345720 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n" -" 12345730 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................\n" -" 12345740 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................\n"; + str += android::base::StringPrintf( + "%08zx %08zx %08zx %08zx %08zx ", addr, static_cast(data[i] & 0xffffffff), + static_cast(data[i] >> 32), static_cast(data[i + 1] & 0xffffffff), + static_cast(data[i + 1] >> 32)); #endif + str += ascii_str + "\n"; + addr += 0x10; + } + return str; +} -const char g_expected_partial_dump[] = \ -"\nmemory near pc:\n" -#if defined(__LP64__) -" 00000000123455e0 0706050403020100 0f0e0d0c0b0a0908 ................\n" -" 00000000123455f0 1716151413121110 1f1e1d1c1b1a1918 ................\n" -" 0000000012345600 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n" -" 0000000012345610 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n" -" 0000000012345620 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n" -" 0000000012345630 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n"; -#else -" 123455e0 03020100 07060504 0b0a0908 0f0e0d0c ................\n" -" 123455f0 13121110 17161514 1b1a1918 1f1e1d1c ................\n" -" 12345600 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n" -" 12345610 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n" -" 12345620 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n" -" 12345630 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n"; -#endif +const std::vector& GetDefaultData() { + static std::vector data( + {0x0706050403020100UL, 0x0f0e0d0c0b0a0908UL, 0x1716151413121110UL, 0x1f1e1d1c1b1a1918UL, + 0x2726252423222120UL, 0x2f2e2d2c2b2a2928UL, 0x3736353433323130UL, 0x3f3e3d3c3b3a3938UL, + 0x4746454443424140UL, 0x4f4e4d4c4b4a4948UL, 0x5756555453525150UL, 0x5f5e5d5c5b5a5958UL, + 0x6766656463626160UL, 0x6f6e6d6c6b6a6968UL, 0x7776757473727170UL, 0x7f7e7d7c7b7a7978UL, + 0x8786858483828180UL, 0x8f8e8d8c8b8a8988UL, 0x9796959493929190UL, 0x9f9e9d9c9b9a9998UL, + 0xa7a6a5a4a3a2a1a0UL, 0xafaeadacabaaa9a8UL, 0xb7b6b5b4b3b2b1b0UL, 0xbfbebdbcbbbab9b8UL, + 0xc7c6c5c4c3c2c1c0UL, 0xcfcecdcccbcac9c8UL, 0xd7d6d5d4d3d2d1d0UL, 0xdfdedddcdbdad9d8UL, + 0xe7e6e5e4e3e2e1e0UL, 0xefeeedecebeae9e8UL, 0xf7f6f5f4f3f2f1f0UL, 0xfffefdfcfbfaf9f8UL}); + return data; +} + +std::string GetFullDumpString() { + std::string str = "\nmemory near r1:\n"; + str += GetMemoryString(0x12345650U, GetDefaultData()); + return str; +} + +std::string GetPartialDumpString() { + std::string str = "\nmemory near pc:\n"; + std::vector data = GetDefaultData(); + data.resize(12); + str += GetMemoryString(0x123455e0U, data); + return str; +} class MemoryMock : public unwindstack::Memory { public: @@ -189,7 +194,7 @@ TEST_F(DumpMemoryTest, aligned_addr) { std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); - ASSERT_STREQ(g_expected_full_dump, tombstone_contents.c_str()); + ASSERT_EQ(GetFullDumpString(), tombstone_contents); // Verify that the log buf is empty, and no error messages. ASSERT_STREQ("", getFakeLogBuf().c_str()); @@ -209,7 +214,7 @@ TEST_F(DumpMemoryTest, partial_read) { std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); - ASSERT_STREQ(g_expected_full_dump, tombstone_contents.c_str()); + ASSERT_EQ(GetFullDumpString(), tombstone_contents); // Verify that the log buf is empty, and no error messages. ASSERT_STREQ("", getFakeLogBuf().c_str()); @@ -228,7 +233,7 @@ TEST_F(DumpMemoryTest, unaligned_addr) { std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); - ASSERT_STREQ(g_expected_full_dump, tombstone_contents.c_str()); + ASSERT_EQ(GetFullDumpString(), tombstone_contents); // Verify that the log buf is empty, and no error messages. ASSERT_STREQ("", getFakeLogBuf().c_str()); @@ -260,7 +265,7 @@ TEST_F(DumpMemoryTest, memory_partially_unreadable) { std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); - ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str()); + ASSERT_EQ(GetPartialDumpString(), tombstone_contents); // Verify that the log buf is empty, and no error messages. ASSERT_STREQ("", getFakeLogBuf().c_str()); @@ -280,7 +285,7 @@ TEST_F(DumpMemoryTest, memory_partially_unreadable_unaligned_return) { std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); - ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str()); + ASSERT_EQ(GetPartialDumpString(), tombstone_contents); #if defined(__LP64__) ASSERT_STREQ("6 DEBUG Bytes read 102, is not a multiple of 8\n", getFakeLogPrint().c_str()); @@ -305,7 +310,7 @@ TEST_F(DumpMemoryTest, memory_partially_unreadable_two_unaligned_reads) { std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); - ASSERT_STREQ(g_expected_partial_dump, tombstone_contents.c_str()); + ASSERT_EQ(GetPartialDumpString(), tombstone_contents); #if defined(__LP64__) ASSERT_STREQ("6 DEBUG Bytes read 45, is not a multiple of 8\n" @@ -331,44 +336,9 @@ TEST_F(DumpMemoryTest, address_low_fence) { std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); - const char* expected_dump = \ -"\nmemory near r1:\n" -#if defined(__LP64__) -" 0000000000001000 0000000000000000 0000000000000000 ................\n" -" 0000000000001010 0000000000000000 0000000000000000 ................\n" -" 0000000000001020 0000000000000000 0000000000000000 ................\n" -" 0000000000001030 0000000000000000 0000000000000000 ................\n" -" 0000000000001040 0000000000000000 0000000000000000 ................\n" -" 0000000000001050 0000000000000000 0000000000000000 ................\n" -" 0000000000001060 0000000000000000 0000000000000000 ................\n" -" 0000000000001070 0000000000000000 0000000000000000 ................\n" -" 0000000000001080 0000000000000000 0000000000000000 ................\n" -" 0000000000001090 0000000000000000 0000000000000000 ................\n" -" 00000000000010a0 0000000000000000 0000000000000000 ................\n" -" 00000000000010b0 0000000000000000 0000000000000000 ................\n" -" 00000000000010c0 0000000000000000 0000000000000000 ................\n" -" 00000000000010d0 0000000000000000 0000000000000000 ................\n" -" 00000000000010e0 0000000000000000 0000000000000000 ................\n" -" 00000000000010f0 0000000000000000 0000000000000000 ................\n"; -#else -" 00001000 00000000 00000000 00000000 00000000 ................\n" -" 00001010 00000000 00000000 00000000 00000000 ................\n" -" 00001020 00000000 00000000 00000000 00000000 ................\n" -" 00001030 00000000 00000000 00000000 00000000 ................\n" -" 00001040 00000000 00000000 00000000 00000000 ................\n" -" 00001050 00000000 00000000 00000000 00000000 ................\n" -" 00001060 00000000 00000000 00000000 00000000 ................\n" -" 00001070 00000000 00000000 00000000 00000000 ................\n" -" 00001080 00000000 00000000 00000000 00000000 ................\n" -" 00001090 00000000 00000000 00000000 00000000 ................\n" -" 000010a0 00000000 00000000 00000000 00000000 ................\n" -" 000010b0 00000000 00000000 00000000 00000000 ................\n" -" 000010c0 00000000 00000000 00000000 00000000 ................\n" -" 000010d0 00000000 00000000 00000000 00000000 ................\n" -" 000010e0 00000000 00000000 00000000 00000000 ................\n" -" 000010f0 00000000 00000000 00000000 00000000 ................\n"; -#endif - ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); + std::string expected_dump = "\nmemory near r1:\n"; + expected_dump += GetMemoryString(0x1000, std::vector(32, 0UL)); + ASSERT_EQ(expected_dump, tombstone_contents); // Verify that the log buf is empty, and no error messages. ASSERT_STREQ("", getFakeLogBuf().c_str()); @@ -414,61 +384,17 @@ TEST_F(DumpMemoryTest, memory_address_nearly_too_high) { std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); - const char* expected_dump = \ -"\nmemory near r4:\n" + std::string expected_dump = "\nmemory near r4:\n"; + uintptr_t addr; #if defined(__aarch64__) -" 00ffffffffffff00 0706050403020100 0f0e0d0c0b0a0908 ................\n" -" 00ffffffffffff10 1716151413121110 1f1e1d1c1b1a1918 ................\n" -" 00ffffffffffff20 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n" -" 00ffffffffffff30 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n" -" 00ffffffffffff40 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n" -" 00ffffffffffff50 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n" -" 00ffffffffffff60 6766656463626160 6f6e6d6c6b6a6968 `abcdefghijklmno\n" -" 00ffffffffffff70 7776757473727170 7f7e7d7c7b7a7978 pqrstuvwxyz{|}~.\n" -" 00ffffffffffff80 8786858483828180 8f8e8d8c8b8a8988 ................\n" -" 00ffffffffffff90 9796959493929190 9f9e9d9c9b9a9998 ................\n" -" 00ffffffffffffa0 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n" -" 00ffffffffffffb0 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n" -" 00ffffffffffffc0 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n" -" 00ffffffffffffd0 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n" -" 00ffffffffffffe0 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n" -" 00fffffffffffff0 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n"; + addr = 0x00ffffffffffff00UL; #elif defined(__LP64__) -" ffffffffffffff00 0706050403020100 0f0e0d0c0b0a0908 ................\n" -" ffffffffffffff10 1716151413121110 1f1e1d1c1b1a1918 ................\n" -" ffffffffffffff20 2726252423222120 2f2e2d2c2b2a2928 !\"#$%&'()*+,-./\n" -" ffffffffffffff30 3736353433323130 3f3e3d3c3b3a3938 0123456789:;<=>?\n" -" ffffffffffffff40 4746454443424140 4f4e4d4c4b4a4948 @ABCDEFGHIJKLMNO\n" -" ffffffffffffff50 5756555453525150 5f5e5d5c5b5a5958 PQRSTUVWXYZ[\\]^_\n" -" ffffffffffffff60 6766656463626160 6f6e6d6c6b6a6968 `abcdefghijklmno\n" -" ffffffffffffff70 7776757473727170 7f7e7d7c7b7a7978 pqrstuvwxyz{|}~.\n" -" ffffffffffffff80 8786858483828180 8f8e8d8c8b8a8988 ................\n" -" ffffffffffffff90 9796959493929190 9f9e9d9c9b9a9998 ................\n" -" ffffffffffffffa0 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................\n" -" ffffffffffffffb0 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................\n" -" ffffffffffffffc0 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n" -" ffffffffffffffd0 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n" -" ffffffffffffffe0 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................\n" -" fffffffffffffff0 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................\n"; + addr = 0xffffffffffffff00UL; #else -" ffffff00 03020100 07060504 0b0a0908 0f0e0d0c ................\n" -" ffffff10 13121110 17161514 1b1a1918 1f1e1d1c ................\n" -" ffffff20 23222120 27262524 2b2a2928 2f2e2d2c !\"#$%&'()*+,-./\n" -" ffffff30 33323130 37363534 3b3a3938 3f3e3d3c 0123456789:;<=>?\n" -" ffffff40 43424140 47464544 4b4a4948 4f4e4d4c @ABCDEFGHIJKLMNO\n" -" ffffff50 53525150 57565554 5b5a5958 5f5e5d5c PQRSTUVWXYZ[\\]^_\n" -" ffffff60 63626160 67666564 6b6a6968 6f6e6d6c `abcdefghijklmno\n" -" ffffff70 73727170 77767574 7b7a7978 7f7e7d7c pqrstuvwxyz{|}~.\n" -" ffffff80 83828180 87868584 8b8a8988 8f8e8d8c ................\n" -" ffffff90 93929190 97969594 9b9a9998 9f9e9d9c ................\n" -" ffffffa0 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................\n" -" ffffffb0 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................\n" -" ffffffc0 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n" -" ffffffd0 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n" -" ffffffe0 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................\n" -" fffffff0 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................\n"; + addr = 0xffffff00UL; #endif - ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); + expected_dump += GetMemoryString(addr, GetDefaultData()); + ASSERT_EQ(expected_dump, tombstone_contents); // Verify that the log buf is empty, and no error messages. ASSERT_STREQ("", getFakeLogBuf().c_str()); @@ -490,30 +416,15 @@ TEST_F(DumpMemoryTest, first_read_empty) { std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); - const char* expected_dump = \ -"\nmemory near r4:\n" -#if defined(__LP64__) -R"( 0000000010001000 8786858483828180 8f8e8d8c8b8a8988 ................ - 0000000010001010 9796959493929190 9f9e9d9c9b9a9998 ................ - 0000000010001020 a7a6a5a4a3a2a1a0 afaeadacabaaa9a8 ................ - 0000000010001030 b7b6b5b4b3b2b1b0 bfbebdbcbbbab9b8 ................ - 0000000010001040 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................ - 0000000010001050 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................ - 0000000010001060 e7e6e5e4e3e2e1e0 efeeedecebeae9e8 ................ - 0000000010001070 f7f6f5f4f3f2f1f0 fffefdfcfbfaf9f8 ................ -)"; -#else -R"( 10001000 83828180 87868584 8b8a8988 8f8e8d8c ................ - 10001010 93929190 97969594 9b9a9998 9f9e9d9c ................ - 10001020 a3a2a1a0 a7a6a5a4 abaaa9a8 afaeadac ................ - 10001030 b3b2b1b0 b7b6b5b4 bbbab9b8 bfbebdbc ................ - 10001040 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................ - 10001050 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................ - 10001060 e3e2e1e0 e7e6e5e4 ebeae9e8 efeeedec ................ - 10001070 f3f2f1f0 f7f6f5f4 fbfaf9f8 fffefdfc ................ -)"; -#endif - ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); + std::string expected_dump = "\nmemory near r4:\n"; + expected_dump += GetMemoryString( + 0x10000000 + page_size, + std::vector{ + 0x8786858483828180UL, 0x8f8e8d8c8b8a8988UL, 0x9796959493929190UL, 0x9f9e9d9c9b9a9998UL, + 0xa7a6a5a4a3a2a1a0UL, 0xafaeadacabaaa9a8UL, 0xb7b6b5b4b3b2b1b0UL, 0xbfbebdbcbbbab9b8UL, + 0xc7c6c5c4c3c2c1c0UL, 0xcfcecdcccbcac9c8UL, 0xd7d6d5d4d3d2d1d0UL, 0xdfdedddcdbdad9d8UL, + 0xe7e6e5e4e3e2e1e0UL, 0xefeeedecebeae9e8UL, 0xf7f6f5f4f3f2f1f0UL, 0xfffefdfcfbfaf9f8UL}); + ASSERT_EQ(expected_dump, tombstone_contents); // Verify that the log buf is empty, and no error messages. ASSERT_STREQ("", getFakeLogBuf().c_str()); @@ -535,16 +446,11 @@ TEST_F(DumpMemoryTest, first_read_empty_second_read_stops) { std::string tombstone_contents; ASSERT_TRUE(lseek(log_.tfd, 0, SEEK_SET) == 0); ASSERT_TRUE(android::base::ReadFdToString(log_.tfd, &tombstone_contents)); - const char* expected_dump = \ -"\nmemory near r4:\n" -#if defined(__LP64__) -" 0000000010001000 c7c6c5c4c3c2c1c0 cfcecdcccbcac9c8 ................\n" -" 0000000010001010 d7d6d5d4d3d2d1d0 dfdedddcdbdad9d8 ................\n"; -#else -" 10001000 c3c2c1c0 c7c6c5c4 cbcac9c8 cfcecdcc ................\n" -" 10001010 d3d2d1d0 d7d6d5d4 dbdad9d8 dfdedddc ................\n"; -#endif - ASSERT_STREQ(expected_dump, tombstone_contents.c_str()); + std::string expected_dump = "\nmemory near r4:\n"; + expected_dump += GetMemoryString( + 0x10000000 + page_size, std::vector{0xc7c6c5c4c3c2c1c0UL, 0xcfcecdcccbcac9c8UL, + 0xd7d6d5d4d3d2d1d0UL, 0xdfdedddcdbdad9d8UL}); + ASSERT_EQ(expected_dump, tombstone_contents); // Verify that the log buf is empty, and no error messages. ASSERT_STREQ("", getFakeLogBuf().c_str()); diff --git a/debuggerd/libdebuggerd/test/host_signal_fixup.h b/debuggerd/libdebuggerd/test/host_signal_fixup.h deleted file mode 100644 index 762bae5fb504..000000000000 --- a/debuggerd/libdebuggerd/test/host_signal_fixup.h +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * 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 - * - * http://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. - */ - -#ifndef _DEBUGGERD_TEST_HOST_SIGNAL_FIXUP_H -#define _DEBUGGERD_TEST_HOST_SIGNAL_FIXUP_H - -#include - -#if !defined(__BIONIC__) - -// In order to compile parts of debuggerd for the host, we need to -// define these values. - -#if !defined(NSIGILL) -#define NSIGILL ILL_BADSTK -#endif - -#if !defined(BUS_MCEERR_AR) -#define BUS_MCEERR_AR 4 -#endif -#if !defined(BUS_MCEERR_AO) -#define BUS_MCEERR_AO 5 -#endif -#if !defined(NSIGBUS) -#define NSIGBUS BUS_MCEERR_AO -#endif - -#if !defined(NSIGFPE) -#define NSIGFPE FPE_FLTSUB -#endif - -#if !defined(NSIGSEGV) -#define NSIGSEGV SEGV_ACCERR -#endif - -#if !defined(TRAP_BRANCH) -#define TRAP_BRANCH 3 -#endif -#if !defined(TRAP_HWBKPT) -#define TRAP_HWBKPT 4 -#endif -#if !defined(NSIGTRAP) -#define NSIGTRAP TRAP_HWBKPT -#endif - -#if !defined(SI_DETHREAD) -#define SI_DETHREAD (-7) -#endif - -#endif - -#endif // _DEBUGGERD_TEST_HOST_SIGNAL_FIXUP_H diff --git a/debuggerd/libdebuggerd/test/mte_stack_record_test.cpp b/debuggerd/libdebuggerd/test/mte_stack_record_test.cpp new file mode 100644 index 000000000000..bcda0ca5d25a --- /dev/null +++ b/debuggerd/libdebuggerd/test/mte_stack_record_test.cpp @@ -0,0 +1,186 @@ +/* + * Copyright (C) 2024 The Android Open Source Project + * + * 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 + * + * http://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. + */ +#if defined(__aarch64__) + +#include +#include + +#include + +#include "bionic/mte.h" +#include "bionic/page.h" +#include "unwindstack/AndroidUnwinder.h" +#include "unwindstack/Memory.h" + +#include +#include + +#include "gtest/gtest.h" + +#include "libdebuggerd/tombstone.h" + +struct ScopedUnmap { + void* ptr; + size_t size; + ~ScopedUnmap() { munmap(ptr, size); } +}; + +class MteStackHistoryTest : public ::testing::TestWithParam { + void SetUp() override { +#if !defined(__aarch64__) + GTEST_SKIP(); +#endif + SKIP_WITH_HWASAN; + unwinder.emplace(); + unwindstack::ErrorData E; + ASSERT_TRUE(unwinder->Initialize(E)); + } + + protected: + // std::optional so we don't construct it for the SKIP cases. + std::optional unwinder; +}; + +TEST(MteStackHistoryUnwindTest, TestOne) { +#if !defined(__aarch64__) + GTEST_SKIP(); +#endif + SKIP_WITH_HWASAN; + size_t size = stack_mte_ringbuffer_size(0); + char* data = static_cast(stack_mte_ringbuffer_allocate(0, nullptr)); + ScopedUnmap s{data, size}; + + uintptr_t taggedfp = (1ULL << 56) | 1; + uintptr_t pc = reinterpret_cast(&memcpy); + memcpy(data, &pc, sizeof(pc)); + memcpy(data + 8, &taggedfp, sizeof(taggedfp)); + + // The MTE TLS is at TLS - 3, so we allocate 3 placeholders. + void* tls[4] = {data + 16}; + + unwindstack::AndroidLocalUnwinder unwinder; + unwindstack::ErrorData E; + unwinder.Initialize(E); + StackHistoryBuffer shb; + dump_stack_history(&unwinder, reinterpret_cast(&tls[3]), shb, /* nounwind= */ false); + ASSERT_EQ(shb.entries_size(), 1); + const StackHistoryBufferEntry& e = shb.entries(0); + EXPECT_EQ(e.addr().pc(), pc); + EXPECT_EQ(e.addr().file_name(), "/apex/com.android.runtime/lib64/bionic/libc.so"); + EXPECT_EQ(e.fp(), 1ULL); + EXPECT_EQ(e.tag(), 1ULL); +} + +static std::optional FindMapping(void* data) { + std::optional result; + android::procinfo::ReadMapFile( + "/proc/self/maps", [&result, data](const android::procinfo::MapInfo& info) { + auto data_int = reinterpret_cast(data) & ((1ULL << 56ULL) - 1ULL); + if (info.start <= data_int && data_int < info.end) { + result = info; + } + }); + return result; +} + +TEST_P(MteStackHistoryTest, TestFree) { + int size_cls = GetParam(); + size_t size = stack_mte_ringbuffer_size(size_cls); + void* data = stack_mte_ringbuffer_allocate(size_cls, nullptr); + EXPECT_EQ(stack_mte_ringbuffer_size_from_pointer(reinterpret_cast(data)), size); + auto before = FindMapping(data); + ASSERT_TRUE(before.has_value()); + EXPECT_EQ(before->end - before->start, size); + stack_mte_free_ringbuffer(reinterpret_cast(data)); + for (size_t i = 0; i < size; i += page_size()) { + auto after = FindMapping(static_cast(data) + i); + EXPECT_TRUE(!after.has_value() || after->name != before->name); + } +} + +TEST_P(MteStackHistoryTest, TestEmpty) { + int size_cls = GetParam(); + size_t size = stack_mte_ringbuffer_size(size_cls); + void* data = stack_mte_ringbuffer_allocate(size_cls, nullptr); + ScopedUnmap s{data, size}; + // The MTE TLS is at TLS - 3, so we allocate 3 placeholders. + void* tls[4] = {data}; + + StackHistoryBuffer shb; + dump_stack_history(&*unwinder, reinterpret_cast(&tls[3]), shb, /* nounwind= */ true); + EXPECT_EQ(shb.entries_size(), 0); +} + +TEST_P(MteStackHistoryTest, TestFull) { + int size_cls = GetParam(); + size_t size = stack_mte_ringbuffer_size(size_cls); + char* data = static_cast(stack_mte_ringbuffer_allocate(size_cls, nullptr)); + ScopedUnmap s{data, size}; + uintptr_t itr = 1; + for (char* d = data; d < &data[size]; d += 16) { + uintptr_t taggedfp = ((itr & 15) << 56) | itr; + uintptr_t pc = itr; + memcpy(d, &pc, sizeof(pc)); + memcpy(d + 8, &taggedfp, sizeof(taggedfp)); + ++itr; + } + // The MTE TLS is at TLS - 3, so we allocate 3 placeholders. + // Because the buffer is full, and we point at one past the last inserted element, + // due to wrap-around we point at the beginning of the buffer. + void* tls[4] = {data}; + + StackHistoryBuffer shb; + dump_stack_history(&*unwinder, reinterpret_cast(&tls[3]), shb, /* nounwind= */ true); + EXPECT_EQ(static_cast(shb.entries_size()), size / 16); + for (const auto& entry : shb.entries()) { + EXPECT_EQ(entry.addr().pc(), --itr); + EXPECT_EQ(entry.addr().pc(), entry.fp()); + EXPECT_EQ(entry.addr().pc() & 15, entry.tag()); + } +} + +TEST_P(MteStackHistoryTest, TestHalfFull) { + int size_cls = GetParam(); + size_t size = stack_mte_ringbuffer_size(size_cls); + size_t half_size = size / 2; + + char* data = static_cast(stack_mte_ringbuffer_allocate(size_cls, nullptr)); + ScopedUnmap s{data, size}; + + uintptr_t itr = 1; + for (char* d = data; d < &data[half_size]; d += 16) { + uintptr_t taggedfp = ((itr & 15) << 56) | itr; + uintptr_t pc = itr; + memcpy(d, &pc, sizeof(pc)); + memcpy(d + 8, &taggedfp, sizeof(taggedfp)); + ++itr; + } + // The MTE TLS is at TLS - 3, so we allocate 3 placeholders. + void* tls[4] = {&data[half_size]}; + + StackHistoryBuffer shb; + dump_stack_history(&*unwinder, reinterpret_cast(&tls[3]), shb, /* nounwind= */ true); + EXPECT_EQ(static_cast(shb.entries_size()), half_size / 16); + for (const auto& entry : shb.entries()) { + EXPECT_EQ(entry.addr().pc(), --itr); + EXPECT_EQ(entry.addr().pc(), entry.fp()); + EXPECT_EQ(entry.addr().pc() & 15, entry.tag()); + } +} + +INSTANTIATE_TEST_SUITE_P(MteStackHistoryTestInstance, MteStackHistoryTest, testing::Range(0, 8)); + +#endif diff --git a/debuggerd/libdebuggerd/test/tombstone_proto_to_text_test.cpp b/debuggerd/libdebuggerd/test/tombstone_proto_to_text_test.cpp new file mode 100644 index 000000000000..988ca0cd1257 --- /dev/null +++ b/debuggerd/libdebuggerd/test/tombstone_proto_to_text_test.cpp @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * 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 + * + * http://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. + */ + +#include +#include + +#include + +#include + +#include "libdebuggerd/tombstone.h" +#include "libdebuggerd/tombstone_proto_to_text.h" +#include "tombstone.pb.h" + +using CallbackType = std::function; + +class TombstoneProtoToTextTest : public ::testing::Test { + public: + void SetUp() { + tombstone_.reset(new Tombstone); + + tombstone_->set_arch(Architecture::ARM64); + tombstone_->set_build_fingerprint("Test fingerprint"); + tombstone_->set_timestamp("1970-01-01 00:00:00"); + tombstone_->set_pid(100); + tombstone_->set_tid(100); + tombstone_->set_uid(0); + tombstone_->set_selinux_label("none"); + + Signal signal; + signal.set_number(SIGSEGV); + signal.set_name("SIGSEGV"); + signal.set_code(0); + signal.set_code_name("none"); + + *tombstone_->mutable_signal_info() = signal; + + Thread thread; + thread.set_id(100); + thread.set_name("main"); + thread.set_tagged_addr_ctrl(0); + thread.set_pac_enabled_keys(0); + + auto& threads = *tombstone_->mutable_threads(); + threads[100] = thread; + main_thread_ = &threads[100]; + } + + void ProtoToString() { + text_ = ""; + EXPECT_TRUE(tombstone_proto_to_text( + *tombstone_, + [this](const std::string& line, bool should_log) { + if (should_log) { + text_ += "LOG "; + } + text_ += line + '\n'; + }, + [&](const BacktraceFrame& frame) { + text_ += "SYMBOLIZE " + frame.build_id() + " " + std::to_string(frame.pc()) + "\n"; + })); + } + + Thread* main_thread_; + std::string text_; + std::unique_ptr tombstone_; +}; + +TEST_F(TombstoneProtoToTextTest, tagged_addr_ctrl) { + main_thread_->set_tagged_addr_ctrl(0); + ProtoToString(); + EXPECT_MATCH(text_, "LOG tagged_addr_ctrl: 0000000000000000\\n"); + + main_thread_->set_tagged_addr_ctrl(PR_TAGGED_ADDR_ENABLE); + ProtoToString(); + EXPECT_MATCH(text_, "LOG tagged_addr_ctrl: 0000000000000001 \\(PR_TAGGED_ADDR_ENABLE\\)\\n"); + + main_thread_->set_tagged_addr_ctrl(PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC | + (0xfffe << PR_MTE_TAG_SHIFT)); + ProtoToString(); + EXPECT_MATCH(text_, + "LOG tagged_addr_ctrl: 000000000007fff3 \\(PR_TAGGED_ADDR_ENABLE, PR_MTE_TCF_SYNC, " + "mask 0xfffe\\)\\n"); + + main_thread_->set_tagged_addr_ctrl(0xf0000000 | PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC | + PR_MTE_TCF_ASYNC | (0xfffe << PR_MTE_TAG_SHIFT)); + ProtoToString(); + EXPECT_MATCH(text_, + "LOG tagged_addr_ctrl: 00000000f007fff7 \\(PR_TAGGED_ADDR_ENABLE, PR_MTE_TCF_SYNC, " + "PR_MTE_TCF_ASYNC, mask 0xfffe, unknown 0xf0000000\\)\\n"); +} + +TEST_F(TombstoneProtoToTextTest, pac_enabled_keys) { + main_thread_->set_pac_enabled_keys(0); + ProtoToString(); + EXPECT_MATCH(text_, "LOG pac_enabled_keys: 0000000000000000\\n"); + + main_thread_->set_pac_enabled_keys(PR_PAC_APIAKEY); + ProtoToString(); + EXPECT_MATCH(text_, "LOG pac_enabled_keys: 0000000000000001 \\(PR_PAC_APIAKEY\\)\\n"); + + main_thread_->set_pac_enabled_keys(PR_PAC_APIAKEY | PR_PAC_APDBKEY); + ProtoToString(); + EXPECT_MATCH(text_, + "LOG pac_enabled_keys: 0000000000000009 \\(PR_PAC_APIAKEY, PR_PAC_APDBKEY\\)\\n"); + + main_thread_->set_pac_enabled_keys(PR_PAC_APIAKEY | PR_PAC_APDBKEY | 0x1000); + ProtoToString(); + EXPECT_MATCH(text_, + "LOG pac_enabled_keys: 0000000000001009 \\(PR_PAC_APIAKEY, PR_PAC_APDBKEY, unknown " + "0x1000\\)\\n"); +} + +TEST_F(TombstoneProtoToTextTest, crash_detail_string) { + auto* crash_detail = tombstone_->add_crash_details(); + crash_detail->set_name("CRASH_DETAIL_NAME"); + crash_detail->set_data("crash_detail_value"); + ProtoToString(); + EXPECT_MATCH(text_, "(CRASH_DETAIL_NAME: 'crash_detail_value')"); +} + +TEST_F(TombstoneProtoToTextTest, crash_detail_bytes) { + auto* crash_detail = tombstone_->add_crash_details(); + crash_detail->set_name("CRASH_DETAIL_NAME"); + crash_detail->set_data("helloworld\1\255\3"); + ProtoToString(); + EXPECT_MATCH(text_, R"(CRASH_DETAIL_NAME: 'helloworld\\1\\255\\3')"); +} + +TEST_F(TombstoneProtoToTextTest, stack_record) { + auto* cause = tombstone_->add_causes(); + cause->set_human_readable("stack tag-mismatch on thread 123"); + auto* stack = tombstone_->mutable_stack_history_buffer(); + stack->set_tid(123); + { + auto* shb_entry = stack->add_entries(); + shb_entry->set_fp(0x1); + shb_entry->set_tag(0xb); + auto* addr = shb_entry->mutable_addr(); + addr->set_rel_pc(0x567); + addr->set_file_name("foo.so"); + addr->set_build_id("ABC123"); + } + { + auto* shb_entry = stack->add_entries(); + shb_entry->set_fp(0x2); + shb_entry->set_tag(0xc); + auto* addr = shb_entry->mutable_addr(); + addr->set_rel_pc(0x678); + addr->set_file_name("bar.so"); + } + ProtoToString(); + EXPECT_MATCH(text_, "stack tag-mismatch on thread 123"); + EXPECT_MATCH(text_, "stack_record fp:0x1 tag:0xb pc:foo\\.so\\+0x567 \\(BuildId: ABC123\\)"); + EXPECT_MATCH(text_, "stack_record fp:0x2 tag:0xc pc:bar\\.so\\+0x678"); +} + +TEST_F(TombstoneProtoToTextTest, symbolize) { + BacktraceFrame* frame = main_thread_->add_current_backtrace(); + frame->set_pc(12345); + frame->set_build_id("0123456789abcdef"); + ProtoToString(); + EXPECT_MATCH(text_, "\\(BuildId: 0123456789abcdef\\)\\nSYMBOLIZE 0123456789abcdef 12345\\n"); +} + +TEST_F(TombstoneProtoToTextTest, uid) { + ProtoToString(); + EXPECT_MATCH(text_, "\\nLOG uid: 0\\n"); +} diff --git a/debuggerd/libdebuggerd/test/utility_test.cpp b/debuggerd/libdebuggerd/test/utility_test.cpp deleted file mode 100644 index dad338054859..000000000000 --- a/debuggerd/libdebuggerd/test/utility_test.cpp +++ /dev/null @@ -1,42 +0,0 @@ -/* - * Copyright (C) 2021 The Android Open Source Project - * - * 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 - * - * http://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. - */ - -#include -#include - -#include "libdebuggerd/utility.h" - -TEST(UtilityTest, describe_tagged_addr_ctrl) { - EXPECT_EQ("", describe_tagged_addr_ctrl(0)); - EXPECT_EQ(" (PR_TAGGED_ADDR_ENABLE)", describe_tagged_addr_ctrl(PR_TAGGED_ADDR_ENABLE)); - EXPECT_EQ(" (PR_TAGGED_ADDR_ENABLE, PR_MTE_TCF_SYNC, mask 0xfffe)", - describe_tagged_addr_ctrl(PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC | - (0xfffe << PR_MTE_TAG_SHIFT))); - EXPECT_EQ( - " (PR_TAGGED_ADDR_ENABLE, PR_MTE_TCF_SYNC, PR_MTE_TCF_ASYNC, mask 0xfffe, unknown " - "0xf0000000)", - describe_tagged_addr_ctrl(0xf0000000 | PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC | - PR_MTE_TCF_ASYNC | (0xfffe << PR_MTE_TAG_SHIFT))); -} - -TEST(UtilityTest, describe_pac_enabled_keys) { - EXPECT_EQ("", describe_pac_enabled_keys(0)); - EXPECT_EQ(" (PR_PAC_APIAKEY)", describe_pac_enabled_keys(PR_PAC_APIAKEY)); - EXPECT_EQ(" (PR_PAC_APIAKEY, PR_PAC_APDBKEY)", - describe_pac_enabled_keys(PR_PAC_APIAKEY | PR_PAC_APDBKEY)); - EXPECT_EQ(" (PR_PAC_APIAKEY, PR_PAC_APDBKEY, unknown 0x1000)", - describe_pac_enabled_keys(PR_PAC_APIAKEY | PR_PAC_APDBKEY | 0x1000)); -} diff --git a/debuggerd/libdebuggerd/tombstone.cpp b/debuggerd/libdebuggerd/tombstone.cpp index 375ed8a2cccc..30c6fe4c50c2 100644 --- a/debuggerd/libdebuggerd/tombstone.cpp +++ b/debuggerd/libdebuggerd/tombstone.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "DEBUG" #include "libdebuggerd/tombstone.h" +#include "libdebuggerd/tombstone_proto_to_text.h" #include #include @@ -76,7 +77,7 @@ void engrave_tombstone_ucontext(int tombstone_fd, int proto_fd, uint64_t abort_m threads[target_tid] = ThreadInfo { .registers = std::move(regs), .uid = uid, .tid = target_tid, .thread_name = std::move(thread_name), .pid = pid, .command_line = std::move(command_line), - .selinux_label = std::move(selinux_label), .siginfo = siginfo, + .selinux_label = std::move(selinux_label), .siginfo = siginfo, .signo = siginfo->si_signo, // Only supported on aarch64 for now. #if defined(__aarch64__) .tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0), @@ -125,10 +126,12 @@ void engrave_tombstone(unique_fd output_fd, unique_fd proto_fd, unwindstack::AndroidUnwinder* unwinder, const std::map& threads, pid_t target_thread, const ProcessInfo& process_info, OpenFilesList* open_files, - std::string* amfd_data) { + std::string* amfd_data, const Architecture* guest_arch, + unwindstack::AndroidUnwinder* guest_unwinder) { // Don't copy log messages to tombstone unless this is a development device. Tombstone tombstone; - engrave_tombstone_proto(&tombstone, unwinder, threads, target_thread, process_info, open_files); + engrave_tombstone_proto(&tombstone, unwinder, threads, target_thread, process_info, open_files, + guest_arch, guest_unwinder); if (proto_fd != -1) { if (!tombstone.SerializeToFileDescriptor(proto_fd.get())) { @@ -143,7 +146,10 @@ void engrave_tombstone(unique_fd output_fd, unique_fd proto_fd, log.tfd = output_fd.get(); log.amfd_data = amfd_data; - tombstone_proto_to_text(tombstone, [&log](const std::string& line, bool should_log) { - _LOG(&log, should_log ? logtype::HEADER : logtype::LOGS, "%s\n", line.c_str()); - }); + tombstone_proto_to_text( + tombstone, + [&log](const std::string& line, bool should_log) { + _LOG(&log, should_log ? logtype::HEADER : logtype::LOGS, "%s\n", line.c_str()); + }, + [](const BacktraceFrame&) {}); } diff --git a/debuggerd/libdebuggerd/tombstone_proto.cpp b/debuggerd/libdebuggerd/tombstone_proto.cpp index acd814efa68b..d3ac49a17f8d 100644 --- a/debuggerd/libdebuggerd/tombstone_proto.cpp +++ b/debuggerd/libdebuggerd/tombstone_proto.cpp @@ -27,16 +27,20 @@ #include #include #include +#include #include #include #include #include #include +#include #include #include #include #include +#include +#include #include @@ -48,8 +52,12 @@ #include #include +#include +#include #include +#include #include +#include #include #include #include @@ -64,12 +72,16 @@ #include "libdebuggerd/open_files_list.h" #include "libdebuggerd/utility.h" +#include "libdebuggerd/utility_host.h" #include "util.h" #include "tombstone.pb.h" using android::base::StringPrintf; +// The maximum number of messages to save in the protobuf per file. +static constexpr size_t kMaxLogMessages = 500; + // Use the demangler from libc++. extern "C" char* __cxa_demangle(const char*, char*, size_t*, int* status); @@ -91,6 +103,11 @@ static Architecture get_arch() { static std::optional get_stack_overflow_cause(uint64_t fault_addr, uint64_t sp, unwindstack::Maps* maps) { + // Under stack MTE the stack pointer and/or the fault address can be tagged. + // In order to calculate deltas between them, strip off the tags off both + // addresses. + fault_addr = untag_address(fault_addr); + sp = untag_address(sp); static constexpr uint64_t kMaxDifferenceBytes = 256; uint64_t difference; if (sp >= fault_addr) { @@ -192,8 +209,117 @@ void set_human_readable_cause(Cause* cause, uint64_t fault_addr) { error_type_str, diff, byte_suffix, location_str, heap_object.size(), heap_object.address())); } +#if defined(__aarch64__) +void dump_stack_history(unwindstack::AndroidUnwinder* unwinder, uintptr_t target_tls, + StackHistoryBuffer& shb_obj, bool nounwind) { + auto process_memory = unwinder->GetProcessMemory(); + target_tls += sizeof(void*) * TLS_SLOT_STACK_MTE; + uintptr_t stack_mte; + if (!process_memory->ReadFully(target_tls, &stack_mte, sizeof(stack_mte))) { + async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, + "dump_stack_history: failed to read TLS_SLOT_STACK_MTE: %m"); + return; + } + if (stack_mte == 0) { + async_safe_format_log(ANDROID_LOG_DEBUG, LOG_TAG, + "dump_stack_history: stack history buffer is null"); + return; + } + uintptr_t untagged_stack_mte = untag_address(stack_mte); + uintptr_t buf_size = stack_mte_ringbuffer_size_from_pointer(stack_mte); + if (buf_size == 0) { + async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "dump_stack_history: empty size"); + return; + } + uintptr_t buf_start = untagged_stack_mte & ~(buf_size - 1ULL); + std::vector buf(buf_size); + if (!process_memory->ReadFully(buf_start, buf.data(), buf.size())) { + async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, + "dump_stack_history: failed to read stack history: %m"); + return; + } + uintptr_t original_off = untagged_stack_mte - buf_start; + if (original_off % 16 || original_off > buf_size) { + async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, + "dump_stack_history: invalid offset: %" PRIuPTR, original_off); + return; + } + + // The original_off is the next slot that would have been written, so the last + // slot that was written is the previous one. + for (uintptr_t idx = 16; idx <= buf_size; idx += 16) { + int64_t off = original_off - idx; + if (off < 0) off += buf_size; + uintptr_t pc, taggedfp; + memcpy(&pc, &(buf[off]), sizeof(pc)); + memcpy(&taggedfp, &(buf[off + sizeof(pc)]), sizeof(taggedfp)); + + if (pc == 0) break; + uintptr_t fp = untag_address(taggedfp); + uintptr_t tag = taggedfp >> 56; + + unwindstack::FrameData frame_data; + + if (nounwind) { + frame_data.pc = pc; + } else { + // +4 is to counteract the "pc adjustment" in BuildFrameFromPcOnly. + // BuildFrameFromPcOnly assumes we are unwinding, so it needs to correct for that + // the PC is the return address. That is not the case here. + // It doesn't really matter, because either should be in the correct function, but + // this is more correct (and consistent with the nounwind case). + frame_data = unwinder->BuildFrameFromPcOnly(pc); + frame_data.pc += 4; + frame_data.rel_pc += 4; + } + + StackHistoryBufferEntry* entry = shb_obj.add_entries(); + fill_in_backtrace_frame(entry->mutable_addr(), frame_data); + entry->set_fp(fp); + entry->set_tag(tag); + } +} + +static pid_t get_containing_thread(unwindstack::MapInfo* map_info, pid_t main_tid) { + if (map_info == nullptr) return 0; + + std::string name = map_info->name(); + if (name == "[stack]") { + return main_tid; + } + int tid; + if (sscanf(name.c_str(), "[anon:stack_and_tls:%d", &tid) != 1) { + return 0; + } + return tid; +} + +static std::optional maybe_stack_mte_cause( + Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder, const ThreadInfo& target_thread, + [[maybe_unused]] const std::map& threads, uint64_t fault_addr) { + unwindstack::Maps* maps = unwinder->GetMaps(); + auto map_info = maps->Find(untag_address(fault_addr)); + pid_t tid = get_containing_thread(map_info.get(), target_thread.tid); + if (!tid) { + return std::nullopt; + } + auto it = threads.find(tid); + if (it != threads.end()) { + StackHistoryBuffer* shb = tombstone->mutable_stack_history_buffer(); + shb->set_tid(tid); + dump_stack_history(unwinder, it->second.tls, *shb); + } else { + async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, + "dump_probable_cause: unknown target thread %d", tid); + } + return StringPrintf("stack tag-mismatch on thread %u", tid); +} + +#endif + static void dump_probable_cause(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder, - const ProcessInfo& process_info, const ThreadInfo& main_thread) { + const ProcessInfo& process_info, const ThreadInfo& target_thread, + [[maybe_unused]] const std::map& threads) { #if defined(USE_SCUDO) ScudoCrashData scudo_crash_data(unwinder->GetProcessMemory().get(), process_info); if (scudo_crash_data.CrashIsMine()) { @@ -203,13 +329,13 @@ static void dump_probable_cause(Tombstone* tombstone, unwindstack::AndroidUnwind #endif GwpAsanCrashData gwp_asan_crash_data(unwinder->GetProcessMemory().get(), process_info, - main_thread); + target_thread); if (gwp_asan_crash_data.CrashIsMine()) { gwp_asan_crash_data.AddCauseProtos(tombstone, unwinder); return; } - const siginfo *si = main_thread.siginfo; + const siginfo *si = target_thread.siginfo; auto fault_addr = reinterpret_cast(si->si_addr); unwindstack::Maps* maps = unwinder->GetMaps(); @@ -228,16 +354,27 @@ static void dump_probable_cause(Tombstone* tombstone, unwindstack::AndroidUnwind } else if (fault_addr == 0xffff0f60) { cause = "call to kuser_cmpxchg64"; } else { - cause = get_stack_overflow_cause(fault_addr, main_thread.registers->sp(), maps); + cause = get_stack_overflow_cause(fault_addr, target_thread.registers->sp(), maps); } } else if (si->si_signo == SIGSEGV && si->si_code == SEGV_ACCERR) { auto map_info = maps->Find(fault_addr); if (map_info != nullptr && map_info->flags() == PROT_EXEC) { cause = "execute-only (no-read) memory access error; likely due to data in .text."; + } else if (fault_addr == target_thread.registers->pc() && + map_info != nullptr && (map_info->flags() & PROT_EXEC) == 0) { + cause = "trying to execute non-executable memory."; } else { - cause = get_stack_overflow_cause(fault_addr, main_thread.registers->sp(), maps); + cause = get_stack_overflow_cause(fault_addr, target_thread.registers->sp(), maps); } - } else if (si->si_signo == SIGSYS && si->si_code == SYS_SECCOMP) { + } +#if defined(__aarch64__) && defined(SEGV_MTESERR) + else if (si->si_signo == SIGSEGV && si->si_code == SEGV_MTESERR) { + // If this was a heap MTE crash, it would have been handled by scudo. Checking whether it + // is a stack one. + cause = maybe_stack_mte_cause(tombstone, unwinder, target_thread, threads, fault_addr); + } +#endif + else if (si->si_signo == SIGSYS && si->si_code == SYS_SECCOMP) { cause = StringPrintf("seccomp prevented call to disallowed %s system call %d", ABI_STRING, si->si_syscall); } @@ -248,6 +385,46 @@ static void dump_probable_cause(Tombstone* tombstone, unwindstack::AndroidUnwind } } +static void dump_crash_details(Tombstone* tombstone, + std::shared_ptr& process_memory, + const ProcessInfo& process_info) { + uintptr_t address = process_info.crash_detail_page; + while (address) { + struct crash_detail_page_t page; + if (!process_memory->ReadFully(address, &page, sizeof(page))) { + async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to read crash detail page: %m"); + break; + } + if (page.used > kNumCrashDetails) { + async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "crash detail: page corrupted"); + break; + } + for (size_t i = 0; i < page.used; ++i) { + const crash_detail_t& crash_detail = page.crash_details[i]; + if (!crash_detail.data) { + continue; + } + std::string name(crash_detail.name_size, '\0'); + if (!process_memory->ReadFully(reinterpret_cast(crash_detail.name), name.data(), + crash_detail.name_size)) { + async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "crash detail: failed to read name: %m"); + continue; + } + std::string data(crash_detail.data_size, '\0'); + if (!process_memory->ReadFully(reinterpret_cast(crash_detail.data), data.data(), + crash_detail.data_size)) { + async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, + "crash detail: failed to read data for %s: %m", name.c_str()); + continue; + } + auto* proto_detail = tombstone->add_crash_details(); + proto_detail->set_name(name); + proto_detail->set_data(data); + } + address = reinterpret_cast(page.prev); + } +} + static void dump_abort_message(Tombstone* tombstone, std::shared_ptr& process_memory, const ProcessInfo& process_info) { @@ -289,7 +466,8 @@ static void dump_abort_message(Tombstone* tombstone, } msg.resize(index); - tombstone->set_abort_message(msg); + // Make sure only UTF8 characters are present since abort_message is a string. + tombstone->set_abort_message(oct_encode_non_ascii_printable(msg)); } static void dump_open_fds(Tombstone* tombstone, const OpenFilesList* open_files) { @@ -432,7 +610,8 @@ static void dump_thread_backtrace(std::vector& frames, T } static void dump_thread(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder, - const ThreadInfo& thread_info, bool memory_dump = false) { + const ThreadInfo& thread_info, bool memory_dump = false, + unwindstack::AndroidUnwinder* guest_unwinder = nullptr) { Thread thread; thread.set_id(thread_info.tid); @@ -459,6 +638,27 @@ static void dump_thread(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwi auto& threads = *tombstone->mutable_threads(); threads[thread_info.tid] = thread; + + if (guest_unwinder) { + if (!thread_info.guest_registers) { + async_safe_format_log(ANDROID_LOG_INFO, LOG_TAG, + "No guest state registers information for tid %d", thread_info.tid); + return; + } + Thread guest_thread; + unwindstack::AndroidUnwinderData guest_data; + guest_data.saved_initial_regs = std::make_optional>(); + if (guest_unwinder->Unwind(thread_info.guest_registers.get(), guest_data)) { + dump_thread_backtrace(guest_data.frames, guest_thread); + } else { + async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, + "Unwind guest state registers failed for tid %d: Error %s", + thread_info.tid, guest_data.GetErrorString().c_str()); + } + dump_registers(guest_unwinder, *guest_data.saved_initial_regs, guest_thread, memory_dump); + auto& guest_threads = *tombstone->mutable_guest_threads(); + guest_threads[thread_info.tid] = guest_thread; + } } static void dump_mappings(Tombstone* tombstone, unwindstack::Maps* maps, @@ -490,27 +690,48 @@ static void dump_mappings(Tombstone* tombstone, unwindstack::Maps* maps, } } +// This creates a fake log message that indicates an error occurred when +// reading the log. +static void add_error_log_msg(Tombstone* tombstone, const std::string&& error_msg) { + LogBuffer buffer; + buffer.set_name("ERROR"); + + LogMessage* log_msg = buffer.add_logs(); + log_msg->set_timestamp("00-00 00:00:00.000"); + log_msg->set_pid(0); + log_msg->set_tid(0); + log_msg->set_priority(ANDROID_LOG_ERROR); + log_msg->set_tag(""); + log_msg->set_message(error_msg); + + *tombstone->add_log_buffers() = std::move(buffer); + + async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "%s", error_msg.c_str()); +} + static void dump_log_file(Tombstone* tombstone, const char* logger, pid_t pid) { - logger_list* logger_list = - android_logger_list_open(android_name_to_log_id(logger), ANDROID_LOG_NONBLOCK, 0, pid); + logger_list* logger_list = android_logger_list_open(android_name_to_log_id(logger), + ANDROID_LOG_NONBLOCK, kMaxLogMessages, pid); + if (logger_list == nullptr) { + add_error_log_msg(tombstone, android::base::StringPrintf("Cannot open log file %s", logger)); + return; + } LogBuffer buffer; - while (true) { log_msg log_entry; ssize_t actual = android_logger_list_read(logger_list, &log_entry); - if (actual < 0) { if (actual == -EINTR) { // interrupted by signal, retry continue; } - if (actual == -EAGAIN) { - // non-blocking EOF; we're done - break; - } else { - break; + // Don't consider EAGAIN an error since this is a non-blocking call. + if (actual != -EAGAIN) { + add_error_log_msg(tombstone, android::base::StringPrintf("reading log %s failed (%s)", + logger, strerror(-actual))); } + break; } else if (actual == 0) { break; } @@ -554,7 +775,8 @@ static void dump_log_file(Tombstone* tombstone, const char* logger, pid_t pid) { log_msg->set_tid(log_entry.entry.tid); log_msg->set_priority(prio); log_msg->set_tag(tag); - log_msg->set_message(msg); + // Make sure only UTF8 characters are present since message is a string. + log_msg->set_message(oct_encode_non_ascii_printable(msg)); } while ((msg = nl)); } android_logger_list_free(logger_list); @@ -614,28 +836,35 @@ static void dump_tags_around_fault_addr(Signal* signal, const Tombstone& tombsto } void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::AndroidUnwinder* unwinder, - const std::map& threads, pid_t target_thread, - const ProcessInfo& process_info, const OpenFilesList* open_files) { + const std::map& threads, pid_t target_tid, + const ProcessInfo& process_info, const OpenFilesList* open_files, + const Architecture* guest_arch, + unwindstack::AndroidUnwinder* guest_unwinder) { Tombstone result; result.set_arch(get_arch()); + if (guest_arch != nullptr) { + result.set_guest_arch(*guest_arch); + } else { + result.set_guest_arch(Architecture::NONE); + } result.set_build_fingerprint(android::base::GetProperty("ro.build.fingerprint", "unknown")); result.set_revision(android::base::GetProperty("ro.revision", "unknown")); result.set_timestamp(get_timestamp()); - const ThreadInfo& main_thread = threads.at(target_thread); - result.set_pid(main_thread.pid); - result.set_tid(main_thread.tid); - result.set_uid(main_thread.uid); - result.set_selinux_label(main_thread.selinux_label); + const ThreadInfo& target_thread = threads.at(target_tid); + result.set_pid(target_thread.pid); + result.set_tid(target_thread.tid); + result.set_uid(target_thread.uid); + result.set_selinux_label(target_thread.selinux_label); // The main thread must have a valid siginfo. - CHECK(main_thread.siginfo != nullptr); + CHECK(target_thread.siginfo != nullptr); struct sysinfo si; sysinfo(&si); android::procinfo::ProcessInfo proc_info; std::string error; - if (android::procinfo::GetProcessInfo(main_thread.pid, &proc_info, &error)) { + if (android::procinfo::GetProcessInfo(target_thread.pid, &proc_info, &error)) { uint64_t starttime = proc_info.starttime / sysconf(_SC_CLK_TCK); result.set_process_uptime(si.uptime - starttime); } else { @@ -643,25 +872,28 @@ void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::AndroidUnwinder* error.c_str()); } + result.set_page_size(getpagesize()); + result.set_has_been_16kb_mode(android::base::GetBoolProperty("ro.misctrl.16kb_before", false)); + auto cmd_line = result.mutable_command_line(); - for (const auto& arg : main_thread.command_line) { + for (const auto& arg : target_thread.command_line) { *cmd_line->Add() = arg; } - if (!main_thread.siginfo) { + if (!target_thread.siginfo) { async_safe_fatal("siginfo missing"); } Signal sig; - sig.set_number(main_thread.signo); - sig.set_name(get_signame(main_thread.siginfo)); - sig.set_code(main_thread.siginfo->si_code); - sig.set_code_name(get_sigcode(main_thread.siginfo)); + sig.set_number(target_thread.signo); + sig.set_name(get_signame(target_thread.siginfo)); + sig.set_code(target_thread.siginfo->si_code); + sig.set_code_name(get_sigcode(target_thread.siginfo)); - if (signal_has_sender(main_thread.siginfo, main_thread.pid)) { + if (signal_has_sender(target_thread.siginfo, target_thread.pid)) { sig.set_has_sender(true); - sig.set_sender_uid(main_thread.siginfo->si_uid); - sig.set_sender_pid(main_thread.siginfo->si_pid); + sig.set_sender_uid(target_thread.siginfo->si_uid); + sig.set_sender_pid(target_thread.siginfo->si_pid); } if (process_info.has_fault_address) { @@ -674,29 +906,29 @@ void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::AndroidUnwinder* *result.mutable_signal_info() = sig; dump_abort_message(&result, unwinder->GetProcessMemory(), process_info); - - // Dump the main thread, but save the memory around the registers. - dump_thread(&result, unwinder, main_thread, /* memory_dump */ true); + dump_crash_details(&result, unwinder->GetProcessMemory(), process_info); + // Dump the target thread, but save the memory around the registers. + dump_thread(&result, unwinder, target_thread, /* memory_dump */ true, guest_unwinder); for (const auto& [tid, thread_info] : threads) { - if (tid != target_thread) { - dump_thread(&result, unwinder, thread_info); + if (tid != target_tid) { + dump_thread(&result, unwinder, thread_info, /* memory_dump */ false, guest_unwinder); } } - dump_probable_cause(&result, unwinder, process_info, main_thread); + dump_probable_cause(&result, unwinder, process_info, target_thread, threads); dump_mappings(&result, unwinder->GetMaps(), unwinder->GetProcessMemory()); // Only dump logs on debuggable devices. if (android::base::GetBoolProperty("ro.debuggable", false)) { // Get the thread that corresponds to the main pid of the process. - const ThreadInfo& thread = threads.at(main_thread.pid); + const ThreadInfo& thread = threads.at(target_thread.pid); // Do not attempt to dump logs of the logd process because the gathering // of logs can hang until a timeout occurs. if (thread.thread_name != "logd") { - dump_logcat(&result, main_thread.pid); + dump_logcat(&result, target_thread.pid); } } diff --git a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp index 8e6abdfa117e..11841b290d5f 100644 --- a/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp +++ b/debuggerd/libdebuggerd/tombstone_proto_to_text.cpp @@ -14,11 +14,15 @@ * limitations under the License. */ -#include +#include +#include +#include #include +#include #include +#include #include #include #include @@ -28,9 +32,8 @@ #include #include #include -#include -#include +#include "libdebuggerd/utility_host.h" #include "tombstone.pb.h" using android::base::StringAppendF; @@ -40,9 +43,24 @@ using android::base::StringPrintf; #define CBL(...) CB(true, __VA_ARGS__) #define CBS(...) CB(false, __VA_ARGS__) using CallbackType = std::function; +using SymbolizeCallbackType = std::function; -static const char* abi_string(const Tombstone& tombstone) { - switch (tombstone.arch()) { +#define DESCRIBE_FLAG(flag) \ + if (value & flag) { \ + desc += ", "; \ + desc += #flag; \ + value &= ~flag; \ + } + +static std::string describe_end(long value, std::string& desc) { + if (value) { + desc += StringPrintf(", unknown 0x%lx", value); + } + return desc.empty() ? "" : " (" + desc.substr(2) + ")"; +} + +static const char* abi_string(const Architecture& arch) { + switch (arch) { case Architecture::ARM32: return "arm"; case Architecture::ARM64: @@ -75,12 +93,21 @@ static int pointer_width(const Tombstone& tombstone) { } } +static uint64_t untag_address(Architecture arch, uint64_t addr) { + if (arch == Architecture::ARM64) { + return addr & ((1ULL << 56) - 1); + } + return addr; +} + static void print_thread_header(CallbackType callback, const Tombstone& tombstone, const Thread& thread, bool should_log) { const char* process_name = ""; if (!tombstone.command_line().empty()) { process_name = tombstone.command_line()[0].c_str(); CB(should_log, "Cmdline: %s", android::base::Join(tombstone.command_line(), " ").c_str()); + } else { + CB(should_log, "Cmdline: "); } CB(should_log, "pid: %d, tid: %d, name: %s >>> %s <<<", tombstone.pid(), thread.id(), thread.name().c_str(), process_name); @@ -136,7 +163,8 @@ static void print_thread_registers(CallbackType callback, const Tombstone& tombs break; default: - async_safe_fatal("unknown architecture"); + CBL("Unknown architecture %d printing thread registers", tombstone.arch()); + return; } for (const auto& reg : thread.registers()) { @@ -159,7 +187,8 @@ static void print_thread_registers(CallbackType callback, const Tombstone& tombs print_register_row(callback, word_size, special_row, should_log); } -static void print_backtrace(CallbackType callback, const Tombstone& tombstone, +static void print_backtrace(CallbackType callback, SymbolizeCallbackType symbolize, + const Tombstone& tombstone, const google::protobuf::RepeatedPtrField& backtrace, bool should_log) { int index = 0; @@ -184,11 +213,14 @@ static void print_backtrace(CallbackType callback, const Tombstone& tombstone, } line += function + build_id; CB(should_log, "%s", line.c_str()); + + symbolize(frame); } } -static void print_thread_backtrace(CallbackType callback, const Tombstone& tombstone, - const Thread& thread, bool should_log) { +static void print_thread_backtrace(CallbackType callback, SymbolizeCallbackType symbolize, + const Tombstone& tombstone, const Thread& thread, + bool should_log) { CBS(""); CB(should_log, "%d total frames", thread.current_backtrace().size()); CB(should_log, "backtrace:"); @@ -196,7 +228,7 @@ static void print_thread_backtrace(CallbackType callback, const Tombstone& tombs CB(should_log, " NOTE: %s", android::base::Join(thread.backtrace_note(), "\n NOTE: ").c_str()); } - print_backtrace(callback, tombstone, thread.current_backtrace(), should_log); + print_backtrace(callback, symbolize, tombstone, thread.current_backtrace(), should_log); } static void print_thread_memory_dump(CallbackType callback, const Tombstone& tombstone, @@ -249,10 +281,11 @@ static void print_thread_memory_dump(CallbackType callback, const Tombstone& tom } } -static void print_thread(CallbackType callback, const Tombstone& tombstone, const Thread& thread) { +static void print_thread(CallbackType callback, SymbolizeCallbackType symbolize, + const Tombstone& tombstone, const Thread& thread) { print_thread_header(callback, tombstone, thread, false); print_thread_registers(callback, tombstone, thread, false); - print_thread_backtrace(callback, tombstone, thread, false); + print_thread_backtrace(callback, symbolize, tombstone, thread, false); print_thread_memory_dump(callback, tombstone, thread); } @@ -280,7 +313,8 @@ static void print_tag_dump(CallbackType callback, const Tombstone& tombstone) { size_t tag_index = 0; size_t num_tags = tags.length(); - uintptr_t fault_granule = untag_address(signal.fault_address()) & ~(kTagGranuleSize - 1); + uintptr_t fault_granule = + untag_address(tombstone.arch(), signal.fault_address()) & ~(kTagGranuleSize - 1); for (size_t row = 0; tag_index < num_tags; ++row) { uintptr_t row_addr = (memory_dump.begin_address() + row * kNumTagColumns * kTagGranuleSize) & kRowStartMask; @@ -328,7 +362,7 @@ static void print_memory_maps(CallbackType callback, const Tombstone& tombstone) const Signal& signal_info = tombstone.signal_info(); bool has_fault_address = signal_info.has_fault_address(); - uint64_t fault_address = untag_address(signal_info.fault_address()); + uint64_t fault_address = untag_address(tombstone.arch(), signal_info.fault_address()); bool preamble_printed = false; bool printed_fault_address_marker = false; for (const auto& map : tombstone.memory_mappings()) { @@ -386,8 +420,8 @@ static void print_memory_maps(CallbackType callback, const Tombstone& tombstone) } } -static void print_main_thread(CallbackType callback, const Tombstone& tombstone, - const Thread& thread) { +static void print_main_thread(CallbackType callback, SymbolizeCallbackType symbolize, + const Tombstone& tombstone, const Thread& thread) { print_thread_header(callback, tombstone, thread, true); const Signal& signal_info = tombstone.signal_info(); @@ -429,13 +463,19 @@ static void print_main_thread(CallbackType callback, const Tombstone& tombstone, CBL("Abort message: '%s'", tombstone.abort_message().c_str()); } + for (const auto& crash_detail : tombstone.crash_details()) { + std::string oct_encoded_name = oct_encode_non_printable(crash_detail.name()); + std::string oct_encoded_data = oct_encode_non_printable(crash_detail.data()); + CBL("Extra crash detail: %s: '%s'", oct_encoded_name.c_str(), oct_encoded_data.c_str()); + } + print_thread_registers(callback, tombstone, thread, true); if (is_async_mte_crash) { CBL("Note: This crash is a delayed async MTE crash. Memory corruption has occurred"); CBL(" in this process. The stack trace below is the first system call or context"); CBL(" switch that was executed after the memory corruption happened."); } - print_thread_backtrace(callback, tombstone, thread, true); + print_thread_backtrace(callback, symbolize, tombstone, thread, true); if (tombstone.causes_size() > 1) { CBS(""); @@ -443,6 +483,19 @@ static void print_main_thread(CallbackType callback, const Tombstone& tombstone, "order of likelihood."); } + if (tombstone.has_stack_history_buffer()) { + for (const StackHistoryBufferEntry& shbe : tombstone.stack_history_buffer().entries()) { + std::string stack_record_str = StringPrintf( + "stack_record fp:0x%" PRIx64 " tag:0x%" PRIx64 " pc:%s+0x%" PRIx64, shbe.fp(), shbe.tag(), + shbe.addr().file_name().c_str(), shbe.addr().rel_pc()); + if (!shbe.addr().build_id().empty()) { + StringAppendF(&stack_record_str, " (BuildId: %s)", shbe.addr().build_id().c_str()); + } + + CBL("%s", stack_record_str.c_str()); + } + } + for (const Cause& cause : tombstone.causes()) { if (tombstone.causes_size() > 1) { CBS(""); @@ -455,13 +508,13 @@ static void print_main_thread(CallbackType callback, const Tombstone& tombstone, if (heap_object.deallocation_backtrace_size() != 0) { CBS(""); CBL("deallocated by thread %" PRIu64 ":", heap_object.deallocation_tid()); - print_backtrace(callback, tombstone, heap_object.deallocation_backtrace(), true); + print_backtrace(callback, symbolize, tombstone, heap_object.deallocation_backtrace(), true); } if (heap_object.allocation_backtrace_size() != 0) { CBS(""); CBL("allocated by thread %" PRIu64 ":", heap_object.allocation_tid()); - print_backtrace(callback, tombstone, heap_object.allocation_backtrace(), true); + print_backtrace(callback, symbolize, tombstone, heap_object.allocation_backtrace(), true); } } } @@ -510,14 +563,40 @@ void print_logs(CallbackType callback, const Tombstone& tombstone, int tail) { } } -bool tombstone_proto_to_text(const Tombstone& tombstone, CallbackType callback) { +static void print_guest_thread(CallbackType callback, SymbolizeCallbackType symbolize, + const Tombstone& tombstone, const Thread& guest_thread, pid_t tid, + bool should_log) { + CBS("--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---"); + CBS("Guest thread information for tid: %d", tid); + print_thread_registers(callback, tombstone, guest_thread, should_log); + + CBS(""); + CB(true, "%d total frames", guest_thread.current_backtrace().size()); + CB(true, "backtrace:"); + print_backtrace(callback, symbolize, tombstone, guest_thread.current_backtrace(), should_log); + + print_thread_memory_dump(callback, tombstone, guest_thread); +} + +bool tombstone_proto_to_text(const Tombstone& tombstone, CallbackType callback, + SymbolizeCallbackType symbolize) { CBL("*** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***"); CBL("Build fingerprint: '%s'", tombstone.build_fingerprint().c_str()); CBL("Revision: '%s'", tombstone.revision().c_str()); - CBL("ABI: '%s'", abi_string(tombstone)); + CBL("ABI: '%s'", abi_string(tombstone.arch())); + if (tombstone.guest_arch() != Architecture::NONE) { + CBL("Guest architecture: '%s'", abi_string(tombstone.guest_arch())); + } CBL("Timestamp: %s", tombstone.timestamp().c_str()); CBL("Process uptime: %ds", tombstone.process_uptime()); + // only print this info if the page size is not 4k or has been in 16k mode + if (tombstone.page_size() != 4096) { + CBL("Page size: %d bytes", tombstone.page_size()); + } else if (tombstone.has_been_16kb_mode()) { + CBL("Has been in 16 KB mode before: yes"); + } + // Process header const auto& threads = tombstone.threads(); auto main_thread_it = threads.find(tombstone.tid()); @@ -528,10 +607,17 @@ bool tombstone_proto_to_text(const Tombstone& tombstone, CallbackType callback) const auto& main_thread = main_thread_it->second; - print_main_thread(callback, tombstone, main_thread); + print_main_thread(callback, symbolize, tombstone, main_thread); print_logs(callback, tombstone, 50); + const auto& guest_threads = tombstone.guest_threads(); + auto main_guest_thread_it = guest_threads.find(tombstone.tid()); + if (main_guest_thread_it != threads.end()) { + print_guest_thread(callback, symbolize, tombstone, main_guest_thread_it->second, + tombstone.tid(), true); + } + // protobuf's map is unordered, so sort the keys first. std::set thread_ids; for (const auto& [tid, _] : threads) { @@ -542,7 +628,11 @@ bool tombstone_proto_to_text(const Tombstone& tombstone, CallbackType callback) for (const auto& tid : thread_ids) { CBS("--- --- --- --- --- --- --- --- --- --- --- --- --- --- --- ---"); - print_thread(callback, tombstone, threads.find(tid)->second); + print_thread(callback, symbolize, tombstone, threads.find(tid)->second); + auto guest_thread_it = guest_threads.find(tid); + if (guest_thread_it != guest_threads.end()) { + print_guest_thread(callback, symbolize, tombstone, guest_thread_it->second, tid, false); + } } if (tombstone.open_fds().size() > 0) { diff --git a/debuggerd/libdebuggerd/utility.cpp b/debuggerd/libdebuggerd/utility.cpp index d71fc6c7f14c..b5a93b7db503 100644 --- a/debuggerd/libdebuggerd/utility.cpp +++ b/debuggerd/libdebuggerd/utility.cpp @@ -17,6 +17,7 @@ #define LOG_TAG "DEBUG" #include "libdebuggerd/utility.h" +#include "libdebuggerd/utility_host.h" #include #include @@ -382,8 +383,10 @@ const char* get_sigcode(const siginfo_t* si) { return "SEGV_MTEAERR"; case SEGV_MTESERR: return "SEGV_MTESERR"; + case SEGV_CPERR: + return "SEGV_CPERR"; } - static_assert(NSIGSEGV == SEGV_MTESERR, "missing SEGV_* si_code"); + static_assert(NSIGSEGV == SEGV_CPERR, "missing SEGV_* si_code"); break; case SIGSYS: switch (si->si_code) { @@ -443,42 +446,6 @@ const char* get_sigcode(const siginfo_t* si) { return "?"; } -#define DESCRIBE_FLAG(flag) \ - if (value & flag) { \ - desc += ", "; \ - desc += #flag; \ - value &= ~flag; \ - } - -static std::string describe_end(long value, std::string& desc) { - if (value) { - desc += StringPrintf(", unknown 0x%lx", value); - } - return desc.empty() ? "" : " (" + desc.substr(2) + ")"; -} - -std::string describe_tagged_addr_ctrl(long value) { - std::string desc; - DESCRIBE_FLAG(PR_TAGGED_ADDR_ENABLE); - DESCRIBE_FLAG(PR_MTE_TCF_SYNC); - DESCRIBE_FLAG(PR_MTE_TCF_ASYNC); - if (value & PR_MTE_TAG_MASK) { - desc += StringPrintf(", mask 0x%04lx", (value & PR_MTE_TAG_MASK) >> PR_MTE_TAG_SHIFT); - value &= ~PR_MTE_TAG_MASK; - } - return describe_end(value, desc); -} - -std::string describe_pac_enabled_keys(long value) { - std::string desc; - DESCRIBE_FLAG(PR_PAC_APIAKEY); - DESCRIBE_FLAG(PR_PAC_APIBKEY); - DESCRIBE_FLAG(PR_PAC_APDAKEY); - DESCRIBE_FLAG(PR_PAC_APDBKEY); - DESCRIBE_FLAG(PR_PAC_APGAKEY); - return describe_end(value, desc); -} - void log_backtrace(log_t* log, unwindstack::AndroidUnwinder* unwinder, unwindstack::AndroidUnwinderData& data, const char* prefix) { std::set unreadable_elf_files; diff --git a/debuggerd/libdebuggerd/utility_host.cpp b/debuggerd/libdebuggerd/utility_host.cpp new file mode 100644 index 000000000000..d87f4fb8e1a7 --- /dev/null +++ b/debuggerd/libdebuggerd/utility_host.cpp @@ -0,0 +1,133 @@ +/* + * Copyright 2024, The Android Open Source Project + * + * 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 + * + * http://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. + */ + +#include "libdebuggerd/utility_host.h" + +#include +#include + +#include +#include +#include + +#include + +using android::base::StringPrintf; + +#ifndef PR_MTE_TAG_SHIFT +#define PR_MTE_TAG_SHIFT 3 +#endif + +#ifndef PR_MTE_TAG_MASK +#define PR_MTE_TAG_MASK (0xffffUL << PR_MTE_TAG_SHIFT) +#endif + +#ifndef PR_MTE_TCF_ASYNC +#define PR_MTE_TCF_ASYNC (1UL << 2) +#endif + +#ifndef PR_MTE_TCF_SYNC +#define PR_MTE_TCF_SYNC (1UL << 1) +#endif + +#ifndef PR_PAC_APIAKEY +#define PR_PAC_APIAKEY (1UL << 0) +#endif + +#ifndef PR_PAC_APIBKEY +#define PR_PAC_APIBKEY (1UL << 1) +#endif + +#ifndef PR_PAC_APDAKEY +#define PR_PAC_APDAKEY (1UL << 2) +#endif + +#ifndef PR_PAC_APDBKEY +#define PR_PAC_APDBKEY (1UL << 3) +#endif + +#ifndef PR_PAC_APGAKEY +#define PR_PAC_APGAKEY (1UL << 4) +#endif + +#ifndef PR_TAGGED_ADDR_ENABLE +#define PR_TAGGED_ADDR_ENABLE (1UL << 0) +#endif + +#define DESCRIBE_FLAG(flag) \ + if (value & flag) { \ + desc += ", "; \ + desc += #flag; \ + value &= ~flag; \ + } + +static std::string describe_end(long value, std::string& desc) { + if (value) { + desc += StringPrintf(", unknown 0x%lx", value); + } + return desc.empty() ? "" : " (" + desc.substr(2) + ")"; +} + +std::string describe_tagged_addr_ctrl(long value) { + std::string desc; + DESCRIBE_FLAG(PR_TAGGED_ADDR_ENABLE); + DESCRIBE_FLAG(PR_MTE_TCF_SYNC); + DESCRIBE_FLAG(PR_MTE_TCF_ASYNC); + if (value & PR_MTE_TAG_MASK) { + desc += StringPrintf(", mask 0x%04lx", (value & PR_MTE_TAG_MASK) >> PR_MTE_TAG_SHIFT); + value &= ~PR_MTE_TAG_MASK; + } + return describe_end(value, desc); +} + +std::string describe_pac_enabled_keys(long value) { + std::string desc; + DESCRIBE_FLAG(PR_PAC_APIAKEY); + DESCRIBE_FLAG(PR_PAC_APIBKEY); + DESCRIBE_FLAG(PR_PAC_APDAKEY); + DESCRIBE_FLAG(PR_PAC_APDBKEY); + DESCRIBE_FLAG(PR_PAC_APGAKEY); + return describe_end(value, desc); +} + +static std::string oct_encode(const std::string& data, bool (*should_encode_func)(int)) { + std::string oct_encoded; + oct_encoded.reserve(data.size()); + + // N.B. the unsigned here is very important, otherwise e.g. \255 would render as + // \-123 (and overflow our buffer). + for (unsigned char c : data) { + if (should_encode_func(c)) { + std::string oct_digits("\\\0\0\0", 4); + // char is encodable in 3 oct digits + static_assert(std::numeric_limits::max() <= 8 * 8 * 8); + auto [ptr, ec] = std::to_chars(oct_digits.data() + 1, oct_digits.data() + 4, c, 8); + oct_digits.resize(ptr - oct_digits.data()); + oct_encoded += oct_digits; + } else { + oct_encoded += c; + } + } + return oct_encoded; +} + +std::string oct_encode_non_ascii_printable(const std::string& data) { + return oct_encode(data, [](int c) { return !isgraph(c) && !isspace(c); }); +} + +std::string oct_encode_non_printable(const std::string& data) { + return oct_encode(data, [](int c) { return !isprint(c); }); +} diff --git a/debuggerd/pbtombstone.cpp b/debuggerd/pbtombstone.cpp index 7527e31e1ffd..0902b386f50e 100644 --- a/debuggerd/pbtombstone.cpp +++ b/debuggerd/pbtombstone.cpp @@ -16,32 +16,55 @@ #include #include +#include #include #include +#include +#include + #include -#include +#include #include "tombstone.pb.h" +#include "tombstone_symbolize.h" using android::base::unique_fd; [[noreturn]] void usage(bool error) { - fprintf(stderr, "usage: pbtombstone TOMBSTONE.PB\n"); + fprintf(stderr, "usage: pbtombstone [OPTION] TOMBSTONE.PB\n"); fprintf(stderr, "Convert a protobuf tombstone to text.\n"); + fprintf(stderr, "Arguments:\n"); + fprintf(stderr, " -h, --help print this message\n"); + fprintf(stderr, " --debug-file-directory PATH specify the path to a symbols directory\n"); exit(error); } -int main(int argc, const char* argv[]) { - if (argc != 2) { - usage(true); +int main(int argc, char* argv[]) { + std::vector debug_file_directories; + static struct option long_options[] = { + {"debug-file-directory", required_argument, 0, 0}, + {"help", no_argument, 0, 'h'}, + {}, + }; + int c; + while ((c = getopt_long(argc, argv, "h", long_options, 0)) != -1) { + switch (c) { + case 0: + debug_file_directories.push_back(optarg); + break; + + case 'h': + usage(false); + break; + } } - if (strcmp("-h", argv[1]) == 0 || strcmp("--help", argv[1]) == 0) { - usage(false); + if (optind != argc-1) { + usage(true); } - unique_fd fd(open(argv[1], O_RDONLY | O_CLOEXEC)); + unique_fd fd(open(argv[optind], O_RDONLY | O_CLOEXEC)); if (fd == -1) { err(1, "failed to open tombstone '%s'", argv[1]); } @@ -51,8 +74,11 @@ int main(int argc, const char* argv[]) { err(1, "failed to parse tombstone"); } + Symbolizer sym; + sym.Start(debug_file_directories); bool result = tombstone_proto_to_text( - tombstone, [](const std::string& line, bool) { printf("%s\n", line.c_str()); }); + tombstone, [](const std::string& line, bool) { printf("%s\n", line.c_str()); }, + [&](const BacktraceFrame& frame) { symbolize_backtrace_frame(frame, sym); }); if (!result) { errx(1, "tombstone was malformed"); diff --git a/debuggerd/proto/Android.bp b/debuggerd/proto/Android.bp index 73cf5737d12e..70deb3cdda48 100644 --- a/debuggerd/proto/Android.bp +++ b/debuggerd/proto/Android.bp @@ -35,6 +35,21 @@ cc_library_static { "com.android.runtime", ], + ramdisk_available: true, recovery_available: true, vendor_ramdisk_available: true, + host_supported: true, +} + +java_library_static { + name: "libtombstone_proto_java", + proto: { + type: "lite", + }, + srcs: [ + "tombstone.proto", + ], + jarjar_rules: "jarjar-rules.txt", + sdk_version: "current", + static_libs: ["libprotobuf-java-lite"], } diff --git a/debuggerd/proto/jarjar-rules.txt b/debuggerd/proto/jarjar-rules.txt new file mode 100644 index 000000000000..66878a9489c1 --- /dev/null +++ b/debuggerd/proto/jarjar-rules.txt @@ -0,0 +1 @@ +rule com.google.protobuf.** com.android.server.os.protobuf.@1 diff --git a/debuggerd/proto/tombstone.proto b/debuggerd/proto/tombstone.proto index 49865a2bb75f..9deeeec9e185 100644 --- a/debuggerd/proto/tombstone.proto +++ b/debuggerd/proto/tombstone.proto @@ -15,8 +15,40 @@ option java_outer_classname = "TombstoneProtos"; // NOTE TO OEMS: // If you add custom fields to this proto, do not use numbers in the reserved range. +// NOTE TO CONSUMERS: +// With proto3 -- unlike proto2 -- HasValue is unreliable for any field +// where the default value for that type is also a valid value for the field. +// This means, for example, that a boolean that is false or an integer that +// is zero will appear to be missing --- but because they're not actually +// marked as `optional` in this schema, consumers should just use values +// without first checking whether or not they're "present". +// https://protobuf.dev/programming-guides/proto3/#default + +message CrashDetail { + bytes name = 1; + bytes data = 2; + + reserved 3 to 999; +} + +message StackHistoryBufferEntry { + BacktraceFrame addr = 1; + uint64 fp = 2; + uint64 tag = 3; + + reserved 4 to 999; +} + +message StackHistoryBuffer { + uint64 tid = 1; + repeated StackHistoryBufferEntry entries = 2; + + reserved 3 to 999; +} + message Tombstone { Architecture arch = 1; + Architecture guest_arch = 24; string build_fingerprint = 2; string revision = 3; string timestamp = 4; @@ -33,14 +65,21 @@ message Tombstone { Signal signal_info = 10; string abort_message = 14; + repeated CrashDetail crash_details = 21; repeated Cause causes = 15; map threads = 16; + map guest_threads = 25; repeated MemoryMapping memory_mappings = 17; repeated LogBuffer log_buffers = 18; repeated FD open_fds = 19; - reserved 21 to 999; + uint32 page_size = 22; + bool has_been_16kb_mode = 23; + + StackHistoryBuffer stack_history_buffer = 26; + + reserved 27 to 999; } enum Architecture { @@ -49,8 +88,9 @@ enum Architecture { X86 = 2; X86_64 = 3; RISCV64 = 4; + NONE = 5; - reserved 5 to 999; + reserved 6 to 999; } message Signal { diff --git a/debuggerd/protocol.h b/debuggerd/protocol.h index b60cf5bb6295..9af7377df802 100644 --- a/debuggerd/protocol.h +++ b/debuggerd/protocol.h @@ -65,7 +65,7 @@ struct InterceptRequest { }; enum class InterceptStatus : uint8_t { - // Returned when an intercept of a different type has already been + // Returned when an intercept of the same type has already been // registered (and is active) for a given PID. kFailedAlreadyRegistered, // Returned in all other failure cases. @@ -99,7 +99,9 @@ struct __attribute__((__packed__)) CrashInfoDataDynamic : public CrashInfoDataSt uintptr_t scudo_region_info; uintptr_t scudo_ring_buffer; size_t scudo_ring_buffer_size; - bool recoverable_gwp_asan_crash; + size_t scudo_stack_depot_size; + bool recoverable_crash; + uintptr_t crash_detail_page; }; struct __attribute__((__packed__)) CrashInfo { diff --git a/debuggerd/rust/tombstoned_client/Android.bp b/debuggerd/rust/tombstoned_client/Android.bp index 2007f39343c6..bf19bb779eff 100644 --- a/debuggerd/rust/tombstoned_client/Android.bp +++ b/debuggerd/rust/tombstoned_client/Android.bp @@ -8,7 +8,7 @@ cc_library_static { "wrapper.cpp", ], generated_sources: [ - "libtombstoned_client_rust_bridge_code" + "libtombstoned_client_rust_bridge_code", ], header_libs: [ "libbase_headers", diff --git a/debuggerd/rust/tombstoned_client/src/lib.rs b/debuggerd/rust/tombstoned_client/src/lib.rs index 5c8abef2cdb8..d1b5e696e481 100644 --- a/debuggerd/rust/tombstoned_client/src/lib.rs +++ b/debuggerd/rust/tombstoned_client/src/lib.rs @@ -39,20 +39,26 @@ pub struct TombstonedConnection { } impl TombstonedConnection { + /// # Safety + /// + /// The file descriptors must be valid and open. unsafe fn from_raw_fds( tombstoned_socket: RawFd, text_output_fd: RawFd, proto_output_fd: RawFd, ) -> Self { Self { - tombstoned_socket: File::from_raw_fd(tombstoned_socket), + // SAFETY: The caller guarantees that the file descriptor is valid and open. + tombstoned_socket: unsafe { File::from_raw_fd(tombstoned_socket) }, text_output: if text_output_fd >= 0 { - Some(File::from_raw_fd(text_output_fd)) + // SAFETY: The caller guarantees that the file descriptor is valid and open. + Some(unsafe { File::from_raw_fd(text_output_fd) }) } else { None }, proto_output: if proto_output_fd >= 0 { - Some(File::from_raw_fd(proto_output_fd)) + // SAFETY: The caller guarantees that the file descriptor is valid and open. + Some(unsafe { File::from_raw_fd(proto_output_fd) }) } else { None }, @@ -71,6 +77,8 @@ impl TombstonedConnection { &mut proto_output_fd, dump_type, ) { + // SAFETY: If tombstoned_connect_files returns successfully then they file descriptors + // are valid and open. Ok(unsafe { Self::from_raw_fds(tombstoned_socket, text_output_fd, proto_output_fd) }) } else { Err(Error) @@ -146,8 +154,6 @@ mod tests { .write_all(b"test data") .expect("Failed to write to text output FD."); - connection - .notify_completion() - .expect("Failed to notify completion."); + connection.notify_completion().expect("Failed to notify completion."); } } diff --git a/debuggerd/seccomp_policy/crash_dump.arm.policy b/debuggerd/seccomp_policy/crash_dump.arm.policy index 8fd03c4273ce..a70ab203d257 100644 --- a/debuggerd/seccomp_policy/crash_dump.arm.policy +++ b/debuggerd/seccomp_policy/crash_dump.arm.policy @@ -20,6 +20,7 @@ getdents64: 1 faccessat: 1 recvmsg: 1 recvfrom: 1 +setsockopt: 1 sysinfo: 1 process_vm_readv: 1 tgkill: 1 diff --git a/debuggerd/seccomp_policy/crash_dump.arm64.policy b/debuggerd/seccomp_policy/crash_dump.arm64.policy index 8241f0ee443b..c5d10d66b8ca 100644 --- a/debuggerd/seccomp_policy/crash_dump.arm64.policy +++ b/debuggerd/seccomp_policy/crash_dump.arm64.policy @@ -19,6 +19,7 @@ getdents64: 1 faccessat: 1 recvmsg: 1 recvfrom: 1 +setsockopt: 1 sysinfo: 1 process_vm_readv: 1 tgkill: 1 @@ -27,11 +28,11 @@ rt_sigaction: 1 rt_tgsigqueueinfo: 1 prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41 || arg0 == PR_PAC_RESET_KEYS || arg0 == 56 || arg0 == 61 madvise: 1 -mprotect: arg2 in 0x1|0x2 +mprotect: arg2 in 0x1|0x2|0x20 munmap: 1 getuid: 1 fstat: 1 -mmap: arg2 in 0x1|0x2 +mmap: arg2 in 0x1|0x2|0x20 geteuid: 1 getgid: 1 getegid: 1 diff --git a/debuggerd/seccomp_policy/crash_dump.policy.def b/debuggerd/seccomp_policy/crash_dump.policy.def index 0cb8e081dfff..dc751da6be5f 100644 --- a/debuggerd/seccomp_policy/crash_dump.policy.def +++ b/debuggerd/seccomp_policy/crash_dump.policy.def @@ -25,6 +25,7 @@ getdents64: 1 faccessat: 1 recvmsg: 1 recvfrom: 1 +setsockopt: 1 sysinfo: 1 process_vm_readv: 1 @@ -52,20 +53,29 @@ prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == PR_SET_VMA #if 0 libminijail on vendor partitions older than P does not have constants from . -Define the values of PROT_READ and PROT_WRITE ourselves to maintain backwards compatibility. +Define values for PROT_READ, PROT_WRITE and PROT_MTE ourselves to maintain backwards compatibility. #else #define PROT_READ 0x1 #define PROT_WRITE 0x2 +#define PROT_MTE 0x20 #endif madvise: 1 +#if defined(__aarch64__) +mprotect: arg2 in PROT_READ|PROT_WRITE|PROT_MTE +#else mprotect: arg2 in PROT_READ|PROT_WRITE +#endif munmap: 1 #if defined(__LP64__) getuid: 1 fstat: 1 +#if defined(__aarch64__) +mmap: arg2 in PROT_READ|PROT_WRITE|PROT_MTE +#else mmap: arg2 in PROT_READ|PROT_WRITE +#endif #else getuid32: 1 fstat64: 1 diff --git a/debuggerd/seccomp_policy/crash_dump.riscv64.policy b/debuggerd/seccomp_policy/crash_dump.riscv64.policy index 21887abe0799..94a56772a672 100644 --- a/debuggerd/seccomp_policy/crash_dump.riscv64.policy +++ b/debuggerd/seccomp_policy/crash_dump.riscv64.policy @@ -19,12 +19,14 @@ getdents64: 1 faccessat: 1 recvmsg: 1 recvfrom: 1 +setsockopt: 1 +sysinfo: 1 process_vm_readv: 1 tgkill: 1 rt_sigprocmask: 1 rt_sigaction: 1 rt_tgsigqueueinfo: 1 -prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41 || arg0 == PR_PAC_RESET_KEYS +prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41 madvise: 1 mprotect: arg2 in 0x1|0x2 munmap: 1 diff --git a/debuggerd/seccomp_policy/crash_dump.x86.policy b/debuggerd/seccomp_policy/crash_dump.x86.policy index 8fd03c4273ce..a70ab203d257 100644 --- a/debuggerd/seccomp_policy/crash_dump.x86.policy +++ b/debuggerd/seccomp_policy/crash_dump.x86.policy @@ -20,6 +20,7 @@ getdents64: 1 faccessat: 1 recvmsg: 1 recvfrom: 1 +setsockopt: 1 sysinfo: 1 process_vm_readv: 1 tgkill: 1 diff --git a/debuggerd/seccomp_policy/crash_dump.x86_64.policy b/debuggerd/seccomp_policy/crash_dump.x86_64.policy index 281e231b0135..94a56772a672 100644 --- a/debuggerd/seccomp_policy/crash_dump.x86_64.policy +++ b/debuggerd/seccomp_policy/crash_dump.x86_64.policy @@ -19,6 +19,7 @@ getdents64: 1 faccessat: 1 recvmsg: 1 recvfrom: 1 +setsockopt: 1 sysinfo: 1 process_vm_readv: 1 tgkill: 1 diff --git a/debuggerd/test_permissive_mte/Android.bp b/debuggerd/test_permissive_mte/Android.bp index d3f7520649a3..4403b8a94c67 100644 --- a/debuggerd/test_permissive_mte/Android.bp +++ b/debuggerd/test_permissive_mte/Android.bp @@ -17,23 +17,28 @@ package { } cc_binary { - name: "mte_crash", - tidy: false, - srcs: ["mte_crash.cpp"], - sanitize: { - memtag_heap: true, - diag: { - memtag_heap: true, + name: "mte_crash", + srcs: ["mte_crash.cpp"], + sanitize: { + memtag_heap: true, + diag: { + memtag_heap: true, + }, }, - }, } java_test_host { name: "permissive_mte_test", libs: ["tradefed"], - static_libs: ["frameworks-base-hostutils", "cts-install-lib-host"], - srcs: ["src/**/PermissiveMteTest.java", ":libtombstone_proto-src"], - data: [":mte_crash"], + static_libs: [ + "frameworks-base-hostutils", + "cts-install-lib-host", + ], + srcs: [ + "src/**/PermissiveMteTest.java", + ":libtombstone_proto-src", + ], + device_first_data: [":mte_crash"], test_config: "AndroidTest.xml", test_suites: ["general-tests"], } diff --git a/debuggerd/test_permissive_mte/AndroidTest.xml b/debuggerd/test_permissive_mte/AndroidTest.xml index bd3d0182c67a..db5f5b81296c 100644 --- a/debuggerd/test_permissive_mte/AndroidTest.xml +++ b/debuggerd/test_permissive_mte/AndroidTest.xml @@ -17,8 +17,6 @@