1*8d67ca89SAndroid Build Coastguard Worker /*
2*8d67ca89SAndroid Build Coastguard Worker * Copyright (C) 2019 The Android Open Source Project
3*8d67ca89SAndroid Build Coastguard Worker * All rights reserved.
4*8d67ca89SAndroid Build Coastguard Worker *
5*8d67ca89SAndroid Build Coastguard Worker * Redistribution and use in source and binary forms, with or without
6*8d67ca89SAndroid Build Coastguard Worker * modification, are permitted provided that the following conditions
7*8d67ca89SAndroid Build Coastguard Worker * are met:
8*8d67ca89SAndroid Build Coastguard Worker * * Redistributions of source code must retain the above copyright
9*8d67ca89SAndroid Build Coastguard Worker * notice, this list of conditions and the following disclaimer.
10*8d67ca89SAndroid Build Coastguard Worker * * Redistributions in binary form must reproduce the above copyright
11*8d67ca89SAndroid Build Coastguard Worker * notice, this list of conditions and the following disclaimer in
12*8d67ca89SAndroid Build Coastguard Worker * the documentation and/or other materials provided with the
13*8d67ca89SAndroid Build Coastguard Worker * distribution.
14*8d67ca89SAndroid Build Coastguard Worker *
15*8d67ca89SAndroid Build Coastguard Worker * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16*8d67ca89SAndroid Build Coastguard Worker * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17*8d67ca89SAndroid Build Coastguard Worker * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18*8d67ca89SAndroid Build Coastguard Worker * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19*8d67ca89SAndroid Build Coastguard Worker * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20*8d67ca89SAndroid Build Coastguard Worker * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21*8d67ca89SAndroid Build Coastguard Worker * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22*8d67ca89SAndroid Build Coastguard Worker * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23*8d67ca89SAndroid Build Coastguard Worker * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24*8d67ca89SAndroid Build Coastguard Worker * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25*8d67ca89SAndroid Build Coastguard Worker * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26*8d67ca89SAndroid Build Coastguard Worker * SUCH DAMAGE.
27*8d67ca89SAndroid Build Coastguard Worker */
28*8d67ca89SAndroid Build Coastguard Worker
29*8d67ca89SAndroid Build Coastguard Worker #include <inttypes.h>
30*8d67ca89SAndroid Build Coastguard Worker #include <pthread.h>
31*8d67ca89SAndroid Build Coastguard Worker #include <stdatomic.h>
32*8d67ca89SAndroid Build Coastguard Worker #include <stdint.h>
33*8d67ca89SAndroid Build Coastguard Worker #include <stdio.h>
34*8d67ca89SAndroid Build Coastguard Worker #include <unistd.h>
35*8d67ca89SAndroid Build Coastguard Worker
36*8d67ca89SAndroid Build Coastguard Worker #include <private/bionic_malloc_dispatch.h>
37*8d67ca89SAndroid Build Coastguard Worker
38*8d67ca89SAndroid Build Coastguard Worker #if __has_feature(hwaddress_sanitizer)
39*8d67ca89SAndroid Build Coastguard Worker #include <sanitizer/allocator_interface.h>
40*8d67ca89SAndroid Build Coastguard Worker #endif
41*8d67ca89SAndroid Build Coastguard Worker
42*8d67ca89SAndroid Build Coastguard Worker #include "malloc_common.h"
43*8d67ca89SAndroid Build Coastguard Worker #include "malloc_common_dynamic.h"
44*8d67ca89SAndroid Build Coastguard Worker #include "malloc_heapprofd.h"
45*8d67ca89SAndroid Build Coastguard Worker #include "malloc_limit.h"
46*8d67ca89SAndroid Build Coastguard Worker
47*8d67ca89SAndroid Build Coastguard Worker __BEGIN_DECLS
48*8d67ca89SAndroid Build Coastguard Worker static void* LimitCalloc(size_t n_elements, size_t elem_size);
49*8d67ca89SAndroid Build Coastguard Worker static void LimitFree(void* mem);
50*8d67ca89SAndroid Build Coastguard Worker static void* LimitMalloc(size_t bytes);
51*8d67ca89SAndroid Build Coastguard Worker static void* LimitMemalign(size_t alignment, size_t bytes);
52*8d67ca89SAndroid Build Coastguard Worker static int LimitPosixMemalign(void** memptr, size_t alignment, size_t size);
53*8d67ca89SAndroid Build Coastguard Worker static void* LimitRealloc(void* old_mem, size_t bytes);
54*8d67ca89SAndroid Build Coastguard Worker static void* LimitAlignedAlloc(size_t alignment, size_t size);
55*8d67ca89SAndroid Build Coastguard Worker #if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
56*8d67ca89SAndroid Build Coastguard Worker static void* LimitPvalloc(size_t bytes);
57*8d67ca89SAndroid Build Coastguard Worker static void* LimitValloc(size_t bytes);
58*8d67ca89SAndroid Build Coastguard Worker #endif
59*8d67ca89SAndroid Build Coastguard Worker
60*8d67ca89SAndroid Build Coastguard Worker // Pass through functions.
61*8d67ca89SAndroid Build Coastguard Worker static size_t LimitUsableSize(const void* mem);
62*8d67ca89SAndroid Build Coastguard Worker static struct mallinfo LimitMallinfo();
63*8d67ca89SAndroid Build Coastguard Worker static int LimitIterate(uintptr_t base, size_t size, void (*callback)(uintptr_t, size_t, void*), void* arg);
64*8d67ca89SAndroid Build Coastguard Worker static void LimitMallocDisable();
65*8d67ca89SAndroid Build Coastguard Worker static void LimitMallocEnable();
66*8d67ca89SAndroid Build Coastguard Worker static int LimitMallocInfo(int options, FILE* fp);
67*8d67ca89SAndroid Build Coastguard Worker static int LimitMallopt(int param, int value);
68*8d67ca89SAndroid Build Coastguard Worker __END_DECLS
69*8d67ca89SAndroid Build Coastguard Worker
70*8d67ca89SAndroid Build Coastguard Worker static constexpr MallocDispatch __limit_dispatch
71*8d67ca89SAndroid Build Coastguard Worker __attribute__((unused)) = {
72*8d67ca89SAndroid Build Coastguard Worker LimitCalloc,
73*8d67ca89SAndroid Build Coastguard Worker LimitFree,
74*8d67ca89SAndroid Build Coastguard Worker LimitMallinfo,
75*8d67ca89SAndroid Build Coastguard Worker LimitMalloc,
76*8d67ca89SAndroid Build Coastguard Worker LimitUsableSize,
77*8d67ca89SAndroid Build Coastguard Worker LimitMemalign,
78*8d67ca89SAndroid Build Coastguard Worker LimitPosixMemalign,
79*8d67ca89SAndroid Build Coastguard Worker #if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
80*8d67ca89SAndroid Build Coastguard Worker LimitPvalloc,
81*8d67ca89SAndroid Build Coastguard Worker #endif
82*8d67ca89SAndroid Build Coastguard Worker LimitRealloc,
83*8d67ca89SAndroid Build Coastguard Worker #if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
84*8d67ca89SAndroid Build Coastguard Worker LimitValloc,
85*8d67ca89SAndroid Build Coastguard Worker #endif
86*8d67ca89SAndroid Build Coastguard Worker LimitIterate,
87*8d67ca89SAndroid Build Coastguard Worker LimitMallocDisable,
88*8d67ca89SAndroid Build Coastguard Worker LimitMallocEnable,
89*8d67ca89SAndroid Build Coastguard Worker LimitMallopt,
90*8d67ca89SAndroid Build Coastguard Worker LimitAlignedAlloc,
91*8d67ca89SAndroid Build Coastguard Worker LimitMallocInfo,
92*8d67ca89SAndroid Build Coastguard Worker };
93*8d67ca89SAndroid Build Coastguard Worker
94*8d67ca89SAndroid Build Coastguard Worker static _Atomic uint64_t gAllocated;
95*8d67ca89SAndroid Build Coastguard Worker static uint64_t gAllocLimit;
96*8d67ca89SAndroid Build Coastguard Worker
CheckLimit(size_t bytes)97*8d67ca89SAndroid Build Coastguard Worker static inline bool CheckLimit(size_t bytes) {
98*8d67ca89SAndroid Build Coastguard Worker uint64_t total;
99*8d67ca89SAndroid Build Coastguard Worker if (__predict_false(__builtin_add_overflow(
100*8d67ca89SAndroid Build Coastguard Worker atomic_load_explicit(&gAllocated, memory_order_relaxed), bytes, &total) ||
101*8d67ca89SAndroid Build Coastguard Worker total > gAllocLimit)) {
102*8d67ca89SAndroid Build Coastguard Worker return false;
103*8d67ca89SAndroid Build Coastguard Worker }
104*8d67ca89SAndroid Build Coastguard Worker return true;
105*8d67ca89SAndroid Build Coastguard Worker }
106*8d67ca89SAndroid Build Coastguard Worker
IncrementLimit(void * mem)107*8d67ca89SAndroid Build Coastguard Worker static inline void* IncrementLimit(void* mem) {
108*8d67ca89SAndroid Build Coastguard Worker if (__predict_false(mem == nullptr)) {
109*8d67ca89SAndroid Build Coastguard Worker return nullptr;
110*8d67ca89SAndroid Build Coastguard Worker }
111*8d67ca89SAndroid Build Coastguard Worker atomic_fetch_add(&gAllocated, LimitUsableSize(mem));
112*8d67ca89SAndroid Build Coastguard Worker return mem;
113*8d67ca89SAndroid Build Coastguard Worker }
114*8d67ca89SAndroid Build Coastguard Worker
LimitCalloc(size_t n_elements,size_t elem_size)115*8d67ca89SAndroid Build Coastguard Worker void* LimitCalloc(size_t n_elements, size_t elem_size) {
116*8d67ca89SAndroid Build Coastguard Worker size_t total;
117*8d67ca89SAndroid Build Coastguard Worker if (__builtin_mul_overflow(n_elements, elem_size, &total) || !CheckLimit(total)) {
118*8d67ca89SAndroid Build Coastguard Worker warning_log("malloc_limit: calloc(%zu, %zu) exceeds limit %" PRId64, n_elements, elem_size,
119*8d67ca89SAndroid Build Coastguard Worker gAllocLimit);
120*8d67ca89SAndroid Build Coastguard Worker return nullptr;
121*8d67ca89SAndroid Build Coastguard Worker }
122*8d67ca89SAndroid Build Coastguard Worker auto dispatch_table = GetDefaultDispatchTable();
123*8d67ca89SAndroid Build Coastguard Worker if (__predict_false(dispatch_table != nullptr)) {
124*8d67ca89SAndroid Build Coastguard Worker return IncrementLimit(dispatch_table->calloc(n_elements, elem_size));
125*8d67ca89SAndroid Build Coastguard Worker }
126*8d67ca89SAndroid Build Coastguard Worker return IncrementLimit(Malloc(calloc)(n_elements, elem_size));
127*8d67ca89SAndroid Build Coastguard Worker }
128*8d67ca89SAndroid Build Coastguard Worker
LimitFree(void * mem)129*8d67ca89SAndroid Build Coastguard Worker void LimitFree(void* mem) {
130*8d67ca89SAndroid Build Coastguard Worker atomic_fetch_sub(&gAllocated, LimitUsableSize(mem));
131*8d67ca89SAndroid Build Coastguard Worker auto dispatch_table = GetDefaultDispatchTable();
132*8d67ca89SAndroid Build Coastguard Worker if (__predict_false(dispatch_table != nullptr)) {
133*8d67ca89SAndroid Build Coastguard Worker return dispatch_table->free(mem);
134*8d67ca89SAndroid Build Coastguard Worker }
135*8d67ca89SAndroid Build Coastguard Worker return Malloc(free)(mem);
136*8d67ca89SAndroid Build Coastguard Worker }
137*8d67ca89SAndroid Build Coastguard Worker
LimitMalloc(size_t bytes)138*8d67ca89SAndroid Build Coastguard Worker void* LimitMalloc(size_t bytes) {
139*8d67ca89SAndroid Build Coastguard Worker if (!CheckLimit(bytes)) {
140*8d67ca89SAndroid Build Coastguard Worker warning_log("malloc_limit: malloc(%zu) exceeds limit %" PRId64, bytes, gAllocLimit);
141*8d67ca89SAndroid Build Coastguard Worker return nullptr;
142*8d67ca89SAndroid Build Coastguard Worker }
143*8d67ca89SAndroid Build Coastguard Worker auto dispatch_table = GetDefaultDispatchTable();
144*8d67ca89SAndroid Build Coastguard Worker if (__predict_false(dispatch_table != nullptr)) {
145*8d67ca89SAndroid Build Coastguard Worker return IncrementLimit(dispatch_table->malloc(bytes));
146*8d67ca89SAndroid Build Coastguard Worker }
147*8d67ca89SAndroid Build Coastguard Worker return IncrementLimit(Malloc(malloc)(bytes));
148*8d67ca89SAndroid Build Coastguard Worker }
149*8d67ca89SAndroid Build Coastguard Worker
LimitMemalign(size_t alignment,size_t bytes)150*8d67ca89SAndroid Build Coastguard Worker static void* LimitMemalign(size_t alignment, size_t bytes) {
151*8d67ca89SAndroid Build Coastguard Worker if (!CheckLimit(bytes)) {
152*8d67ca89SAndroid Build Coastguard Worker warning_log("malloc_limit: memalign(%zu, %zu) exceeds limit %" PRId64, alignment, bytes,
153*8d67ca89SAndroid Build Coastguard Worker gAllocLimit);
154*8d67ca89SAndroid Build Coastguard Worker return nullptr;
155*8d67ca89SAndroid Build Coastguard Worker }
156*8d67ca89SAndroid Build Coastguard Worker auto dispatch_table = GetDefaultDispatchTable();
157*8d67ca89SAndroid Build Coastguard Worker if (__predict_false(dispatch_table != nullptr)) {
158*8d67ca89SAndroid Build Coastguard Worker return IncrementLimit(dispatch_table->memalign(alignment, bytes));
159*8d67ca89SAndroid Build Coastguard Worker }
160*8d67ca89SAndroid Build Coastguard Worker return IncrementLimit(Malloc(memalign)(alignment, bytes));
161*8d67ca89SAndroid Build Coastguard Worker }
162*8d67ca89SAndroid Build Coastguard Worker
LimitPosixMemalign(void ** memptr,size_t alignment,size_t size)163*8d67ca89SAndroid Build Coastguard Worker static int LimitPosixMemalign(void** memptr, size_t alignment, size_t size) {
164*8d67ca89SAndroid Build Coastguard Worker if (!CheckLimit(size)) {
165*8d67ca89SAndroid Build Coastguard Worker warning_log("malloc_limit: posix_memalign(%zu, %zu) exceeds limit %" PRId64, alignment, size,
166*8d67ca89SAndroid Build Coastguard Worker gAllocLimit);
167*8d67ca89SAndroid Build Coastguard Worker return ENOMEM;
168*8d67ca89SAndroid Build Coastguard Worker }
169*8d67ca89SAndroid Build Coastguard Worker int retval;
170*8d67ca89SAndroid Build Coastguard Worker auto dispatch_table = GetDefaultDispatchTable();
171*8d67ca89SAndroid Build Coastguard Worker if (__predict_false(dispatch_table != nullptr)) {
172*8d67ca89SAndroid Build Coastguard Worker retval = dispatch_table->posix_memalign(memptr, alignment, size);
173*8d67ca89SAndroid Build Coastguard Worker } else {
174*8d67ca89SAndroid Build Coastguard Worker retval = Malloc(posix_memalign)(memptr, alignment, size);
175*8d67ca89SAndroid Build Coastguard Worker }
176*8d67ca89SAndroid Build Coastguard Worker if (__predict_false(retval != 0)) {
177*8d67ca89SAndroid Build Coastguard Worker return retval;
178*8d67ca89SAndroid Build Coastguard Worker }
179*8d67ca89SAndroid Build Coastguard Worker IncrementLimit(*memptr);
180*8d67ca89SAndroid Build Coastguard Worker return 0;
181*8d67ca89SAndroid Build Coastguard Worker }
182*8d67ca89SAndroid Build Coastguard Worker
LimitAlignedAlloc(size_t alignment,size_t size)183*8d67ca89SAndroid Build Coastguard Worker static void* LimitAlignedAlloc(size_t alignment, size_t size) {
184*8d67ca89SAndroid Build Coastguard Worker if (!CheckLimit(size)) {
185*8d67ca89SAndroid Build Coastguard Worker warning_log("malloc_limit: aligned_alloc(%zu, %zu) exceeds limit %" PRId64, alignment, size,
186*8d67ca89SAndroid Build Coastguard Worker gAllocLimit);
187*8d67ca89SAndroid Build Coastguard Worker return nullptr;
188*8d67ca89SAndroid Build Coastguard Worker }
189*8d67ca89SAndroid Build Coastguard Worker auto dispatch_table = GetDefaultDispatchTable();
190*8d67ca89SAndroid Build Coastguard Worker if (__predict_false(dispatch_table != nullptr)) {
191*8d67ca89SAndroid Build Coastguard Worker return IncrementLimit(dispatch_table->aligned_alloc(alignment, size));
192*8d67ca89SAndroid Build Coastguard Worker }
193*8d67ca89SAndroid Build Coastguard Worker return IncrementLimit(Malloc(aligned_alloc)(alignment, size));
194*8d67ca89SAndroid Build Coastguard Worker }
195*8d67ca89SAndroid Build Coastguard Worker
LimitRealloc(void * old_mem,size_t bytes)196*8d67ca89SAndroid Build Coastguard Worker static void* LimitRealloc(void* old_mem, size_t bytes) {
197*8d67ca89SAndroid Build Coastguard Worker size_t old_usable_size = LimitUsableSize(old_mem);
198*8d67ca89SAndroid Build Coastguard Worker void* new_ptr;
199*8d67ca89SAndroid Build Coastguard Worker // Need to check the size only if the allocation will increase in size.
200*8d67ca89SAndroid Build Coastguard Worker if (bytes > old_usable_size && !CheckLimit(bytes - old_usable_size)) {
201*8d67ca89SAndroid Build Coastguard Worker warning_log("malloc_limit: realloc(%p, %zu) exceeds limit %" PRId64, old_mem, bytes,
202*8d67ca89SAndroid Build Coastguard Worker gAllocLimit);
203*8d67ca89SAndroid Build Coastguard Worker // Free the old pointer.
204*8d67ca89SAndroid Build Coastguard Worker LimitFree(old_mem);
205*8d67ca89SAndroid Build Coastguard Worker return nullptr;
206*8d67ca89SAndroid Build Coastguard Worker }
207*8d67ca89SAndroid Build Coastguard Worker
208*8d67ca89SAndroid Build Coastguard Worker auto dispatch_table = GetDefaultDispatchTable();
209*8d67ca89SAndroid Build Coastguard Worker if (__predict_false(dispatch_table != nullptr)) {
210*8d67ca89SAndroid Build Coastguard Worker new_ptr = dispatch_table->realloc(old_mem, bytes);
211*8d67ca89SAndroid Build Coastguard Worker } else {
212*8d67ca89SAndroid Build Coastguard Worker new_ptr = Malloc(realloc)(old_mem, bytes);
213*8d67ca89SAndroid Build Coastguard Worker }
214*8d67ca89SAndroid Build Coastguard Worker
215*8d67ca89SAndroid Build Coastguard Worker if (__predict_false(new_ptr == nullptr)) {
216*8d67ca89SAndroid Build Coastguard Worker // This acts as if the pointer was freed.
217*8d67ca89SAndroid Build Coastguard Worker atomic_fetch_sub(&gAllocated, old_usable_size);
218*8d67ca89SAndroid Build Coastguard Worker return nullptr;
219*8d67ca89SAndroid Build Coastguard Worker }
220*8d67ca89SAndroid Build Coastguard Worker
221*8d67ca89SAndroid Build Coastguard Worker size_t new_usable_size = LimitUsableSize(new_ptr);
222*8d67ca89SAndroid Build Coastguard Worker // Assumes that most allocations increase in size, rather than shrink.
223*8d67ca89SAndroid Build Coastguard Worker if (__predict_false(old_usable_size > new_usable_size)) {
224*8d67ca89SAndroid Build Coastguard Worker atomic_fetch_sub(&gAllocated, old_usable_size - new_usable_size);
225*8d67ca89SAndroid Build Coastguard Worker } else {
226*8d67ca89SAndroid Build Coastguard Worker atomic_fetch_add(&gAllocated, new_usable_size - old_usable_size);
227*8d67ca89SAndroid Build Coastguard Worker }
228*8d67ca89SAndroid Build Coastguard Worker return new_ptr;
229*8d67ca89SAndroid Build Coastguard Worker }
230*8d67ca89SAndroid Build Coastguard Worker
231*8d67ca89SAndroid Build Coastguard Worker #if defined(HAVE_DEPRECATED_MALLOC_FUNCS)
LimitPvalloc(size_t bytes)232*8d67ca89SAndroid Build Coastguard Worker static void* LimitPvalloc(size_t bytes) {
233*8d67ca89SAndroid Build Coastguard Worker if (!CheckLimit(bytes)) {
234*8d67ca89SAndroid Build Coastguard Worker warning_log("malloc_limit: pvalloc(%zu) exceeds limit %" PRId64, bytes, gAllocLimit);
235*8d67ca89SAndroid Build Coastguard Worker return nullptr;
236*8d67ca89SAndroid Build Coastguard Worker }
237*8d67ca89SAndroid Build Coastguard Worker auto dispatch_table = GetDefaultDispatchTable();
238*8d67ca89SAndroid Build Coastguard Worker if (__predict_false(dispatch_table != nullptr)) {
239*8d67ca89SAndroid Build Coastguard Worker return IncrementLimit(dispatch_table->pvalloc(bytes));
240*8d67ca89SAndroid Build Coastguard Worker }
241*8d67ca89SAndroid Build Coastguard Worker return IncrementLimit(Malloc(pvalloc)(bytes));
242*8d67ca89SAndroid Build Coastguard Worker }
243*8d67ca89SAndroid Build Coastguard Worker
LimitValloc(size_t bytes)244*8d67ca89SAndroid Build Coastguard Worker static void* LimitValloc(size_t bytes) {
245*8d67ca89SAndroid Build Coastguard Worker if (!CheckLimit(bytes)) {
246*8d67ca89SAndroid Build Coastguard Worker warning_log("malloc_limit: valloc(%zu) exceeds limit %" PRId64, bytes, gAllocLimit);
247*8d67ca89SAndroid Build Coastguard Worker return nullptr;
248*8d67ca89SAndroid Build Coastguard Worker }
249*8d67ca89SAndroid Build Coastguard Worker auto dispatch_table = GetDefaultDispatchTable();
250*8d67ca89SAndroid Build Coastguard Worker if (__predict_false(dispatch_table != nullptr)) {
251*8d67ca89SAndroid Build Coastguard Worker return IncrementLimit(dispatch_table->valloc(bytes));
252*8d67ca89SAndroid Build Coastguard Worker }
253*8d67ca89SAndroid Build Coastguard Worker return IncrementLimit(Malloc(valloc)(bytes));
254*8d67ca89SAndroid Build Coastguard Worker }
255*8d67ca89SAndroid Build Coastguard Worker #endif
256*8d67ca89SAndroid Build Coastguard Worker
MallocLimitInstalled()257*8d67ca89SAndroid Build Coastguard Worker bool MallocLimitInstalled() {
258*8d67ca89SAndroid Build Coastguard Worker return GetDispatchTable() == &__limit_dispatch;
259*8d67ca89SAndroid Build Coastguard Worker }
260*8d67ca89SAndroid Build Coastguard Worker
261*8d67ca89SAndroid Build Coastguard Worker #if defined(LIBC_STATIC)
EnableLimitDispatchTable()262*8d67ca89SAndroid Build Coastguard Worker static bool EnableLimitDispatchTable() {
263*8d67ca89SAndroid Build Coastguard Worker // This is the only valid way to modify the dispatch tables for a
264*8d67ca89SAndroid Build Coastguard Worker // static executable so no locks are necessary.
265*8d67ca89SAndroid Build Coastguard Worker __libc_globals.mutate([](libc_globals* globals) {
266*8d67ca89SAndroid Build Coastguard Worker atomic_store(&globals->current_dispatch_table, &__limit_dispatch);
267*8d67ca89SAndroid Build Coastguard Worker });
268*8d67ca89SAndroid Build Coastguard Worker return true;
269*8d67ca89SAndroid Build Coastguard Worker }
270*8d67ca89SAndroid Build Coastguard Worker #else
EnableLimitDispatchTable()271*8d67ca89SAndroid Build Coastguard Worker static bool EnableLimitDispatchTable() {
272*8d67ca89SAndroid Build Coastguard Worker pthread_mutex_lock(&gGlobalsMutateLock);
273*8d67ca89SAndroid Build Coastguard Worker // All other code that calls mutate will grab the gGlobalsMutateLock.
274*8d67ca89SAndroid Build Coastguard Worker // However, there is one case where the lock cannot be acquired, in the
275*8d67ca89SAndroid Build Coastguard Worker // signal handler that enables heapprofd. In order to avoid having two
276*8d67ca89SAndroid Build Coastguard Worker // threads calling mutate at the same time, use an atomic variable to
277*8d67ca89SAndroid Build Coastguard Worker // verify that only this function or the signal handler are calling mutate.
278*8d67ca89SAndroid Build Coastguard Worker // If this function is called at the same time as the signal handler is
279*8d67ca89SAndroid Build Coastguard Worker // being called, allow a short period for the signal handler to complete
280*8d67ca89SAndroid Build Coastguard Worker // before failing.
281*8d67ca89SAndroid Build Coastguard Worker bool enabled = false;
282*8d67ca89SAndroid Build Coastguard Worker size_t num_tries = 200;
283*8d67ca89SAndroid Build Coastguard Worker while (true) {
284*8d67ca89SAndroid Build Coastguard Worker if (!atomic_exchange(&gGlobalsMutating, true)) {
285*8d67ca89SAndroid Build Coastguard Worker __libc_globals.mutate([](libc_globals* globals) {
286*8d67ca89SAndroid Build Coastguard Worker atomic_store(&globals->current_dispatch_table, &__limit_dispatch);
287*8d67ca89SAndroid Build Coastguard Worker });
288*8d67ca89SAndroid Build Coastguard Worker atomic_store(&gGlobalsMutating, false);
289*8d67ca89SAndroid Build Coastguard Worker enabled = true;
290*8d67ca89SAndroid Build Coastguard Worker break;
291*8d67ca89SAndroid Build Coastguard Worker }
292*8d67ca89SAndroid Build Coastguard Worker if (--num_tries == 0) {
293*8d67ca89SAndroid Build Coastguard Worker break;
294*8d67ca89SAndroid Build Coastguard Worker }
295*8d67ca89SAndroid Build Coastguard Worker usleep(1000);
296*8d67ca89SAndroid Build Coastguard Worker }
297*8d67ca89SAndroid Build Coastguard Worker pthread_mutex_unlock(&gGlobalsMutateLock);
298*8d67ca89SAndroid Build Coastguard Worker if (enabled) {
299*8d67ca89SAndroid Build Coastguard Worker info_log("malloc_limit: Allocation limit enabled, max size %" PRId64 " bytes\n", gAllocLimit);
300*8d67ca89SAndroid Build Coastguard Worker } else {
301*8d67ca89SAndroid Build Coastguard Worker error_log("malloc_limit: Failed to enable allocation limit.");
302*8d67ca89SAndroid Build Coastguard Worker }
303*8d67ca89SAndroid Build Coastguard Worker return enabled;
304*8d67ca89SAndroid Build Coastguard Worker }
305*8d67ca89SAndroid Build Coastguard Worker #endif
306*8d67ca89SAndroid Build Coastguard Worker
LimitEnable(void * arg,size_t arg_size)307*8d67ca89SAndroid Build Coastguard Worker bool LimitEnable(void* arg, size_t arg_size) {
308*8d67ca89SAndroid Build Coastguard Worker if (arg == nullptr || arg_size != sizeof(size_t)) {
309*8d67ca89SAndroid Build Coastguard Worker errno = EINVAL;
310*8d67ca89SAndroid Build Coastguard Worker return false;
311*8d67ca89SAndroid Build Coastguard Worker }
312*8d67ca89SAndroid Build Coastguard Worker
313*8d67ca89SAndroid Build Coastguard Worker static _Atomic bool limit_enabled;
314*8d67ca89SAndroid Build Coastguard Worker if (atomic_exchange(&limit_enabled, true)) {
315*8d67ca89SAndroid Build Coastguard Worker // The limit can only be enabled once.
316*8d67ca89SAndroid Build Coastguard Worker error_log("malloc_limit: The allocation limit has already been set, it can only be set once.");
317*8d67ca89SAndroid Build Coastguard Worker return false;
318*8d67ca89SAndroid Build Coastguard Worker }
319*8d67ca89SAndroid Build Coastguard Worker
320*8d67ca89SAndroid Build Coastguard Worker gAllocLimit = *reinterpret_cast<size_t*>(arg);
321*8d67ca89SAndroid Build Coastguard Worker #if __has_feature(hwaddress_sanitizer)
322*8d67ca89SAndroid Build Coastguard Worker size_t current_allocated = __sanitizer_get_current_allocated_bytes();
323*8d67ca89SAndroid Build Coastguard Worker #else
324*8d67ca89SAndroid Build Coastguard Worker size_t current_allocated;
325*8d67ca89SAndroid Build Coastguard Worker auto dispatch_table = GetDefaultDispatchTable();
326*8d67ca89SAndroid Build Coastguard Worker if (__predict_false(dispatch_table != nullptr)) {
327*8d67ca89SAndroid Build Coastguard Worker current_allocated = dispatch_table->mallinfo().uordblks;
328*8d67ca89SAndroid Build Coastguard Worker } else {
329*8d67ca89SAndroid Build Coastguard Worker current_allocated = Malloc(mallinfo)().uordblks;
330*8d67ca89SAndroid Build Coastguard Worker }
331*8d67ca89SAndroid Build Coastguard Worker #endif
332*8d67ca89SAndroid Build Coastguard Worker // This has to be set before the enable occurs since "gAllocated" is used
333*8d67ca89SAndroid Build Coastguard Worker // to compute the limit. If the enable fails, "gAllocated" is never used.
334*8d67ca89SAndroid Build Coastguard Worker atomic_store(&gAllocated, current_allocated);
335*8d67ca89SAndroid Build Coastguard Worker
336*8d67ca89SAndroid Build Coastguard Worker if (!EnableLimitDispatchTable()) {
337*8d67ca89SAndroid Build Coastguard Worker // Failed to enable, reset so a future enable will pass.
338*8d67ca89SAndroid Build Coastguard Worker atomic_store(&limit_enabled, false);
339*8d67ca89SAndroid Build Coastguard Worker return false;
340*8d67ca89SAndroid Build Coastguard Worker }
341*8d67ca89SAndroid Build Coastguard Worker return true;
342*8d67ca89SAndroid Build Coastguard Worker }
343*8d67ca89SAndroid Build Coastguard Worker
LimitUsableSize(const void * mem)344*8d67ca89SAndroid Build Coastguard Worker static size_t LimitUsableSize(const void* mem) {
345*8d67ca89SAndroid Build Coastguard Worker auto dispatch_table = GetDefaultDispatchTable();
346*8d67ca89SAndroid Build Coastguard Worker if (__predict_false(dispatch_table != nullptr)) {
347*8d67ca89SAndroid Build Coastguard Worker return dispatch_table->malloc_usable_size(mem);
348*8d67ca89SAndroid Build Coastguard Worker }
349*8d67ca89SAndroid Build Coastguard Worker return Malloc(malloc_usable_size)(mem);
350*8d67ca89SAndroid Build Coastguard Worker }
351*8d67ca89SAndroid Build Coastguard Worker
LimitMallinfo()352*8d67ca89SAndroid Build Coastguard Worker static struct mallinfo LimitMallinfo() {
353*8d67ca89SAndroid Build Coastguard Worker auto dispatch_table = GetDefaultDispatchTable();
354*8d67ca89SAndroid Build Coastguard Worker if (__predict_false(dispatch_table != nullptr)) {
355*8d67ca89SAndroid Build Coastguard Worker return dispatch_table->mallinfo();
356*8d67ca89SAndroid Build Coastguard Worker }
357*8d67ca89SAndroid Build Coastguard Worker return Malloc(mallinfo)();
358*8d67ca89SAndroid Build Coastguard Worker }
359*8d67ca89SAndroid Build Coastguard Worker
LimitIterate(uintptr_t base,size_t size,void (* callback)(uintptr_t,size_t,void *),void * arg)360*8d67ca89SAndroid Build Coastguard Worker static int LimitIterate(uintptr_t base, size_t size, void (*callback)(uintptr_t, size_t, void*), void* arg) {
361*8d67ca89SAndroid Build Coastguard Worker auto dispatch_table = GetDefaultDispatchTable();
362*8d67ca89SAndroid Build Coastguard Worker if (__predict_false(dispatch_table != nullptr)) {
363*8d67ca89SAndroid Build Coastguard Worker return dispatch_table->malloc_iterate(base, size, callback, arg);
364*8d67ca89SAndroid Build Coastguard Worker }
365*8d67ca89SAndroid Build Coastguard Worker return Malloc(malloc_iterate)(base, size, callback, arg);
366*8d67ca89SAndroid Build Coastguard Worker }
367*8d67ca89SAndroid Build Coastguard Worker
LimitMallocDisable()368*8d67ca89SAndroid Build Coastguard Worker static void LimitMallocDisable() {
369*8d67ca89SAndroid Build Coastguard Worker auto dispatch_table = GetDefaultDispatchTable();
370*8d67ca89SAndroid Build Coastguard Worker if (__predict_false(dispatch_table != nullptr)) {
371*8d67ca89SAndroid Build Coastguard Worker dispatch_table->malloc_disable();
372*8d67ca89SAndroid Build Coastguard Worker } else {
373*8d67ca89SAndroid Build Coastguard Worker Malloc(malloc_disable)();
374*8d67ca89SAndroid Build Coastguard Worker }
375*8d67ca89SAndroid Build Coastguard Worker }
376*8d67ca89SAndroid Build Coastguard Worker
LimitMallocEnable()377*8d67ca89SAndroid Build Coastguard Worker static void LimitMallocEnable() {
378*8d67ca89SAndroid Build Coastguard Worker auto dispatch_table = GetDefaultDispatchTable();
379*8d67ca89SAndroid Build Coastguard Worker if (__predict_false(dispatch_table != nullptr)) {
380*8d67ca89SAndroid Build Coastguard Worker dispatch_table->malloc_enable();
381*8d67ca89SAndroid Build Coastguard Worker } else {
382*8d67ca89SAndroid Build Coastguard Worker Malloc(malloc_enable)();
383*8d67ca89SAndroid Build Coastguard Worker }
384*8d67ca89SAndroid Build Coastguard Worker }
385*8d67ca89SAndroid Build Coastguard Worker
LimitMallocInfo(int options,FILE * fp)386*8d67ca89SAndroid Build Coastguard Worker static int LimitMallocInfo(int options, FILE* fp) {
387*8d67ca89SAndroid Build Coastguard Worker auto dispatch_table = GetDefaultDispatchTable();
388*8d67ca89SAndroid Build Coastguard Worker if (__predict_false(dispatch_table != nullptr)) {
389*8d67ca89SAndroid Build Coastguard Worker return dispatch_table->malloc_info(options, fp);
390*8d67ca89SAndroid Build Coastguard Worker }
391*8d67ca89SAndroid Build Coastguard Worker return Malloc(malloc_info)(options, fp);
392*8d67ca89SAndroid Build Coastguard Worker }
393*8d67ca89SAndroid Build Coastguard Worker
LimitMallopt(int param,int value)394*8d67ca89SAndroid Build Coastguard Worker static int LimitMallopt(int param, int value) {
395*8d67ca89SAndroid Build Coastguard Worker auto dispatch_table = GetDefaultDispatchTable();
396*8d67ca89SAndroid Build Coastguard Worker if (__predict_false(dispatch_table != nullptr)) {
397*8d67ca89SAndroid Build Coastguard Worker return dispatch_table->mallopt(param, value);
398*8d67ca89SAndroid Build Coastguard Worker }
399*8d67ca89SAndroid Build Coastguard Worker return Malloc(mallopt)(param, value);
400*8d67ca89SAndroid Build Coastguard Worker }
401