Merge "Read fault address on arm64 using proposed kernel API."
This commit is contained in:
commit
cd63cae6b2
7 changed files with 89 additions and 11 deletions
|
|
@ -295,6 +295,13 @@ static void ReadCrashInfo(unique_fd& fd, siginfo_t* siginfo,
|
||||||
case 3:
|
case 3:
|
||||||
process_info->abort_msg_address = crash_info->data.s.abort_msg_address;
|
process_info->abort_msg_address = crash_info->data.s.abort_msg_address;
|
||||||
*siginfo = crash_info->data.s.siginfo;
|
*siginfo = crash_info->data.s.siginfo;
|
||||||
|
if (signal_has_si_addr(siginfo)) {
|
||||||
|
// Make a copy of the ucontext field because otherwise it is not aligned enough (due to
|
||||||
|
// being in a packed struct) and clang complains about that.
|
||||||
|
ucontext_t ucontext = crash_info->data.s.ucontext;
|
||||||
|
process_info->has_fault_address = true;
|
||||||
|
process_info->fault_address = get_fault_address(siginfo, &ucontext);
|
||||||
|
}
|
||||||
regs->reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(),
|
regs->reset(unwindstack::Regs::CreateFromUcontext(unwindstack::Regs::CurrentArch(),
|
||||||
&crash_info->data.s.ucontext));
|
&crash_info->data.s.ucontext));
|
||||||
break;
|
break;
|
||||||
|
|
|
||||||
|
|
@ -305,6 +305,32 @@ TEST_F(CrasherTest, smoke) {
|
||||||
ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0xdead)");
|
ASSERT_MATCH(result, R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr 0xdead)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(CrasherTest, tagged_fault_addr) {
|
||||||
|
#if !defined(__aarch64__)
|
||||||
|
GTEST_SKIP() << "Requires aarch64";
|
||||||
|
#endif
|
||||||
|
int intercept_result;
|
||||||
|
unique_fd output_fd;
|
||||||
|
StartProcess([]() {
|
||||||
|
*reinterpret_cast<volatile char*>(0x100000000000dead) = '1';
|
||||||
|
});
|
||||||
|
|
||||||
|
StartIntercept(&output_fd);
|
||||||
|
FinishCrasher();
|
||||||
|
AssertDeath(SIGSEGV);
|
||||||
|
FinishIntercept(&intercept_result);
|
||||||
|
|
||||||
|
ASSERT_EQ(1, intercept_result) << "tombstoned reported failure";
|
||||||
|
|
||||||
|
std::string result;
|
||||||
|
ConsumeFd(std::move(output_fd), &result);
|
||||||
|
|
||||||
|
// The address can either be tagged (new kernels) or untagged (old kernels).
|
||||||
|
ASSERT_MATCH(
|
||||||
|
result,
|
||||||
|
R"(signal 11 \(SIGSEGV\), code 1 \(SEGV_MAPERR\), fault addr (0x100000000000dead|0xdead))");
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(CrasherTest, LD_PRELOAD) {
|
TEST_F(CrasherTest, LD_PRELOAD) {
|
||||||
int intercept_result;
|
int intercept_result;
|
||||||
unique_fd output_fd;
|
unique_fd output_fd;
|
||||||
|
|
|
||||||
|
|
@ -167,7 +167,7 @@ static bool get_main_thread_name(char* buf, size_t len) {
|
||||||
* mutex is being held, so we don't want to use any libc functions that
|
* mutex is being held, so we don't want to use any libc functions that
|
||||||
* could allocate memory or hold a lock.
|
* could allocate memory or hold a lock.
|
||||||
*/
|
*/
|
||||||
static void log_signal_summary(const siginfo_t* info) {
|
static void log_signal_summary(const siginfo_t* info, const ucontext_t* ucontext) {
|
||||||
char thread_name[MAX_TASK_NAME_LEN + 1]; // one more for termination
|
char thread_name[MAX_TASK_NAME_LEN + 1]; // one more for termination
|
||||||
if (prctl(PR_GET_NAME, reinterpret_cast<unsigned long>(thread_name), 0, 0, 0) != 0) {
|
if (prctl(PR_GET_NAME, reinterpret_cast<unsigned long>(thread_name), 0, 0, 0) != 0) {
|
||||||
strcpy(thread_name, "<name unknown>");
|
strcpy(thread_name, "<name unknown>");
|
||||||
|
|
@ -186,7 +186,8 @@ static void log_signal_summary(const siginfo_t* info) {
|
||||||
// Many signals don't have an address or sender.
|
// Many signals don't have an address or sender.
|
||||||
char addr_desc[32] = ""; // ", fault addr 0x1234"
|
char addr_desc[32] = ""; // ", fault addr 0x1234"
|
||||||
if (signal_has_si_addr(info)) {
|
if (signal_has_si_addr(info)) {
|
||||||
async_safe_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p", info->si_addr);
|
async_safe_format_buffer(addr_desc, sizeof(addr_desc), ", fault addr %p",
|
||||||
|
reinterpret_cast<void*>(get_fault_address(info, ucontext)));
|
||||||
}
|
}
|
||||||
pid_t self_pid = __getpid();
|
pid_t self_pid = __getpid();
|
||||||
char sender_desc[32] = {}; // " from pid 1234, uid 666"
|
char sender_desc[32] = {}; // " from pid 1234, uid 666"
|
||||||
|
|
@ -476,6 +477,8 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c
|
||||||
// making a syscall and checking errno.
|
// making a syscall and checking errno.
|
||||||
ErrnoRestorer restorer;
|
ErrnoRestorer restorer;
|
||||||
|
|
||||||
|
auto *ucontext = static_cast<ucontext_t*>(context);
|
||||||
|
|
||||||
// It's possible somebody cleared the SA_SIGINFO flag, which would mean
|
// It's possible somebody cleared the SA_SIGINFO flag, which would mean
|
||||||
// our "info" arg holds an undefined value.
|
// our "info" arg holds an undefined value.
|
||||||
if (!have_siginfo(signal_number)) {
|
if (!have_siginfo(signal_number)) {
|
||||||
|
|
@ -522,7 +525,7 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c
|
||||||
// This check might be racy if another thread sets NO_NEW_PRIVS, but this should be unlikely,
|
// This check might be racy if another thread sets NO_NEW_PRIVS, but this should be unlikely,
|
||||||
// you can only set NO_NEW_PRIVS to 1, and the effect should be at worst a single missing
|
// you can only set NO_NEW_PRIVS to 1, and the effect should be at worst a single missing
|
||||||
// ANR trace.
|
// ANR trace.
|
||||||
debuggerd_fallback_handler(info, static_cast<ucontext_t*>(context), process_info.abort_msg);
|
debuggerd_fallback_handler(info, ucontext, process_info.abort_msg);
|
||||||
resend_signal(info);
|
resend_signal(info);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -534,7 +537,7 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
log_signal_summary(info);
|
log_signal_summary(info, ucontext);
|
||||||
|
|
||||||
debugger_thread_info thread_info = {
|
debugger_thread_info thread_info = {
|
||||||
.crashing_tid = __gettid(),
|
.crashing_tid = __gettid(),
|
||||||
|
|
|
||||||
|
|
@ -41,4 +41,7 @@ struct ProcessInfo {
|
||||||
uintptr_t fdsan_table_address = 0;
|
uintptr_t fdsan_table_address = 0;
|
||||||
uintptr_t gwp_asan_state = 0;
|
uintptr_t gwp_asan_state = 0;
|
||||||
uintptr_t gwp_asan_metadata = 0;
|
uintptr_t gwp_asan_metadata = 0;
|
||||||
|
|
||||||
|
bool has_fault_address = false;
|
||||||
|
uintptr_t fault_address = 0;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -93,4 +93,6 @@ void get_signal_sender(char* buf, size_t n, const siginfo_t*);
|
||||||
const char* get_signame(const siginfo_t*);
|
const char* get_signame(const siginfo_t*);
|
||||||
const char* get_sigcode(const siginfo_t*);
|
const char* get_sigcode(const siginfo_t*);
|
||||||
|
|
||||||
|
uintptr_t get_fault_address(const siginfo_t* siginfo, const ucontext_t* ucontext);
|
||||||
|
|
||||||
#endif // _DEBUGGERD_UTILITY_H
|
#endif // _DEBUGGERD_UTILITY_H
|
||||||
|
|
|
||||||
|
|
@ -154,16 +154,16 @@ static void dump_probable_cause(log_t* log, const siginfo_t* si, unwindstack::Ma
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump_signal_info(log_t* log, const ThreadInfo& thread_info,
|
static void dump_signal_info(log_t* log, const ThreadInfo& thread_info,
|
||||||
unwindstack::Memory* process_memory) {
|
const ProcessInfo& process_info, unwindstack::Memory* process_memory) {
|
||||||
char addr_desc[64]; // ", fault addr 0x1234"
|
char addr_desc[64]; // ", fault addr 0x1234"
|
||||||
if (signal_has_si_addr(thread_info.siginfo)) {
|
if (process_info.has_fault_address) {
|
||||||
void* addr = thread_info.siginfo->si_addr;
|
size_t addr = process_info.fault_address;
|
||||||
if (thread_info.siginfo->si_signo == SIGILL) {
|
if (thread_info.siginfo->si_signo == SIGILL) {
|
||||||
uint32_t instruction = {};
|
uint32_t instruction = {};
|
||||||
process_memory->Read(reinterpret_cast<uint64_t>(addr), &instruction, sizeof(instruction));
|
process_memory->Read(addr, &instruction, sizeof(instruction));
|
||||||
snprintf(addr_desc, sizeof(addr_desc), "%p (*pc=%#08x)", addr, instruction);
|
snprintf(addr_desc, sizeof(addr_desc), "0x%zx (*pc=%#08x)", addr, instruction);
|
||||||
} else {
|
} else {
|
||||||
snprintf(addr_desc, sizeof(addr_desc), "%p", addr);
|
snprintf(addr_desc, sizeof(addr_desc), "0x%zx", addr);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
snprintf(addr_desc, sizeof(addr_desc), "--------");
|
snprintf(addr_desc, sizeof(addr_desc), "--------");
|
||||||
|
|
@ -384,7 +384,7 @@ static bool dump_thread(log_t* log, unwindstack::Unwinder* unwinder, const Threa
|
||||||
dump_thread_info(log, thread_info);
|
dump_thread_info(log, thread_info);
|
||||||
|
|
||||||
if (thread_info.siginfo) {
|
if (thread_info.siginfo) {
|
||||||
dump_signal_info(log, thread_info, unwinder->GetProcessMemory().get());
|
dump_signal_info(log, thread_info, process_info, unwinder->GetProcessMemory().get());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<GwpAsanCrashData> gwp_asan_crash_data;
|
std::unique_ptr<GwpAsanCrashData> gwp_asan_crash_data;
|
||||||
|
|
|
||||||
|
|
@ -449,3 +449,40 @@ void log_backtrace(log_t* log, unwindstack::Unwinder* unwinder, const char* pref
|
||||||
_LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, unwinder->FormatFrame(i).c_str());
|
_LOG(log, logtype::BACKTRACE, "%s%s\n", prefix, unwinder->FormatFrame(i).c_str());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(__aarch64__)
|
||||||
|
#define FAR_MAGIC 0x46415201
|
||||||
|
|
||||||
|
struct far_context {
|
||||||
|
struct _aarch64_ctx head;
|
||||||
|
__u64 far;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uintptr_t get_fault_address(const siginfo_t* siginfo, const ucontext_t* ucontext) {
|
||||||
|
(void)ucontext;
|
||||||
|
#if defined(__aarch64__)
|
||||||
|
// This relies on a kernel patch:
|
||||||
|
// https://patchwork.kernel.org/patch/11435077/
|
||||||
|
// that hasn't been accepted into the kernel yet. TODO(pcc): Update this to
|
||||||
|
// use the official interface once it lands.
|
||||||
|
auto* begin = reinterpret_cast<const char*>(ucontext->uc_mcontext.__reserved);
|
||||||
|
auto* end = begin + sizeof(ucontext->uc_mcontext.__reserved);
|
||||||
|
auto* ptr = begin;
|
||||||
|
while (1) {
|
||||||
|
auto* ctx = reinterpret_cast<const _aarch64_ctx*>(ptr);
|
||||||
|
if (ctx->magic == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (ctx->magic == FAR_MAGIC) {
|
||||||
|
auto* far_ctx = reinterpret_cast<const far_context*>(ctx);
|
||||||
|
return far_ctx->far;
|
||||||
|
}
|
||||||
|
ptr += ctx->size;
|
||||||
|
if (ctx->size % sizeof(void*) != 0 || ptr < begin || ptr >= end) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return reinterpret_cast<uintptr_t>(siginfo->si_addr);
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue