diff options
author | avi@chromium.org <avi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-19 20:23:46 +0000 |
---|---|---|
committer | avi@chromium.org <avi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-05-19 20:23:46 +0000 |
commit | a1a82d5215198f9ed128f6fc0b1f14cdf3034867 (patch) | |
tree | 26e6d481fbcd1c45e1c58b50065dd7fb9c75eeb1 /base | |
parent | de72a9ee38aff40738a6d46587760236d959c4f1 (diff) | |
download | chromium_src-a1a82d5215198f9ed128f6fc0b1f14cdf3034867.zip chromium_src-a1a82d5215198f9ed128f6fc0b1f14cdf3034867.tar.gz chromium_src-a1a82d5215198f9ed128f6fc0b1f14cdf3034867.tar.bz2 |
Patch out posix_memalign in the out-of-memory killer.
BUG=http://crbug.com/44189
TEST=included in base_unittests
Review URL: http://codereview.chromium.org/2110010
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@47714 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base')
-rw-r--r-- | base/process_util_mac.mm | 87 | ||||
-rw-r--r-- | base/process_util_unittest.cc | 26 |
2 files changed, 97 insertions, 16 deletions
diff --git a/base/process_util_mac.mm b/base/process_util_mac.mm index 602be56..136e650 100644 --- a/base/process_util_mac.mm +++ b/base/process_util_mac.mm @@ -357,7 +357,33 @@ namespace { bool g_oom_killer_enabled; -// === C malloc/calloc/valloc/realloc === +// === C malloc/calloc/valloc/realloc/posix_memalign === + +// The extended version of malloc_zone_t from the 10.6 SDK's <malloc/malloc.h>, +// included here to allow for compilation in 10.5. (10.5 has version 3 zone +// allocators, while 10.6 has version 6 allocators.) +struct ChromeMallocZone { + void* reserved1; + void* reserved2; + size_t (*size)(struct _malloc_zone_t* zone, const void* ptr); + void* (*malloc)(struct _malloc_zone_t* zone, size_t size); + void* (*calloc)(struct _malloc_zone_t* zone, size_t num_items, size_t size); + void* (*valloc)(struct _malloc_zone_t* zone, size_t size); + void (*free)(struct _malloc_zone_t* zone, void* ptr); + void* (*realloc)(struct _malloc_zone_t* zone, void* ptr, size_t size); + void (*destroy)(struct _malloc_zone_t* zone); + const char* zone_name; + unsigned (*batch_malloc)(struct _malloc_zone_t* zone, size_t size, + void** results, unsigned num_requested); + void (*batch_free)(struct _malloc_zone_t* zone, void** to_be_freed, + unsigned num_to_be_freed); + struct malloc_introspection_t* introspect; + unsigned version; + void* (*memalign)(struct _malloc_zone_t* zone, size_t alignment, + size_t size); // version >= 5 + void (*free_definite_size)(struct _malloc_zone_t* zone, void* ptr, + size_t size); // version >= 6 +}; typedef void* (*malloc_type)(struct _malloc_zone_t* zone, size_t size); @@ -369,16 +395,20 @@ typedef void* (*valloc_type)(struct _malloc_zone_t* zone, typedef void* (*realloc_type)(struct _malloc_zone_t* zone, void* ptr, size_t size); +typedef void* (*memalign_type)(struct _malloc_zone_t* zone, + size_t alignment, + size_t size); malloc_type g_old_malloc; calloc_type g_old_calloc; valloc_type g_old_valloc; realloc_type g_old_realloc; +memalign_type g_old_memalign; void* oom_killer_malloc(struct _malloc_zone_t* zone, size_t size) { void* result = g_old_malloc(zone, size); - if (size && !result) + if (!result && size) DebugUtil::BreakDebugger(); return result; } @@ -387,7 +417,7 @@ void* oom_killer_calloc(struct _malloc_zone_t* zone, size_t num_items, size_t size) { void* result = g_old_calloc(zone, num_items, size); - if (num_items && size && !result) + if (!result && num_items && size) DebugUtil::BreakDebugger(); return result; } @@ -395,7 +425,7 @@ void* oom_killer_calloc(struct _malloc_zone_t* zone, void* oom_killer_valloc(struct _malloc_zone_t* zone, size_t size) { void* result = g_old_valloc(zone, size); - if (size && !result) + if (!result && size) DebugUtil::BreakDebugger(); return result; } @@ -404,11 +434,25 @@ void* oom_killer_realloc(struct _malloc_zone_t* zone, void* ptr, size_t size) { void* result = g_old_realloc(zone, ptr, size); - if (size && !result) + if (!result && size) DebugUtil::BreakDebugger(); return result; } +void* oom_killer_memalign(struct _malloc_zone_t* zone, + size_t alignment, + size_t size) { + 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) { + DebugUtil::BreakDebugger(); + } + return result; +} + // === C++ operator new === void oom_killer_new() { @@ -502,7 +546,7 @@ void EnableTerminationOnOutOfMemory() { g_oom_killer_enabled = true; - // === C malloc/calloc/valloc/realloc === + // === C malloc/calloc/valloc/realloc/posix_memalign === // This approach is not perfect, as requests for amounts of memory larger than // MALLOC_ABSOLUTE_MAX_SIZE (currently SIZE_T_MAX - (2 * PAGE_SIZE)) will @@ -511,8 +555,8 @@ void EnableTerminationOnOutOfMemory() { // Unfortunately, it's the best we can do. Also note that this does not affect // allocations from non-default zones. - CHECK(!g_old_malloc && !g_old_calloc && !g_old_valloc && !g_old_realloc) - << "Old allocators unexpectedly non-null"; + CHECK(!g_old_malloc && !g_old_calloc && !g_old_valloc && !g_old_realloc && + !g_old_memalign) << "Old allocators unexpectedly non-null"; int32 major; int32 minor; @@ -520,7 +564,8 @@ void EnableTerminationOnOutOfMemory() { SysInfo::OperatingSystemVersionNumbers(&major, &minor, &bugfix); bool zone_allocators_protected = ((major == 10 && minor > 6) || major > 10); - malloc_zone_t* default_zone = malloc_default_zone(); + ChromeMallocZone* default_zone = + reinterpret_cast<ChromeMallocZone*>(malloc_default_zone()); vm_address_t page_start = NULL; vm_size_t len = 0; @@ -545,10 +590,34 @@ void EnableTerminationOnOutOfMemory() { default_zone->valloc = oom_killer_valloc; default_zone->realloc = oom_killer_realloc; + if (default_zone->version >= 5) { + g_old_memalign = default_zone->memalign; + if (g_old_memalign) + default_zone->memalign = oom_killer_memalign; + } + if (zone_allocators_protected) { mprotect(reinterpret_cast<void*>(page_start), len, PROT_READ); } + // === C malloc_zone_batch_malloc === + + // batch_malloc is omitted because the default malloc zone's implementation + // only supports batch_malloc for "tiny" allocations from the free list. It + // will fail for allocations larger than "tiny", and will only allocate as + // many blocks as it's able to from the free list. These factors mean that it + // can return less than the requested memory even in a non-out-of-memory + // situation. There's no good way to detect whether a batch_malloc failure is + // due to these other factors, or due to genuine memory or address space + // exhaustion. The fact that it only allocates space from the "tiny" free list + // means that it's likely that a failure will not be due to memory exhaustion. + // Similarly, these constraints on batch_malloc mean that callers must always + // be expecting to receive less memory than was requested, even in situations + // where memory pressure is not a concern. Finally, the only public interface + // to batch_malloc is malloc_zone_batch_malloc, which is specific to the + // system's malloc implementation. It's unlikely that anyone's even heard of + // it. + // === C++ operator new === // Yes, operator new does call through to malloc, but this will catch failures diff --git a/base/process_util_unittest.cc b/base/process_util_unittest.cc index d58db37..96dca2f 100644 --- a/base/process_util_unittest.cc +++ b/base/process_util_unittest.cc @@ -17,12 +17,12 @@ #include "testing/gtest/include/gtest/gtest.h" #if defined(OS_LINUX) -#include <dlfcn.h> #include <errno.h> #include <malloc.h> #include <glib.h> #endif #if defined(OS_POSIX) +#include <dlfcn.h> #include <fcntl.h> #include <sys/resource.h> #include <sys/socket.h> @@ -617,15 +617,27 @@ TEST_F(OutOfMemoryTest, ViaSharedLibraries) { // libraries as well as for our code. ASSERT_DEATH(value_ = g_try_malloc(test_size_), ""); } +#endif // OS_LINUX - +#if defined(OS_POSIX) TEST_F(OutOfMemoryTest, Posix_memalign) { - // Grab the return value of posix_memalign to silence a compiler warning - // about unused return values. We don't actually care about the return - // value, since we're asserting death. - ASSERT_DEATH(EXPECT_EQ(ENOMEM, posix_memalign(&value_, 8, test_size_)), ""); + typedef int (*memalign_t)(void **, size_t, size_t); +#if defined(OS_MACOSX) + // posix_memalign only exists on >= 10.6. Use dlsym to grab it at runtime + // because it may not be present in the SDK used for compilation. + memalign_t memalign = + reinterpret_cast<memalign_t>(dlsym(RTLD_DEFAULT, "posix_memalign")); +#else + memalign_t memalign = posix_memalign; +#endif // OS_* + if (memalign) { + // Grab the return value of posix_memalign to silence a compiler warning + // about unused return values. We don't actually care about the return + // value, since we're asserting death. + ASSERT_DEATH(EXPECT_EQ(ENOMEM, memalign(&value_, 8, test_size_)), ""); + } } -#endif // OS_LINUX +#endif // OS_POSIX #if defined(OS_MACOSX) |