diff options
-rw-r--r-- | src/client/linux/minidump_writer/linux_dumper.cc | 73 | ||||
-rw-r--r-- | src/client/linux/minidump_writer/linux_dumper.h | 13 |
2 files changed, 71 insertions, 15 deletions
diff --git a/src/client/linux/minidump_writer/linux_dumper.cc b/src/client/linux/minidump_writer/linux_dumper.cc index 578b788c..4885136e 100644 --- a/src/client/linux/minidump_writer/linux_dumper.cc +++ b/src/client/linux/minidump_writer/linux_dumper.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2009, Google Inc. +// Copyright (c) 2010, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -36,7 +36,6 @@ #include <asm/ptrace.h> #include <assert.h> -#include <elf.h> #include <errno.h> #include <fcntl.h> #include <limits.h> @@ -47,7 +46,6 @@ #include <stdlib.h> #include <stdio.h> #include <string.h> -#include <sys/types.h> #include <sys/ptrace.h> #include <sys/wait.h> #include <unistd.h> @@ -61,6 +59,7 @@ #include "third_party/lss/linux_syscall_support.h" static const char kMappedFileUnsafePrefix[] = "/dev/"; +static const char kDeletedSuffix[] = " (deleted)"; // Suspend a thread by attaching to it. static bool SuspendThread(pid_t pid) { @@ -200,7 +199,7 @@ LinuxDumper::BuildProcPath(char* path, pid_t pid, const char* node) const { my_itos(path + 6, pid, pid_len); memcpy(path + 6 + pid_len, "/", 1); memcpy(path + 6 + pid_len + 1, node, node_len); - memcpy(path + total_length, "\0", 1); + path[total_length] = '\0'; } bool @@ -209,11 +208,20 @@ LinuxDumper::ElfFileIdentifierForMapping(unsigned int mapping_id, { assert(mapping_id < mappings_.size()); my_memset(identifier, 0, sizeof(MDGUID)); - const MappingInfo* mapping = mappings_[mapping_id]; - if (IsMappedFileOpenUnsafe(mapping)) { + MappingInfo* mapping = mappings_[mapping_id]; + if (IsMappedFileOpenUnsafe(mapping)) return false; - } - int fd = sys_open(mapping->name, O_RDONLY, 0); + + char filename[NAME_MAX]; + size_t filename_len = my_strlen(mapping->name); + assert(filename_len < NAME_MAX); + if (filename_len >= NAME_MAX) + return false; + memcpy(filename, mapping->name, filename_len); + filename[filename_len] = '\0'; + bool filename_modified = HandleDeletedFileInMapping(filename); + + int fd = sys_open(filename, O_RDONLY, 0); if (fd < 0) return false; struct kernel_stat st; @@ -231,6 +239,8 @@ LinuxDumper::ElfFileIdentifierForMapping(unsigned int mapping_id, bool success = FileID::ElfFileIdentifierFromMappedFile(base, identifier); sys_munmap(base, st.st_size); + if (success && filename_modified) + mapping->name[filename_len - sizeof(kDeletedSuffix) + 1] = '\0'; return success; } @@ -428,11 +438,8 @@ bool LinuxDumper::ThreadInfoGet(pid_t tid, ThreadInfo* info) { #error "This code hasn't been ported to your platform yet." #endif - if (!GetStackInfo(&info->stack, &info->stack_len, - (uintptr_t) stack_pointer)) - return false; - - return true; + return GetStackInfo(&info->stack, &info->stack_len, + (uintptr_t) stack_pointer); } // Get information about the stack, given the stack pointer. We don't try to @@ -447,7 +454,7 @@ bool LinuxDumper::GetStackInfo(const void** stack, size_t* stack_len, reinterpret_cast<uint8_t*>(int_stack_pointer & ~(page_size - 1)); // The number of bytes of stack which we try to capture. - static ptrdiff_t kStackToCapture = 32 * 1024; + static const ptrdiff_t kStackToCapture = 32 * 1024; const MappingInfo* mapping = FindMapping(stack_pointer); if (!mapping) @@ -493,4 +500,42 @@ const MappingInfo* LinuxDumper::FindMapping(const void* address) const { return NULL; } +bool LinuxDumper::HandleDeletedFileInMapping(char* path) { + static const size_t kDeletedSuffixLen = sizeof(kDeletedSuffix) - 1; + + // Check for ' (deleted)' in |path|. + // |path| has to be at least as long as "/x (deleted)". + const size_t path_len = my_strlen(path); + if (path_len < kDeletedSuffixLen + 2) + return false; + if (my_strncmp(path + path_len - kDeletedSuffixLen, kDeletedSuffix, + kDeletedSuffixLen) != 0) { + return false; + } + + // Check |path| against the /proc/pid/exe 'symlink'. + char exe_link[NAME_MAX]; + char new_path[NAME_MAX]; + BuildProcPath(exe_link, pid_, "exe"); + ssize_t new_path_len = sys_readlink(exe_link, new_path, NAME_MAX); + if (new_path_len <= 0 || new_path_len == NAME_MAX) + return false; + new_path[new_path_len] = '\0'; + if (my_strcmp(path, new_path) != 0) + return false; + + // Check to see if someone actually named their executable 'foo (deleted)'. + struct kernel_stat exe_stat; + struct kernel_stat new_path_stat; + if (sys_stat(exe_link, &exe_stat) == 0 && + sys_stat(new_path, &new_path_stat) == 0 && + exe_stat.st_dev == new_path_stat.st_dev && + exe_stat.st_ino == new_path_stat.st_ino) { + return false; + } + + memcpy(path, exe_link, NAME_MAX); + return true; +} + } // namespace google_breakpad diff --git a/src/client/linux/minidump_writer/linux_dumper.h b/src/client/linux/minidump_writer/linux_dumper.h index 71d1188f..16b15f3c 100644 --- a/src/client/linux/minidump_writer/linux_dumper.h +++ b/src/client/linux/minidump_writer/linux_dumper.h @@ -1,4 +1,4 @@ -// Copyright (c) 2009, Google Inc. +// Copyright (c) 2010, Google Inc. // All rights reserved. // // Redistribution and use in source and binary forms, with or without @@ -165,6 +165,17 @@ class LinuxDumper { bool EnumerateMappings(wasteful_vector<MappingInfo*>* result) const; bool EnumerateThreads(wasteful_vector<pid_t>* result) const; + // For the case where a running program has been deleted, it'll show up in + // /proc/pid/maps as "/path/to/program (deleted)". If this is the case, then + // see if '/path/to/program (deleted)' matches /proc/pid/exe and return + // /proc/pid/exe in |path| so ELF identifier generation works correctly. This + // also checks to see if '/path/to/program (deleted)' exists, so it does not + // get fooled by a poorly named binary. + // For programs that don't end with ' (deleted)', this is a no-op. + // This assumes |path| is a buffer with length NAME_MAX. + // Returns true if |path| is modified. + bool HandleDeletedFileInMapping(char* path); + const pid_t pid_; mutable PageAllocator allocator_; |