Merge "Read data set by android_add_crash_detail into tombstone." into main

This commit is contained in:
Florian Mayer 2024-02-14 17:06:07 +00:00 committed by Gerrit Code Review
commit 75800c9c3a
10 changed files with 283 additions and 2 deletions

View file

@ -322,6 +322,7 @@ static void ReadCrashInfo(unique_fd& fd, siginfo_t* siginfo,
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;
process_info->crash_detail_page = crash_info->data.d.crash_detail_page;
FALLTHROUGH_INTENDED;
case 1:
case 2:

View file

@ -939,6 +939,187 @@ 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_register_crash_detail(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_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_register_crash_detail("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_unregister_crash_detail(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_unregister_crash_detail(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;

View file

@ -397,6 +397,7 @@ static int debuggerd_dispatch_pseudothread(void* arg) {
ASSERT_SAME_OFFSET(scudo_ring_buffer_size, scudo_ring_buffer_size);
ASSERT_SAME_OFFSET(scudo_stack_depot_size, scudo_stack_depot_size);
ASSERT_SAME_OFFSET(recoverable_gwp_asan_crash, recoverable_gwp_asan_crash);
ASSERT_SAME_OFFSET(crash_detail_page, crash_detail_page);
#undef ASSERT_SAME_OFFSET
iovs[3] = {.iov_base = &thread_info->process_info,

View file

@ -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 {
@ -46,6 +48,7 @@ struct __attribute__((packed)) debugger_process_info {
size_t scudo_ring_buffer_size;
size_t scudo_stack_depot_size;
bool recoverable_gwp_asan_crash;
struct crash_detail_page_t* crash_detail_page;
};
// GWP-ASan calbacks to support the recoverable mode. Separate from the

View file

@ -56,4 +56,5 @@ struct ProcessInfo {
bool has_fault_address = false;
uintptr_t untagged_fault_address = 0;
uintptr_t maybe_tagged_fault_address = 0;
uintptr_t crash_detail_page = 0;
};

View file

@ -118,3 +118,19 @@ TEST_F(TombstoneProtoToTextTest, pac_enabled_keys) {
"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')");
}

View file

@ -48,8 +48,10 @@
#include <android-base/unique_fd.h>
#include <android/log.h>
#include <android/set_abort_message.h>
#include <bionic/macros.h>
#include <bionic/reserved_signals.h>
#include <bionic/set_abort_message_internal.h>
#include <log/log.h>
#include <log/log_read.h>
#include <log/logprint.h>
@ -251,6 +253,46 @@ static void dump_probable_cause(Tombstone* tombstone, unwindstack::AndroidUnwind
}
}
static void dump_crash_details(Tombstone* tombstone,
std::shared_ptr<unwindstack::Memory>& 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<uintptr_t>(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<uintptr_t>(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<uintptr_t>(page.prev);
}
}
static void dump_abort_message(Tombstone* tombstone,
std::shared_ptr<unwindstack::Memory>& process_memory,
const ProcessInfo& process_info) {
@ -698,7 +740,7 @@ void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::AndroidUnwinder*
*result.mutable_signal_info() = sig;
dump_abort_message(&result, unwinder->GetProcessMemory(), process_info);
dump_crash_details(&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);

View file

@ -18,7 +18,9 @@
#include <inttypes.h>
#include <charconv>
#include <functional>
#include <limits>
#include <set>
#include <string>
#include <unordered_set>
@ -425,6 +427,27 @@ static void print_memory_maps(CallbackType callback, const Tombstone& tombstone)
}
}
static std::string oct_encode(const std::string& data) {
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 (isprint(c)) {
oct_encoded += c;
} else {
std::string oct_digits("\\\0\0\0", 4);
// char is encodable in 3 oct digits
static_assert(std::numeric_limits<unsigned char>::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;
}
}
return oct_encoded;
}
static void print_main_thread(CallbackType callback, const Tombstone& tombstone,
const Thread& thread) {
print_thread_header(callback, tombstone, thread, true);
@ -468,6 +491,12 @@ 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(crash_detail.name());
std::string oct_encoded_data = oct_encode(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");

View file

@ -15,6 +15,11 @@ option java_outer_classname = "TombstoneProtos";
// NOTE TO OEMS:
// If you add custom fields to this proto, do not use numbers in the reserved range.
message CrashDetail {
bytes name = 1;
bytes data = 2;
}
message Tombstone {
Architecture arch = 1;
string build_fingerprint = 2;
@ -33,6 +38,7 @@ message Tombstone {
Signal signal_info = 10;
string abort_message = 14;
repeated CrashDetail crash_details = 21;
repeated Cause causes = 15;
map<uint32, Thread> threads = 16;
@ -40,7 +46,7 @@ message Tombstone {
repeated LogBuffer log_buffers = 18;
repeated FD open_fds = 19;
reserved 21 to 999;
reserved 22 to 999;
}
enum Architecture {

View file

@ -101,6 +101,7 @@ struct __attribute__((__packed__)) CrashInfoDataDynamic : public CrashInfoDataSt
size_t scudo_ring_buffer_size;
size_t scudo_stack_depot_size;
bool recoverable_gwp_asan_crash;
uintptr_t crash_detail_page;
};
struct __attribute__((__packed__)) CrashInfo {