diff --git a/debuggerd/Android.bp b/debuggerd/Android.bp index 385f3850a..03b328727 100644 --- a/debuggerd/Android.bp +++ b/debuggerd/Android.bp @@ -166,6 +166,9 @@ cc_library_static { local_include_dirs: ["libdebuggerd/include"], export_include_dirs: ["libdebuggerd/include"], + // Needed for private/bionic_fdsan.h + include_dirs: ["bionic/libc"], + static_libs: [ "libbacktrace", "libunwindstack", diff --git a/debuggerd/crash_dump.cpp b/debuggerd/crash_dump.cpp index 43b7e05fc..93f757236 100644 --- a/debuggerd/crash_dump.cpp +++ b/debuggerd/crash_dump.cpp @@ -412,7 +412,7 @@ int main(int argc, char** argv) { OpenFilesList open_files; { ATRACE_NAME("open files"); - populate_open_files_list(g_target_thread, &open_files); + populate_open_files_list(&open_files, g_target_thread); } // In order to reduce the duration that we pause the process for, we ptrace @@ -567,9 +567,16 @@ int main(int argc, char** argv) { ATRACE_NAME("dump_backtrace"); dump_backtrace(std::move(g_output_fd), map.get(), thread_info, g_target_thread); } else { - ATRACE_NAME("engrave_tombstone"); - engrave_tombstone(std::move(g_output_fd), map.get(), process_memory.get(), thread_info, - g_target_thread, abort_msg_address, &open_files, &amfd_data); + { + ATRACE_NAME("fdsan table dump"); + populate_fdsan_table(&open_files, process_memory, fdsan_table_address); + } + + { + ATRACE_NAME("engrave_tombstone"); + engrave_tombstone(std::move(g_output_fd), map.get(), process_memory.get(), thread_info, + g_target_thread, abort_msg_address, &open_files, &amfd_data); + } } if (fatal_signal) { diff --git a/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h b/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h index 4727ca4d7..d47f2ddf6 100644 --- a/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h +++ b/debuggerd/libdebuggerd/include/libdebuggerd/open_files_list.h @@ -14,23 +14,31 @@ * limitations under the License. */ -#ifndef _DEBUGGERD_OPEN_FILES_LIST_H -#define _DEBUGGERD_OPEN_FILES_LIST_H +#pragma once +#include #include +#include +#include #include #include -#include #include "utility.h" -typedef std::vector> OpenFilesList; +struct FDInfo { + std::optional path; + std::optional fdsan_owner; +}; -/* Populates the given list with open files for the given process. */ -void populate_open_files_list(pid_t pid, OpenFilesList* list); +using OpenFilesList = std::map; -/* Dumps the open files list to the log. */ +// Populates the given list with open files for the given process. +void populate_open_files_list(OpenFilesList* list, pid_t pid); + +// Populates the given list with the target process's fdsan table. +void populate_fdsan_table(OpenFilesList* list, std::shared_ptr memory, + uint64_t fdsan_table_address); + +// Dumps the open files list to the log. void dump_open_files_list(log_t* log, const OpenFilesList& files, const char* prefix); - -#endif // _DEBUGGERD_OPEN_FILES_LIST_H diff --git a/debuggerd/libdebuggerd/open_files_list.cpp b/debuggerd/libdebuggerd/open_files_list.cpp index b12703e14..1fdf2369a 100644 --- a/debuggerd/libdebuggerd/open_files_list.cpp +++ b/debuggerd/libdebuggerd/open_files_list.cpp @@ -32,10 +32,12 @@ #include #include +#include #include "libdebuggerd/utility.h" +#include "private/bionic_fdsan.h" -void populate_open_files_list(pid_t pid, OpenFilesList* list) { +void populate_open_files_list(OpenFilesList* list, pid_t pid) { std::string fd_dir_name = "/proc/" + std::to_string(pid) + "/fd"; std::unique_ptr dir(opendir(fd_dir_name.c_str()), closedir); if (dir == nullptr) { @@ -53,17 +55,84 @@ void populate_open_files_list(pid_t pid, OpenFilesList* list) { std::string path = fd_dir_name + "/" + std::string(de->d_name); std::string target; if (android::base::Readlink(path, &target)) { - list->emplace_back(fd, target); + (*list)[fd].path = target; } else { + (*list)[fd].path = "???"; ALOGE("failed to readlink %s: %s", path.c_str(), strerror(errno)); - list->emplace_back(fd, "???"); } } } +void populate_fdsan_table(OpenFilesList* list, std::shared_ptr memory, + uint64_t fdsan_table_address) { + constexpr size_t inline_fds = sizeof(FdTable::entries) / sizeof(*FdTable::entries); + static_assert(inline_fds == 128); + size_t entry_offset = offsetof(FdTable, entries); + for (size_t i = 0; i < inline_fds; ++i) { + uint64_t address = fdsan_table_address + entry_offset + sizeof(FdEntry) * i; + FdEntry entry; + if (!memory->Read(address, &entry, sizeof(entry))) { + ALOGE("failed to read fdsan table entry %zu: %s", i, strerror(errno)); + return; + } + ALOGE("fd %zu = %#" PRIx64, i, entry.close_tag.load()); + if (entry.close_tag) { + (*list)[i].fdsan_owner = entry.close_tag.load(); + } + } + + size_t overflow_offset = offsetof(FdTable, overflow); + uintptr_t overflow = 0; + if (!memory->Read(fdsan_table_address + overflow_offset, &overflow, sizeof(overflow))) { + ALOGE("failed to read fdsan table overflow pointer: %s", strerror(errno)); + return; + } + + if (!overflow) { + return; + } + + size_t overflow_length; + if (!memory->Read(overflow, &overflow_length, sizeof(overflow_length))) { + ALOGE("failed to read fdsan overflow table length: %s", strerror(errno)); + return; + } + + if (overflow_length > 131072) { + ALOGE("unreasonable large fdsan overflow table size %zu, bailing out", overflow_length); + return; + } + + for (size_t i = 0; i < overflow_length; ++i) { + int fd = i + inline_fds; + uint64_t address = overflow + offsetof(FdTableOverflow, entries) + i * sizeof(FdEntry); + FdEntry entry; + if (!memory->Read(address, &entry, sizeof(entry))) { + ALOGE("failed to read fdsan overflow entry for fd %d: %s", fd, strerror(errno)); + return; + } + if (entry.close_tag) { + (*list)[fd].fdsan_owner = entry.close_tag; + } + } + return; +} + void dump_open_files_list(log_t* log, const OpenFilesList& files, const char* prefix) { - for (auto& file : files) { - _LOG(log, logtype::OPEN_FILES, "%sfd %i: %s\n", prefix, file.first, file.second.c_str()); + for (auto& [fd, entry] : files) { + const std::optional& path = entry.path; + const std::optional& fdsan_owner = entry.fdsan_owner; + if (path && fdsan_owner) { + _LOG(log, logtype::OPEN_FILES, "%sfd %i: %s (owned by %#" PRIx64 ")\n", prefix, fd, + path->c_str(), *fdsan_owner); + } else if (path && !fdsan_owner) { + _LOG(log, logtype::OPEN_FILES, "%sfd %i: %s (unowned)\n", prefix, fd, path->c_str()); + } else if (!path && fdsan_owner) { + _LOG(log, logtype::OPEN_FILES, "%sfd %i: (owned by %#" PRIx64 ")\n", prefix, fd, + *fdsan_owner); + } else { + ALOGE("OpenFilesList contains an entry (fd %d) with no path or owner", fd); + } } } diff --git a/debuggerd/libdebuggerd/test/open_files_list_test.cpp b/debuggerd/libdebuggerd/test/open_files_list_test.cpp index acac72c22..d7036fd39 100644 --- a/debuggerd/libdebuggerd/test/open_files_list_test.cpp +++ b/debuggerd/libdebuggerd/test/open_files_list_test.cpp @@ -34,13 +34,13 @@ TEST(OpenFilesListTest, BasicTest) { // Get the list of open files for this process. OpenFilesList list; - populate_open_files_list(getpid(), &list); + populate_open_files_list(&list, getpid()); // Verify our open file is in the list. bool found = false; - for (auto& file : list) { + for (auto& file : list) { if (file.first == tf.fd) { - EXPECT_EQ(file.second, std::string(tf.path)); + EXPECT_EQ(file.second.path.value_or(""), std::string(tf.path)); found = true; break; }