mirror of
https://github.com/yuzu-emu/breakpad.git
synced 2024-12-23 05:15:34 +00:00
Fixup non-canonical fault addresses for amd64.
This uses DisassemblerObjdump to add a processing step in MinidumpProcessor to compute the true faulting address from register state and disassembly of the fault instruction when the fault address is suspicious (-1). Bug: 901847 Change-Id: Ia1f77d542c4055c82ce2504db8c84a9e52001866 Reviewed-on: https://chromium-review.googlesource.com/c/breakpad/breakpad/+/3932957 Reviewed-by: Ivan Penkov <ivanpe@chromium.org>
This commit is contained in:
parent
6289830b67
commit
57d1743662
|
@ -101,8 +101,10 @@ class MinidumpProcessor {
|
||||||
// exception, if this information is available. This will be a code
|
// exception, if this information is available. This will be a code
|
||||||
// address when the crash was caused by problems such as illegal
|
// address when the crash was caused by problems such as illegal
|
||||||
// instructions or divisions by zero, or a data address when the crash
|
// instructions or divisions by zero, or a data address when the crash
|
||||||
// was caused by a memory access violation.
|
// was caused by a memory access violation. If enable_objdump is set, this
|
||||||
static string GetCrashReason(Minidump* dump, uint64_t* address);
|
// may use disassembly to compute the faulting address.
|
||||||
|
static string GetCrashReason(Minidump* dump, uint64_t* address,
|
||||||
|
bool enable_objdump);
|
||||||
|
|
||||||
// This function returns true if the passed-in error code is
|
// This function returns true if the passed-in error code is
|
||||||
// something unrecoverable(i.e. retry should not happen). For
|
// something unrecoverable(i.e. retry should not happen). For
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <limits>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
@ -43,6 +44,7 @@
|
||||||
#include "google_breakpad/processor/process_state.h"
|
#include "google_breakpad/processor/process_state.h"
|
||||||
#include "google_breakpad/processor/exploitability.h"
|
#include "google_breakpad/processor/exploitability.h"
|
||||||
#include "google_breakpad/processor/stack_frame_symbolizer.h"
|
#include "google_breakpad/processor/stack_frame_symbolizer.h"
|
||||||
|
#include "processor/disassembler_objdump.h"
|
||||||
#include "processor/logging.h"
|
#include "processor/logging.h"
|
||||||
#include "processor/stackwalker_x86.h"
|
#include "processor/stackwalker_x86.h"
|
||||||
#include "processor/symbolic_constants_win.h"
|
#include "processor/symbolic_constants_win.h"
|
||||||
|
@ -117,7 +119,7 @@ ProcessResult MinidumpProcessor::Process(
|
||||||
has_requesting_thread = exception->GetThreadID(&requesting_thread_id);
|
has_requesting_thread = exception->GetThreadID(&requesting_thread_id);
|
||||||
|
|
||||||
process_state->crash_reason_ = GetCrashReason(
|
process_state->crash_reason_ = GetCrashReason(
|
||||||
dump, &process_state->crash_address_);
|
dump, &process_state->crash_address_, enable_objdump_);
|
||||||
|
|
||||||
process_state->exception_record_.set_code(
|
process_state->exception_record_.set_code(
|
||||||
exception->exception()->exception_record.exception_code,
|
exception->exception()->exception_record.exception_code,
|
||||||
|
@ -758,8 +760,82 @@ bool MinidumpProcessor::GetProcessCreateTime(Minidump* dump,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool IsCanonicalAddress(uint64_t address) {
|
||||||
|
uint64_t sign_bit = (address >> 63) & 1;
|
||||||
|
for (int shift = 48; shift < 63; ++shift) {
|
||||||
|
if (sign_bit != ((address >> shift) & 1)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CalculateFaultAddressFromInstruction(Minidump* dump,
|
||||||
|
uint64_t* address) {
|
||||||
|
MinidumpException* exception = dump->GetException();
|
||||||
|
if (exception == NULL) {
|
||||||
|
BPLOG(INFO) << "Failed to get exception.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
MinidumpContext* context = exception->GetContext();
|
||||||
|
if (context == NULL) {
|
||||||
|
BPLOG(INFO) << "Failed to get exception context.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t instruction_ptr = 0;
|
||||||
|
if (!context->GetInstructionPointer(&instruction_ptr)) {
|
||||||
|
BPLOG(INFO) << "Failed to get instruction pointer.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get memory region containing instruction pointer.
|
||||||
|
MinidumpMemoryList* memory_list = dump->GetMemoryList();
|
||||||
|
MinidumpMemoryRegion* memory_region =
|
||||||
|
memory_list ?
|
||||||
|
memory_list->GetMemoryRegionForAddress(instruction_ptr) : NULL;
|
||||||
|
if (!memory_region) {
|
||||||
|
BPLOG(INFO) << "No memory region around instruction pointer.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DisassemblerObjdump disassembler(context->GetContextCPU(), memory_region,
|
||||||
|
instruction_ptr);
|
||||||
|
fprintf(stderr, "%s %s %s\n", disassembler.operation().c_str(),
|
||||||
|
disassembler.src().c_str(), disassembler.dest().c_str());
|
||||||
|
if (!disassembler.IsValid()) {
|
||||||
|
BPLOG(INFO) << "Disassembling fault instruction failed.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// It's possible that we reach here when the faulting address is already
|
||||||
|
// correct, so we only update it if we find that at least one of the src/dest
|
||||||
|
// addresses is non-canonical. If both are non-canonical, we arbitrarily set
|
||||||
|
// it to the larger of the two, as this is more likely to be a known poison
|
||||||
|
// value.
|
||||||
|
|
||||||
|
bool valid_read, valid_write;
|
||||||
|
uint64_t read_address, write_address;
|
||||||
|
|
||||||
|
valid_read = disassembler.CalculateSrcAddress(*context, read_address);
|
||||||
|
valid_read &= !IsCanonicalAddress(read_address);
|
||||||
|
|
||||||
|
valid_write = disassembler.CalculateDestAddress(*context, write_address);
|
||||||
|
valid_write &= !IsCanonicalAddress(write_address);
|
||||||
|
|
||||||
|
if (valid_read && valid_write) {
|
||||||
|
*address = read_address > write_address ? read_address : write_address;
|
||||||
|
} else if (valid_read) {
|
||||||
|
*address = read_address;
|
||||||
|
} else if (valid_write) {
|
||||||
|
*address = write_address;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
string MinidumpProcessor::GetCrashReason(Minidump* dump, uint64_t* address) {
|
string MinidumpProcessor::GetCrashReason(Minidump* dump, uint64_t* address,
|
||||||
|
bool enable_objdump) {
|
||||||
MinidumpException* exception = dump->GetException();
|
MinidumpException* exception = dump->GetException();
|
||||||
if (!exception)
|
if (!exception)
|
||||||
return "";
|
return "";
|
||||||
|
@ -1985,6 +2061,15 @@ string MinidumpProcessor::GetCrashReason(Minidump* dump, uint64_t* address) {
|
||||||
*address = GetAddressForArchitecture(
|
*address = GetAddressForArchitecture(
|
||||||
static_cast<MDCPUArchitecture>(raw_system_info->processor_architecture),
|
static_cast<MDCPUArchitecture>(raw_system_info->processor_architecture),
|
||||||
*address);
|
*address);
|
||||||
|
|
||||||
|
// For invalid accesses to non-canonical addresses, amd64 cpus don't provide
|
||||||
|
// the fault address, so recover it from the disassembly and register state
|
||||||
|
// if possible.
|
||||||
|
if (enable_objdump
|
||||||
|
&& raw_system_info->processor_architecture == MD_CPU_ARCHITECTURE_AMD64
|
||||||
|
&& std::numeric_limits<uint64_t>::max() == *address) {
|
||||||
|
CalculateFaultAddressFromInstruction(dump, address);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return reason;
|
return reason;
|
||||||
|
|
|
@ -799,6 +799,25 @@ TEST_F(MinidumpProcessorTest, TestFastFailException) {
|
||||||
ASSERT_EQ(state.crash_reason(), "FAST_FAIL_FATAL_APP_EXIT");
|
ASSERT_EQ(state.crash_reason(), "FAST_FAIL_FATAL_APP_EXIT");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef __linux__
|
||||||
|
TEST_F(MinidumpProcessorTest, TestNonCanonicalAddress) {
|
||||||
|
// This tests if we can correctly fixup non-canonical address GPF fault
|
||||||
|
// addresses.
|
||||||
|
// Dump is captured from a toy executable and is readable by windbg.
|
||||||
|
MinidumpProcessor processor(nullptr, nullptr /*&supplier, &resolver*/);
|
||||||
|
processor.set_enable_objdump(true);
|
||||||
|
|
||||||
|
string minidump_file = GetTestDataPath()
|
||||||
|
+ "write_av_non_canonical.dmp";
|
||||||
|
|
||||||
|
ProcessState state;
|
||||||
|
ASSERT_EQ(processor.Process(minidump_file, &state),
|
||||||
|
google_breakpad::PROCESS_OK);
|
||||||
|
ASSERT_TRUE(state.crashed());
|
||||||
|
ASSERT_EQ(state.crash_address(), 0xfefefefefefefefeU);
|
||||||
|
}
|
||||||
|
#endif // __linux__
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
int main(int argc, char* argv[]) {
|
int main(int argc, char* argv[]) {
|
||||||
|
|
BIN
src/processor/testdata/write_av_non_canonical.dmp
vendored
Normal file
BIN
src/processor/testdata/write_av_non_canonical.dmp
vendored
Normal file
Binary file not shown.
Loading…
Reference in a new issue