summaryrefslogtreecommitdiffstats
path: root/base
diff options
context:
space:
mode:
authorrsesek@chromium.org <rsesek@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-08-18 20:23:04 +0000
committerrsesek@chromium.org <rsesek@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-08-18 20:23:04 +0000
commit2558a4d98d5bc9a828d289e7df795e9649745036 (patch)
tree29998b8ebd027673ca3491267d3b020b412ec452 /base
parent65f629a9e07e8e26e3f7a217e080a4e6fc511e4e (diff)
downloadchromium_src-2558a4d98d5bc9a828d289e7df795e9649745036.zip
chromium_src-2558a4d98d5bc9a828d289e7df795e9649745036.tar.gz
chromium_src-2558a4d98d5bc9a828d289e7df795e9649745036.tar.bz2
[Mac] Implement base::EnableTerminationOnHeapCorruption() by overriding malloc_error_break().
This makes malloc_error_break() fatal for all processes in an attempt to get better stack traces when heap corruption may occur. BUG=90884,91068,93191 TEST=See bug 93191 for repro steps. A crash report gets generated with a hopefully more-useful stack. Originally landed: http://src.chromium.org/viewvc/chrome?view=rev&revision=97315 Reverted: http://src.chromium.org/viewvc/chrome?view=rev&revision=97322 Review URL: http://codereview.chromium.org/7670025 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@97351 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base')
-rw-r--r--base/DEPS1
-rw-r--r--base/base.gypi3
-rw-r--r--base/process_util_linux.cc4
-rw-r--r--base/process_util_mac.mm85
-rw-r--r--base/process_util_posix.cc4
-rw-r--r--base/process_util_unittest.cc15
6 files changed, 108 insertions, 4 deletions
diff --git a/base/DEPS b/base/DEPS
index 81e9cda..80df202 100644
--- a/base/DEPS
+++ b/base/DEPS
@@ -5,6 +5,7 @@ include_rules = [
"+third_party/libevent",
"+third_party/dmg_fp",
"+third_party/GTM",
+ "+third_party/mach_override",
"+third_party/modp_b64",
"+third_party/tcmalloc",
diff --git a/base/base.gypi b/base/base.gypi
index 642e47f..4363799 100644
--- a/base/base.gypi
+++ b/base/base.gypi
@@ -536,6 +536,9 @@
'$(SDKROOT)/System/Library/Frameworks/Security.framework',
],
},
+ 'dependencies': [
+ '../third_party/mach_override/mach_override.gyp:mach_override',
+ ],
}],
[ 'OS != "win"', {
'dependencies': ['../third_party/libevent/libevent.gyp:libevent'],
diff --git a/base/process_util_linux.cc b/base/process_util_linux.cc
index 1f71f4d..b350517 100644
--- a/base/process_util_linux.cc
+++ b/base/process_util_linux.cc
@@ -718,6 +718,10 @@ int posix_memalign(void** ptr, size_t alignment, size_t size) {
#endif // !defined(USE_TCMALLOC)
} // extern C
+void EnableTerminationOnHeapCorruption() {
+ // On Linux, there nothing to do AFAIK.
+}
+
void EnableTerminationOnOutOfMemory() {
#if defined(OS_ANDROID)
// Android doesn't support setting a new handler.
diff --git a/base/process_util_mac.mm b/base/process_util_mac.mm
index 7db44d9..ceb4f0a 100644
--- a/base/process_util_mac.mm
+++ b/base/process_util_mac.mm
@@ -12,6 +12,8 @@
#include <mach/mach_vm.h>
#include <mach/shared_region.h>
#include <mach/task.h>
+#include <mach-o/dyld.h>
+#include <mach-o/nlist.h>
#include <malloc/malloc.h>
#import <objc/runtime.h>
#include <spawn.h>
@@ -34,6 +36,7 @@
#include "base/time.h"
#include "third_party/apple_apsl/CFBase.h"
#include "third_party/apple_apsl/malloc.h"
+#include "third_party/mach_override/mach_override.h"
namespace base {
@@ -483,6 +486,88 @@ size_t GetSystemCommitCharge() {
return (data.active_count * page_size) / 1024;
}
+namespace {
+
+// Finds the library path for malloc() and thus the libC part of libSystem,
+// which in Lion is in a separate image.
+const char* LookUpLibCPath() {
+ const void* addr = reinterpret_cast<void*>(&malloc);
+
+ Dl_info info;
+ if (dladdr(addr, &info))
+ return info.dli_fname;
+
+ LOG(WARNING) << "Could not find image path for malloc()";
+ return NULL;
+}
+
+typedef void(*malloc_error_break_t)(void);
+malloc_error_break_t g_original_malloc_error_break = NULL;
+
+// Returns the function pointer for malloc_error_break. This symbol is declared
+// as __private_extern__ and cannot be dlsym()ed. Instead, use nlist() to
+// get it.
+malloc_error_break_t LookUpMallocErrorBreak() {
+#if ARCH_CPU_32_BITS
+ const char* lib_c_path = LookUpLibCPath();
+ if (!lib_c_path)
+ return NULL;
+
+ // Only need to look up two symbols, but nlist() requires a NULL-terminated
+ // array and takes no count.
+ struct nlist nl[3];
+ bzero(&nl, sizeof(nl));
+
+ // The symbol to find.
+ nl[0].n_un.n_name = const_cast<char*>("_malloc_error_break");
+
+ // A reference symbol by which the address of the desired symbol will be
+ // calculated.
+ nl[1].n_un.n_name = const_cast<char*>("_malloc");
+
+ int rv = nlist(lib_c_path, nl);
+ if (rv != 0 || nl[0].n_type == N_UNDF || nl[1].n_type == N_UNDF) {
+ return NULL;
+ }
+
+ // nlist() returns addresses as offsets in the image, not the instruction
+ // pointer in memory. Use the known in-memory address of malloc()
+ // to compute the offset for malloc_error_break().
+ uintptr_t reference_addr = reinterpret_cast<uintptr_t>(&malloc);
+ reference_addr -= nl[1].n_value;
+ reference_addr += nl[0].n_value;
+
+ return reinterpret_cast<malloc_error_break_t>(reference_addr);
+#endif // ARCH_CPU_32_BITS
+
+ return NULL;
+}
+
+void CrMallocErrorBreak() {
+ g_original_malloc_error_break();
+ LOG(ERROR) <<
+ "Terminating process due to a potential for future heap corruption";
+ base::debug::BreakDebugger();
+}
+
+} // namespace
+
+void EnableTerminationOnHeapCorruption() {
+ malloc_error_break_t malloc_error_break = LookUpMallocErrorBreak();
+ if (!malloc_error_break) {
+ LOG(WARNING) << "Could not find malloc_error_break";
+ return;
+ }
+
+ mach_error_t err = mach_override_ptr(
+ (void*)malloc_error_break,
+ (void*)&CrMallocErrorBreak,
+ (void**)&g_original_malloc_error_break);
+
+ if (err != err_none)
+ LOG(WARNING) << "Could not override malloc_error_break; error = " << err;
+}
+
// ------------------------------------------------------------------------
namespace {
diff --git a/base/process_util_posix.cc b/base/process_util_posix.cc
index db380bd..a81acbc 100644
--- a/base/process_util_posix.cc
+++ b/base/process_util_posix.cc
@@ -656,10 +656,6 @@ bool LaunchProcess(const CommandLine& cmdline,
ProcessMetrics::~ProcessMetrics() { }
-void EnableTerminationOnHeapCorruption() {
- // On POSIX, there nothing to do AFAIK.
-}
-
bool EnableInProcessStackDumping() {
// When running in an application, our code typically expects SIGPIPE
// to be ignored. Therefore, when testing that same code, it should run
diff --git a/base/process_util_unittest.cc b/base/process_util_unittest.cc
index 3b05d03..8a653d4 100644
--- a/base/process_util_unittest.cc
+++ b/base/process_util_unittest.cc
@@ -397,6 +397,21 @@ TEST_F(ProcessUtilTest, LaunchAsUser) {
#endif // defined(OS_WIN)
+#if defined(OS_MACOSX)
+
+TEST_F(ProcessUtilTest, MacTerminateOnHeapCorruption) {
+ // Note that base::EnableTerminationOnHeapCorruption() is called as part of
+ // test suite setup and does not need to be done again, else mach_override
+ // will fail.
+
+ char buf[3];
+ ASSERT_DEATH(free(buf), "being freed.*"
+ "\\*\\*\\* set a breakpoint in malloc_error_break to debug.*"
+ "Terminating process due to a potential for future heap corruption");
+}
+
+#endif // defined(OS_MACOSX)
+
#if defined(OS_POSIX)
namespace {