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