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