diff options
-rw-r--r-- | base/DEPS | 1 | ||||
-rw-r--r-- | base/base.gypi | 3 | ||||
-rw-r--r-- | base/process_util_linux.cc | 4 | ||||
-rw-r--r-- | base/process_util_mac.mm | 85 | ||||
-rw-r--r-- | base/process_util_posix.cc | 4 | ||||
-rw-r--r-- | base/process_util_unittest.cc | 15 | ||||
-rw-r--r-- | third_party/mach_override/README.chromium | 3 | ||||
-rw-r--r-- | third_party/mach_override/mach_override.c | 1 |
8 files changed, 111 insertions, 5 deletions
@@ -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 { diff --git a/third_party/mach_override/README.chromium b/third_party/mach_override/README.chromium index 065ade9..75a9938 100644 --- a/third_party/mach_override/README.chromium +++ b/third_party/mach_override/README.chromium @@ -19,4 +19,5 @@ implementations at run time. Local Modifications: -None. All local changes have been upstreamed. +Added the instructions to |possibleInstructions| for the very short +malloc_error_break() on 10.5 so that it can be overriden on that platform. diff --git a/third_party/mach_override/mach_override.c b/third_party/mach_override/mach_override.c index 1274133..2d12e43 100644 --- a/third_party/mach_override/mach_override.c +++ b/third_party/mach_override/mach_override.c @@ -530,6 +530,7 @@ typedef struct { #if defined(__i386__) static AsmInstructionMatch possibleInstructions[] = { + { 0x5, {0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, {0x55, 0x89, 0xe5, 0xc9, 0xc3} }, // push %ebp; mov %esp,%ebp; leave; ret { 0x1, {0xFF}, {0x90} }, // nop { 0x1, {0xFF}, {0x55} }, // push %esp { 0x2, {0xFF, 0xFF}, {0x89, 0xE5} }, // mov %esp,%ebp |