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

Commit f3a800b1 authored by Christopher Ferris's avatar Christopher Ferris Committed by Gerrit Code Review
Browse files

Merge "Dump threads in tombstone fallback path."

parents 78b86ae2 b999b82e
Loading
Loading
Loading
Loading
+17 −10
Original line number Diff line number Diff line
@@ -1409,6 +1409,16 @@ __attribute__((noinline)) extern "C" bool raise_debugger_signal(DebuggerdDumpTyp
  return true;
}

extern "C" void foo() {
  LOG(INFO) << "foo";
  std::this_thread::sleep_for(1s);
}

extern "C" void bar() {
  LOG(INFO) << "bar";
  std::this_thread::sleep_for(1s);
}

TEST_F(CrasherTest, seccomp_tombstone) {
  int intercept_result;
  unique_fd output_fd;
@@ -1416,6 +1426,11 @@ TEST_F(CrasherTest, seccomp_tombstone) {
  static const auto dump_type = kDebuggerdTombstone;
  StartProcess(
      []() {
        std::thread a(foo);
        std::thread b(bar);

        std::this_thread::sleep_for(100ms);

        raise_debugger_signal(dump_type);
        _exit(0);
      },
@@ -1430,16 +1445,8 @@ TEST_F(CrasherTest, seccomp_tombstone) {
  std::string result;
  ConsumeFd(std::move(output_fd), &result);
  ASSERT_BACKTRACE_FRAME(result, "raise_debugger_signal");
}

extern "C" void foo() {
  LOG(INFO) << "foo";
  std::this_thread::sleep_for(1s);
}

extern "C" void bar() {
  LOG(INFO) << "bar";
  std::this_thread::sleep_for(1s);
  ASSERT_BACKTRACE_FRAME(result, "foo");
  ASSERT_BACKTRACE_FRAME(result, "bar");
}

TEST_F(CrasherTest, seccomp_backtrace) {
+17 −44
Original line number Diff line number Diff line
@@ -98,32 +98,6 @@ static void debuggerd_fallback_tombstone(int output_fd, int proto_fd, ucontext_t
  __linker_disable_fallback_allocator();
}

static void iterate_siblings(bool (*callback)(pid_t, int), int output_fd) {
  pid_t current_tid = gettid();
  char buf[BUFSIZ];
  snprintf(buf, sizeof(buf), "/proc/%d/task", current_tid);
  DIR* dir = opendir(buf);

  if (!dir) {
    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to open %s: %s", buf, strerror(errno));
    return;
  }

  struct dirent* ent;
  while ((ent = readdir(dir))) {
    char* end;
    long tid = strtol(ent->d_name, &end, 10);
    if (end == ent->d_name || *end != '\0') {
      continue;
    }

    if (tid != current_tid) {
      callback(tid, output_fd);
    }
  }
  closedir(dir);
}

static bool forward_output(int src_fd, int dst_fd, pid_t expected_tid) {
  // Make sure the thread actually got the signal.
  struct pollfd pfd = {
@@ -216,21 +190,21 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) {
  }

  // Only allow one thread to perform a trace at a time.
  static pthread_mutex_t trace_mutex = PTHREAD_MUTEX_INITIALIZER;
  int ret = pthread_mutex_trylock(&trace_mutex);
  if (ret != 0) {
    async_safe_format_log(ANDROID_LOG_INFO, "libc", "pthread_mutex_try_lock failed: %s",
                          strerror(ret));
  static std::mutex trace_mutex;
  if (!trace_mutex.try_lock()) {
    async_safe_format_log(ANDROID_LOG_INFO, "libc", "trace lock failed");
    return;
  }

  std::lock_guard<std::mutex> scoped_lock(trace_mutex, std::adopt_lock);

  // Fetch output fd from tombstoned.
  unique_fd tombstone_socket, output_fd;
  if (!tombstoned_connect(getpid(), &tombstone_socket, &output_fd, nullptr,
                          kDebuggerdNativeBacktrace)) {
    async_safe_format_log(ANDROID_LOG_ERROR, "libc",
                          "missing crash_dump_fallback() in selinux policy?");
    goto exit;
    return;
  }

  dump_backtrace_header(output_fd.get());
@@ -239,15 +213,15 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) {
  debuggerd_fallback_trace(output_fd.get(), ucontext);

  // Send a signal to all of our siblings, asking them to dump their stack.
  iterate_siblings(
      [](pid_t tid, int output_fd) {
  pid_t current_tid = gettid();
  if (!iterate_tids(current_tid, [&output_fd](pid_t tid) {
        // Use a pipe, to be able to detect situations where the thread gracefully exits before
        // receiving our signal.
        unique_fd pipe_read, pipe_write;
        if (!Pipe(&pipe_read, &pipe_write)) {
          async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to create pipe: %s",
                                strerror(errno));
          return false;
          return;
        }

        uint64_t expected = pack_thread_fd(-1, -1);
@@ -257,7 +231,7 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) {
          async_safe_format_log(ANDROID_LOG_ERROR, "libc",
                                "thread %d is already outputting to fd %d?", tid, fd);
          close(sent_fd);
          return false;
          return;
        }

        siginfo_t siginfo = {};
@@ -269,10 +243,10 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) {
        if (syscall(__NR_rt_tgsigqueueinfo, getpid(), tid, BIONIC_SIGNAL_DEBUGGER, &siginfo) != 0) {
          async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to send trace signal to %d: %s",
                                tid, strerror(errno));
          return false;
          return;
        }

        bool success = forward_output(pipe_read.get(), output_fd, tid);
        bool success = forward_output(pipe_read.get(), output_fd.get(), tid);
        if (!success) {
          async_safe_format_log(ANDROID_LOG_ERROR, "libc",
                                "timeout expired while waiting for thread %d to dump", tid);
@@ -288,15 +262,14 @@ static void trace_handler(siginfo_t* info, ucontext_t* ucontext) {
          }
        }

        return true;
      },
      output_fd.get());
        return;
      })) {
    async_safe_format_log(ANDROID_LOG_ERROR, "libc", "failed to open /proc/%d/task: %s",
                          current_tid, strerror(errno));
  }

  dump_backtrace_footer(output_fd.get());
  tombstoned_notify_completion(tombstone_socket.get());

exit:
  pthread_mutex_unlock(&trace_mutex);
}

static void crash_handler(siginfo_t* info, ucontext_t* ucontext, void* abort_message) {
+28 −9
Original line number Diff line number Diff line
@@ -23,6 +23,7 @@
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/prctl.h>
#include <sys/types.h>
#include <unistd.h>

@@ -73,22 +74,40 @@ void engrave_tombstone_ucontext(int tombstone_fd, int proto_fd, uint64_t abort_m

  std::map<pid_t, ThreadInfo> threads;
  threads[tid] = ThreadInfo{
      .registers = std::move(regs),
      .uid = uid,
      .tid = tid,
      .thread_name = std::move(thread_name),
      .pid = pid,
      .command_line = std::move(command_line),
      .selinux_label = std::move(selinux_label),
    .registers = std::move(regs), .uid = uid, .tid = tid, .thread_name = std::move(thread_name),
    .pid = pid, .command_line = std::move(command_line), .selinux_label = std::move(selinux_label),
    .siginfo = siginfo,
#if defined(__aarch64__)
    // Only supported on aarch64 for now.
        .tagged_addr_ctrl = prctl(PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0),
    .pac_enabled_keys = prctl(PR_PAC_GET_ENABLED_KEYS, 0, 0, 0, 0),
#endif
  };
  if (pid == tid) {
    const ThreadInfo& thread = threads[pid];
    if (!iterate_tids(pid, [&threads, &thread](pid_t tid) {
          threads[tid] = ThreadInfo{
              .uid = thread.uid,
              .tid = tid,
              .pid = thread.pid,
              .command_line = thread.command_line,
              .thread_name = get_thread_name(tid),
              .tagged_addr_ctrl = thread.tagged_addr_ctrl,
              .pac_enabled_keys = thread.pac_enabled_keys,
          };
        })) {
      async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to open /proc/%d/task: %s", pid,
                            strerror(errno));
    }
  }

  unwindstack::UnwinderFromPid unwinder(kMaxFrames, pid, unwindstack::Regs::CurrentArch());
  auto process_memory =
      unwindstack::Memory::CreateProcessMemoryCached(getpid());
  unwinder.SetProcessMemory(process_memory);
  if (!unwinder.Init()) {
    async_safe_fatal("failed to init unwinder object");
    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to init unwinder object");
    return;
  }

  ProcessInfo process_info;
+106 −82
Original line number Diff line number Diff line
@@ -48,6 +48,7 @@

#include <android/log.h>
#include <bionic/macros.h>
#include <bionic/reserved_signals.h>
#include <log/log.h>
#include <log/log_read.h>
#include <log/logprint.h>
@@ -346,20 +347,17 @@ void fill_in_backtrace_frame(BacktraceFrame* f, const unwindstack::FrameData& fr
  f->set_build_id(frame.map_info->GetPrintableBuildID());
}

static void dump_thread(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
                        const ThreadInfo& thread_info, bool memory_dump = false) {
  Thread thread;

  thread.set_id(thread_info.tid);
  thread.set_name(thread_info.thread_name);
  thread.set_tagged_addr_ctrl(thread_info.tagged_addr_ctrl);
  thread.set_pac_enabled_keys(thread_info.pac_enabled_keys);
static void dump_registers(unwindstack::Unwinder* unwinder,
                           const std::unique_ptr<unwindstack::Regs>& regs, Thread& thread,
                           bool memory_dump) {
  if (regs == nullptr) {
    return;
  }

  unwindstack::Maps* maps = unwinder->GetMaps();
  unwindstack::Memory* memory = unwinder->GetProcessMemory().get();

  thread_info.registers->IterateRegisters(
      [&thread, memory_dump, maps, memory](const char* name, uint64_t value) {
  regs->IterateRegisters([&thread, memory_dump, maps, memory](const char* name, uint64_t value) {
    Register r;
    r.set_name(name);
    r.set_u64(value);
@@ -378,18 +376,11 @@ static void dump_thread(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
      constexpr size_t kNumTagsAroundRegister = kNumBytesAroundRegister / kTagGranuleSize;
      char buf[kNumBytesAroundRegister];
      uint8_t tags[kNumTagsAroundRegister];
          size_t start_offset = 0;
      ssize_t bytes = dump_memory(buf, sizeof(buf), tags, sizeof(tags), &value, memory);
      if (bytes == -1) {
        return;
      }
      dump.set_begin_address(value);

          if (start_offset + bytes > sizeof(buf)) {
            async_safe_fatal("dump_memory overflowed? start offset = %zu, bytes read = %zd",
                             start_offset, bytes);
          }

      dump.set_memory(buf, bytes);

      bool has_tags = false;
@@ -408,25 +399,31 @@ static void dump_thread(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
      *thread.add_memory_dump() = std::move(dump);
    }
  });
}

static void log_unwinder_error(unwindstack::Unwinder* unwinder) {
  if (unwinder->LastErrorCode() == unwindstack::ERROR_NONE) {
    return;
  }

  std::unique_ptr<unwindstack::Regs> regs_copy(thread_info.registers->Clone());
  unwinder->SetRegs(regs_copy.get());
  unwinder->Unwind();
  if (unwinder->NumFrames() == 0) {
    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to unwind");
    if (unwinder->LastErrorCode() != unwindstack::ERROR_NONE) {
  async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "  error code: %s",
                        unwinder->LastErrorCodeString());
  async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "  error address: 0x%" PRIx64,
                        unwinder->LastErrorAddress());
}
  } else {

static void dump_thread_backtrace(unwindstack::Unwinder* unwinder, Thread& thread) {
  if (unwinder->NumFrames() == 0) {
    async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG, "failed to unwind");
    log_unwinder_error(unwinder);
    return;
  }

  if (unwinder->elf_from_memory_not_file()) {
    auto backtrace_note = thread.mutable_backtrace_note();
    *backtrace_note->Add() =
        "Function names and BuildId information is missing for some frames due";
      *backtrace_note->Add() =
          "to unreadable libraries. For unwinds of apps, only shared libraries";
    *backtrace_note->Add() = "to unreadable libraries. For unwinds of apps, only shared libraries";
    *backtrace_note->Add() = "found under the lib/ directory are readable.";
    *backtrace_note->Add() = "On this device, run setenforce 0 to make the libraries readable.";
  }
@@ -437,13 +434,39 @@ static void dump_thread(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
  }
}

  auto& threads = *tombstone->mutable_threads();
  threads[thread_info.tid] = thread;
static void dump_thread(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
                        const ThreadInfo& thread_info, bool memory_dump = false) {
  Thread thread;

  thread.set_id(thread_info.tid);
  thread.set_name(thread_info.thread_name);
  thread.set_tagged_addr_ctrl(thread_info.tagged_addr_ctrl);
  thread.set_pac_enabled_keys(thread_info.pac_enabled_keys);

  if (thread_info.pid == getpid() && thread_info.pid != thread_info.tid) {
    // Fallback path for non-main thread, doing unwind from running process.
    unwindstack::ThreadUnwinder thread_unwinder(kMaxFrames, unwinder->GetMaps());
    if (!thread_unwinder.Init()) {
      async_safe_format_log(ANDROID_LOG_ERROR, LOG_TAG,
                            "Unable to initialize ThreadUnwinder object.");
      log_unwinder_error(&thread_unwinder);
      return;
    }

    std::unique_ptr<unwindstack::Regs> initial_regs;
    thread_unwinder.UnwindWithSignal(BIONIC_SIGNAL_BACKTRACE, thread_info.tid, &initial_regs);
    dump_registers(&thread_unwinder, initial_regs, thread, memory_dump);
    dump_thread_backtrace(&thread_unwinder, thread);
  } else {
    dump_registers(unwinder, thread_info.registers, thread, memory_dump);
    std::unique_ptr<unwindstack::Regs> regs_copy(thread_info.registers->Clone());
    unwinder->SetRegs(regs_copy.get());
    unwinder->Unwind();
    dump_thread_backtrace(unwinder, thread);
  }

static void dump_main_thread(Tombstone* tombstone, unwindstack::Unwinder* unwinder,
                             const ThreadInfo& thread_info) {
  dump_thread(tombstone, unwinder, thread_info, true);
  auto& threads = *tombstone->mutable_threads();
  threads[thread_info.tid] = thread;
}

static void dump_mappings(Tombstone* tombstone, unwindstack::Unwinder* unwinder) {
@@ -663,7 +686,8 @@ void engrave_tombstone_proto(Tombstone* tombstone, unwindstack::Unwinder* unwind

  dump_abort_message(&result, unwinder, process_info);

  dump_main_thread(&result, unwinder, main_thread);
  // Dump the main thread, but save the memory around the registers.
  dump_thread(&result, unwinder, main_thread, /* memory_dump */ true);

  for (const auto& [tid, thread_info] : threads) {
    if (tid != target_thread) {
+1 −1
Original line number Diff line number Diff line
@@ -25,7 +25,7 @@ tgkill: 1
rt_sigprocmask: 1
rt_sigaction: 1
rt_tgsigqueueinfo: 1
prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41 || arg0 == PR_PAC_RESET_KEYS
prctl: arg0 == PR_GET_NO_NEW_PRIVS || arg0 == 0x53564d41 || arg0 == PR_PAC_RESET_KEYS || arg0 == PR_GET_TAGGED_ADDR_CTRL || arg0 == PR_PAC_GET_ENABLED_KEYS
madvise: 1
mprotect: arg2 in 0x1|0x2
munmap: 1
Loading