1 // Copyright 2016 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 <limits>
6 
7 #include "partition_alloc/oom.h"
8 #include "partition_alloc/partition_alloc_base/compiler_specific.h"
9 #include "partition_alloc/partition_alloc_base/numerics/checked_math.h"
10 #include "partition_alloc/shim/allocator_shim.h"
11 
12 #include <dlfcn.h>
13 #include <malloc.h>
14 
15 // This translation unit defines a default dispatch for the allocator shim which
16 // routes allocations to libc functions.
17 // The code here is strongly inspired from tcmalloc's libc_override_glibc.h.
18 
19 extern "C" {
20 void* __libc_malloc(size_t size);
21 void* __libc_calloc(size_t n, size_t size);
22 void* __libc_realloc(void* address, size_t size);
23 void* __libc_memalign(size_t alignment, size_t size);
24 void __libc_free(void* ptr);
25 }  // extern "C"
26 
27 namespace {
28 
29 using allocator_shim::AllocatorDispatch;
30 
31 // Strictly speaking, it would make more sense to not subtract amything, but
32 // other shims limit to something lower than INT_MAX (which is 0x7FFFFFFF on
33 // most platforms), and tests expect that.
34 constexpr size_t kMaxAllowedSize = std::numeric_limits<int>::max() - (1 << 12);
35 
GlibcMalloc(const AllocatorDispatch *,size_t size,void * context)36 void* GlibcMalloc(const AllocatorDispatch*, size_t size, void* context) {
37   // Cannot force glibc's malloc() to crash when a large size is requested, do
38   // it in the shim instead.
39   if (PA_UNLIKELY(size >= kMaxAllowedSize)) {
40     partition_alloc::TerminateBecauseOutOfMemory(size);
41   }
42 
43   return __libc_malloc(size);
44 }
45 
GlibcUncheckedMalloc(const AllocatorDispatch *,size_t size,void * context)46 void* GlibcUncheckedMalloc(const AllocatorDispatch*,
47                            size_t size,
48                            void* context) {
49   if (PA_UNLIKELY(size >= kMaxAllowedSize)) {
50     return nullptr;
51   }
52 
53   return __libc_malloc(size);
54 }
55 
GlibcCalloc(const AllocatorDispatch *,size_t n,size_t size,void * context)56 void* GlibcCalloc(const AllocatorDispatch*,
57                   size_t n,
58                   size_t size,
59                   void* context) {
60   const auto total = partition_alloc::internal::base::CheckMul(n, size);
61   if (PA_UNLIKELY(!total.IsValid() || total.ValueOrDie() >= kMaxAllowedSize)) {
62     partition_alloc::TerminateBecauseOutOfMemory(size * n);
63   }
64 
65   return __libc_calloc(n, size);
66 }
67 
GlibcRealloc(const AllocatorDispatch *,void * address,size_t size,void * context)68 void* GlibcRealloc(const AllocatorDispatch*,
69                    void* address,
70                    size_t size,
71                    void* context) {
72   if (PA_UNLIKELY(size >= kMaxAllowedSize)) {
73     partition_alloc::TerminateBecauseOutOfMemory(size);
74   }
75 
76   return __libc_realloc(address, size);
77 }
78 
GlibcMemalign(const AllocatorDispatch *,size_t alignment,size_t size,void * context)79 void* GlibcMemalign(const AllocatorDispatch*,
80                     size_t alignment,
81                     size_t size,
82                     void* context) {
83   if (PA_UNLIKELY(size >= kMaxAllowedSize)) {
84     partition_alloc::TerminateBecauseOutOfMemory(size);
85   }
86 
87   return __libc_memalign(alignment, size);
88 }
89 
GlibcFree(const AllocatorDispatch *,void * address,void * context)90 void GlibcFree(const AllocatorDispatch*, void* address, void* context) {
91   __libc_free(address);
92 }
93 
94 PA_NO_SANITIZE("cfi-icall")
GlibcGetSizeEstimate(const AllocatorDispatch *,void * address,void * context)95 size_t GlibcGetSizeEstimate(const AllocatorDispatch*,
96                             void* address,
97                             void* context) {
98   // glibc does not expose an alias to resolve malloc_usable_size. Dynamically
99   // resolve it instead. This should be safe because glibc (and hence dlfcn)
100   // does not use malloc_size internally and so there should not be a risk of
101   // recursion.
102   using MallocUsableSizeFunction = decltype(malloc_usable_size)*;
103   static MallocUsableSizeFunction fn_ptr =
104       reinterpret_cast<MallocUsableSizeFunction>(
105           dlsym(RTLD_NEXT, "malloc_usable_size"));
106 
107   return fn_ptr(address);
108 }
109 
110 }  // namespace
111 
112 const AllocatorDispatch AllocatorDispatch::default_dispatch = {
113     &GlibcMalloc,          /* alloc_function */
114     &GlibcUncheckedMalloc, /* alloc_unchecked_function */
115     &GlibcCalloc,          /* alloc_zero_initialized_function */
116     &GlibcMemalign,        /* alloc_aligned_function */
117     &GlibcRealloc,         /* realloc_function */
118     &GlibcFree,            /* free_function */
119     &GlibcGetSizeEstimate, /* get_size_estimate_function */
120     nullptr,               /* good_size_function */
121     nullptr,               /* claimed_address */
122     nullptr,               /* batch_malloc_function */
123     nullptr,               /* batch_free_function */
124     nullptr,               /* free_definite_size_function */
125     nullptr,               /* try_free_default_function */
126     nullptr,               /* aligned_malloc_function */
127     nullptr,               /* aligned_realloc_function */
128     nullptr,               /* aligned_free_function */
129     nullptr,               /* next */
130 };
131