• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
__anon59fb04ab0602null724 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