Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f03af884 authored by Peter Collingbourne's avatar Peter Collingbourne
Browse files

Read fault address on arm64 using proposed kernel API.

On aarch64, the top 8 bits of the address (i.e. the tag bits) of
the fault address in si_addr are always clear. This isn't ideal for
MTE which will require these bits in order to correctly diagnose
tag mismatches.

A proposed kernel patch [1] exposes the full fault address including
the tag bits as part of the ucontext. Change debuggerd to read this
fault address if available.

[1] https://patchwork.kernel.org/patch/11435077/

Bug: 135772972
Change-Id: Ia05be574113860f4e9ecc36a310c4b740e0c4afb
parent f3d542fe
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -295,6 +295,13 @@ static void ReadCrashInfo(unique_fd& fd, siginfo_t* siginfo,
    case 3:
      process_info->abort_msg_address = crash_info->data.s.abort_msg_address;
      *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(),
                                                        &crash_info->data.s.ucontext));
      break;
+26 −0
Original line number Diff line number Diff line
@@ -305,6 +305,32 @@ TEST_F(CrasherTest, smoke) {
  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) {
  int intercept_result;
  unique_fd output_fd;
+7 −4
Original line number Diff line number Diff line
@@ -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
 * 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
  if (prctl(PR_GET_NAME, reinterpret_cast<unsigned long>(thread_name), 0, 0, 0) != 0) {
    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.
  char addr_desc[32] = "";  // ", fault addr 0x1234"
  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();
  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.
  ErrnoRestorer restorer;

  auto *ucontext = static_cast<ucontext_t*>(context);

  // It's possible somebody cleared the SA_SIGINFO flag, which would mean
  // our "info" arg holds an undefined value.
  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,
    // 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, static_cast<ucontext_t*>(context), process_info.abort_msg);
    debuggerd_fallback_handler(info, ucontext, process_info.abort_msg);
    resend_signal(info);
    return;
  }
@@ -534,7 +537,7 @@ static void debuggerd_signal_handler(int signal_number, siginfo_t* info, void* c
    return;
  }

  log_signal_summary(info);
  log_signal_summary(info, ucontext);

  debugger_thread_info thread_info = {
      .crashing_tid = __gettid(),
+3 −0
Original line number Diff line number Diff line
@@ -41,4 +41,7 @@ struct ProcessInfo {
  uintptr_t fdsan_table_address = 0;
  uintptr_t gwp_asan_state = 0;
  uintptr_t gwp_asan_metadata = 0;

  bool has_fault_address = false;
  uintptr_t fault_address = 0;
};
+2 −0
Original line number Diff line number Diff line
@@ -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_sigcode(const siginfo_t*);

uintptr_t get_fault_address(const siginfo_t* siginfo, const ucontext_t* ucontext);

#endif // _DEBUGGERD_UTILITY_H
Loading