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