1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #define _CRT_SECURE_NO_WARNINGS
6
7 #include "base/process/memory.h"
8
9 #include <stddef.h>
10
11 #include <limits>
12
13 #include "base/allocator/allocator_check.h"
14 #include "base/allocator/buildflags.h"
15 #include "base/compiler_specific.h"
16 #include "base/debug/alias.h"
17 #include "base/memory/aligned_memory.h"
18 #include "base/strings/stringprintf.h"
19 #include "build/build_config.h"
20 #include "testing/gtest/include/gtest/gtest.h"
21
22 #if defined(OS_WIN)
23 #include <windows.h>
24 #endif
25 #if defined(OS_POSIX)
26 #include <errno.h>
27 #endif
28 #if defined(OS_MACOSX)
29 #include <malloc/malloc.h>
30 #include "base/allocator/allocator_interception_mac.h"
31 #include "base/allocator/allocator_shim.h"
32 #include "base/process/memory_unittest_mac.h"
33 #endif
34 #if defined(OS_LINUX)
35 #include <malloc.h>
36 #include "base/test/malloc_wrapper.h"
37 #endif
38
39 #if defined(OS_WIN)
40
41 #if defined(COMPILER_MSVC)
42 // ssize_t needed for OutOfMemoryTest.
43 #if defined(_WIN64)
44 typedef __int64 ssize_t;
45 #else
46 typedef long ssize_t;
47 #endif
48 #endif
49
50 // HeapQueryInformation function pointer.
51 typedef BOOL (WINAPI* HeapQueryFn) \
52 (HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T, PSIZE_T);
53
54 #endif // defined(OS_WIN)
55
56 #if defined(OS_MACOSX)
57
58 // For the following Mac tests:
59 // Note that base::EnableTerminationOnHeapCorruption() is called as part of
60 // test suite setup and does not need to be done again, else mach_override
61 // will fail.
62
TEST(ProcessMemoryTest,MacTerminateOnHeapCorruption)63 TEST(ProcessMemoryTest, MacTerminateOnHeapCorruption) {
64 #if BUILDFLAG(USE_ALLOCATOR_SHIM)
65 base::allocator::InitializeAllocatorShim();
66 #endif
67 // Assert that freeing an unallocated pointer will crash the process.
68 char buf[9];
69 asm("" : "=r" (buf)); // Prevent clang from being too smart.
70 #if ARCH_CPU_64_BITS
71 // On 64 bit Macs, the malloc system automatically abort()s on heap corruption
72 // but does not output anything.
73 ASSERT_DEATH(free(buf), "");
74 #elif defined(ADDRESS_SANITIZER)
75 // AddressSanitizer replaces malloc() and prints a different error message on
76 // heap corruption.
77 ASSERT_DEATH(free(buf), "attempting free on address which "
78 "was not malloc\\(\\)-ed");
79 #else
80 ADD_FAILURE() << "This test is not supported in this build configuration.";
81 #endif
82
83 #if BUILDFLAG(USE_ALLOCATOR_SHIM)
84 base::allocator::UninterceptMallocZonesForTesting();
85 #endif
86 }
87
88 #endif // defined(OS_MACOSX)
89
TEST(MemoryTest,AllocatorShimWorking)90 TEST(MemoryTest, AllocatorShimWorking) {
91 #if defined(OS_MACOSX)
92 #if BUILDFLAG(USE_ALLOCATOR_SHIM)
93 base::allocator::InitializeAllocatorShim();
94 #endif
95 base::allocator::InterceptAllocationsMac();
96 #endif
97 ASSERT_TRUE(base::allocator::IsAllocatorInitialized());
98
99 #if defined(OS_MACOSX)
100 base::allocator::UninterceptMallocZonesForTesting();
101 #endif
102 }
103
104 // OpenBSD does not support these tests. Don't test these on ASan/TSan/MSan
105 // configurations: only test the real allocator.
106 // Windows only supports these tests with the allocator shim in place.
107 #if !defined(OS_OPENBSD) && BUILDFLAG(USE_ALLOCATOR_SHIM) && \
108 !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
109
110 namespace {
111 #if defined(OS_WIN)
112 // Windows raises an exception rather than using LOG(FATAL) in order to make the
113 // exit code unique to OOM.
114 const char* kOomRegex = "";
115 const int kExitCode = base::win::kOomExceptionCode;
116 #else
117 const char* kOomRegex = "Out of memory";
118 const int kExitCode = 1;
119 #endif
120 } // namespace
121
122 class OutOfMemoryTest : public testing::Test {
123 public:
OutOfMemoryTest()124 OutOfMemoryTest()
125 : value_(nullptr),
126 // Make test size as large as possible minus a few pages so
127 // that alignment or other rounding doesn't make it wrap.
128 test_size_(std::numeric_limits<std::size_t>::max() - 12 * 1024),
129 // A test size that is > 2Gb and will cause the allocators to reject
130 // the allocation due to security restrictions. See crbug.com/169327.
131 insecure_test_size_(std::numeric_limits<int>::max()),
132 signed_test_size_(std::numeric_limits<ssize_t>::max()) {}
133
134 protected:
135 void* value_;
136 size_t test_size_;
137 size_t insecure_test_size_;
138 ssize_t signed_test_size_;
139 };
140
141 class OutOfMemoryDeathTest : public OutOfMemoryTest {
142 public:
SetUpInDeathAssert()143 void SetUpInDeathAssert() {
144 #if defined(OS_MACOSX) && BUILDFLAG(USE_ALLOCATOR_SHIM)
145 base::allocator::InitializeAllocatorShim();
146 #endif
147
148 // Must call EnableTerminationOnOutOfMemory() because that is called from
149 // chrome's main function and therefore hasn't been called yet.
150 // Since this call may result in another thread being created and death
151 // tests shouldn't be started in a multithread environment, this call
152 // should be done inside of the ASSERT_DEATH.
153 base::EnableTerminationOnOutOfMemory();
154 }
155
156 #if defined(OS_MACOSX)
TearDown()157 void TearDown() override {
158 base::allocator::UninterceptMallocZonesForTesting();
159 }
160 #endif
161 };
162
TEST_F(OutOfMemoryDeathTest,New)163 TEST_F(OutOfMemoryDeathTest, New) {
164 ASSERT_EXIT({
165 SetUpInDeathAssert();
166 value_ = operator new(test_size_);
167 }, testing::ExitedWithCode(kExitCode), kOomRegex);
168 }
169
TEST_F(OutOfMemoryDeathTest,NewArray)170 TEST_F(OutOfMemoryDeathTest, NewArray) {
171 ASSERT_EXIT({
172 SetUpInDeathAssert();
173 value_ = new char[test_size_];
174 }, testing::ExitedWithCode(kExitCode), kOomRegex);
175 }
176
TEST_F(OutOfMemoryDeathTest,Malloc)177 TEST_F(OutOfMemoryDeathTest, Malloc) {
178 ASSERT_EXIT({
179 SetUpInDeathAssert();
180 value_ = malloc(test_size_);
181 }, testing::ExitedWithCode(kExitCode), kOomRegex);
182 }
183
TEST_F(OutOfMemoryDeathTest,Realloc)184 TEST_F(OutOfMemoryDeathTest, Realloc) {
185 ASSERT_EXIT({
186 SetUpInDeathAssert();
187 value_ = realloc(nullptr, test_size_);
188 }, testing::ExitedWithCode(kExitCode), kOomRegex);
189 }
190
TEST_F(OutOfMemoryDeathTest,Calloc)191 TEST_F(OutOfMemoryDeathTest, Calloc) {
192 ASSERT_EXIT({
193 SetUpInDeathAssert();
194 value_ = calloc(1024, test_size_ / 1024L);
195 }, testing::ExitedWithCode(kExitCode), kOomRegex);
196 }
197
TEST_F(OutOfMemoryDeathTest,AlignedAlloc)198 TEST_F(OutOfMemoryDeathTest, AlignedAlloc) {
199 ASSERT_EXIT({
200 SetUpInDeathAssert();
201 value_ = base::AlignedAlloc(test_size_, 8);
202 }, testing::ExitedWithCode(kExitCode), kOomRegex);
203 }
204
205 // POSIX does not define an aligned realloc function.
206 #if defined(OS_WIN)
TEST_F(OutOfMemoryDeathTest,AlignedRealloc)207 TEST_F(OutOfMemoryDeathTest, AlignedRealloc) {
208 ASSERT_EXIT({
209 SetUpInDeathAssert();
210 value_ = _aligned_realloc(NULL, test_size_, 8);
211 }, testing::ExitedWithCode(kExitCode), kOomRegex);
212 }
213
214 namespace {
215
216 constexpr uint32_t kUnhandledExceptionExitCode = 0xBADA55;
217
218 // This unhandled exception filter exits the process with an exit code distinct
219 // from the exception code. This is to verify that the out of memory new handler
220 // causes an unhandled exception.
ExitingUnhandledExceptionFilter(EXCEPTION_POINTERS * ExceptionInfo)221 LONG WINAPI ExitingUnhandledExceptionFilter(EXCEPTION_POINTERS* ExceptionInfo) {
222 _exit(kUnhandledExceptionExitCode);
223 }
224
225 } // namespace
226
TEST_F(OutOfMemoryDeathTest,NewHandlerGeneratesUnhandledException)227 TEST_F(OutOfMemoryDeathTest, NewHandlerGeneratesUnhandledException) {
228 ASSERT_EXIT(
229 {
230 SetUpInDeathAssert();
231 SetUnhandledExceptionFilter(&ExitingUnhandledExceptionFilter);
232 value_ = new char[test_size_];
233 },
234 testing::ExitedWithCode(kUnhandledExceptionExitCode), kOomRegex);
235 }
236 #endif // defined(OS_WIN)
237
238 // OS X and Android have no 2Gb allocation limit.
239 // See https://crbug.com/169327.
240 #if !defined(OS_MACOSX) && !defined(OS_ANDROID)
TEST_F(OutOfMemoryDeathTest,SecurityNew)241 TEST_F(OutOfMemoryDeathTest, SecurityNew) {
242 ASSERT_EXIT({
243 SetUpInDeathAssert();
244 value_ = operator new(insecure_test_size_);
245 }, testing::ExitedWithCode(kExitCode), kOomRegex);
246 }
247
TEST_F(OutOfMemoryDeathTest,SecurityNewArray)248 TEST_F(OutOfMemoryDeathTest, SecurityNewArray) {
249 ASSERT_EXIT({
250 SetUpInDeathAssert();
251 value_ = new char[insecure_test_size_];
252 }, testing::ExitedWithCode(kExitCode), kOomRegex);
253 }
254
TEST_F(OutOfMemoryDeathTest,SecurityMalloc)255 TEST_F(OutOfMemoryDeathTest, SecurityMalloc) {
256 ASSERT_EXIT({
257 SetUpInDeathAssert();
258 value_ = malloc(insecure_test_size_);
259 }, testing::ExitedWithCode(kExitCode), kOomRegex);
260 }
261
TEST_F(OutOfMemoryDeathTest,SecurityRealloc)262 TEST_F(OutOfMemoryDeathTest, SecurityRealloc) {
263 ASSERT_EXIT({
264 SetUpInDeathAssert();
265 value_ = realloc(nullptr, insecure_test_size_);
266 }, testing::ExitedWithCode(kExitCode), kOomRegex);
267 }
268
TEST_F(OutOfMemoryDeathTest,SecurityCalloc)269 TEST_F(OutOfMemoryDeathTest, SecurityCalloc) {
270 ASSERT_EXIT({
271 SetUpInDeathAssert();
272 value_ = calloc(1024, insecure_test_size_ / 1024L);
273 }, testing::ExitedWithCode(kExitCode), kOomRegex);
274 }
275
TEST_F(OutOfMemoryDeathTest,SecurityAlignedAlloc)276 TEST_F(OutOfMemoryDeathTest, SecurityAlignedAlloc) {
277 ASSERT_EXIT({
278 SetUpInDeathAssert();
279 value_ = base::AlignedAlloc(insecure_test_size_, 8);
280 }, testing::ExitedWithCode(kExitCode), kOomRegex);
281 }
282
283 // POSIX does not define an aligned realloc function.
284 #if defined(OS_WIN)
TEST_F(OutOfMemoryDeathTest,SecurityAlignedRealloc)285 TEST_F(OutOfMemoryDeathTest, SecurityAlignedRealloc) {
286 ASSERT_EXIT({
287 SetUpInDeathAssert();
288 value_ = _aligned_realloc(NULL, insecure_test_size_, 8);
289 }, testing::ExitedWithCode(kExitCode), kOomRegex);
290 }
291 #endif // defined(OS_WIN)
292 #endif // !defined(OS_MACOSX) && !defined(OS_ANDROID)
293
294 #if defined(OS_LINUX)
295
TEST_F(OutOfMemoryDeathTest,Valloc)296 TEST_F(OutOfMemoryDeathTest, Valloc) {
297 ASSERT_DEATH({
298 SetUpInDeathAssert();
299 value_ = valloc(test_size_);
300 }, kOomRegex);
301 }
302
TEST_F(OutOfMemoryDeathTest,SecurityValloc)303 TEST_F(OutOfMemoryDeathTest, SecurityValloc) {
304 ASSERT_DEATH({
305 SetUpInDeathAssert();
306 value_ = valloc(insecure_test_size_);
307 }, kOomRegex);
308 }
309
310 #if PVALLOC_AVAILABLE == 1
TEST_F(OutOfMemoryDeathTest,Pvalloc)311 TEST_F(OutOfMemoryDeathTest, Pvalloc) {
312 ASSERT_DEATH({
313 SetUpInDeathAssert();
314 value_ = pvalloc(test_size_);
315 }, kOomRegex);
316 }
317
TEST_F(OutOfMemoryDeathTest,SecurityPvalloc)318 TEST_F(OutOfMemoryDeathTest, SecurityPvalloc) {
319 ASSERT_DEATH({
320 SetUpInDeathAssert();
321 value_ = pvalloc(insecure_test_size_);
322 }, kOomRegex);
323 }
324 #endif // PVALLOC_AVAILABLE == 1
325
TEST_F(OutOfMemoryDeathTest,Memalign)326 TEST_F(OutOfMemoryDeathTest, Memalign) {
327 ASSERT_DEATH({
328 SetUpInDeathAssert();
329 value_ = memalign(4, test_size_);
330 }, kOomRegex);
331 }
332
TEST_F(OutOfMemoryDeathTest,ViaSharedLibraries)333 TEST_F(OutOfMemoryDeathTest, ViaSharedLibraries) {
334 // This tests that the run-time symbol resolution is overriding malloc for
335 // shared libraries as well as for our code.
336 ASSERT_DEATH({
337 SetUpInDeathAssert();
338 value_ = MallocWrapper(test_size_);
339 }, kOomRegex);
340 }
341 #endif // OS_LINUX
342
343 // Android doesn't implement posix_memalign().
344 #if defined(OS_POSIX) && !defined(OS_ANDROID)
TEST_F(OutOfMemoryDeathTest,Posix_memalign)345 TEST_F(OutOfMemoryDeathTest, Posix_memalign) {
346 // Grab the return value of posix_memalign to silence a compiler warning
347 // about unused return values. We don't actually care about the return
348 // value, since we're asserting death.
349 ASSERT_DEATH({
350 SetUpInDeathAssert();
351 EXPECT_EQ(ENOMEM, posix_memalign(&value_, 8, test_size_));
352 }, kOomRegex);
353 }
354 #endif // defined(OS_POSIX) && !defined(OS_ANDROID)
355
356 #if defined(OS_MACOSX)
357
358 // Purgeable zone tests
359
TEST_F(OutOfMemoryDeathTest,MallocPurgeable)360 TEST_F(OutOfMemoryDeathTest, MallocPurgeable) {
361 malloc_zone_t* zone = malloc_default_purgeable_zone();
362 ASSERT_DEATH({
363 SetUpInDeathAssert();
364 value_ = malloc_zone_malloc(zone, test_size_);
365 }, kOomRegex);
366 }
367
TEST_F(OutOfMemoryDeathTest,ReallocPurgeable)368 TEST_F(OutOfMemoryDeathTest, ReallocPurgeable) {
369 malloc_zone_t* zone = malloc_default_purgeable_zone();
370 ASSERT_DEATH({
371 SetUpInDeathAssert();
372 value_ = malloc_zone_realloc(zone, NULL, test_size_);
373 }, kOomRegex);
374 }
375
TEST_F(OutOfMemoryDeathTest,CallocPurgeable)376 TEST_F(OutOfMemoryDeathTest, CallocPurgeable) {
377 malloc_zone_t* zone = malloc_default_purgeable_zone();
378 ASSERT_DEATH({
379 SetUpInDeathAssert();
380 value_ = malloc_zone_calloc(zone, 1024, test_size_ / 1024L);
381 }, kOomRegex);
382 }
383
TEST_F(OutOfMemoryDeathTest,VallocPurgeable)384 TEST_F(OutOfMemoryDeathTest, VallocPurgeable) {
385 malloc_zone_t* zone = malloc_default_purgeable_zone();
386 ASSERT_DEATH({
387 SetUpInDeathAssert();
388 value_ = malloc_zone_valloc(zone, test_size_);
389 }, kOomRegex);
390 }
391
TEST_F(OutOfMemoryDeathTest,PosixMemalignPurgeable)392 TEST_F(OutOfMemoryDeathTest, PosixMemalignPurgeable) {
393 malloc_zone_t* zone = malloc_default_purgeable_zone();
394 ASSERT_DEATH({
395 SetUpInDeathAssert();
396 value_ = malloc_zone_memalign(zone, 8, test_size_);
397 }, kOomRegex);
398 }
399
400 // Since these allocation functions take a signed size, it's possible that
401 // calling them just once won't be enough to exhaust memory. In the 32-bit
402 // environment, it's likely that these allocation attempts will fail because
403 // not enough contiguous address space is available. In the 64-bit environment,
404 // it's likely that they'll fail because they would require a preposterous
405 // amount of (virtual) memory.
406
TEST_F(OutOfMemoryDeathTest,CFAllocatorSystemDefault)407 TEST_F(OutOfMemoryDeathTest, CFAllocatorSystemDefault) {
408 ASSERT_DEATH({
409 SetUpInDeathAssert();
410 while ((value_ =
411 base::AllocateViaCFAllocatorSystemDefault(signed_test_size_))) {}
412 }, kOomRegex);
413 }
414
TEST_F(OutOfMemoryDeathTest,CFAllocatorMalloc)415 TEST_F(OutOfMemoryDeathTest, CFAllocatorMalloc) {
416 ASSERT_DEATH({
417 SetUpInDeathAssert();
418 while ((value_ =
419 base::AllocateViaCFAllocatorMalloc(signed_test_size_))) {}
420 }, kOomRegex);
421 }
422
TEST_F(OutOfMemoryDeathTest,CFAllocatorMallocZone)423 TEST_F(OutOfMemoryDeathTest, CFAllocatorMallocZone) {
424 ASSERT_DEATH({
425 SetUpInDeathAssert();
426 while ((value_ =
427 base::AllocateViaCFAllocatorMallocZone(signed_test_size_))) {}
428 }, kOomRegex);
429 }
430
431 #if !defined(ARCH_CPU_64_BITS)
432
433 // See process_util_unittest_mac.mm for an explanation of why this test isn't
434 // run in the 64-bit environment.
435
TEST_F(OutOfMemoryDeathTest,PsychoticallyBigObjCObject)436 TEST_F(OutOfMemoryDeathTest, PsychoticallyBigObjCObject) {
437 ASSERT_DEATH({
438 SetUpInDeathAssert();
439 while ((value_ = base::AllocatePsychoticallyBigObjCObject())) {}
440 }, kOomRegex);
441 }
442
443 #endif // !ARCH_CPU_64_BITS
444 #endif // OS_MACOSX
445
446 class OutOfMemoryHandledTest : public OutOfMemoryTest {
447 public:
448 static const size_t kSafeMallocSize = 512;
449 static const size_t kSafeCallocSize = 128;
450 static const size_t kSafeCallocItems = 4;
451
SetUp()452 void SetUp() override {
453 OutOfMemoryTest::SetUp();
454
455 // We enable termination on OOM - just as Chrome does at early
456 // initialization - and test that UncheckedMalloc and UncheckedCalloc
457 // properly by-pass this in order to allow the caller to handle OOM.
458 base::EnableTerminationOnOutOfMemory();
459 }
460
TearDown()461 void TearDown() override {
462 #if defined(OS_MACOSX)
463 base::allocator::UninterceptMallocZonesForTesting();
464 #endif
465 }
466 };
467
468 #if defined(OS_WIN)
469
470 namespace {
471
HandleOutOfMemoryException(EXCEPTION_POINTERS * exception_ptrs,size_t expected_size)472 DWORD HandleOutOfMemoryException(EXCEPTION_POINTERS* exception_ptrs,
473 size_t expected_size) {
474 EXPECT_EQ(base::win::kOomExceptionCode,
475 exception_ptrs->ExceptionRecord->ExceptionCode);
476 EXPECT_LE(1U, exception_ptrs->ExceptionRecord->NumberParameters);
477 EXPECT_EQ(expected_size,
478 exception_ptrs->ExceptionRecord->ExceptionInformation[0]);
479 return EXCEPTION_EXECUTE_HANDLER;
480 }
481
482 } // namespace
483
TEST_F(OutOfMemoryTest,TerminateBecauseOutOfMemoryReportsAllocSize)484 TEST_F(OutOfMemoryTest, TerminateBecauseOutOfMemoryReportsAllocSize) {
485 // On Windows, TerminateBecauseOutOfMemory reports the attempted allocation
486 // size in the exception raised.
487 #if defined(ARCH_CPU_64_BITS)
488 // Test with a size larger than 32 bits on 64 bit machines.
489 const size_t kAttemptedAllocationSize = 0xBADA55F00DULL;
490 #else
491 const size_t kAttemptedAllocationSize = 0xBADA55;
492 #endif
493
494 __try {
495 base::TerminateBecauseOutOfMemory(kAttemptedAllocationSize);
496 } __except (HandleOutOfMemoryException(GetExceptionInformation(),
497 kAttemptedAllocationSize)) {
498 }
499 }
500 #endif // OS_WIN
501
502 // TODO(b.kelemen): make UncheckedMalloc and UncheckedCalloc work
503 // on Windows as well.
TEST_F(OutOfMemoryHandledTest,UncheckedMalloc)504 TEST_F(OutOfMemoryHandledTest, UncheckedMalloc) {
505 EXPECT_TRUE(base::UncheckedMalloc(kSafeMallocSize, &value_));
506 EXPECT_TRUE(value_ != nullptr);
507 free(value_);
508
509 EXPECT_FALSE(base::UncheckedMalloc(test_size_, &value_));
510 EXPECT_TRUE(value_ == nullptr);
511 }
512
TEST_F(OutOfMemoryHandledTest,UncheckedCalloc)513 TEST_F(OutOfMemoryHandledTest, UncheckedCalloc) {
514 EXPECT_TRUE(base::UncheckedCalloc(1, kSafeMallocSize, &value_));
515 EXPECT_TRUE(value_ != nullptr);
516 const char* bytes = static_cast<const char*>(value_);
517 for (size_t i = 0; i < kSafeMallocSize; ++i)
518 EXPECT_EQ(0, bytes[i]);
519 free(value_);
520
521 EXPECT_TRUE(
522 base::UncheckedCalloc(kSafeCallocItems, kSafeCallocSize, &value_));
523 EXPECT_TRUE(value_ != nullptr);
524 bytes = static_cast<const char*>(value_);
525 for (size_t i = 0; i < (kSafeCallocItems * kSafeCallocSize); ++i)
526 EXPECT_EQ(0, bytes[i]);
527 free(value_);
528
529 EXPECT_FALSE(base::UncheckedCalloc(1, test_size_, &value_));
530 EXPECT_TRUE(value_ == nullptr);
531 }
532 #endif // !defined(OS_OPENBSD) && BUILDFLAG(ENABLE_WIN_ALLOCATOR_SHIM_TESTS) &&
533 // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
534