Handle failures of copying process data from a core file.

When LinuxCoreDumper fails to copy process data from a core file, it
fills the return buffer with a repeated sequence of a special marker.
However, MinidumpWriter doesn't know about that and may incorrectly
interpret the data. In many cases, MinidumpWriter simply copies the
gibberish data to the minidump, which isn't too bad. However, the
gibberish data may cause MinidumpWriter to behave badly in some other
cases. For example, when MinidumpWriter tries to iterate through the
linked list of all loaded DSOs via the r_map field of a r_debug struct,
if the linked list is filed with the special marker, the code keeps
iterating through the same address.

This CL addresses the issue by having LinuxCoreDumper::CopyFromProcess()
returns a Boolean value to indicate if the expected data is found from
the core file. MinidumpWriter can then decide how to handle that.

BUG=chromium:453484
TEST=Run core2md with the test data attached to chromium:453484.
R=mark@chromium.org

Review URL: https://breakpad.appspot.com/4724002

git-svn-id: http://google-breakpad.googlecode.com/svn/trunk@1420 4c0a9323-5329-0410-9bdc-e9ce6186880e
This commit is contained in:
benchan@chromium.org 2015-02-02 23:27:27 +00:00
parent 8aa26b79f9
commit 4c01a9c389
6 changed files with 31 additions and 16 deletions

View file

@ -74,7 +74,7 @@ bool LinuxCoreDumper::BuildProcPath(char* path, pid_t pid,
return true; return true;
} }
void LinuxCoreDumper::CopyFromProcess(void* dest, pid_t child, bool LinuxCoreDumper::CopyFromProcess(void* dest, pid_t child,
const void* src, size_t length) { const void* src, size_t length) {
ElfCoreDump::Addr virtual_address = reinterpret_cast<ElfCoreDump::Addr>(src); ElfCoreDump::Addr virtual_address = reinterpret_cast<ElfCoreDump::Addr>(src);
// TODO(benchan): Investigate whether the data to be copied could span // TODO(benchan): Investigate whether the data to be copied could span
@ -84,7 +84,9 @@ void LinuxCoreDumper::CopyFromProcess(void* dest, pid_t child,
// If the data segment is not found in the core dump, fill the result // If the data segment is not found in the core dump, fill the result
// with marker characters. // with marker characters.
memset(dest, 0xab, length); memset(dest, 0xab, length);
return false;
} }
return true;
} }
bool LinuxCoreDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) { bool LinuxCoreDumper::GetThreadInfoByIndex(size_t index, ThreadInfo* info) {

View file

@ -68,8 +68,9 @@ class LinuxCoreDumper : public LinuxDumper {
// Copies content of |length| bytes from a given process |child|, // Copies content of |length| bytes from a given process |child|,
// starting from |src|, into |dest|. This method extracts the content // starting from |src|, into |dest|. This method extracts the content
// the core dump and fills |dest| with a sequence of marker bytes // the core dump and fills |dest| with a sequence of marker bytes
// if the expected data is not found in the core dump. // if the expected data is not found in the core dump. Returns true if
virtual void CopyFromProcess(void* dest, pid_t child, const void* src, // the expected data is found in the core dump.
virtual bool CopyFromProcess(void* dest, pid_t child, const void* src,
size_t length); size_t length);
// Implements LinuxDumper::GetThreadInfoByIndex(). // Implements LinuxDumper::GetThreadInfoByIndex().

View file

@ -100,8 +100,8 @@ class LinuxDumper {
PageAllocator* allocator() { return &allocator_; } PageAllocator* allocator() { return &allocator_; }
// Copy content of |length| bytes from a given process |child|, // Copy content of |length| bytes from a given process |child|,
// starting from |src|, into |dest|. // starting from |src|, into |dest|. Returns true on success.
virtual void CopyFromProcess(void* dest, pid_t child, const void* src, virtual bool CopyFromProcess(void* dest, pid_t child, const void* src,
size_t length) = 0; size_t length) = 0;
// Builds a proc path for a certain pid for a node (/proc/<pid>/<node>). // Builds a proc path for a certain pid for a node (/proc/<pid>/<node>).

View file

@ -130,7 +130,7 @@ bool LinuxPtraceDumper::BuildProcPath(char* path, pid_t pid,
return true; return true;
} }
void LinuxPtraceDumper::CopyFromProcess(void* dest, pid_t child, bool LinuxPtraceDumper::CopyFromProcess(void* dest, pid_t child,
const void* src, size_t length) { const void* src, size_t length) {
unsigned long tmp = 55; unsigned long tmp = 55;
size_t done = 0; size_t done = 0;
@ -146,6 +146,7 @@ void LinuxPtraceDumper::CopyFromProcess(void* dest, pid_t child,
my_memcpy(local + done, &tmp, l); my_memcpy(local + done, &tmp, l);
done += l; done += l;
} }
return true;
} }
// Read thread info from /proc/$pid/status. // Read thread info from /proc/$pid/status.

View file

@ -55,8 +55,8 @@ class LinuxPtraceDumper : public LinuxDumper {
// Implements LinuxDumper::CopyFromProcess(). // Implements LinuxDumper::CopyFromProcess().
// Copies content of |length| bytes from a given process |child|, // Copies content of |length| bytes from a given process |child|,
// starting from |src|, into |dest|. This method uses ptrace to extract // starting from |src|, into |dest|. This method uses ptrace to extract
// the content from the target process. // the content from the target process. Always returns true.
virtual void CopyFromProcess(void* dest, pid_t child, const void* src, virtual bool CopyFromProcess(void* dest, pid_t child, const void* src,
size_t length); size_t length);
// Implements LinuxDumper::GetThreadInfoByIndex(). // Implements LinuxDumper::GetThreadInfoByIndex().

View file

@ -658,7 +658,9 @@ class MinidumpWriter {
ElfW(Addr) dyn_addr = 0; ElfW(Addr) dyn_addr = 0;
for (; phnum >= 0; phnum--, phdr++) { for (; phnum >= 0; phnum--, phdr++) {
ElfW(Phdr) ph; ElfW(Phdr) ph;
dumper_->CopyFromProcess(&ph, GetCrashThread(), phdr, sizeof(ph)); if (!dumper_->CopyFromProcess(&ph, GetCrashThread(), phdr, sizeof(ph)))
return false;
// Adjust base address with the virtual address of the PT_LOAD segment // Adjust base address with the virtual address of the PT_LOAD segment
// corresponding to offset 0 // corresponding to offset 0
if (ph.p_type == PT_LOAD && ph.p_offset == 0) { if (ph.p_type == PT_LOAD && ph.p_offset == 0) {
@ -679,11 +681,14 @@ class MinidumpWriter {
struct r_debug* r_debug = NULL; struct r_debug* r_debug = NULL;
uint32_t dynamic_length = 0; uint32_t dynamic_length = 0;
for (int i = 0;;) { for (int i = 0; ; ++i) {
ElfW(Dyn) dyn; ElfW(Dyn) dyn;
dynamic_length += sizeof(dyn); dynamic_length += sizeof(dyn);
dumper_->CopyFromProcess(&dyn, GetCrashThread(), dynamic+i++, if (!dumper_->CopyFromProcess(&dyn, GetCrashThread(), dynamic + i,
sizeof(dyn)); sizeof(dyn))) {
return false;
}
if (dyn.d_tag == DT_DEBUG) { if (dyn.d_tag == DT_DEBUG) {
r_debug = reinterpret_cast<struct r_debug*>(dyn.d_un.d_ptr); r_debug = reinterpret_cast<struct r_debug*>(dyn.d_un.d_ptr);
continue; continue;
@ -703,11 +708,15 @@ class MinidumpWriter {
// Count the number of loaded DSOs // Count the number of loaded DSOs
int dso_count = 0; int dso_count = 0;
struct r_debug debug_entry; struct r_debug debug_entry;
dumper_->CopyFromProcess(&debug_entry, GetCrashThread(), r_debug, if (!dumper_->CopyFromProcess(&debug_entry, GetCrashThread(), r_debug,
sizeof(debug_entry)); sizeof(debug_entry))) {
return false;
}
for (struct link_map* ptr = debug_entry.r_map; ptr; ) { for (struct link_map* ptr = debug_entry.r_map; ptr; ) {
struct link_map map; struct link_map map;
dumper_->CopyFromProcess(&map, GetCrashThread(), ptr, sizeof(map)); if (!dumper_->CopyFromProcess(&map, GetCrashThread(), ptr, sizeof(map)))
return false;
ptr = map.l_next; ptr = map.l_next;
dso_count++; dso_count++;
} }
@ -725,7 +734,9 @@ class MinidumpWriter {
// Iterate over DSOs and write their information to mini dump // Iterate over DSOs and write their information to mini dump
for (struct link_map* ptr = debug_entry.r_map; ptr; ) { for (struct link_map* ptr = debug_entry.r_map; ptr; ) {
struct link_map map; struct link_map map;
dumper_->CopyFromProcess(&map, GetCrashThread(), ptr, sizeof(map)); if (!dumper_->CopyFromProcess(&map, GetCrashThread(), ptr, sizeof(map)))
return false;
ptr = map.l_next; ptr = map.l_next;
char filename[257] = { 0 }; char filename[257] = { 0 };
if (map.l_name) { if (map.l_name) {