Teach debuggerd to pass the secondary ring buffer to __scudo_get_error_info().

With this change we can report memory errors involving secondary
allocations. Update the existing crasher tests to also test
UAF/overflow/underflow on allocations with sizes sufficient to trigger
the secondary allocator.

Bug: 135772972
Change-Id: Ic8925c1f18621a8f272e26d5630e5d11d6d34d38
This commit is contained in:
Peter Collingbourne 2021-01-06 21:02:19 -08:00
parent 7b204ac4ca
commit bb4b49c63c
6 changed files with 29 additions and 16 deletions

View file

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

View file

@ -391,7 +391,11 @@ static void SetTagCheckingLevelSync() {
}
#endif
TEST_F(CrasherTest, mte_uaf) {
struct SizeParamCrasherTest : CrasherTest, testing::WithParamInterface<size_t> {};
INSTANTIATE_TEST_SUITE_P(Sizes, SizeParamCrasherTest, testing::Values(16, 131072));
TEST_P(SizeParamCrasherTest, mte_uaf) {
#if defined(__aarch64__)
if (!mte_supported()) {
GTEST_SKIP() << "Requires MTE";
@ -399,9 +403,9 @@ TEST_F(CrasherTest, mte_uaf) {
int intercept_result;
unique_fd output_fd;
StartProcess([]() {
StartProcess([&]() {
SetTagCheckingLevelSync();
volatile int* p = (volatile int*)malloc(16);
volatile int* p = (volatile int*)malloc(GetParam());
free((void *)p);
p[0] = 42;
});
@ -416,8 +420,9 @@ TEST_F(CrasherTest, mte_uaf) {
std::string result;
ConsumeFd(std::move(output_fd), &result);
ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 9 \(SEGV_MTESERR\))");
ASSERT_MATCH(result, R"(Cause: \[MTE\]: Use After Free, 0 bytes into a 16-byte allocation.*
ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))");
ASSERT_MATCH(result, R"(Cause: \[MTE\]: Use After Free, 0 bytes into a )" +
std::to_string(GetParam()) + R"(-byte allocation.*
allocated by thread .*
#00 pc)");
@ -428,7 +433,7 @@ allocated by thread .*
#endif
}
TEST_F(CrasherTest, mte_overflow) {
TEST_P(SizeParamCrasherTest, mte_overflow) {
#if defined(__aarch64__)
if (!mte_supported()) {
GTEST_SKIP() << "Requires MTE";
@ -436,10 +441,10 @@ TEST_F(CrasherTest, mte_overflow) {
int intercept_result;
unique_fd output_fd;
StartProcess([]() {
StartProcess([&]() {
SetTagCheckingLevelSync();
volatile int* p = (volatile int*)malloc(16);
p[4] = 42;
volatile char* p = (volatile char*)malloc(GetParam());
p[GetParam()] = 42;
});
StartIntercept(&output_fd);
@ -453,7 +458,8 @@ TEST_F(CrasherTest, mte_overflow) {
ConsumeFd(std::move(output_fd), &result);
ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\))");
ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Overflow, 0 bytes right of a 16-byte allocation.*
ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Overflow, 0 bytes right of a )" +
std::to_string(GetParam()) + R"(-byte allocation.*
allocated by thread .*
#00 pc)");
@ -462,7 +468,7 @@ allocated by thread .*
#endif
}
TEST_F(CrasherTest, mte_underflow) {
TEST_P(SizeParamCrasherTest, mte_underflow) {
#if defined(__aarch64__)
if (!mte_supported()) {
GTEST_SKIP() << "Requires MTE";
@ -470,9 +476,9 @@ TEST_F(CrasherTest, mte_underflow) {
int intercept_result;
unique_fd output_fd;
StartProcess([]() {
StartProcess([&]() {
SetTagCheckingLevelSync();
volatile int* p = (volatile int*)malloc(16);
volatile int* p = (volatile int*)malloc(GetParam());
p[-1] = 42;
});
@ -487,7 +493,8 @@ TEST_F(CrasherTest, mte_underflow) {
ConsumeFd(std::move(output_fd), &result);
ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 9 \(SEGV_MTESERR\))");
ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Underflow, 4 bytes left of a 16-byte allocation.*
ASSERT_MATCH(result, R"(Cause: \[MTE\]: Buffer Underflow, 4 bytes left of a )" +
std::to_string(GetParam()) + R"(-byte allocation.*
allocated by thread .*
#00 pc)");

View file

@ -42,6 +42,7 @@ struct debugger_process_info {
const gwp_asan::AllocationMetadata* gwp_asan_metadata;
const char* scudo_stack_depot;
const char* scudo_region_info;
const char* scudo_ring_buffer;
};
// These callbacks are called in a signal handler, and thus must be async signal safe.

View file

@ -46,6 +46,7 @@ struct ProcessInfo {
uintptr_t gwp_asan_metadata = 0;
uintptr_t scudo_stack_depot = 0;
uintptr_t scudo_region_info = 0;
uintptr_t scudo_ring_buffer = 0;
bool has_fault_address = false;
uintptr_t untagged_fault_address = 0;

View file

@ -43,6 +43,8 @@ ScudoCrashData::ScudoCrashData(unwindstack::Memory* process_memory,
__scudo_get_stack_depot_size());
auto region_info = AllocAndReadFully(process_memory, process_info.scudo_region_info,
__scudo_get_region_info_size());
auto ring_buffer = AllocAndReadFully(process_memory, process_info.scudo_ring_buffer,
__scudo_get_ring_buffer_size());
untagged_fault_addr_ = process_info.untagged_fault_address;
uintptr_t fault_page = untagged_fault_addr_ & ~(PAGE_SIZE - 1);
@ -68,8 +70,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(), memory.get(), memory_tags.get(), memory_begin,
memory_end - memory_begin);
region_info.get(), ring_buffer.get(), memory.get(), memory_tags.get(),
memory_begin, memory_end - memory_begin);
}
bool ScudoCrashData::CrashIsMine() const {

View file

@ -97,6 +97,7 @@ struct __attribute__((__packed__)) CrashInfoDataDynamic : public CrashInfoDataSt
uintptr_t gwp_asan_metadata;
uintptr_t scudo_stack_depot;
uintptr_t scudo_region_info;
uintptr_t scudo_ring_buffer;
};
struct __attribute__((__packed__)) CrashInfo {