1// Copyright 2013 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/process/memory.h" 6 7#include <stdlib.h> 8 9#include <new> 10 11#include "build/build_config.h" 12#include "partition_alloc/buildflags.h" 13#include "partition_alloc/shim/allocator_interception_apple.h" 14#include "partition_alloc/shim/allocator_shim.h" 15 16namespace base { 17 18namespace { 19void oom_killer_new() { 20 TerminateBecauseOutOfMemory(0); 21} 22} // namespace 23 24void EnableTerminationOnHeapCorruption() { 25#if !ARCH_CPU_64_BITS 26 DLOG(WARNING) << "EnableTerminationOnHeapCorruption only works on 64-bit"; 27#endif 28} 29 30bool UncheckedMalloc(size_t size, void** result) { 31#if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) 32 // Unchecked allocations can happen before the default malloc() zone is 33 // registered. In this case, going straight to the shim may explode, since the 34 // memory will come from a zone which is unknown to the dispatching code in 35 // libmalloc. Meaning that if the memory gets free()-d, realloc()-ed, or its 36 // actual size is queried with malloc_size() *before* we get to register our 37 // zone, we crash. 38 // 39 // The cleanest solution would be to detect it and forbid it, but tests (at 40 // least) allocate in static constructors. Meaning that this code is 41 // sufficient to cause a crash: 42 // 43 // void* ptr = [] { 44 // void* ptr; 45 // bool ok = base::UncheckedMalloc(1000, &ptr); 46 // CHECK(ok); 47 // free(ptr); 48 // }(); 49 // 50 // (Our static initializer is supposed to have priority, but it doesn't seem 51 // to work in practice, at least for MachO). 52 // 53 // Since unchecked allocations are rare, let's err on the side of caution. 54 if (!allocator_shim::IsDefaultAllocatorPartitionRootInitialized()) { 55 *result = malloc(size); 56 return *result != nullptr; 57 } 58 59 // Unlike use_partition_alloc_as_malloc=false, the default malloc zone is 60 // replaced with PartitionAlloc, so the allocator shim functions work best. 61 *result = allocator_shim::UncheckedAlloc(size); 62 return *result != nullptr; 63#elif PA_BUILDFLAG(USE_ALLOCATOR_SHIM) 64 return allocator_shim::UncheckedMallocMac(size, result); 65#else // !PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && 66 // !PA_BUILDFLAG(USE_ALLOCATOR_SHIM) 67 *result = malloc(size); 68 return *result != nullptr; 69#endif // !PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) && 70 // !PA_BUILDFLAG(USE_ALLOCATOR_SHIM) 71} 72 73// The standard version is defined in memory.cc in case of 74// USE_PARTITION_ALLOC_AS_MALLOC. 75#if !PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) 76bool UncheckedCalloc(size_t num_items, size_t size, void** result) { 77#if PA_BUILDFLAG(USE_ALLOCATOR_SHIM) 78 return allocator_shim::UncheckedCallocMac(num_items, size, result); 79#else 80 *result = calloc(num_items, size); 81 return *result != nullptr; 82#endif // PA_BUILDFLAG(USE_ALLOCATOR_SHIM) 83} 84#endif // !PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) 85 86void EnableTerminationOnOutOfMemory() { 87 // Step 1: Enable OOM killer on C++ failures. 88 std::set_new_handler(oom_killer_new); 89 90// Step 2: Enable OOM killer on C-malloc failures for the default zone (if we 91// have a shim). 92#if PA_BUILDFLAG(USE_ALLOCATOR_SHIM) 93 allocator_shim::SetCallNewHandlerOnMallocFailure(true); 94 95 // Step 3: Enable OOM killer on all other malloc zones (or just "all" without 96 // "other" if shim is disabled). 97 allocator_shim::InterceptAllocationsMac(); 98#endif // PA_BUILDFLAG(USE_ALLOCATOR_SHIM) 99} 100 101void UncheckedFree(void* ptr) { 102#if PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) 103 // Important: might be different from free(), because in some cases, free() 104 // does not necessarily know about allocator_shim::* functions. 105 allocator_shim::UncheckedFree(ptr); 106#else // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) 107 free(ptr); 108#endif // PA_BUILDFLAG(USE_PARTITION_ALLOC_AS_MALLOC) 109} 110 111} // namespace base 112