Index: base/security_unittest.cc |
diff --git a/base/security_unittest.cc b/base/security_unittest.cc |
index d786273c023aa1fbdf33c834f718d07eab057656..a7e3bc4bd6e72e95097fa198702337a7664fc7ac 100644 |
--- a/base/security_unittest.cc |
+++ b/base/security_unittest.cc |
@@ -23,11 +23,46 @@ |
#include <unistd.h> |
#endif |
+#if defined(OS_WIN) |
+#include <new.h> |
+#endif |
+ |
using std::nothrow; |
using std::numeric_limits; |
namespace { |
+#if defined(OS_WIN) |
+// This is a permitted size but exhausts memory pretty quickly. |
+const size_t kLargePermittedAllocation = 0x7FFFE000; |
+ |
+int OnNoMemory(size_t) { |
+ _exit(1); |
+} |
+ |
+void ExhaustMemoryWithMalloc() { |
+ for (;;) { |
+ void* buf = malloc(kLargePermittedAllocation); |
+ if (!buf) |
+ break; |
+ } |
+} |
+ |
+void ExhaustMemoryWithRealloc() { |
+ size_t size = kLargePermittedAllocation; |
+ void* buf = malloc(size); |
+ if (!buf) |
+ return; |
+ for (;;) { |
+ size += kLargePermittedAllocation; |
+ void* new_buf = realloc(buf, size); |
+ if (!buf) |
+ break; |
+ buf = new_buf; |
+ } |
+} |
+#endif |
+ |
// This function acts as a compiler optimization barrier. We use it to |
// prevent the compiler from making an expression a compile-time constant. |
// We also use it so that the compiler doesn't discard certain return values |
@@ -42,15 +77,18 @@ Type HideValueFromCompiler(volatile Type value) { |
return value; |
} |
+// Tcmalloc and Windows allocator shim support setting malloc limits. |
// - NO_TCMALLOC (should be defined if compiled with use_allocator!="tcmalloc") |
// - ADDRESS_SANITIZER and SYZYASAN because they have their own memory allocator |
// - IOS does not use tcmalloc |
// - OS_MACOSX does not use tcmalloc |
-#if !defined(NO_TCMALLOC) && !defined(ADDRESS_SANITIZER) && \ |
- !defined(OS_IOS) && !defined(OS_MACOSX) && !defined(SYZYASAN) |
- #define TCMALLOC_TEST(function) function |
+// - Windows allocator shim defines ALLOCATOR_SHIM |
+#if (!defined(NO_TCMALLOC) || defined(ALLOCATOR_SHIM)) && \ |
+ !defined(ADDRESS_SANITIZER) && !defined(OS_IOS) && !defined(OS_MACOSX) && \ |
+ !defined(SYZYASAN) |
+#define MALLOC_OVERFLOW_TEST(function) function |
#else |
- #define TCMALLOC_TEST(function) DISABLED_##function |
+#define MALLOC_OVERFLOW_TEST(function) DISABLED_##function |
#endif |
// TODO(jln): switch to std::numeric_limits<int>::max() when we switch to |
@@ -64,12 +102,6 @@ bool IsTcMallocBypassed() { |
char* g_slice = getenv("G_SLICE"); |
if (g_slice && !strcmp(g_slice, "always-malloc")) |
return true; |
-#elif defined(OS_WIN) |
- // This should detect a TCMalloc bypass from setting |
- // the CHROME_ALLOCATOR environment variable. |
- char* allocator = getenv("CHROME_ALLOCATOR"); |
- if (allocator && strcmp(allocator, "tcmalloc")) |
- return true; |
#endif |
return false; |
} |
@@ -89,7 +121,7 @@ bool CallocDiesOnOOM() { |
} |
// Fake test that allow to know the state of TCMalloc by looking at bots. |
-TEST(SecurityTest, TCMALLOC_TEST(IsTCMallocDynamicallyBypassed)) { |
+TEST(SecurityTest, MALLOC_OVERFLOW_TEST(IsTCMallocDynamicallyBypassed)) { |
printf("Malloc is dynamically bypassed: %s\n", |
IsTcMallocBypassed() ? "yes." : "no."); |
} |
@@ -99,7 +131,7 @@ TEST(SecurityTest, TCMALLOC_TEST(IsTCMallocDynamicallyBypassed)) { |
// vulnerabilities in libraries that use int instead of size_t. See |
// crbug.com/169327. |
-TEST(SecurityTest, TCMALLOC_TEST(MemoryAllocationRestrictionsMalloc)) { |
+TEST(SecurityTest, MALLOC_OVERFLOW_TEST(MemoryAllocationRestrictionsMalloc)) { |
if (!IsTcMallocBypassed()) { |
scoped_ptr<char, base::FreeDeleter> ptr(static_cast<char*>( |
HideValueFromCompiler(malloc(kTooBigAllocSize)))); |
@@ -107,7 +139,43 @@ TEST(SecurityTest, TCMALLOC_TEST(MemoryAllocationRestrictionsMalloc)) { |
} |
} |
-TEST(SecurityTest, TCMALLOC_TEST(MemoryAllocationRestrictionsCalloc)) { |
+#if defined(GTEST_HAS_DEATH_TEST) && defined(OS_WIN) |
+TEST(SecurityTest, MALLOC_OVERFLOW_TEST(MemoryAllocationMallocDeathTest)) { |
+ _set_new_handler(&OnNoMemory); |
+ _set_new_mode(1); |
+ { |
+ scoped_ptr<char, base::FreeDeleter> ptr; |
+ EXPECT_DEATH(ptr.reset(static_cast<char*>( |
+ HideValueFromCompiler(malloc(kTooBigAllocSize)))), |
+ ""); |
+ ASSERT_TRUE(!ptr); |
+ } |
+ _set_new_handler(NULL); |
+ _set_new_mode(0); |
+} |
+ |
+TEST(SecurityTest, MALLOC_OVERFLOW_TEST(MemoryAllocationExhaustDeathTest)) { |
+ _set_new_handler(&OnNoMemory); |
+ _set_new_mode(1); |
+ { |
+ ASSERT_DEATH(ExhaustMemoryWithMalloc(), ""); |
+ } |
+ _set_new_handler(NULL); |
+ _set_new_mode(0); |
+} |
+ |
+TEST(SecurityTest, MALLOC_OVERFLOW_TEST(MemoryReallocationExhaustDeathTest)) { |
+ _set_new_handler(&OnNoMemory); |
+ _set_new_mode(1); |
+ { |
+ ASSERT_DEATH(ExhaustMemoryWithRealloc(), ""); |
+ } |
+ _set_new_handler(NULL); |
+ _set_new_mode(0); |
+} |
+#endif |
+ |
+TEST(SecurityTest, MALLOC_OVERFLOW_TEST(MemoryAllocationRestrictionsCalloc)) { |
if (!IsTcMallocBypassed()) { |
scoped_ptr<char, base::FreeDeleter> ptr(static_cast<char*>( |
HideValueFromCompiler(calloc(kTooBigAllocSize, 1)))); |
@@ -115,7 +183,7 @@ TEST(SecurityTest, TCMALLOC_TEST(MemoryAllocationRestrictionsCalloc)) { |
} |
} |
-TEST(SecurityTest, TCMALLOC_TEST(MemoryAllocationRestrictionsRealloc)) { |
+TEST(SecurityTest, MALLOC_OVERFLOW_TEST(MemoryAllocationRestrictionsRealloc)) { |
if (!IsTcMallocBypassed()) { |
char* orig_ptr = static_cast<char*>(malloc(1)); |
ASSERT_TRUE(orig_ptr); |
@@ -131,7 +199,7 @@ typedef struct { |
char large_array[kTooBigAllocSize]; |
} VeryLargeStruct; |
-TEST(SecurityTest, TCMALLOC_TEST(MemoryAllocationRestrictionsNew)) { |
+TEST(SecurityTest, MALLOC_OVERFLOW_TEST(MemoryAllocationRestrictionsNew)) { |
if (!IsTcMallocBypassed()) { |
scoped_ptr<VeryLargeStruct> ptr( |
HideValueFromCompiler(new (nothrow) VeryLargeStruct)); |
@@ -139,7 +207,20 @@ TEST(SecurityTest, TCMALLOC_TEST(MemoryAllocationRestrictionsNew)) { |
} |
} |
-TEST(SecurityTest, TCMALLOC_TEST(MemoryAllocationRestrictionsNewArray)) { |
+#if defined(GTEST_HAS_DEATH_TEST) && defined(OS_WIN) |
+TEST(SecurityTest, MALLOC_OVERFLOW_TEST(MemoryAllocationNewDeathTest)) { |
+ _set_new_handler(&OnNoMemory); |
+ { |
+ scoped_ptr<VeryLargeStruct> ptr; |
+ EXPECT_DEATH( |
+ ptr.reset(HideValueFromCompiler(new (nothrow) VeryLargeStruct)), ""); |
+ ASSERT_TRUE(!ptr); |
+ } |
+ _set_new_handler(NULL); |
+} |
+#endif |
+ |
+TEST(SecurityTest, MALLOC_OVERFLOW_TEST(MemoryAllocationRestrictionsNewArray)) { |
if (!IsTcMallocBypassed()) { |
scoped_ptr<char[]> ptr( |
HideValueFromCompiler(new (nothrow) char[kTooBigAllocSize])); |
@@ -242,7 +323,7 @@ bool ArePointersToSameArea(void* ptr1, void* ptr2, size_t size) { |
} |
// Check if TCMalloc uses an underlying random memory allocator. |
-TEST(SecurityTest, TCMALLOC_TEST(RandomMemoryAllocations)) { |
+TEST(SecurityTest, MALLOC_OVERFLOW_TEST(RandomMemoryAllocations)) { |
if (IsTcMallocBypassed()) |
return; |
size_t kPageSize = 4096; // We support x86_64 only. |