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

Commit 298b4ace authored by Christopher Ferris's avatar Christopher Ferris Committed by Automerger Merge Worker
Browse files

Merge "Dump threads in tombstone fallback path." am: f3a800b1 am: 2d9bbfd7 am: 8ea14f7c

Original change: https://android-review.googlesource.com/c/platform/system/core/+/1981146

Change-Id: Ifb7509b2d815de0f8856cf8bb84c5f5cb8a2ffe9
parents 9d6bad2d 8ea14f7c
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