Linux: Attempt to generate an ELF identifier for deleted running binaries.

Review URL: http://breakpad.appspot.com/228001

git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@733 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
thestig@chromium.org 2010-11-19 19:57:07 +00:00
parent d9d863e153
commit 3665a7d09b
2 changed files with 71 additions and 15 deletions

View File

@ -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

View File

@ -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_;