| Index: base/process/memory_mac.mm
|
| diff --git a/base/process/memory_mac.mm b/base/process/memory_mac.mm
|
| index e59e63fa87f1890b64348d2da5c1f7c48af47384..0f0414bb4b009f2c87ab6c3d49fcb4caeeb86d9a 100644
|
| --- a/base/process/memory_mac.mm
|
| +++ b/base/process/memory_mac.mm
|
| @@ -4,12 +4,6 @@
|
|
|
| #include "base/process/memory.h"
|
|
|
| -// AddressSanitizer handles heap corruption, and on 64 bit Macs, the malloc
|
| -// system automatically abort()s on heap corruption.
|
| -#if !defined(ADDRESS_SANITIZER) && ARCH_CPU_32_BITS
|
| -#define HANDLE_MEMORY_CORRUPTION_MANUALLY
|
| -#endif
|
| -
|
| #include <CoreFoundation/CoreFoundation.h>
|
| #include <errno.h>
|
| #include <mach/mach.h>
|
| @@ -27,170 +21,12 @@
|
| #include "third_party/apple_apsl/CFBase.h"
|
| #include "third_party/apple_apsl/malloc.h"
|
|
|
| -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| -#include <dlfcn.h>
|
| -#include <mach-o/nlist.h>
|
| -
|
| -#include "base/threading/thread_local.h"
|
| -#include "third_party/mach_override/mach_override.h"
|
| -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| -
|
| namespace base {
|
|
|
| -// These are helpers for EnableTerminationOnHeapCorruption, which is a no-op
|
| -// on 64 bit Macs.
|
| -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| -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;
|
| -
|
| - DLOG(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() {
|
| - 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);
|
| -}
|
| -
|
| -// Combines ThreadLocalBoolean with AutoReset. It would be convenient
|
| -// to compose ThreadLocalPointer<bool> with base::AutoReset<bool>, but that
|
| -// would require allocating some storage for the bool.
|
| -class ThreadLocalBooleanAutoReset {
|
| - public:
|
| - ThreadLocalBooleanAutoReset(ThreadLocalBoolean* tlb, bool new_value)
|
| - : scoped_tlb_(tlb),
|
| - original_value_(tlb->Get()) {
|
| - scoped_tlb_->Set(new_value);
|
| - }
|
| - ~ThreadLocalBooleanAutoReset() {
|
| - scoped_tlb_->Set(original_value_);
|
| - }
|
| -
|
| - private:
|
| - ThreadLocalBoolean* scoped_tlb_;
|
| - bool original_value_;
|
| -
|
| - DISALLOW_COPY_AND_ASSIGN(ThreadLocalBooleanAutoReset);
|
| -};
|
| -
|
| -base::LazyInstance<ThreadLocalBoolean>::Leaky
|
| - g_unchecked_alloc = LAZY_INSTANCE_INITIALIZER;
|
| -
|
| -// NOTE(shess): This is called when the malloc library noticed that the heap
|
| -// is fubar. Avoid calls which will re-enter the malloc library.
|
| -void CrMallocErrorBreak() {
|
| - g_original_malloc_error_break();
|
| -
|
| - // Out of memory is certainly not heap corruption, and not necessarily
|
| - // something for which the process should be terminated. Leave that decision
|
| - // to the OOM killer.
|
| - if (errno == ENOMEM)
|
| - return;
|
| -
|
| - // The malloc library attempts to log to ASL (syslog) before calling this
|
| - // code, which fails accessing a Unix-domain socket when sandboxed. The
|
| - // failed socket results in writing to a -1 fd, leaving EBADF in errno. If
|
| - // UncheckedMalloc() is on the stack, for large allocations (15k and up) only
|
| - // an OOM failure leads here. Smaller allocations could also arrive here due
|
| - // to freelist corruption, but there is no way to distinguish that from OOM at
|
| - // this point.
|
| - //
|
| - // NOTE(shess): I hypothesize that EPERM case in 10.9 is the same root cause
|
| - // as EBADF. Unfortunately, 10.9's opensource releases don't include malloc
|
| - // source code at this time.
|
| - // <http://crbug.com/312234>
|
| - if ((errno == EBADF || errno == EPERM) && g_unchecked_alloc.Get().Get())
|
| - return;
|
| -
|
| - // A unit test checks this error message, so it needs to be in release builds.
|
| - char buf[1024] =
|
| - "Terminating process due to a potential for future heap corruption: "
|
| - "errno=";
|
| - char errnobuf[] = {
|
| - '0' + ((errno / 100) % 10),
|
| - '0' + ((errno / 10) % 10),
|
| - '0' + (errno % 10),
|
| - '\000'
|
| - };
|
| - COMPILE_ASSERT(ELAST <= 999, errno_too_large_to_encode);
|
| - strlcat(buf, errnobuf, sizeof(buf));
|
| - RAW_LOG(ERROR, buf);
|
| -
|
| - // Crash by writing to NULL+errno to allow analyzing errno from
|
| - // crash dump info (setting a breakpad key would re-enter the malloc
|
| - // library). Max documented errno in intro(2) is actually 102, but
|
| - // it really just needs to be "small" to stay on the right vm page.
|
| - const int kMaxErrno = 256;
|
| - char* volatile death_ptr = NULL;
|
| - death_ptr += std::min(errno, kMaxErrno);
|
| - *death_ptr = '!';
|
| -}
|
| -
|
| -} // namespace
|
| -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| -
|
| void EnableTerminationOnHeapCorruption() {
|
| -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| - // Only override once, otherwise CrMallocErrorBreak() will recurse
|
| - // to itself.
|
| - if (g_original_malloc_error_break)
|
| - return;
|
| -
|
| - malloc_error_break_t malloc_error_break = LookUpMallocErrorBreak();
|
| - if (!malloc_error_break) {
|
| - DLOG(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)
|
| - DLOG(WARNING) << "Could not override malloc_error_break; error = " << err;
|
| -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| +#if !ARCH_CPU_64_BITS
|
| + DLOG(WARNING) << "EnableTerminationOnHeapCorruption only works on 64-bit";
|
| +#endif
|
| }
|
|
|
| // ------------------------------------------------------------------------
|
| @@ -293,9 +129,6 @@ memalign_type g_old_memalign_purgeable;
|
|
|
| void* oom_killer_malloc(struct _malloc_zone_t* zone,
|
| size_t size) {
|
| -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| - ScopedClearErrno clear_errno;
|
| -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| void* result = g_old_malloc(zone, size);
|
| if (!result && size)
|
| debug::BreakDebugger();
|
| @@ -305,9 +138,6 @@ void* oom_killer_malloc(struct _malloc_zone_t* zone,
|
| void* oom_killer_calloc(struct _malloc_zone_t* zone,
|
| size_t num_items,
|
| size_t size) {
|
| -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| - ScopedClearErrno clear_errno;
|
| -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| void* result = g_old_calloc(zone, num_items, size);
|
| if (!result && num_items && size)
|
| debug::BreakDebugger();
|
| @@ -316,9 +146,6 @@ void* oom_killer_calloc(struct _malloc_zone_t* zone,
|
|
|
| void* oom_killer_valloc(struct _malloc_zone_t* zone,
|
| size_t size) {
|
| -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| - ScopedClearErrno clear_errno;
|
| -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| void* result = g_old_valloc(zone, size);
|
| if (!result && size)
|
| debug::BreakDebugger();
|
| @@ -327,18 +154,12 @@ void* oom_killer_valloc(struct _malloc_zone_t* zone,
|
|
|
| void oom_killer_free(struct _malloc_zone_t* zone,
|
| void* ptr) {
|
| -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| - ScopedClearErrno clear_errno;
|
| -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| g_old_free(zone, ptr);
|
| }
|
|
|
| void* oom_killer_realloc(struct _malloc_zone_t* zone,
|
| void* ptr,
|
| size_t size) {
|
| -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| - ScopedClearErrno clear_errno;
|
| -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| void* result = g_old_realloc(zone, ptr, size);
|
| if (!result && size)
|
| debug::BreakDebugger();
|
| @@ -348,15 +169,12 @@ void* oom_killer_realloc(struct _malloc_zone_t* zone,
|
| void* oom_killer_memalign(struct _malloc_zone_t* zone,
|
| size_t alignment,
|
| size_t size) {
|
| -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| - ScopedClearErrno clear_errno;
|
| -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| void* result = g_old_memalign(zone, alignment, size);
|
| // Only die if posix_memalign would have returned ENOMEM, since there are
|
| // other reasons why NULL might be returned (see
|
| // http://opensource.apple.com/source/Libc/Libc-583/gen/malloc.c ).
|
| - if (!result && size && alignment >= sizeof(void*)
|
| - && (alignment & (alignment - 1)) == 0) {
|
| + if (!result && size && alignment >= sizeof(void*) &&
|
| + (alignment & (alignment - 1)) == 0) {
|
| debug::BreakDebugger();
|
| }
|
| return result;
|
| @@ -364,9 +182,6 @@ void* oom_killer_memalign(struct _malloc_zone_t* zone,
|
|
|
| void* oom_killer_malloc_purgeable(struct _malloc_zone_t* zone,
|
| size_t size) {
|
| -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| - ScopedClearErrno clear_errno;
|
| -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| void* result = g_old_malloc_purgeable(zone, size);
|
| if (!result && size)
|
| debug::BreakDebugger();
|
| @@ -376,9 +191,6 @@ void* oom_killer_malloc_purgeable(struct _malloc_zone_t* zone,
|
| void* oom_killer_calloc_purgeable(struct _malloc_zone_t* zone,
|
| size_t num_items,
|
| size_t size) {
|
| -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| - ScopedClearErrno clear_errno;
|
| -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| void* result = g_old_calloc_purgeable(zone, num_items, size);
|
| if (!result && num_items && size)
|
| debug::BreakDebugger();
|
| @@ -387,9 +199,6 @@ void* oom_killer_calloc_purgeable(struct _malloc_zone_t* zone,
|
|
|
| void* oom_killer_valloc_purgeable(struct _malloc_zone_t* zone,
|
| size_t size) {
|
| -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| - ScopedClearErrno clear_errno;
|
| -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| void* result = g_old_valloc_purgeable(zone, size);
|
| if (!result && size)
|
| debug::BreakDebugger();
|
| @@ -398,18 +207,12 @@ void* oom_killer_valloc_purgeable(struct _malloc_zone_t* zone,
|
|
|
| void oom_killer_free_purgeable(struct _malloc_zone_t* zone,
|
| void* ptr) {
|
| -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| - ScopedClearErrno clear_errno;
|
| -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| g_old_free_purgeable(zone, ptr);
|
| }
|
|
|
| void* oom_killer_realloc_purgeable(struct _malloc_zone_t* zone,
|
| void* ptr,
|
| size_t size) {
|
| -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| - ScopedClearErrno clear_errno;
|
| -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| void* result = g_old_realloc_purgeable(zone, ptr, size);
|
| if (!result && size)
|
| debug::BreakDebugger();
|
| @@ -419,9 +222,6 @@ void* oom_killer_realloc_purgeable(struct _malloc_zone_t* zone,
|
| void* oom_killer_memalign_purgeable(struct _malloc_zone_t* zone,
|
| size_t alignment,
|
| size_t size) {
|
| -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| - ScopedClearErrno clear_errno;
|
| -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| void* result = g_old_memalign_purgeable(zone, alignment, size);
|
| // Only die if posix_memalign would have returned ENOMEM, since there are
|
| // other reasons why NULL might be returned (see
|
| @@ -521,10 +321,6 @@ bool UncheckedMalloc(size_t size, void** result) {
|
| *result = malloc(size);
|
| #else
|
| if (g_old_malloc) {
|
| -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| - ScopedClearErrno clear_errno;
|
| - ThreadLocalBooleanAutoReset flag(g_unchecked_alloc.Pointer(), true);
|
| -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| *result = g_old_malloc(malloc_default_zone(), size);
|
| } else {
|
| *result = malloc(size);
|
| @@ -539,10 +335,6 @@ bool UncheckedCalloc(size_t num_items, size_t size, void** result) {
|
| *result = calloc(num_items, size);
|
| #else
|
| if (g_old_calloc) {
|
| -#if defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| - ScopedClearErrno clear_errno;
|
| - ThreadLocalBooleanAutoReset flag(g_unchecked_alloc.Pointer(), true);
|
| -#endif // defined(HANDLE_MEMORY_CORRUPTION_MANUALLY)
|
| *result = g_old_calloc(malloc_default_zone(), num_items, size);
|
| } else {
|
| *result = calloc(num_items, size);
|
|
|