1 // Copyright 2012 The Chromium Authors
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 #include <tuple>
13 #include <vector>
14
15 #include "base/allocator/allocator_check.h"
16 #include "base/compiler_specific.h"
17 #include "base/debug/alias.h"
18 #include "base/memory/aligned_memory.h"
19 #include "base/memory/page_size.h"
20 #include "build/build_config.h"
21 #include "partition_alloc/page_allocator.h"
22 #include "partition_alloc/partition_alloc_buildflags.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24
25 #if BUILDFLAG(IS_WIN)
26 #include <windows.h>
27 #endif
28 #if BUILDFLAG(IS_POSIX)
29 #include <errno.h>
30 #endif
31 #if BUILDFLAG(IS_MAC)
32 #include <malloc/malloc.h>
33 #include "base/check_op.h"
34 #include "base/process/memory_unittest_mac.h"
35 #include "partition_alloc/shim/allocator_interception_apple.h"
36 #include "partition_alloc/shim/allocator_shim.h"
37 #endif
38 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
39 #include <malloc.h>
40 #include "base/test/malloc_wrapper.h"
41 #endif
42 #if BUILDFLAG(IS_ANDROID)
43 #include "base/android/build_info.h"
44 #endif
45
46 #if BUILDFLAG(IS_WIN)
47
48 #if defined(COMPILER_MSVC)
49 // ssize_t needed for OutOfMemoryTest.
50 #if defined(_WIN64)
51 typedef __int64 ssize_t;
52 #else
53 typedef long ssize_t;
54 #endif
55 #endif
56
57 // HeapQueryInformation function pointer.
58 typedef BOOL (WINAPI* HeapQueryFn) \
59 (HANDLE, HEAP_INFORMATION_CLASS, PVOID, SIZE_T, PSIZE_T);
60
61 #endif // BUILDFLAG(IS_WIN)
62
63 #if BUILDFLAG(IS_MAC)
64
65 // For the following Mac tests:
66 // Note that base::EnableTerminationOnHeapCorruption() is called as part of
67 // test suite setup and does not need to be done again, else mach_override
68 // will fail.
69
70 // Wrap free() in a function to thwart Clang's -Wfree-nonheap-object warning.
callFree(void * ptr)71 static void callFree(void *ptr) {
72 free(ptr);
73 }
74
TEST(ProcessMemoryTest,MacTerminateOnHeapCorruption)75 TEST(ProcessMemoryTest, MacTerminateOnHeapCorruption) {
76 #if BUILDFLAG(USE_ALLOCATOR_SHIM)
77 allocator_shim::InitializeAllocatorShim();
78 #endif
79 // Assert that freeing an unallocated pointer will crash the process.
80 char buf[9];
81 asm("" : "=m"(buf)); // Prevent clang from being too smart.
82 #if ARCH_CPU_64_BITS
83 // On 64 bit Macs, the malloc system automatically abort()s on heap corruption
84 // but does not output anything.
85 ASSERT_DEATH(callFree(buf), "");
86 #elif defined(ADDRESS_SANITIZER)
87 // AddressSanitizer replaces malloc() and prints a different error message on
88 // heap corruption.
89 ASSERT_DEATH(callFree(buf), "attempting free on address which "
90 "was not malloc\\(\\)-ed");
91 #else
92 ADD_FAILURE() << "This test is not supported in this build configuration.";
93 #endif
94
95 #if BUILDFLAG(USE_ALLOCATOR_SHIM)
96 allocator_shim::UninterceptMallocZonesForTesting();
97 #endif
98 }
99
100 #endif // BUILDFLAG(IS_MAC)
101
102 #if BUILDFLAG(USE_ALLOCATOR_SHIM)
TEST(MemoryTest,AllocatorShimWorking)103 TEST(MemoryTest, AllocatorShimWorking) {
104 #if BUILDFLAG(IS_MAC)
105 allocator_shim::InitializeAllocatorShim();
106 allocator_shim::InterceptAllocationsMac();
107 #endif
108 ASSERT_TRUE(base::allocator::IsAllocatorInitialized());
109
110 #if BUILDFLAG(IS_MAC)
111 allocator_shim::UninterceptMallocZonesForTesting();
112 #endif
113 }
114 #endif // BUILDFLAG(USE_ALLOCATOR_SHIM)
115
116 // OpenBSD does not support these tests. Don't test these on ASan/TSan/MSan
117 // configurations: only test the real allocator.
118 #if !BUILDFLAG(IS_OPENBSD) && BUILDFLAG(USE_ALLOCATOR_SHIM) && \
119 !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
120
121 namespace {
122
123 #if BUILDFLAG(IS_WIN)
124
125 // Windows raises an exception in order to make the exit code unique to OOM.
126 #define ASSERT_OOM_DEATH(statement) \
127 ASSERT_EXIT(statement, \
128 testing::ExitedWithCode(base::win::kOomExceptionCode), "")
129
130 #else
131
132 #define ASSERT_OOM_DEATH(statement) ASSERT_DEATH(statement, "")
133
134 #endif // BUILDFLAG(IS_WIN)
135
136 } // namespace
137
138 class OutOfMemoryTest : public testing::Test {
139 public:
OutOfMemoryTest()140 OutOfMemoryTest()
141 : // Make test size as large as possible minus a few pages so that
142 // alignment or other rounding doesn't make it wrap.
143 test_size_(std::numeric_limits<std::size_t>::max() -
144 3 * base::GetPageSize()),
145 // A test size that is > 2Gb and will cause the allocators to reject
146 // the allocation due to security restrictions. See crbug.com/169327.
147 insecure_test_size_(std::numeric_limits<int>::max()),
148 signed_test_size_(std::numeric_limits<ssize_t>::max()) {}
149
150 protected:
151 size_t test_size_;
152 size_t insecure_test_size_;
153 ssize_t signed_test_size_;
154 };
155
156 class OutOfMemoryDeathTest : public OutOfMemoryTest {
157 public:
SetUpInDeathAssert()158 void SetUpInDeathAssert() {
159 #if BUILDFLAG(IS_MAC) && BUILDFLAG(USE_ALLOCATOR_SHIM)
160 allocator_shim::InitializeAllocatorShim();
161 #endif
162
163 // Must call EnableTerminationOnOutOfMemory() because that is called from
164 // chrome's main function and therefore hasn't been called yet.
165 // Since this call may result in another thread being created and death
166 // tests shouldn't be started in a multithread environment, this call
167 // should be done inside of the ASSERT_DEATH.
168 base::EnableTerminationOnOutOfMemory();
169 }
170
171 #if BUILDFLAG(IS_MAC)
TearDown()172 void TearDown() override {
173 allocator_shim::UninterceptMallocZonesForTesting();
174 }
175 #endif
176
177 // These tests don't work properly on old x86 Android; crbug.com/1181112
ShouldSkipTest()178 bool ShouldSkipTest() {
179 #if BUILDFLAG(IS_ANDROID) && defined(ARCH_CPU_X86)
180 return base::android::BuildInfo::GetInstance()->sdk_int() <
181 base::android::SDK_VERSION_NOUGAT;
182 #else
183 return false;
184 #endif
185 }
186 };
187
TEST_F(OutOfMemoryDeathTest,New)188 TEST_F(OutOfMemoryDeathTest, New) {
189 if (ShouldSkipTest()) {
190 return;
191 }
192 ASSERT_OOM_DEATH({
193 SetUpInDeathAssert();
194 [[maybe_unused]] void* volatile ptr = operator new(test_size_);
195 });
196 }
197
TEST_F(OutOfMemoryDeathTest,NewArray)198 TEST_F(OutOfMemoryDeathTest, NewArray) {
199 if (ShouldSkipTest()) {
200 return;
201 }
202 ASSERT_OOM_DEATH({
203 SetUpInDeathAssert();
204 [[maybe_unused]] void* volatile ptr = new char[test_size_];
205 });
206 }
207
TEST_F(OutOfMemoryDeathTest,Malloc)208 TEST_F(OutOfMemoryDeathTest, Malloc) {
209 if (ShouldSkipTest()) {
210 return;
211 }
212 ASSERT_OOM_DEATH({
213 SetUpInDeathAssert();
214 [[maybe_unused]] void* volatile ptr = malloc(test_size_);
215 });
216 }
217
TEST_F(OutOfMemoryDeathTest,Realloc)218 TEST_F(OutOfMemoryDeathTest, Realloc) {
219 if (ShouldSkipTest()) {
220 return;
221 }
222 ASSERT_OOM_DEATH({
223 SetUpInDeathAssert();
224 [[maybe_unused]] void* volatile ptr = realloc(nullptr, test_size_);
225 });
226 }
227
TEST_F(OutOfMemoryDeathTest,Calloc)228 TEST_F(OutOfMemoryDeathTest, Calloc) {
229 if (ShouldSkipTest()) {
230 return;
231 }
232 ASSERT_OOM_DEATH({
233 SetUpInDeathAssert();
234 [[maybe_unused]] void* volatile ptr = calloc(1024, test_size_ / 1024L);
235 });
236 }
237
TEST_F(OutOfMemoryDeathTest,AlignedAlloc)238 TEST_F(OutOfMemoryDeathTest, AlignedAlloc) {
239 if (ShouldSkipTest()) {
240 return;
241 }
242 ASSERT_OOM_DEATH({
243 SetUpInDeathAssert();
244 [[maybe_unused]] void* volatile ptr = base::AlignedAlloc(test_size_, 8);
245 });
246 }
247
248 // POSIX does not define an aligned realloc function.
249 #if BUILDFLAG(IS_WIN)
TEST_F(OutOfMemoryDeathTest,AlignedRealloc)250 TEST_F(OutOfMemoryDeathTest, AlignedRealloc) {
251 if (ShouldSkipTest()) {
252 return;
253 }
254 ASSERT_OOM_DEATH({
255 SetUpInDeathAssert();
256 [[maybe_unused]] void* volatile ptr =
257 _aligned_realloc(nullptr, test_size_, 8);
258 });
259 }
260
261 namespace {
262
263 constexpr uint32_t kUnhandledExceptionExitCode = 0xBADA55;
264
265 // This unhandled exception filter exits the process with an exit code distinct
266 // from the exception code. This is to verify that the out of memory new handler
267 // causes an unhandled exception.
ExitingUnhandledExceptionFilter(EXCEPTION_POINTERS * ExceptionInfo)268 LONG WINAPI ExitingUnhandledExceptionFilter(EXCEPTION_POINTERS* ExceptionInfo) {
269 _exit(kUnhandledExceptionExitCode);
270 }
271
272 } // namespace
273
TEST_F(OutOfMemoryDeathTest,NewHandlerGeneratesUnhandledException)274 TEST_F(OutOfMemoryDeathTest, NewHandlerGeneratesUnhandledException) {
275 ASSERT_EXIT(
276 {
277 SetUpInDeathAssert();
278 SetUnhandledExceptionFilter(&ExitingUnhandledExceptionFilter);
279 [[maybe_unused]] void* volatile ptr = new char[test_size_];
280 },
281 testing::ExitedWithCode(kUnhandledExceptionExitCode), "");
282 }
283 #endif // BUILDFLAG(IS_WIN)
284
285 // OS X has no 2Gb allocation limit.
286 // See https://crbug.com/169327.
287 // PartitionAlloc is not active in component builds, so cannot enforce
288 // this limit. (//BUILD.gn asserts that we cannot have an official component
289 // build.)
290 #if !BUILDFLAG(IS_MAC) && !defined(COMPONENT_BUILD)
TEST_F(OutOfMemoryDeathTest,SecurityNew)291 TEST_F(OutOfMemoryDeathTest, SecurityNew) {
292 if (ShouldSkipTest()) {
293 return;
294 }
295 ASSERT_OOM_DEATH({
296 SetUpInDeathAssert();
297 [[maybe_unused]] void* volatile ptr = operator new(insecure_test_size_);
298 });
299 }
300
TEST_F(OutOfMemoryDeathTest,SecurityNewArray)301 TEST_F(OutOfMemoryDeathTest, SecurityNewArray) {
302 if (ShouldSkipTest()) {
303 return;
304 }
305 ASSERT_OOM_DEATH({
306 SetUpInDeathAssert();
307 [[maybe_unused]] void* volatile ptr = new char[insecure_test_size_];
308 });
309 }
310
TEST_F(OutOfMemoryDeathTest,SecurityMalloc)311 TEST_F(OutOfMemoryDeathTest, SecurityMalloc) {
312 if (ShouldSkipTest()) {
313 return;
314 }
315 ASSERT_OOM_DEATH({
316 SetUpInDeathAssert();
317 [[maybe_unused]] void* volatile ptr = malloc(insecure_test_size_);
318 });
319 }
320
TEST_F(OutOfMemoryDeathTest,SecurityRealloc)321 TEST_F(OutOfMemoryDeathTest, SecurityRealloc) {
322 if (ShouldSkipTest()) {
323 return;
324 }
325 ASSERT_OOM_DEATH({
326 SetUpInDeathAssert();
327 [[maybe_unused]] void* volatile ptr = realloc(nullptr, insecure_test_size_);
328 });
329 }
330
TEST_F(OutOfMemoryDeathTest,SecurityCalloc)331 TEST_F(OutOfMemoryDeathTest, SecurityCalloc) {
332 if (ShouldSkipTest()) {
333 return;
334 }
335 ASSERT_OOM_DEATH({
336 SetUpInDeathAssert();
337 [[maybe_unused]] void* volatile ptr =
338 calloc(1024, insecure_test_size_ / 1024L);
339 });
340 }
341
TEST_F(OutOfMemoryDeathTest,SecurityAlignedAlloc)342 TEST_F(OutOfMemoryDeathTest, SecurityAlignedAlloc) {
343 if (ShouldSkipTest()) {
344 return;
345 }
346 ASSERT_OOM_DEATH({
347 SetUpInDeathAssert();
348 [[maybe_unused]] void* volatile ptr =
349 base::AlignedAlloc(insecure_test_size_, 8);
350 });
351 }
352
353 // POSIX does not define an aligned realloc function.
354 #if BUILDFLAG(IS_WIN)
TEST_F(OutOfMemoryDeathTest,SecurityAlignedRealloc)355 TEST_F(OutOfMemoryDeathTest, SecurityAlignedRealloc) {
356 if (ShouldSkipTest()) {
357 return;
358 }
359 ASSERT_OOM_DEATH({
360 SetUpInDeathAssert();
361 [[maybe_unused]] void* volatile ptr =
362 _aligned_realloc(nullptr, insecure_test_size_, 8);
363 });
364 }
365 #endif // BUILDFLAG(IS_WIN)
366 #endif // !BUILDFLAG(IS_MAC)
367
368 #if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
369
TEST_F(OutOfMemoryDeathTest,Valloc)370 TEST_F(OutOfMemoryDeathTest, Valloc) {
371 ASSERT_OOM_DEATH({
372 SetUpInDeathAssert();
373 [[maybe_unused]] void* volatile ptr = valloc(test_size_);
374 EXPECT_TRUE(ptr);
375 });
376 }
377
TEST_F(OutOfMemoryDeathTest,SecurityValloc)378 TEST_F(OutOfMemoryDeathTest, SecurityValloc) {
379 ASSERT_OOM_DEATH({
380 SetUpInDeathAssert();
381 [[maybe_unused]] void* volatile ptr = valloc(insecure_test_size_);
382 });
383 }
384
TEST_F(OutOfMemoryDeathTest,Pvalloc)385 TEST_F(OutOfMemoryDeathTest, Pvalloc) {
386 ASSERT_OOM_DEATH({
387 SetUpInDeathAssert();
388 [[maybe_unused]] void* volatile ptr = pvalloc(test_size_);
389 });
390 }
391
TEST_F(OutOfMemoryDeathTest,SecurityPvalloc)392 TEST_F(OutOfMemoryDeathTest, SecurityPvalloc) {
393 ASSERT_OOM_DEATH({
394 SetUpInDeathAssert();
395 [[maybe_unused]] void* volatile ptr = pvalloc(insecure_test_size_);
396 });
397 }
398
TEST_F(OutOfMemoryDeathTest,Memalign)399 TEST_F(OutOfMemoryDeathTest, Memalign) {
400 ASSERT_OOM_DEATH({
401 SetUpInDeathAssert();
402 [[maybe_unused]] void* volatile ptr = memalign(4, test_size_);
403 });
404 }
405
TEST_F(OutOfMemoryDeathTest,ViaSharedLibraries)406 TEST_F(OutOfMemoryDeathTest, ViaSharedLibraries) {
407 // This tests that the run-time symbol resolution is overriding malloc for
408 // shared libraries as well as for our code.
409 ASSERT_OOM_DEATH({
410 SetUpInDeathAssert();
411 [[maybe_unused]] void* volatile ptr = MallocWrapper(test_size_);
412 });
413 }
414 #endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
415
416 // Android doesn't implement posix_memalign().
417 #if BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
TEST_F(OutOfMemoryDeathTest,Posix_memalign)418 TEST_F(OutOfMemoryDeathTest, Posix_memalign) {
419 // Grab the return value of posix_memalign to silence a compiler warning
420 // about unused return values. We don't actually care about the return
421 // value, since we're asserting death.
422 ASSERT_OOM_DEATH({
423 SetUpInDeathAssert();
424 void* ptr;
425 EXPECT_EQ(ENOMEM, posix_memalign(&ptr, 8, test_size_));
426 });
427 }
428 #endif // BUILDFLAG(IS_POSIX) && !BUILDFLAG(IS_ANDROID)
429
430 #if BUILDFLAG(IS_MAC)
431
432 // Purgeable zone tests
433
TEST_F(OutOfMemoryDeathTest,MallocPurgeable)434 TEST_F(OutOfMemoryDeathTest, MallocPurgeable) {
435 malloc_zone_t* zone = malloc_default_purgeable_zone();
436 ASSERT_OOM_DEATH({
437 SetUpInDeathAssert();
438 [[maybe_unused]] void* volatile ptr = malloc_zone_malloc(zone, test_size_);
439 });
440 }
441
TEST_F(OutOfMemoryDeathTest,ReallocPurgeable)442 TEST_F(OutOfMemoryDeathTest, ReallocPurgeable) {
443 malloc_zone_t* zone = malloc_default_purgeable_zone();
444 ASSERT_OOM_DEATH({
445 SetUpInDeathAssert();
446 [[maybe_unused]] void* volatile ptr =
447 malloc_zone_realloc(zone, nullptr, test_size_);
448 });
449 }
450
TEST_F(OutOfMemoryDeathTest,CallocPurgeable)451 TEST_F(OutOfMemoryDeathTest, CallocPurgeable) {
452 malloc_zone_t* zone = malloc_default_purgeable_zone();
453 ASSERT_OOM_DEATH({
454 SetUpInDeathAssert();
455 [[maybe_unused]] void* volatile ptr =
456 malloc_zone_calloc(zone, 1024, test_size_ / 1024L);
457 });
458 }
459
TEST_F(OutOfMemoryDeathTest,VallocPurgeable)460 TEST_F(OutOfMemoryDeathTest, VallocPurgeable) {
461 malloc_zone_t* zone = malloc_default_purgeable_zone();
462 ASSERT_OOM_DEATH({
463 SetUpInDeathAssert();
464 [[maybe_unused]] void* volatile ptr = malloc_zone_valloc(zone, test_size_);
465 });
466 }
467
TEST_F(OutOfMemoryDeathTest,PosixMemalignPurgeable)468 TEST_F(OutOfMemoryDeathTest, PosixMemalignPurgeable) {
469 malloc_zone_t* zone = malloc_default_purgeable_zone();
470 ASSERT_OOM_DEATH({
471 SetUpInDeathAssert();
472 [[maybe_unused]] void* volatile ptr =
473 malloc_zone_memalign(zone, 8, test_size_);
474 });
475 }
476
477 // Since these allocation functions take a signed size, it's possible that
478 // calling them just once won't be enough to exhaust memory. In the 32-bit
479 // environment, it's likely that these allocation attempts will fail because
480 // not enough contiguous address space is available. In the 64-bit environment,
481 // it's likely that they'll fail because they would require a preposterous
482 // amount of (virtual) memory.
483
TEST_F(OutOfMemoryDeathTest,CFAllocatorMalloc)484 TEST_F(OutOfMemoryDeathTest, CFAllocatorMalloc) {
485 ASSERT_OOM_DEATH({
486 SetUpInDeathAssert();
487 [[maybe_unused]] void* ptr;
488 while ((ptr = base::AllocateViaCFAllocatorMalloc(signed_test_size_))) {
489 }
490 });
491 }
492
493 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
494 // PartitionAlloc-Everywhere does not intercept other malloc zones than the
495 // default (the top) malloc zone. Plus,
496 // CFAllocatorAllocate(kCFAllocatorSystemDefault, size, 0) does not call the
497 // default (the top) malloc zone on macOS 10.xx (does call it on macOS 11 and
498 // later though).
499 #define MAYBE_CFAllocatorSystemDefault DISABLED_CFAllocatorSystemDefault
500 #else
501 #define MAYBE_CFAllocatorSystemDefault CFAllocatorSystemDefault
502 #endif
TEST_F(OutOfMemoryDeathTest,MAYBE_CFAllocatorSystemDefault)503 TEST_F(OutOfMemoryDeathTest, MAYBE_CFAllocatorSystemDefault) {
504 ASSERT_OOM_DEATH({
505 SetUpInDeathAssert();
506 [[maybe_unused]] void* ptr;
507 while (
508 (ptr = base::AllocateViaCFAllocatorSystemDefault(signed_test_size_))) {
509 }
510 });
511 }
512
513 #if BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
514 // PartitionAlloc-Everywhere does not intercept other malloc zones than the
515 // default (the top) malloc zone. Plus,
516 // CFAllocatorAllocate(kCFAllocatorMallocZone, size, 0) does not call the
517 // default (the top) malloc zone on macOS 10.xx (does call it on macOS 11 and
518 // later though).
519 #define MAYBE_CFAllocatorMallocZone DISABLED_CFAllocatorMallocZone
520 #else
521 #define MAYBE_CFAllocatorMallocZone CFAllocatorMallocZone
522 #endif
TEST_F(OutOfMemoryDeathTest,MAYBE_CFAllocatorMallocZone)523 TEST_F(OutOfMemoryDeathTest, MAYBE_CFAllocatorMallocZone) {
524 ASSERT_OOM_DEATH({
525 SetUpInDeathAssert();
526 [[maybe_unused]] void* ptr;
527 while ((ptr = base::AllocateViaCFAllocatorMallocZone(signed_test_size_))) {
528 }
529 });
530 }
531
532 #endif // BUILDFLAG(IS_MAC)
533
534 class OutOfMemoryHandledTest : public OutOfMemoryTest {
535 public:
536 static const size_t kSafeMallocSize = 512;
537 static const size_t kSafeCallocSize = 128;
538 static const size_t kSafeCallocItems = 4;
539
SetUp()540 void SetUp() override {
541 OutOfMemoryTest::SetUp();
542
543 // We enable termination on OOM - just as Chrome does at early
544 // initialization - and test that UncheckedMalloc and UncheckedCalloc
545 // properly by-pass this in order to allow the caller to handle OOM.
546 base::EnableTerminationOnOutOfMemory();
547 }
548
TearDown()549 void TearDown() override {
550 #if BUILDFLAG(IS_MAC)
551 allocator_shim::UninterceptMallocZonesForTesting();
552 #endif
553 }
554 };
555
556 #if BUILDFLAG(IS_WIN)
557
558 namespace {
559
HandleOutOfMemoryException(EXCEPTION_POINTERS * exception_ptrs,size_t expected_size)560 DWORD HandleOutOfMemoryException(EXCEPTION_POINTERS* exception_ptrs,
561 size_t expected_size) {
562 EXPECT_EQ(base::win::kOomExceptionCode,
563 exception_ptrs->ExceptionRecord->ExceptionCode);
564 EXPECT_LE(1U, exception_ptrs->ExceptionRecord->NumberParameters);
565 EXPECT_EQ(expected_size,
566 exception_ptrs->ExceptionRecord->ExceptionInformation[0]);
567 return EXCEPTION_EXECUTE_HANDLER;
568 }
569
570 } // namespace
571
TEST_F(OutOfMemoryTest,TerminateBecauseOutOfMemoryReportsAllocSize)572 TEST_F(OutOfMemoryTest, TerminateBecauseOutOfMemoryReportsAllocSize) {
573 // On Windows, TerminateBecauseOutOfMemory reports the attempted allocation
574 // size in the exception raised.
575 #if defined(ARCH_CPU_64_BITS)
576 // Test with a size larger than 32 bits on 64 bit machines.
577 const size_t kAttemptedAllocationSize = 0xBADA55F00DULL;
578 #else
579 const size_t kAttemptedAllocationSize = 0xBADA55;
580 #endif
581
582 __try {
583 base::TerminateBecauseOutOfMemory(kAttemptedAllocationSize);
584 } __except (HandleOutOfMemoryException(GetExceptionInformation(),
585 kAttemptedAllocationSize)) {
586 }
587 }
588 #endif // BUILDFLAG(IS_WIN)
589
590 #if defined(ARCH_CPU_32_BITS) && \
591 (BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS))
592
TestAllocationsReleaseReservation(void * (* alloc_fn)(size_t),void (* free_fn)(void *))593 void TestAllocationsReleaseReservation(void* (*alloc_fn)(size_t),
594 void (*free_fn)(void*)) {
595 partition_alloc::ReleaseReservation();
596 base::EnableTerminationOnOutOfMemory();
597
598 constexpr size_t kMiB = 1 << 20;
599 constexpr size_t kReservationSize = 512 * kMiB; // MiB.
600
601 size_t reservation_size = kReservationSize;
602 while (!partition_alloc::ReserveAddressSpace(reservation_size)) {
603 reservation_size -= 16 * kMiB;
604 }
605 ASSERT_TRUE(partition_alloc::HasReservationForTesting());
606 ASSERT_GT(reservation_size, 0u);
607
608 // Allocate a large area at a time to bump into address space exhaustion
609 // before other limits. It is important not to do a larger allocation, to
610 // verify that we can allocate without removing the reservation. On the other
611 // hand, must be large enough to make the underlying implementation call
612 // mmap()/VirtualAlloc().
613 size_t allocation_size = reservation_size / 2;
614
615 std::vector<void*> areas;
616 // Pre-reserve the vector to make sure that we don't hit the address space
617 // limit while resizing the array.
618 areas.reserve(((2 * 4096 * kMiB) / allocation_size) + 1);
619
620 while (true) {
621 void* area = alloc_fn(allocation_size / 2);
622 ASSERT_TRUE(area);
623 areas.push_back(area);
624
625 // Working as intended, the allocation was successful, and the reservation
626 // was dropped instead of crashing.
627 //
628 // Meaning that the test is either successful, or crashes.
629 if (!partition_alloc::HasReservationForTesting())
630 break;
631 }
632
633 EXPECT_GE(areas.size(), 2u)
634 << "Should be able to allocate without releasing the reservation";
635
636 for (void* ptr : areas)
637 free_fn(ptr);
638 }
639
TEST_F(OutOfMemoryHandledTest,MallocReleasesReservation)640 TEST_F(OutOfMemoryHandledTest, MallocReleasesReservation) {
641 TestAllocationsReleaseReservation(malloc, free);
642 }
643
TEST_F(OutOfMemoryHandledTest,NewReleasesReservation)644 TEST_F(OutOfMemoryHandledTest, NewReleasesReservation) {
645 TestAllocationsReleaseReservation(
646 [](size_t size) { return static_cast<void*>(new char[size]); },
647 [](void* ptr) { delete[] static_cast<char*>(ptr); });
648 }
649 #endif // defined(ARCH_CPU_32_BITS) && (BUILDFLAG(IS_WIN) ||
650 // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS))
651
652 #if BUILDFLAG(IS_ANDROID)
653
654 // Android's allocator does not allow overcommits, so very large
655 // UncheckedMallocs will yield OOM errors.
656 // TODO(crbug.com/1112840): Fails on some Android bots.
657 #define MAYBE_UncheckedMallocDies DISABLED_UncheckedMallocDies
658 #define MAYBE_UncheckedCallocDies DISABLED_UncheckedCallocDies
TEST_F(OutOfMemoryDeathTest,MAYBE_UncheckedMallocDies)659 TEST_F(OutOfMemoryDeathTest, MAYBE_UncheckedMallocDies) {
660 ASSERT_OOM_DEATH({
661 SetUpInDeathAssert();
662 void* data;
663 std::ignore = base::UncheckedMalloc(test_size_, &data);
664 // Death expected here.
665 });
666 }
667
TEST_F(OutOfMemoryDeathTest,MAYBE_UncheckedCallocDies)668 TEST_F(OutOfMemoryDeathTest, MAYBE_UncheckedCallocDies) {
669 ASSERT_OOM_DEATH({
670 SetUpInDeathAssert();
671 void* data;
672 std::ignore = base::UncheckedCalloc(1, test_size_, &data);
673 // Death expected here.
674 });
675 }
676
677 #else
678
TEST_F(OutOfMemoryHandledTest,UncheckedMalloc)679 TEST_F(OutOfMemoryHandledTest, UncheckedMalloc) {
680 void* ptr;
681 EXPECT_TRUE(base::UncheckedMalloc(kSafeMallocSize, &ptr));
682 EXPECT_TRUE(ptr != nullptr);
683 base::UncheckedFree(ptr);
684
685 EXPECT_FALSE(base::UncheckedMalloc(test_size_, &ptr));
686 EXPECT_TRUE(ptr == nullptr);
687 }
688
TEST_F(OutOfMemoryHandledTest,UncheckedCalloc)689 TEST_F(OutOfMemoryHandledTest, UncheckedCalloc) {
690 void* ptr;
691 EXPECT_TRUE(base::UncheckedCalloc(1, kSafeMallocSize, &ptr));
692 EXPECT_TRUE(ptr != nullptr);
693 const char* bytes = static_cast<const char*>(ptr);
694 for (size_t i = 0; i < kSafeMallocSize; ++i)
695 EXPECT_EQ(0, bytes[i]);
696 base::UncheckedFree(ptr);
697
698 EXPECT_TRUE(base::UncheckedCalloc(kSafeCallocItems, kSafeCallocSize, &ptr));
699 EXPECT_TRUE(ptr != nullptr);
700 bytes = static_cast<const char*>(ptr);
701 for (size_t i = 0; i < (kSafeCallocItems * kSafeCallocSize); ++i)
702 EXPECT_EQ(0, bytes[i]);
703 base::UncheckedFree(ptr);
704
705 EXPECT_FALSE(base::UncheckedCalloc(1, test_size_, &ptr));
706 EXPECT_TRUE(ptr == nullptr);
707 }
708
709 #endif // BUILDFLAG(IS_ANDROID)
710 #endif // !BUILDFLAG(IS_OPENBSD) && BUILDFLAG(USE_ALLOCATOR_SHIM) &&
711 // !defined(MEMORY_TOOL_REPLACES_ALLOCATOR)
712
713 #if BUILDFLAG(IS_MAC) && BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
714
715 // Not a proper test because it needs to be in a static initializer, see the
716 // comment in UncheckedMalloc() in memory_mac.mm.
717 //
718 // The "test" passes if the binary doesn't crash.
__anon9b2557250602() 719 size_t need_a_static_initializer = []() {
720 void* ptr;
721 constexpr size_t kRequestedSize = 1000u;
722 bool ok = base::UncheckedMalloc(kRequestedSize, &ptr);
723 CHECK(ok);
724 size_t actual_size = malloc_size(ptr);
725 // If no known zone owns the pointer, dispatching code in libmalloc returns 0.
726 CHECK_GE(actual_size, kRequestedSize);
727 // If no zone owns the pointer, libmalloc aborts here.
728 free(ptr);
729
730 return actual_size;
731 }();
732
733 #endif // BUILDFLAG(IS_MAC) && BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC)
734