From 4d690507171bff0f29dcf382b6909888d2f3bd17 Mon Sep 17 00:00:00 2001 From: Dominik Laskowski Date: Thu, 3 Mar 2016 16:14:00 -0500 Subject: Add an optional root prefix to Linux dumpers The Linux dumpers use absolute paths for shared libraries referenced by dumps, so they fail to locate them if the crash originated in a chroot. This CL enables callers to specify a root prefix, which is prepended to mapping paths before opening them. BUG=chromium:591792 TEST=make check Review URL: https://codereview.chromium.org/1761023002/ --- .../linux/microdump_writer/microdump_writer.cc | 2 +- .../linux/minidump_writer/linux_core_dumper.cc | 5 ++- .../linux/minidump_writer/linux_core_dumper.h | 4 +- .../minidump_writer/linux_core_dumper_unittest.cc | 10 +++++ src/client/linux/minidump_writer/linux_dumper.cc | 48 +++++++++++----------- src/client/linux/minidump_writer/linux_dumper.h | 22 +++++++--- .../linux/minidump_writer/minidump_writer.cc | 2 +- 7 files changed, 58 insertions(+), 35 deletions(-) (limited to 'src/client') diff --git a/src/client/linux/microdump_writer/microdump_writer.cc b/src/client/linux/microdump_writer/microdump_writer.cc index fc29b714..91697ed8 100644 --- a/src/client/linux/microdump_writer/microdump_writer.cc +++ b/src/client/linux/microdump_writer/microdump_writer.cc @@ -350,7 +350,7 @@ class MicrodumpWriter { char file_name[NAME_MAX]; char file_path[NAME_MAX]; - LinuxDumper::GetMappingEffectiveNameAndPath( + dumper_->GetMappingEffectiveNameAndPath( mapping, file_path, sizeof(file_path), file_name, sizeof(file_name)); LogAppend("M "); diff --git a/src/client/linux/minidump_writer/linux_core_dumper.cc b/src/client/linux/minidump_writer/linux_core_dumper.cc index d7328245..622f0506 100644 --- a/src/client/linux/minidump_writer/linux_core_dumper.cc +++ b/src/client/linux/minidump_writer/linux_core_dumper.cc @@ -49,8 +49,9 @@ namespace google_breakpad { LinuxCoreDumper::LinuxCoreDumper(pid_t pid, const char* core_path, - const char* procfs_path) - : LinuxDumper(pid), + const char* procfs_path, + const char* root_prefix) + : LinuxDumper(pid, root_prefix), core_path_(core_path), procfs_path_(procfs_path), thread_infos_(&allocator_, 8) { diff --git a/src/client/linux/minidump_writer/linux_core_dumper.h b/src/client/linux/minidump_writer/linux_core_dumper.h index 8537896e..8a7c924b 100644 --- a/src/client/linux/minidump_writer/linux_core_dumper.h +++ b/src/client/linux/minidump_writer/linux_core_dumper.h @@ -47,7 +47,9 @@ class LinuxCoreDumper : public LinuxDumper { // its proc files at |procfs_path|. If |procfs_path| is a copy of // /proc/, it should contain the following files: // auxv, cmdline, environ, exe, maps, status - LinuxCoreDumper(pid_t pid, const char* core_path, const char* procfs_path); + // See LinuxDumper for the purpose of |root_prefix|. + LinuxCoreDumper(pid_t pid, const char* core_path, const char* procfs_path, + const char* root_prefix = ""); // Implements LinuxDumper::BuildProcPath(). // Builds a proc path for a certain pid for a node (/proc//). diff --git a/src/client/linux/minidump_writer/linux_core_dumper_unittest.cc b/src/client/linux/minidump_writer/linux_core_dumper_unittest.cc index 8f6a423e..ae0c965b 100644 --- a/src/client/linux/minidump_writer/linux_core_dumper_unittest.cc +++ b/src/client/linux/minidump_writer/linux_core_dumper_unittest.cc @@ -39,6 +39,16 @@ using namespace google_breakpad; +TEST(LinuxCoreDumperTest, GetMappingAbsolutePath) { + const LinuxCoreDumper dumper(getpid(), "core", "/tmp", "/mnt/root"); + const MappingInfo mapping = { 0, 0, 0, false, "/usr/lib/libc.so" }; + + char path[PATH_MAX]; + dumper.GetMappingAbsolutePath(mapping, path); + + EXPECT_STREQ("/mnt/root/usr/lib/libc.so", path); +} + TEST(LinuxCoreDumperTest, BuildProcPath) { const pid_t pid = getpid(); const char procfs_path[] = "/procfs_copy"; diff --git a/src/client/linux/minidump_writer/linux_dumper.cc b/src/client/linux/minidump_writer/linux_dumper.cc index 43b74ad9..8d4df9ad 100644 --- a/src/client/linux/minidump_writer/linux_dumper.cc +++ b/src/client/linux/minidump_writer/linux_dumper.cc @@ -88,14 +88,16 @@ namespace google_breakpad { // All interesting auvx entry types are below AT_SYSINFO_EHDR #define AT_MAX AT_SYSINFO_EHDR -LinuxDumper::LinuxDumper(pid_t pid) +LinuxDumper::LinuxDumper(pid_t pid, const char* root_prefix) : pid_(pid), + root_prefix_(root_prefix), crash_address_(0), crash_signal_(0), crash_thread_(pid), threads_(&allocator_, 8), mappings_(&allocator_), auxv_(&allocator_, AT_MAX + 1) { + assert(root_prefix_ && my_strlen(root_prefix_) < PATH_MAX); // The passed-in size to the constructor (above) is only a hint. // Must call .resize() to do actual initialization of the elements. auxv_.resize(AT_MAX + 1); @@ -139,14 +141,9 @@ LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping, return FileID::ElfFileIdentifierFromMappedFile(linux_gate, identifier); } - char filename[NAME_MAX]; - size_t filename_len = my_strlen(mapping.name); - if (filename_len >= NAME_MAX) { - assert(false); + char filename[PATH_MAX]; + if (!GetMappingAbsolutePath(mapping, filename)) return false; - } - my_memcpy(filename, mapping.name, filename_len); - filename[filename_len] = '\0'; bool filename_modified = HandleDeletedFileInMapping(filename); MemoryMappedFile mapped_file(filename, mapping.offset); @@ -156,13 +153,19 @@ LinuxDumper::ElfFileIdentifierForMapping(const MappingInfo& mapping, bool success = FileID::ElfFileIdentifierFromMappedFile(mapped_file.data(), identifier); if (success && member && filename_modified) { - mappings_[mapping_id]->name[filename_len - + mappings_[mapping_id]->name[my_strlen(mapping.name) - sizeof(kDeletedSuffix) + 1] = '\0'; } return success; } +bool LinuxDumper::GetMappingAbsolutePath(const MappingInfo& mapping, + char path[PATH_MAX]) const { + return my_strlcpy(path, root_prefix_, PATH_MAX) < PATH_MAX && + my_strlcat(path, mapping.name, PATH_MAX) < PATH_MAX; +} + namespace { bool ElfFileSoNameFromMappedFile( const void* elf_base, char* soname, size_t soname_size) { @@ -212,23 +215,16 @@ bool ElfFileSoNameFromMappedFile( // for |mapping|. If the SONAME is found copy it into the passed buffer // |soname| and return true. The size of the buffer is |soname_size|. // The SONAME will be truncated if it is too long to fit in the buffer. -bool ElfFileSoName( +bool ElfFileSoName(const LinuxDumper& dumper, const MappingInfo& mapping, char* soname, size_t soname_size) { if (IsMappedFileOpenUnsafe(mapping)) { // Not safe return false; } - char filename[NAME_MAX]; - size_t filename_len = my_strlen(mapping.name); - if (filename_len >= NAME_MAX) { - assert(false); - // name too long + char filename[PATH_MAX]; + if (!dumper.GetMappingAbsolutePath(mapping, filename)) return false; - } - - my_memcpy(filename, mapping.name, filename_len); - filename[filename_len] = '\0'; MemoryMappedFile mapped_file(filename, mapping.offset); if (!mapped_file.data() || mapped_file.size() < SELFMAG) { @@ -242,7 +238,6 @@ bool ElfFileSoName( } // namespace -// static void LinuxDumper::GetMappingEffectiveNameAndPath(const MappingInfo& mapping, char* file_path, size_t file_path_size, @@ -255,8 +250,10 @@ void LinuxDumper::GetMappingEffectiveNameAndPath(const MappingInfo& mapping, // apk on Android). We try to find the name of the shared object (SONAME) by // looking in the file for ELF sections. bool mapped_from_archive = false; - if (mapping.exec && mapping.offset != 0) - mapped_from_archive = ElfFileSoName(mapping, file_name, file_name_size); + if (mapping.exec && mapping.offset != 0) { + mapped_from_archive = + ElfFileSoName(*this, mapping, file_name, file_name_size); + } if (mapped_from_archive) { // Some tools (e.g., stackwalk) extract the basename from the pathname. In @@ -580,10 +577,13 @@ bool LinuxDumper::HandleDeletedFileInMapping(char* path) const { // Check |path| against the /proc/pid/exe 'symlink'. char exe_link[NAME_MAX]; - char new_path[NAME_MAX]; if (!BuildProcPath(exe_link, pid_, "exe")) return false; - if (!SafeReadLink(exe_link, new_path)) + MappingInfo new_mapping = {0}; + if (!SafeReadLink(exe_link, new_mapping.name)) + return false; + char new_path[PATH_MAX]; + if (!GetMappingAbsolutePath(new_mapping, new_path)) return false; if (my_strcmp(path, new_path) != 0) return false; diff --git a/src/client/linux/minidump_writer/linux_dumper.h b/src/client/linux/minidump_writer/linux_dumper.h index 6a3a100f..f7fe1dd9 100644 --- a/src/client/linux/minidump_writer/linux_dumper.h +++ b/src/client/linux/minidump_writer/linux_dumper.h @@ -72,7 +72,9 @@ const char kLinuxGateLibraryName[] = "linux-gate.so"; class LinuxDumper { public: - explicit LinuxDumper(pid_t pid); + // The |root_prefix| is prepended to mapping paths before opening them, which + // is useful if the crash originates from a chroot. + explicit LinuxDumper(pid_t pid, const char* root_prefix = ""); virtual ~LinuxDumper(); @@ -140,16 +142,21 @@ class LinuxDumper { pid_t crash_thread() const { return crash_thread_; } void set_crash_thread(pid_t crash_thread) { crash_thread_ = crash_thread; } + // Concatenates the |root_prefix_| and |mapping| path. Writes into |path| and + // returns true unless the string is too long. + bool GetMappingAbsolutePath(const MappingInfo& mapping, + char path[PATH_MAX]) const; + // Extracts the effective path and file name of from |mapping|. In most cases // the effective name/path are just the mapping's path and basename. In some // other cases, however, a library can be mapped from an archive (e.g., when // loading .so libs from an apk on Android) and this method is able to // reconstruct the original file name. - static void GetMappingEffectiveNameAndPath(const MappingInfo& mapping, - char* file_path, - size_t file_path_size, - char* file_name, - size_t file_name_size); + void GetMappingEffectiveNameAndPath(const MappingInfo& mapping, + char* file_path, + size_t file_path_size, + char* file_name, + size_t file_name_size); protected: bool ReadAuxv(); @@ -172,6 +179,9 @@ class LinuxDumper { // ID of the crashed process. const pid_t pid_; + // Path of the root directory to which mapping paths are relative. + const char* const root_prefix_; + // Virtual address at which the process crashed. uintptr_t crash_address_; diff --git a/src/client/linux/minidump_writer/minidump_writer.cc b/src/client/linux/minidump_writer/minidump_writer.cc index 04327338..3103761f 100644 --- a/src/client/linux/minidump_writer/minidump_writer.cc +++ b/src/client/linux/minidump_writer/minidump_writer.cc @@ -567,7 +567,7 @@ class MinidumpWriter { char file_name[NAME_MAX]; char file_path[NAME_MAX]; - LinuxDumper::GetMappingEffectiveNameAndPath( + dumper_->GetMappingEffectiveNameAndPath( mapping, file_path, sizeof(file_path), file_name, sizeof(file_name)); const size_t file_name_len = my_strlen(file_name); -- cgit v1.2.1