diff options
author | Zeno Albisser <zeno.albisser@digia.com> | 2013-08-15 21:46:11 +0200 |
---|---|---|
committer | Zeno Albisser <zeno.albisser@digia.com> | 2013-08-15 21:46:11 +0200 |
commit | 679147eead574d186ebf3069647b4c23e8ccace6 (patch) | |
tree | fc247a0ac8ff119f7c8550879ebb6d3dd8d1ff69 /chromium/base/process/memory_unittest.cc |
Initial import.
Diffstat (limited to 'chromium/base/process/memory_unittest.cc')
-rw-r--r-- | chromium/base/process/memory_unittest.cc | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/chromium/base/process/memory_unittest.cc b/chromium/base/process/memory_unittest.cc new file mode 100644 index 00000000000..a1f30526aa3 --- /dev/null +++ b/chromium/base/process/memory_unittest.cc @@ -0,0 +1,379 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#define _CRT_SECURE_NO_WARNINGS + +#include "base/process/memory.h" + +#include <limits> + +#include "base/compiler_specific.h" +#include "base/debug/alias.h" +#include "testing/gtest/include/gtest/gtest.h" + +#if defined(OS_WIN) +#include <windows.h> +#endif +#if defined(OS_POSIX) +#include <errno.h> +#endif +#if defined(OS_MACOSX) +#include <malloc/malloc.h> +#include "base/process/memory_unittest_mac.h" +#endif +#if defined(OS_LINUX) +#include <glib.h> +#include <malloc.h> +#endif + +#if defined(OS_WIN) +// HeapQueryInformation function pointer. +typedef BOOL (WINAPI* HeapQueryFn) \ + (HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T, PSIZE_T); + +const int kConstantInModule = 42; + +TEST(ProcessMemoryTest, GetModuleFromAddress) { + // Since the unit tests are their own EXE, this should be + // equivalent to the EXE's HINSTANCE. + // + // kConstantInModule is a constant in this file and + // therefore within the unit test EXE. + EXPECT_EQ(::GetModuleHandle(NULL), + base::GetModuleFromAddress( + const_cast<int*>(&kConstantInModule))); + + // Any address within the kernel32 module should return + // kernel32's HMODULE. Our only assumption here is that + // kernel32 is larger than 4 bytes. + HMODULE kernel32 = ::GetModuleHandle(L"kernel32.dll"); + HMODULE kernel32_from_address = + base::GetModuleFromAddress(reinterpret_cast<DWORD*>(kernel32) + 1); + EXPECT_EQ(kernel32, kernel32_from_address); +} + +TEST(ProcessMemoryTest, EnableLFH) { + ASSERT_TRUE(base::EnableLowFragmentationHeap()); + if (IsDebuggerPresent()) { + // Under these conditions, LFH can't be enabled. There's no point to test + // anything. + const char* no_debug_env = getenv("_NO_DEBUG_HEAP"); + if (!no_debug_env || strcmp(no_debug_env, "1")) + return; + } + HMODULE kernel32 = GetModuleHandle(L"kernel32.dll"); + ASSERT_TRUE(kernel32 != NULL); + HeapQueryFn heap_query = reinterpret_cast<HeapQueryFn>(GetProcAddress( + kernel32, + "HeapQueryInformation")); + + // On Windows 2000, the function is not exported. This is not a reason to + // fail but we won't be able to retrieves information about the heap, so we + // should stop here. + if (heap_query == NULL) + return; + + HANDLE heaps[1024] = { 0 }; + unsigned number_heaps = GetProcessHeaps(1024, heaps); + EXPECT_GT(number_heaps, 0u); + for (unsigned i = 0; i < number_heaps; ++i) { + ULONG flag = 0; + SIZE_T length; + ASSERT_NE(0, heap_query(heaps[i], + HeapCompatibilityInformation, + &flag, + sizeof(flag), + &length)); + // If flag is 0, the heap is a standard heap that does not support + // look-asides. If flag is 1, the heap supports look-asides. If flag is 2, + // the heap is a low-fragmentation heap (LFH). Note that look-asides are not + // supported on the LFH. + + // We don't have any documented way of querying the HEAP_NO_SERIALIZE flag. + EXPECT_LE(flag, 2u); + EXPECT_NE(flag, 1u); + } +} +#endif // defined(OS_WIN) + +#if defined(OS_MACOSX) + +// For the following Mac tests: +// 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. + +#if !defined(ADDRESS_SANITIZER) +// The following code tests the system implementation of malloc() thus no need +// to test it under AddressSanitizer. +TEST(ProcessMemoryTest, MacMallocFailureDoesNotTerminate) { + // Test that ENOMEM doesn't crash via CrMallocErrorBreak two ways: the exit + // code and lack of the error string. The number of bytes is one less than + // MALLOC_ABSOLUTE_MAX_SIZE, more than which the system early-returns NULL and + // does not call through malloc_error_break(). See the comment at + // EnableTerminationOnOutOfMemory() for more information. + void* buf = NULL; + ASSERT_EXIT( + { + base::EnableTerminationOnOutOfMemory(); + + buf = malloc(std::numeric_limits<size_t>::max() - (2 * PAGE_SIZE) - 1); + }, + testing::KilledBySignal(SIGTRAP), + "\\*\\*\\* error: can't allocate region.*" + "(Terminating process due to a potential for future heap " + "corruption){0}"); + + base::debug::Alias(buf); +} +#endif // !defined(ADDRESS_SANITIZER) + +TEST(ProcessMemoryTest, MacTerminateOnHeapCorruption) { + // Assert that freeing an unallocated pointer will crash the process. + char buf[9]; + asm("" : "=r" (buf)); // Prevent clang from being too smart. +#if ARCH_CPU_64_BITS + // On 64 bit Macs, the malloc system automatically abort()s on heap corruption + // but does not output anything. + ASSERT_DEATH(free(buf), ""); +#elif defined(ADDRESS_SANITIZER) + // AddressSanitizer replaces malloc() and prints a different error message on + // heap corruption. + ASSERT_DEATH(free(buf), "attempting free on address which " + "was not malloc\\(\\)-ed"); +#else + 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 // ARCH_CPU_64_BITS || defined(ADDRESS_SANITIZER) +} + +#endif // defined(OS_MACOSX) + +// Android doesn't implement set_new_handler, so we can't use the +// OutOfMemoryTest cases. +// OpenBSD does not support these tests either. +// AddressSanitizer and ThreadSanitizer define the malloc()/free()/etc. +// functions so that they don't crash if the program is out of memory, so the +// OOM tests aren't supposed to work. +// TODO(vandebo) make this work on Windows too. +#if !defined(OS_ANDROID) && !defined(OS_OPENBSD) && \ + !defined(OS_WIN) && \ + !defined(ADDRESS_SANITIZER) && !defined(THREAD_SANITIZER) + +#if defined(USE_TCMALLOC) +extern "C" { +int tc_set_new_mode(int mode); +} +#endif // defined(USE_TCMALLOC) + +class OutOfMemoryDeathTest : public testing::Test { + public: + OutOfMemoryDeathTest() + : value_(NULL), + // Make test size as large as possible minus a few pages so + // that alignment or other rounding doesn't make it wrap. + test_size_(std::numeric_limits<std::size_t>::max() - 12 * 1024), + signed_test_size_(std::numeric_limits<ssize_t>::max()) { + } + +#if defined(USE_TCMALLOC) + virtual void SetUp() OVERRIDE { + tc_set_new_mode(1); + } + + virtual void TearDown() OVERRIDE { + tc_set_new_mode(0); + } +#endif // defined(USE_TCMALLOC) + + void SetUpInDeathAssert() { + // Must call EnableTerminationOnOutOfMemory() because that is called from + // chrome's main function and therefore hasn't been called yet. + // Since this call may result in another thread being created and death + // tests shouldn't be started in a multithread environment, this call + // should be done inside of the ASSERT_DEATH. + base::EnableTerminationOnOutOfMemory(); + } + + void* value_; + size_t test_size_; + ssize_t signed_test_size_; +}; + +TEST_F(OutOfMemoryDeathTest, New) { + ASSERT_DEATH({ + SetUpInDeathAssert(); + value_ = operator new(test_size_); + }, ""); +} + +TEST_F(OutOfMemoryDeathTest, NewArray) { + ASSERT_DEATH({ + SetUpInDeathAssert(); + value_ = new char[test_size_]; + }, ""); +} + +TEST_F(OutOfMemoryDeathTest, Malloc) { + ASSERT_DEATH({ + SetUpInDeathAssert(); + value_ = malloc(test_size_); + }, ""); +} + +TEST_F(OutOfMemoryDeathTest, Realloc) { + ASSERT_DEATH({ + SetUpInDeathAssert(); + value_ = realloc(NULL, test_size_); + }, ""); +} + +TEST_F(OutOfMemoryDeathTest, Calloc) { + ASSERT_DEATH({ + SetUpInDeathAssert(); + value_ = calloc(1024, test_size_ / 1024L); + }, ""); +} + +TEST_F(OutOfMemoryDeathTest, Valloc) { + ASSERT_DEATH({ + SetUpInDeathAssert(); + value_ = valloc(test_size_); + }, ""); +} + +#if defined(OS_LINUX) +TEST_F(OutOfMemoryDeathTest, Pvalloc) { + ASSERT_DEATH({ + SetUpInDeathAssert(); + value_ = pvalloc(test_size_); + }, ""); +} + +TEST_F(OutOfMemoryDeathTest, Memalign) { + ASSERT_DEATH({ + SetUpInDeathAssert(); + value_ = memalign(4, test_size_); + }, ""); +} + +TEST_F(OutOfMemoryDeathTest, ViaSharedLibraries) { + // g_try_malloc is documented to return NULL on failure. (g_malloc is the + // 'safe' default that crashes if allocation fails). However, since we have + // hopefully overridden malloc, even g_try_malloc should fail. This tests + // that the run-time symbol resolution is overriding malloc for shared + // libraries as well as for our code. + ASSERT_DEATH({ + SetUpInDeathAssert(); + value_ = g_try_malloc(test_size_); + }, ""); +} +#endif // OS_LINUX + +// Android doesn't implement posix_memalign(). +#if defined(OS_POSIX) && !defined(OS_ANDROID) +TEST_F(OutOfMemoryDeathTest, 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({ + SetUpInDeathAssert(); + EXPECT_EQ(ENOMEM, posix_memalign(&value_, 8, test_size_)); + }, ""); +} +#endif // defined(OS_POSIX) && !defined(OS_ANDROID) + +#if defined(OS_MACOSX) + +// Purgeable zone tests + +TEST_F(OutOfMemoryDeathTest, MallocPurgeable) { + malloc_zone_t* zone = malloc_default_purgeable_zone(); + ASSERT_DEATH({ + SetUpInDeathAssert(); + value_ = malloc_zone_malloc(zone, test_size_); + }, ""); +} + +TEST_F(OutOfMemoryDeathTest, ReallocPurgeable) { + malloc_zone_t* zone = malloc_default_purgeable_zone(); + ASSERT_DEATH({ + SetUpInDeathAssert(); + value_ = malloc_zone_realloc(zone, NULL, test_size_); + }, ""); +} + +TEST_F(OutOfMemoryDeathTest, CallocPurgeable) { + malloc_zone_t* zone = malloc_default_purgeable_zone(); + ASSERT_DEATH({ + SetUpInDeathAssert(); + value_ = malloc_zone_calloc(zone, 1024, test_size_ / 1024L); + }, ""); +} + +TEST_F(OutOfMemoryDeathTest, VallocPurgeable) { + malloc_zone_t* zone = malloc_default_purgeable_zone(); + ASSERT_DEATH({ + SetUpInDeathAssert(); + value_ = malloc_zone_valloc(zone, test_size_); + }, ""); +} + +TEST_F(OutOfMemoryDeathTest, PosixMemalignPurgeable) { + malloc_zone_t* zone = malloc_default_purgeable_zone(); + ASSERT_DEATH({ + SetUpInDeathAssert(); + value_ = malloc_zone_memalign(zone, 8, test_size_); + }, ""); +} + +// Since these allocation functions take a signed size, it's possible that +// calling them just once won't be enough to exhaust memory. In the 32-bit +// environment, it's likely that these allocation attempts will fail because +// not enough contiguous address space is available. In the 64-bit environment, +// it's likely that they'll fail because they would require a preposterous +// amount of (virtual) memory. + +TEST_F(OutOfMemoryDeathTest, CFAllocatorSystemDefault) { + ASSERT_DEATH({ + SetUpInDeathAssert(); + while ((value_ = + base::AllocateViaCFAllocatorSystemDefault(signed_test_size_))) {} + }, ""); +} + +TEST_F(OutOfMemoryDeathTest, CFAllocatorMalloc) { + ASSERT_DEATH({ + SetUpInDeathAssert(); + while ((value_ = + base::AllocateViaCFAllocatorMalloc(signed_test_size_))) {} + }, ""); +} + +TEST_F(OutOfMemoryDeathTest, CFAllocatorMallocZone) { + ASSERT_DEATH({ + SetUpInDeathAssert(); + while ((value_ = + base::AllocateViaCFAllocatorMallocZone(signed_test_size_))) {} + }, ""); +} + +#if !defined(ARCH_CPU_64_BITS) + +// See process_util_unittest_mac.mm for an explanation of why this test isn't +// run in the 64-bit environment. + +TEST_F(OutOfMemoryDeathTest, PsychoticallyBigObjCObject) { + ASSERT_DEATH({ + SetUpInDeathAssert(); + while ((value_ = base::AllocatePsychoticallyBigObjCObject())) {} + }, ""); +} + +#endif // !ARCH_CPU_64_BITS +#endif // OS_MACOSX + +#endif // !defined(OS_ANDROID) && !defined(OS_OPENBSD) && + // !defined(OS_WIN) && !defined(ADDRESS_SANITIZER) |