1 #define JEMALLOC_C_
2 #include "jemalloc/internal/jemalloc_preamble.h"
3 #include "jemalloc/internal/jemalloc_internal_includes.h"
4
5 #include "jemalloc/internal/assert.h"
6 #include "jemalloc/internal/atomic.h"
7 #include "jemalloc/internal/buf_writer.h"
8 #include "jemalloc/internal/ctl.h"
9 #include "jemalloc/internal/emap.h"
10 #include "jemalloc/internal/extent_dss.h"
11 #include "jemalloc/internal/extent_mmap.h"
12 #include "jemalloc/internal/fxp.h"
13 #include "jemalloc/internal/san.h"
14 #include "jemalloc/internal/hook.h"
15 #include "jemalloc/internal/jemalloc_internal_types.h"
16 #include "jemalloc/internal/log.h"
17 #include "jemalloc/internal/malloc_io.h"
18 #include "jemalloc/internal/mutex.h"
19 #include "jemalloc/internal/nstime.h"
20 #include "jemalloc/internal/rtree.h"
21 #include "jemalloc/internal/safety_check.h"
22 #include "jemalloc/internal/sc.h"
23 #include "jemalloc/internal/spin.h"
24 #include "jemalloc/internal/sz.h"
25 #include "jemalloc/internal/ticker.h"
26 #include "jemalloc/internal/thread_event.h"
27 #include "jemalloc/internal/util.h"
28
29 /******************************************************************************/
30 /* Data. */
31
32 /* Work around <http://llvm.org/bugs/show_bug.cgi?id=12623>: */
33 const char *__malloc_options_1_0 = NULL;
34 __sym_compat(_malloc_options, __malloc_options_1_0, FBSD_1.0);
35
36 /* Runtime configuration options. */
37 const char *je_malloc_conf
38 #ifndef _WIN32
39 JEMALLOC_ATTR(weak)
40 #endif
41 ;
42 /*
43 * The usual rule is that the closer to runtime you are, the higher priority
44 * your configuration settings are (so the jemalloc config options get lower
45 * priority than the per-binary setting, which gets lower priority than the /etc
46 * setting, which gets lower priority than the environment settings).
47 *
48 * But it's a fairly common use case in some testing environments for a user to
49 * be able to control the binary, but nothing else (e.g. a performancy canary
50 * uses the production OS and environment variables, but can run any binary in
51 * those circumstances). For these use cases, it's handy to have an in-binary
52 * mechanism for overriding environment variable settings, with the idea that if
53 * the results are positive they get promoted to the official settings, and
54 * moved from the binary to the environment variable.
55 *
56 * We don't actually want this to be widespread, so we'll give it a silly name
57 * and not mention it in headers or documentation.
58 */
59 const char *je_malloc_conf_2_conf_harder
60 #ifndef _WIN32
61 JEMALLOC_ATTR(weak)
62 #endif
63 ;
64
65 bool opt_abort =
66 #ifdef JEMALLOC_DEBUG
67 true
68 #else
69 false
70 #endif
71 ;
72 bool opt_abort_conf =
73 #ifdef JEMALLOC_DEBUG
74 true
75 #else
76 false
77 #endif
78 ;
79 /* Intentionally default off, even with debug builds. */
80 bool opt_confirm_conf = false;
81 const char *opt_junk =
82 #if (defined(JEMALLOC_DEBUG) && defined(JEMALLOC_FILL))
83 "true"
84 #else
85 "false"
86 #endif
87 ;
88 bool opt_junk_alloc =
89 #if (defined(JEMALLOC_DEBUG) && defined(JEMALLOC_FILL))
90 true
91 #else
92 false
93 #endif
94 ;
95 bool opt_junk_free =
96 #if (defined(JEMALLOC_DEBUG) && defined(JEMALLOC_FILL))
97 true
98 #else
99 false
100 #endif
101 ;
102 bool opt_trust_madvise =
103 #ifdef JEMALLOC_PURGE_MADVISE_DONTNEED_ZEROS
104 false
105 #else
106 true
107 #endif
108 ;
109
110 bool opt_cache_oblivious =
111 #ifdef JEMALLOC_CACHE_OBLIVIOUS
112 true
113 #else
114 false
115 #endif
116 ;
117
118 zero_realloc_action_t opt_zero_realloc_action =
119 #ifdef JEMALLOC_ZERO_REALLOC_DEFAULT_FREE
120 zero_realloc_action_free
121 #else
122 zero_realloc_action_alloc
123 #endif
124 ;
125
126 atomic_zu_t zero_realloc_count = ATOMIC_INIT(0);
127
128 const char *zero_realloc_mode_names[] = {
129 "alloc",
130 "free",
131 "abort",
132 };
133
134 /*
135 * These are the documented values for junk fill debugging facilities -- see the
136 * man page.
137 */
138 static const uint8_t junk_alloc_byte = 0xa5;
139 static const uint8_t junk_free_byte = 0x5a;
140
default_junk_alloc(void * ptr,size_t usize)141 static void default_junk_alloc(void *ptr, size_t usize) {
142 memset(ptr, junk_alloc_byte, usize);
143 }
144
default_junk_free(void * ptr,size_t usize)145 static void default_junk_free(void *ptr, size_t usize) {
146 memset(ptr, junk_free_byte, usize);
147 }
148
149 void (*junk_alloc_callback)(void *ptr, size_t size) = &default_junk_alloc;
150 void (*junk_free_callback)(void *ptr, size_t size) = &default_junk_free;
151
152 bool opt_utrace = false;
153 bool opt_xmalloc = false;
154 bool opt_experimental_infallible_new = false;
155 bool opt_zero = false;
156 unsigned opt_narenas = 0;
157 fxp_t opt_narenas_ratio = FXP_INIT_INT(4);
158
159 unsigned ncpus;
160
161 /* Protects arenas initialization. */
162 malloc_mutex_t arenas_lock;
163
164 /* The global hpa, and whether it's on. */
165 bool opt_hpa = false;
166 hpa_shard_opts_t opt_hpa_opts = HPA_SHARD_OPTS_DEFAULT;
167 sec_opts_t opt_hpa_sec_opts = SEC_OPTS_DEFAULT;
168
169 /*
170 * Arenas that are used to service external requests. Not all elements of the
171 * arenas array are necessarily used; arenas are created lazily as needed.
172 *
173 * arenas[0..narenas_auto) are used for automatic multiplexing of threads and
174 * arenas. arenas[narenas_auto..narenas_total) are only used if the application
175 * takes some action to create them and allocate from them.
176 *
177 * Points to an arena_t.
178 */
179 JEMALLOC_ALIGNED(CACHELINE)
180 atomic_p_t arenas[MALLOCX_ARENA_LIMIT];
181 static atomic_u_t narenas_total; /* Use narenas_total_*(). */
182 /* Below three are read-only after initialization. */
183 static arena_t *a0; /* arenas[0]. */
184 unsigned narenas_auto;
185 unsigned manual_arena_base;
186
187 malloc_init_t malloc_init_state = malloc_init_uninitialized;
188
189 /* False should be the common case. Set to true to trigger initialization. */
190 bool malloc_slow = true;
191
192 /* When malloc_slow is true, set the corresponding bits for sanity check. */
193 enum {
194 flag_opt_junk_alloc = (1U),
195 flag_opt_junk_free = (1U << 1),
196 flag_opt_zero = (1U << 2),
197 flag_opt_utrace = (1U << 3),
198 flag_opt_xmalloc = (1U << 4)
199 };
200 static uint8_t malloc_slow_flags;
201
202 #ifdef JEMALLOC_THREADED_INIT
203 /* Used to let the initializing thread recursively allocate. */
204 # define NO_INITIALIZER ((unsigned long)0)
205 # define INITIALIZER pthread_self()
206 # define IS_INITIALIZER (malloc_initializer == pthread_self())
207 static pthread_t malloc_initializer = NO_INITIALIZER;
208 #else
209 # define NO_INITIALIZER false
210 # define INITIALIZER true
211 # define IS_INITIALIZER malloc_initializer
212 static bool malloc_initializer = NO_INITIALIZER;
213 #endif
214
215 /* Used to avoid initialization races. */
216 #ifdef _WIN32
217 #if _WIN32_WINNT >= 0x0600
218 static malloc_mutex_t init_lock = SRWLOCK_INIT;
219 #else
220 static malloc_mutex_t init_lock;
221 static bool init_lock_initialized = false;
222
JEMALLOC_ATTR(constructor)223 JEMALLOC_ATTR(constructor)
224 static void WINAPI
225 _init_init_lock(void) {
226 /*
227 * If another constructor in the same binary is using mallctl to e.g.
228 * set up extent hooks, it may end up running before this one, and
229 * malloc_init_hard will crash trying to lock the uninitialized lock. So
230 * we force an initialization of the lock in malloc_init_hard as well.
231 * We don't try to care about atomicity of the accessed to the
232 * init_lock_initialized boolean, since it really only matters early in
233 * the process creation, before any separate thread normally starts
234 * doing anything.
235 */
236 if (!init_lock_initialized) {
237 malloc_mutex_init(&init_lock, "init", WITNESS_RANK_INIT,
238 malloc_mutex_rank_exclusive);
239 }
240 init_lock_initialized = true;
241 }
242
243 #ifdef _MSC_VER
244 # pragma section(".CRT$XCU", read)
245 JEMALLOC_SECTION(".CRT$XCU") JEMALLOC_ATTR(used)
246 static const void (WINAPI *init_init_lock)(void) = _init_init_lock;
247 #endif
248 #endif
249 #else
250 static malloc_mutex_t init_lock = MALLOC_MUTEX_INITIALIZER;
251 #endif
252
253 typedef struct {
254 void *p; /* Input pointer (as in realloc(p, s)). */
255 size_t s; /* Request size. */
256 void *r; /* Result pointer. */
257 } malloc_utrace_t;
258
259 #ifdef JEMALLOC_UTRACE
260 # define UTRACE(a, b, c) do { \
261 if (unlikely(opt_utrace)) { \
262 int utrace_serrno = errno; \
263 malloc_utrace_t ut; \
264 ut.p = (a); \
265 ut.s = (b); \
266 ut.r = (c); \
267 UTRACE_CALL(&ut, sizeof(ut)); \
268 errno = utrace_serrno; \
269 } \
270 } while (0)
271 #else
272 # define UTRACE(a, b, c)
273 #endif
274
275 /* Whether encountered any invalid config options. */
276 static bool had_conf_error = false;
277
278 /******************************************************************************/
279 /*
280 * Function prototypes for static functions that are referenced prior to
281 * definition.
282 */
283
284 static bool malloc_init_hard_a0(void);
285 static bool malloc_init_hard(void);
286
287 /******************************************************************************/
288 /*
289 * Begin miscellaneous support functions.
290 */
291
292 JEMALLOC_ALWAYS_INLINE bool
malloc_init_a0(void)293 malloc_init_a0(void) {
294 if (unlikely(malloc_init_state == malloc_init_uninitialized)) {
295 return malloc_init_hard_a0();
296 }
297 return false;
298 }
299
300 JEMALLOC_ALWAYS_INLINE bool
malloc_init(void)301 malloc_init(void) {
302 if (unlikely(!malloc_initialized()) && malloc_init_hard()) {
303 return true;
304 }
305 return false;
306 }
307
308 /*
309 * The a0*() functions are used instead of i{d,}alloc() in situations that
310 * cannot tolerate TLS variable access.
311 */
312
313 static void *
a0ialloc(size_t size,bool zero,bool is_internal)314 a0ialloc(size_t size, bool zero, bool is_internal) {
315 if (unlikely(malloc_init_a0())) {
316 return NULL;
317 }
318
319 return iallocztm(TSDN_NULL, size, sz_size2index(size), zero, NULL,
320 is_internal, arena_get(TSDN_NULL, 0, true), true);
321 }
322
323 static void
a0idalloc(void * ptr,bool is_internal)324 a0idalloc(void *ptr, bool is_internal) {
325 idalloctm(TSDN_NULL, ptr, NULL, NULL, is_internal, true);
326 }
327
328 void *
a0malloc(size_t size)329 a0malloc(size_t size) {
330 return a0ialloc(size, false, true);
331 }
332
333 void
a0dalloc(void * ptr)334 a0dalloc(void *ptr) {
335 a0idalloc(ptr, true);
336 }
337
338 /*
339 * FreeBSD's libc uses the bootstrap_*() functions in bootstrap-sensitive
340 * situations that cannot tolerate TLS variable access (TLS allocation and very
341 * early internal data structure initialization).
342 */
343
344 void *
bootstrap_malloc(size_t size)345 bootstrap_malloc(size_t size) {
346 if (unlikely(size == 0)) {
347 size = 1;
348 }
349
350 return a0ialloc(size, false, false);
351 }
352
353 void *
bootstrap_calloc(size_t num,size_t size)354 bootstrap_calloc(size_t num, size_t size) {
355 size_t num_size;
356
357 num_size = num * size;
358 if (unlikely(num_size == 0)) {
359 assert(num == 0 || size == 0);
360 num_size = 1;
361 }
362
363 return a0ialloc(num_size, true, false);
364 }
365
366 void
bootstrap_free(void * ptr)367 bootstrap_free(void *ptr) {
368 if (unlikely(ptr == NULL)) {
369 return;
370 }
371
372 a0idalloc(ptr, false);
373 }
374
375 void
arena_set(unsigned ind,arena_t * arena)376 arena_set(unsigned ind, arena_t *arena) {
377 atomic_store_p(&arenas[ind], arena, ATOMIC_RELEASE);
378 }
379
380 static void
narenas_total_set(unsigned narenas)381 narenas_total_set(unsigned narenas) {
382 atomic_store_u(&narenas_total, narenas, ATOMIC_RELEASE);
383 }
384
385 static void
narenas_total_inc(void)386 narenas_total_inc(void) {
387 atomic_fetch_add_u(&narenas_total, 1, ATOMIC_RELEASE);
388 }
389
390 unsigned
narenas_total_get(void)391 narenas_total_get(void) {
392 return atomic_load_u(&narenas_total, ATOMIC_ACQUIRE);
393 }
394
395 /* Create a new arena and insert it into the arenas array at index ind. */
396 static arena_t *
arena_init_locked(tsdn_t * tsdn,unsigned ind,const arena_config_t * config)397 arena_init_locked(tsdn_t *tsdn, unsigned ind, const arena_config_t *config) {
398 arena_t *arena;
399
400 assert(ind <= narenas_total_get());
401 if (ind >= MALLOCX_ARENA_LIMIT) {
402 return NULL;
403 }
404 if (ind == narenas_total_get()) {
405 narenas_total_inc();
406 }
407
408 /*
409 * Another thread may have already initialized arenas[ind] if it's an
410 * auto arena.
411 */
412 arena = arena_get(tsdn, ind, false);
413 if (arena != NULL) {
414 assert(arena_is_auto(arena));
415 return arena;
416 }
417
418 /* Actually initialize the arena. */
419 arena = arena_new(tsdn, ind, config);
420
421 return arena;
422 }
423
424 static void
arena_new_create_background_thread(tsdn_t * tsdn,unsigned ind)425 arena_new_create_background_thread(tsdn_t *tsdn, unsigned ind) {
426 if (ind == 0) {
427 return;
428 }
429 /*
430 * Avoid creating a new background thread just for the huge arena, which
431 * purges eagerly by default.
432 */
433 if (have_background_thread && !arena_is_huge(ind)) {
434 if (background_thread_create(tsdn_tsd(tsdn), ind)) {
435 malloc_printf("<jemalloc>: error in background thread "
436 "creation for arena %u. Abort.\n", ind);
437 abort();
438 }
439 }
440 }
441
442 arena_t *
arena_init(tsdn_t * tsdn,unsigned ind,const arena_config_t * config)443 arena_init(tsdn_t *tsdn, unsigned ind, const arena_config_t *config) {
444 arena_t *arena;
445
446 malloc_mutex_lock(tsdn, &arenas_lock);
447 arena = arena_init_locked(tsdn, ind, config);
448 malloc_mutex_unlock(tsdn, &arenas_lock);
449
450 arena_new_create_background_thread(tsdn, ind);
451
452 return arena;
453 }
454
455 static void
arena_bind(tsd_t * tsd,unsigned ind,bool internal)456 arena_bind(tsd_t *tsd, unsigned ind, bool internal) {
457 arena_t *arena = arena_get(tsd_tsdn(tsd), ind, false);
458 arena_nthreads_inc(arena, internal);
459
460 if (internal) {
461 tsd_iarena_set(tsd, arena);
462 } else {
463 tsd_arena_set(tsd, arena);
464 unsigned shard = atomic_fetch_add_u(&arena->binshard_next, 1,
465 ATOMIC_RELAXED);
466 tsd_binshards_t *bins = tsd_binshardsp_get(tsd);
467 for (unsigned i = 0; i < SC_NBINS; i++) {
468 assert(bin_infos[i].n_shards > 0 &&
469 bin_infos[i].n_shards <= BIN_SHARDS_MAX);
470 bins->binshard[i] = shard % bin_infos[i].n_shards;
471 }
472 }
473 }
474
475 void
arena_migrate(tsd_t * tsd,arena_t * oldarena,arena_t * newarena)476 arena_migrate(tsd_t *tsd, arena_t *oldarena, arena_t *newarena) {
477 assert(oldarena != NULL);
478 assert(newarena != NULL);
479
480 arena_nthreads_dec(oldarena, false);
481 arena_nthreads_inc(newarena, false);
482 tsd_arena_set(tsd, newarena);
483
484 if (arena_nthreads_get(oldarena, false) == 0) {
485 /* Purge if the old arena has no associated threads anymore. */
486 arena_decay(tsd_tsdn(tsd), oldarena,
487 /* is_background_thread */ false, /* all */ true);
488 }
489 }
490
491 static void
arena_unbind(tsd_t * tsd,unsigned ind,bool internal)492 arena_unbind(tsd_t *tsd, unsigned ind, bool internal) {
493 arena_t *arena;
494
495 arena = arena_get(tsd_tsdn(tsd), ind, false);
496 arena_nthreads_dec(arena, internal);
497
498 if (internal) {
499 tsd_iarena_set(tsd, NULL);
500 } else {
501 tsd_arena_set(tsd, NULL);
502 }
503 }
504
505 /* Slow path, called only by arena_choose(). */
506 arena_t *
arena_choose_hard(tsd_t * tsd,bool internal)507 arena_choose_hard(tsd_t *tsd, bool internal) {
508 arena_t *ret JEMALLOC_CC_SILENCE_INIT(NULL);
509
510 if (have_percpu_arena && PERCPU_ARENA_ENABLED(opt_percpu_arena)) {
511 unsigned choose = percpu_arena_choose();
512 ret = arena_get(tsd_tsdn(tsd), choose, true);
513 assert(ret != NULL);
514 arena_bind(tsd, arena_ind_get(ret), false);
515 arena_bind(tsd, arena_ind_get(ret), true);
516
517 return ret;
518 }
519
520 if (narenas_auto > 1) {
521 unsigned i, j, choose[2], first_null;
522 bool is_new_arena[2];
523
524 /*
525 * Determine binding for both non-internal and internal
526 * allocation.
527 *
528 * choose[0]: For application allocation.
529 * choose[1]: For internal metadata allocation.
530 */
531
532 for (j = 0; j < 2; j++) {
533 choose[j] = 0;
534 is_new_arena[j] = false;
535 }
536
537 first_null = narenas_auto;
538 malloc_mutex_lock(tsd_tsdn(tsd), &arenas_lock);
539 assert(arena_get(tsd_tsdn(tsd), 0, false) != NULL);
540 for (i = 1; i < narenas_auto; i++) {
541 if (arena_get(tsd_tsdn(tsd), i, false) != NULL) {
542 /*
543 * Choose the first arena that has the lowest
544 * number of threads assigned to it.
545 */
546 for (j = 0; j < 2; j++) {
547 if (arena_nthreads_get(arena_get(
548 tsd_tsdn(tsd), i, false), !!j) <
549 arena_nthreads_get(arena_get(
550 tsd_tsdn(tsd), choose[j], false),
551 !!j)) {
552 choose[j] = i;
553 }
554 }
555 } else if (first_null == narenas_auto) {
556 /*
557 * Record the index of the first uninitialized
558 * arena, in case all extant arenas are in use.
559 *
560 * NB: It is possible for there to be
561 * discontinuities in terms of initialized
562 * versus uninitialized arenas, due to the
563 * "thread.arena" mallctl.
564 */
565 first_null = i;
566 }
567 }
568
569 for (j = 0; j < 2; j++) {
570 if (arena_nthreads_get(arena_get(tsd_tsdn(tsd),
571 choose[j], false), !!j) == 0 || first_null ==
572 narenas_auto) {
573 /*
574 * Use an unloaded arena, or the least loaded
575 * arena if all arenas are already initialized.
576 */
577 if (!!j == internal) {
578 ret = arena_get(tsd_tsdn(tsd),
579 choose[j], false);
580 }
581 } else {
582 arena_t *arena;
583
584 /* Initialize a new arena. */
585 choose[j] = first_null;
586 arena = arena_init_locked(tsd_tsdn(tsd),
587 choose[j], &arena_config_default);
588 if (arena == NULL) {
589 malloc_mutex_unlock(tsd_tsdn(tsd),
590 &arenas_lock);
591 return NULL;
592 }
593 is_new_arena[j] = true;
594 if (!!j == internal) {
595 ret = arena;
596 }
597 }
598 arena_bind(tsd, choose[j], !!j);
599 }
600 malloc_mutex_unlock(tsd_tsdn(tsd), &arenas_lock);
601
602 for (j = 0; j < 2; j++) {
603 if (is_new_arena[j]) {
604 assert(choose[j] > 0);
605 arena_new_create_background_thread(
606 tsd_tsdn(tsd), choose[j]);
607 }
608 }
609
610 } else {
611 ret = arena_get(tsd_tsdn(tsd), 0, false);
612 arena_bind(tsd, 0, false);
613 arena_bind(tsd, 0, true);
614 }
615
616 return ret;
617 }
618
619 void
iarena_cleanup(tsd_t * tsd)620 iarena_cleanup(tsd_t *tsd) {
621 arena_t *iarena;
622
623 iarena = tsd_iarena_get(tsd);
624 if (iarena != NULL) {
625 arena_unbind(tsd, arena_ind_get(iarena), true);
626 }
627 }
628
629 void
arena_cleanup(tsd_t * tsd)630 arena_cleanup(tsd_t *tsd) {
631 arena_t *arena;
632
633 arena = tsd_arena_get(tsd);
634 if (arena != NULL) {
635 arena_unbind(tsd, arena_ind_get(arena), false);
636 }
637 }
638
639 static void
stats_print_atexit(void)640 stats_print_atexit(void) {
641 if (config_stats) {
642 tsdn_t *tsdn;
643 unsigned narenas, i;
644
645 tsdn = tsdn_fetch();
646
647 /*
648 * Merge stats from extant threads. This is racy, since
649 * individual threads do not lock when recording tcache stats
650 * events. As a consequence, the final stats may be slightly
651 * out of date by the time they are reported, if other threads
652 * continue to allocate.
653 */
654 for (i = 0, narenas = narenas_total_get(); i < narenas; i++) {
655 arena_t *arena = arena_get(tsdn, i, false);
656 if (arena != NULL) {
657 tcache_slow_t *tcache_slow;
658
659 malloc_mutex_lock(tsdn, &arena->tcache_ql_mtx);
660 ql_foreach(tcache_slow, &arena->tcache_ql,
661 link) {
662 tcache_stats_merge(tsdn,
663 tcache_slow->tcache, arena);
664 }
665 malloc_mutex_unlock(tsdn,
666 &arena->tcache_ql_mtx);
667 }
668 }
669 }
670 je_malloc_stats_print(NULL, NULL, opt_stats_print_opts);
671 }
672
673 /*
674 * Ensure that we don't hold any locks upon entry to or exit from allocator
675 * code (in a "broad" sense that doesn't count a reentrant allocation as an
676 * entrance or exit).
677 */
678 JEMALLOC_ALWAYS_INLINE void
check_entry_exit_locking(tsdn_t * tsdn)679 check_entry_exit_locking(tsdn_t *tsdn) {
680 if (!config_debug) {
681 return;
682 }
683 if (tsdn_null(tsdn)) {
684 return;
685 }
686 tsd_t *tsd = tsdn_tsd(tsdn);
687 /*
688 * It's possible we hold locks at entry/exit if we're in a nested
689 * allocation.
690 */
691 int8_t reentrancy_level = tsd_reentrancy_level_get(tsd);
692 if (reentrancy_level != 0) {
693 return;
694 }
695 witness_assert_lockless(tsdn_witness_tsdp_get(tsdn));
696 }
697
698 /*
699 * End miscellaneous support functions.
700 */
701 /******************************************************************************/
702 /*
703 * Begin initialization functions.
704 */
705
706 static char *
jemalloc_secure_getenv(const char * name)707 jemalloc_secure_getenv(const char *name) {
708 #ifdef JEMALLOC_HAVE_SECURE_GETENV
709 return secure_getenv(name);
710 #else
711 # ifdef JEMALLOC_HAVE_ISSETUGID
712 if (issetugid() != 0) {
713 return NULL;
714 }
715 # endif
716 return getenv(name);
717 #endif
718 }
719
720 static unsigned
malloc_ncpus(void)721 malloc_ncpus(void) {
722 long result;
723
724 #ifdef _WIN32
725 SYSTEM_INFO si;
726 GetSystemInfo(&si);
727 result = si.dwNumberOfProcessors;
728 #elif defined(CPU_COUNT)
729 /*
730 * glibc >= 2.6 has the CPU_COUNT macro.
731 *
732 * glibc's sysconf() uses isspace(). glibc allocates for the first time
733 * *before* setting up the isspace tables. Therefore we need a
734 * different method to get the number of CPUs.
735 *
736 * The getaffinity approach is also preferred when only a subset of CPUs
737 * is available, to avoid using more arenas than necessary.
738 */
739 {
740 # if defined(__FreeBSD__) || defined(__DragonFly__)
741 cpuset_t set;
742 # else
743 cpu_set_t set;
744 # endif
745 # if defined(JEMALLOC_HAVE_SCHED_SETAFFINITY)
746 sched_getaffinity(0, sizeof(set), &set);
747 # else
748 pthread_getaffinity_np(pthread_self(), sizeof(set), &set);
749 # endif
750 result = CPU_COUNT(&set);
751 }
752 #else
753 result = sysconf(_SC_NPROCESSORS_ONLN);
754 #endif
755 return ((result == -1) ? 1 : (unsigned)result);
756 }
757
758 /*
759 * Ensure that number of CPUs is determistinc, i.e. it is the same based on:
760 * - sched_getaffinity()
761 * - _SC_NPROCESSORS_ONLN
762 * - _SC_NPROCESSORS_CONF
763 * Since otherwise tricky things is possible with percpu arenas in use.
764 */
765 static bool
malloc_cpu_count_is_deterministic()766 malloc_cpu_count_is_deterministic()
767 {
768 #ifdef _WIN32
769 return true;
770 #else
771 long cpu_onln = sysconf(_SC_NPROCESSORS_ONLN);
772 long cpu_conf = sysconf(_SC_NPROCESSORS_CONF);
773 if (cpu_onln != cpu_conf) {
774 return false;
775 }
776 # if defined(CPU_COUNT)
777 # if defined(__FreeBSD__) || defined(__DragonFly__)
778 cpuset_t set;
779 # else
780 cpu_set_t set;
781 # endif /* __FreeBSD__ */
782 # if defined(JEMALLOC_HAVE_SCHED_SETAFFINITY)
783 sched_getaffinity(0, sizeof(set), &set);
784 # else /* !JEMALLOC_HAVE_SCHED_SETAFFINITY */
785 pthread_getaffinity_np(pthread_self(), sizeof(set), &set);
786 # endif /* JEMALLOC_HAVE_SCHED_SETAFFINITY */
787 long cpu_affinity = CPU_COUNT(&set);
788 if (cpu_affinity != cpu_conf) {
789 return false;
790 }
791 # endif /* CPU_COUNT */
792 return true;
793 #endif
794 }
795
796 static void
init_opt_stats_opts(const char * v,size_t vlen,char * dest)797 init_opt_stats_opts(const char *v, size_t vlen, char *dest) {
798 size_t opts_len = strlen(dest);
799 assert(opts_len <= stats_print_tot_num_options);
800
801 for (size_t i = 0; i < vlen; i++) {
802 switch (v[i]) {
803 #define OPTION(o, v, d, s) case o: break;
804 STATS_PRINT_OPTIONS
805 #undef OPTION
806 default: continue;
807 }
808
809 if (strchr(dest, v[i]) != NULL) {
810 /* Ignore repeated. */
811 continue;
812 }
813
814 dest[opts_len++] = v[i];
815 dest[opts_len] = '\0';
816 assert(opts_len <= stats_print_tot_num_options);
817 }
818 assert(opts_len == strlen(dest));
819 }
820
821 /* Reads the next size pair in a multi-sized option. */
822 static bool
malloc_conf_multi_sizes_next(const char ** slab_size_segment_cur,size_t * vlen_left,size_t * slab_start,size_t * slab_end,size_t * new_size)823 malloc_conf_multi_sizes_next(const char **slab_size_segment_cur,
824 size_t *vlen_left, size_t *slab_start, size_t *slab_end, size_t *new_size) {
825 const char *cur = *slab_size_segment_cur;
826 char *end;
827 uintmax_t um;
828
829 set_errno(0);
830
831 /* First number, then '-' */
832 um = malloc_strtoumax(cur, &end, 0);
833 if (get_errno() != 0 || *end != '-') {
834 return true;
835 }
836 *slab_start = (size_t)um;
837 cur = end + 1;
838
839 /* Second number, then ':' */
840 um = malloc_strtoumax(cur, &end, 0);
841 if (get_errno() != 0 || *end != ':') {
842 return true;
843 }
844 *slab_end = (size_t)um;
845 cur = end + 1;
846
847 /* Last number */
848 um = malloc_strtoumax(cur, &end, 0);
849 if (get_errno() != 0) {
850 return true;
851 }
852 *new_size = (size_t)um;
853
854 /* Consume the separator if there is one. */
855 if (*end == '|') {
856 end++;
857 }
858
859 *vlen_left -= end - *slab_size_segment_cur;
860 *slab_size_segment_cur = end;
861
862 return false;
863 }
864
865 static bool
malloc_conf_next(char const ** opts_p,char const ** k_p,size_t * klen_p,char const ** v_p,size_t * vlen_p)866 malloc_conf_next(char const **opts_p, char const **k_p, size_t *klen_p,
867 char const **v_p, size_t *vlen_p) {
868 bool accept;
869 const char *opts = *opts_p;
870
871 *k_p = opts;
872
873 for (accept = false; !accept;) {
874 switch (*opts) {
875 case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
876 case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
877 case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R':
878 case 'S': case 'T': case 'U': case 'V': case 'W': case 'X':
879 case 'Y': case 'Z':
880 case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
881 case 'g': case 'h': case 'i': case 'j': case 'k': case 'l':
882 case 'm': case 'n': case 'o': case 'p': case 'q': case 'r':
883 case 's': case 't': case 'u': case 'v': case 'w': case 'x':
884 case 'y': case 'z':
885 case '0': case '1': case '2': case '3': case '4': case '5':
886 case '6': case '7': case '8': case '9':
887 case '_':
888 opts++;
889 break;
890 case ':':
891 opts++;
892 *klen_p = (uintptr_t)opts - 1 - (uintptr_t)*k_p;
893 *v_p = opts;
894 accept = true;
895 break;
896 case '\0':
897 if (opts != *opts_p) {
898 malloc_write("<jemalloc>: Conf string ends "
899 "with key\n");
900 had_conf_error = true;
901 }
902 return true;
903 default:
904 malloc_write("<jemalloc>: Malformed conf string\n");
905 had_conf_error = true;
906 return true;
907 }
908 }
909
910 for (accept = false; !accept;) {
911 switch (*opts) {
912 case ',':
913 opts++;
914 /*
915 * Look ahead one character here, because the next time
916 * this function is called, it will assume that end of
917 * input has been cleanly reached if no input remains,
918 * but we have optimistically already consumed the
919 * comma if one exists.
920 */
921 if (*opts == '\0') {
922 malloc_write("<jemalloc>: Conf string ends "
923 "with comma\n");
924 had_conf_error = true;
925 }
926 *vlen_p = (uintptr_t)opts - 1 - (uintptr_t)*v_p;
927 accept = true;
928 break;
929 case '\0':
930 *vlen_p = (uintptr_t)opts - (uintptr_t)*v_p;
931 accept = true;
932 break;
933 default:
934 opts++;
935 break;
936 }
937 }
938
939 *opts_p = opts;
940 return false;
941 }
942
943 static void
malloc_abort_invalid_conf(void)944 malloc_abort_invalid_conf(void) {
945 assert(opt_abort_conf);
946 malloc_printf("<jemalloc>: Abort (abort_conf:true) on invalid conf "
947 "value (see above).\n");
948 abort();
949 }
950
951 static void
malloc_conf_error(const char * msg,const char * k,size_t klen,const char * v,size_t vlen)952 malloc_conf_error(const char *msg, const char *k, size_t klen, const char *v,
953 size_t vlen) {
954 malloc_printf("<jemalloc>: %s: %.*s:%.*s\n", msg, (int)klen, k,
955 (int)vlen, v);
956 /* If abort_conf is set, error out after processing all options. */
957 const char *experimental = "experimental_";
958 if (strncmp(k, experimental, strlen(experimental)) == 0) {
959 /* However, tolerate experimental features. */
960 return;
961 }
962 had_conf_error = true;
963 }
964
965 static void
malloc_slow_flag_init(void)966 malloc_slow_flag_init(void) {
967 /*
968 * Combine the runtime options into malloc_slow for fast path. Called
969 * after processing all the options.
970 */
971 malloc_slow_flags |= (opt_junk_alloc ? flag_opt_junk_alloc : 0)
972 | (opt_junk_free ? flag_opt_junk_free : 0)
973 | (opt_zero ? flag_opt_zero : 0)
974 | (opt_utrace ? flag_opt_utrace : 0)
975 | (opt_xmalloc ? flag_opt_xmalloc : 0);
976
977 malloc_slow = (malloc_slow_flags != 0);
978 }
979
980 /* Number of sources for initializing malloc_conf */
981 #define MALLOC_CONF_NSOURCES 5
982
983 static const char *
obtain_malloc_conf(unsigned which_source,char buf[PATH_MAX+1])984 obtain_malloc_conf(unsigned which_source, char buf[PATH_MAX + 1]) {
985 if (config_debug) {
986 static unsigned read_source = 0;
987 /*
988 * Each source should only be read once, to minimize # of
989 * syscalls on init.
990 */
991 assert(read_source++ == which_source);
992 }
993 assert(which_source < MALLOC_CONF_NSOURCES);
994
995 const char *ret;
996 switch (which_source) {
997 case 0:
998 ret = config_malloc_conf;
999 break;
1000 case 1:
1001 if (je_malloc_conf != NULL) {
1002 /* Use options that were compiled into the program. */
1003 ret = je_malloc_conf;
1004 } else {
1005 /* No configuration specified. */
1006 ret = NULL;
1007 }
1008 break;
1009 case 2: {
1010 ssize_t linklen = 0;
1011 #ifndef _WIN32
1012 int saved_errno = errno;
1013 const char *linkname =
1014 # ifdef JEMALLOC_PREFIX
1015 "/etc/"JEMALLOC_PREFIX"malloc.conf"
1016 # else
1017 "/etc/malloc.conf"
1018 # endif
1019 ;
1020
1021 /*
1022 * Try to use the contents of the "/etc/malloc.conf" symbolic
1023 * link's name.
1024 */
1025 #ifndef JEMALLOC_READLINKAT
1026 linklen = readlink(linkname, buf, PATH_MAX);
1027 #else
1028 linklen = readlinkat(AT_FDCWD, linkname, buf, PATH_MAX);
1029 #endif
1030 if (linklen == -1) {
1031 /* No configuration specified. */
1032 linklen = 0;
1033 /* Restore errno. */
1034 set_errno(saved_errno);
1035 }
1036 #endif
1037 buf[linklen] = '\0';
1038 ret = buf;
1039 break;
1040 } case 3: {
1041 const char *envname =
1042 #ifdef JEMALLOC_PREFIX
1043 JEMALLOC_CPREFIX"MALLOC_CONF"
1044 #else
1045 "MALLOC_CONF"
1046 #endif
1047 ;
1048
1049 if ((ret = jemalloc_secure_getenv(envname)) != NULL) {
1050 /*
1051 * Do nothing; opts is already initialized to the value
1052 * of the MALLOC_CONF environment variable.
1053 */
1054 } else {
1055 /* No configuration specified. */
1056 ret = NULL;
1057 }
1058 break;
1059 } case 4: {
1060 ret = je_malloc_conf_2_conf_harder;
1061 break;
1062 } default:
1063 not_reached();
1064 ret = NULL;
1065 }
1066 return ret;
1067 }
1068
1069 static void
malloc_conf_init_helper(sc_data_t * sc_data,unsigned bin_shard_sizes[SC_NBINS],bool initial_call,const char * opts_cache[MALLOC_CONF_NSOURCES],char buf[PATH_MAX+1])1070 malloc_conf_init_helper(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS],
1071 bool initial_call, const char *opts_cache[MALLOC_CONF_NSOURCES],
1072 char buf[PATH_MAX + 1]) {
1073 static const char *opts_explain[MALLOC_CONF_NSOURCES] = {
1074 "string specified via --with-malloc-conf",
1075 "string pointed to by the global variable malloc_conf",
1076 "\"name\" of the file referenced by the symbolic link named "
1077 "/etc/malloc.conf",
1078 "value of the environment variable MALLOC_CONF",
1079 "string pointed to by the global variable "
1080 "malloc_conf_2_conf_harder",
1081 };
1082 unsigned i;
1083 const char *opts, *k, *v;
1084 size_t klen, vlen;
1085
1086 for (i = 0; i < MALLOC_CONF_NSOURCES; i++) {
1087 /* Get runtime configuration. */
1088 if (initial_call) {
1089 opts_cache[i] = obtain_malloc_conf(i, buf);
1090 }
1091 opts = opts_cache[i];
1092 if (!initial_call && opt_confirm_conf) {
1093 malloc_printf(
1094 "<jemalloc>: malloc_conf #%u (%s): \"%s\"\n",
1095 i + 1, opts_explain[i], opts != NULL ? opts : "");
1096 }
1097 if (opts == NULL) {
1098 continue;
1099 }
1100
1101 while (*opts != '\0' && !malloc_conf_next(&opts, &k, &klen, &v,
1102 &vlen)) {
1103
1104 #define CONF_ERROR(msg, k, klen, v, vlen) \
1105 if (!initial_call) { \
1106 malloc_conf_error( \
1107 msg, k, klen, v, vlen); \
1108 cur_opt_valid = false; \
1109 }
1110 #define CONF_CONTINUE { \
1111 if (!initial_call && opt_confirm_conf \
1112 && cur_opt_valid) { \
1113 malloc_printf("<jemalloc>: -- " \
1114 "Set conf value: %.*s:%.*s" \
1115 "\n", (int)klen, k, \
1116 (int)vlen, v); \
1117 } \
1118 continue; \
1119 }
1120 #define CONF_MATCH(n) \
1121 (sizeof(n)-1 == klen && strncmp(n, k, klen) == 0)
1122 #define CONF_MATCH_VALUE(n) \
1123 (sizeof(n)-1 == vlen && strncmp(n, v, vlen) == 0)
1124 #define CONF_HANDLE_BOOL(o, n) \
1125 if (CONF_MATCH(n)) { \
1126 if (CONF_MATCH_VALUE("true")) { \
1127 o = true; \
1128 } else if (CONF_MATCH_VALUE("false")) { \
1129 o = false; \
1130 } else { \
1131 CONF_ERROR("Invalid conf value",\
1132 k, klen, v, vlen); \
1133 } \
1134 CONF_CONTINUE; \
1135 }
1136 /*
1137 * One of the CONF_MIN macros below expands, in one of the use points,
1138 * to "unsigned integer < 0", which is always false, triggering the
1139 * GCC -Wtype-limits warning, which we disable here and re-enable below.
1140 */
1141 JEMALLOC_DIAGNOSTIC_PUSH
1142 JEMALLOC_DIAGNOSTIC_IGNORE_TYPE_LIMITS
1143
1144 #define CONF_DONT_CHECK_MIN(um, min) false
1145 #define CONF_CHECK_MIN(um, min) ((um) < (min))
1146 #define CONF_DONT_CHECK_MAX(um, max) false
1147 #define CONF_CHECK_MAX(um, max) ((um) > (max))
1148
1149 #define CONF_VALUE_READ(max_t, result) \
1150 char *end; \
1151 set_errno(0); \
1152 result = (max_t)malloc_strtoumax(v, &end, 0);
1153 #define CONF_VALUE_READ_FAIL() \
1154 (get_errno() != 0 || (uintptr_t)end - (uintptr_t)v != vlen)
1155
1156 #define CONF_HANDLE_T(t, max_t, o, n, min, max, check_min, check_max, clip) \
1157 if (CONF_MATCH(n)) { \
1158 max_t mv; \
1159 CONF_VALUE_READ(max_t, mv) \
1160 if (CONF_VALUE_READ_FAIL()) { \
1161 CONF_ERROR("Invalid conf value",\
1162 k, klen, v, vlen); \
1163 } else if (clip) { \
1164 if (check_min(mv, (t)(min))) { \
1165 o = (t)(min); \
1166 } else if ( \
1167 check_max(mv, (t)(max))) { \
1168 o = (t)(max); \
1169 } else { \
1170 o = (t)mv; \
1171 } \
1172 } else { \
1173 if (check_min(mv, (t)(min)) || \
1174 check_max(mv, (t)(max))) { \
1175 CONF_ERROR( \
1176 "Out-of-range " \
1177 "conf value", \
1178 k, klen, v, vlen); \
1179 } else { \
1180 o = (t)mv; \
1181 } \
1182 } \
1183 CONF_CONTINUE; \
1184 }
1185 #define CONF_HANDLE_T_U(t, o, n, min, max, check_min, check_max, clip) \
1186 CONF_HANDLE_T(t, uintmax_t, o, n, min, max, check_min, \
1187 check_max, clip)
1188 #define CONF_HANDLE_T_SIGNED(t, o, n, min, max, check_min, check_max, clip)\
1189 CONF_HANDLE_T(t, intmax_t, o, n, min, max, check_min, \
1190 check_max, clip)
1191
1192 #define CONF_HANDLE_UNSIGNED(o, n, min, max, check_min, check_max, \
1193 clip) \
1194 CONF_HANDLE_T_U(unsigned, o, n, min, max, \
1195 check_min, check_max, clip)
1196 #define CONF_HANDLE_SIZE_T(o, n, min, max, check_min, check_max, clip) \
1197 CONF_HANDLE_T_U(size_t, o, n, min, max, \
1198 check_min, check_max, clip)
1199 #define CONF_HANDLE_INT64_T(o, n, min, max, check_min, check_max, clip) \
1200 CONF_HANDLE_T_SIGNED(int64_t, o, n, min, max, \
1201 check_min, check_max, clip)
1202 #define CONF_HANDLE_UINT64_T(o, n, min, max, check_min, check_max, clip)\
1203 CONF_HANDLE_T_U(uint64_t, o, n, min, max, \
1204 check_min, check_max, clip)
1205 #define CONF_HANDLE_SSIZE_T(o, n, min, max) \
1206 CONF_HANDLE_T_SIGNED(ssize_t, o, n, min, max, \
1207 CONF_CHECK_MIN, CONF_CHECK_MAX, false)
1208 #define CONF_HANDLE_CHAR_P(o, n, d) \
1209 if (CONF_MATCH(n)) { \
1210 size_t cpylen = (vlen <= \
1211 sizeof(o)-1) ? vlen : \
1212 sizeof(o)-1; \
1213 strncpy(o, v, cpylen); \
1214 o[cpylen] = '\0'; \
1215 CONF_CONTINUE; \
1216 }
1217
1218 bool cur_opt_valid = true;
1219
1220 CONF_HANDLE_BOOL(opt_confirm_conf, "confirm_conf")
1221 if (initial_call) {
1222 continue;
1223 }
1224
1225 CONF_HANDLE_BOOL(opt_abort, "abort")
1226 CONF_HANDLE_BOOL(opt_abort_conf, "abort_conf")
1227 CONF_HANDLE_BOOL(opt_trust_madvise, "trust_madvise")
1228 if (strncmp("metadata_thp", k, klen) == 0) {
1229 int m;
1230 bool match = false;
1231 for (m = 0; m < metadata_thp_mode_limit; m++) {
1232 if (strncmp(metadata_thp_mode_names[m],
1233 v, vlen) == 0) {
1234 opt_metadata_thp = m;
1235 match = true;
1236 break;
1237 }
1238 }
1239 if (!match) {
1240 CONF_ERROR("Invalid conf value",
1241 k, klen, v, vlen);
1242 }
1243 CONF_CONTINUE;
1244 }
1245 CONF_HANDLE_BOOL(opt_retain, "retain")
1246 if (strncmp("dss", k, klen) == 0) {
1247 int m;
1248 bool match = false;
1249 for (m = 0; m < dss_prec_limit; m++) {
1250 if (strncmp(dss_prec_names[m], v, vlen)
1251 == 0) {
1252 if (extent_dss_prec_set(m)) {
1253 CONF_ERROR(
1254 "Error setting dss",
1255 k, klen, v, vlen);
1256 } else {
1257 opt_dss =
1258 dss_prec_names[m];
1259 match = true;
1260 break;
1261 }
1262 }
1263 }
1264 if (!match) {
1265 CONF_ERROR("Invalid conf value",
1266 k, klen, v, vlen);
1267 }
1268 CONF_CONTINUE;
1269 }
1270 if (CONF_MATCH("narenas")) {
1271 if (CONF_MATCH_VALUE("default")) {
1272 opt_narenas = 0;
1273 CONF_CONTINUE;
1274 } else {
1275 CONF_HANDLE_UNSIGNED(opt_narenas,
1276 "narenas", 1, UINT_MAX,
1277 CONF_CHECK_MIN, CONF_DONT_CHECK_MAX,
1278 /* clip */ false)
1279 }
1280 }
1281 if (CONF_MATCH("narenas_ratio")) {
1282 char *end;
1283 bool err = fxp_parse(&opt_narenas_ratio, v,
1284 &end);
1285 if (err || (size_t)(end - v) != vlen) {
1286 CONF_ERROR("Invalid conf value",
1287 k, klen, v, vlen);
1288 }
1289 CONF_CONTINUE;
1290 }
1291 if (CONF_MATCH("bin_shards")) {
1292 const char *bin_shards_segment_cur = v;
1293 size_t vlen_left = vlen;
1294 do {
1295 size_t size_start;
1296 size_t size_end;
1297 size_t nshards;
1298 bool err = malloc_conf_multi_sizes_next(
1299 &bin_shards_segment_cur, &vlen_left,
1300 &size_start, &size_end, &nshards);
1301 if (err || bin_update_shard_size(
1302 bin_shard_sizes, size_start,
1303 size_end, nshards)) {
1304 CONF_ERROR(
1305 "Invalid settings for "
1306 "bin_shards", k, klen, v,
1307 vlen);
1308 break;
1309 }
1310 } while (vlen_left > 0);
1311 CONF_CONTINUE;
1312 }
1313 CONF_HANDLE_INT64_T(opt_mutex_max_spin,
1314 "mutex_max_spin", -1, INT64_MAX, CONF_CHECK_MIN,
1315 CONF_DONT_CHECK_MAX, false);
1316 CONF_HANDLE_SSIZE_T(opt_dirty_decay_ms,
1317 "dirty_decay_ms", -1, NSTIME_SEC_MAX * KQU(1000) <
1318 QU(SSIZE_MAX) ? NSTIME_SEC_MAX * KQU(1000) :
1319 SSIZE_MAX);
1320 CONF_HANDLE_SSIZE_T(opt_muzzy_decay_ms,
1321 "muzzy_decay_ms", -1, NSTIME_SEC_MAX * KQU(1000) <
1322 QU(SSIZE_MAX) ? NSTIME_SEC_MAX * KQU(1000) :
1323 SSIZE_MAX);
1324 CONF_HANDLE_BOOL(opt_stats_print, "stats_print")
1325 if (CONF_MATCH("stats_print_opts")) {
1326 init_opt_stats_opts(v, vlen,
1327 opt_stats_print_opts);
1328 CONF_CONTINUE;
1329 }
1330 CONF_HANDLE_INT64_T(opt_stats_interval,
1331 "stats_interval", -1, INT64_MAX,
1332 CONF_CHECK_MIN, CONF_DONT_CHECK_MAX, false)
1333 if (CONF_MATCH("stats_interval_opts")) {
1334 init_opt_stats_opts(v, vlen,
1335 opt_stats_interval_opts);
1336 CONF_CONTINUE;
1337 }
1338 if (config_fill) {
1339 if (CONF_MATCH("junk")) {
1340 if (CONF_MATCH_VALUE("true")) {
1341 opt_junk = "true";
1342 opt_junk_alloc = opt_junk_free =
1343 true;
1344 } else if (CONF_MATCH_VALUE("false")) {
1345 opt_junk = "false";
1346 opt_junk_alloc = opt_junk_free =
1347 false;
1348 } else if (CONF_MATCH_VALUE("alloc")) {
1349 opt_junk = "alloc";
1350 opt_junk_alloc = true;
1351 opt_junk_free = false;
1352 } else if (CONF_MATCH_VALUE("free")) {
1353 opt_junk = "free";
1354 opt_junk_alloc = false;
1355 opt_junk_free = true;
1356 } else {
1357 CONF_ERROR(
1358 "Invalid conf value",
1359 k, klen, v, vlen);
1360 }
1361 CONF_CONTINUE;
1362 }
1363 CONF_HANDLE_BOOL(opt_zero, "zero")
1364 }
1365 if (config_utrace) {
1366 CONF_HANDLE_BOOL(opt_utrace, "utrace")
1367 }
1368 if (config_xmalloc) {
1369 CONF_HANDLE_BOOL(opt_xmalloc, "xmalloc")
1370 }
1371 if (config_enable_cxx) {
1372 CONF_HANDLE_BOOL(
1373 opt_experimental_infallible_new,
1374 "experimental_infallible_new")
1375 }
1376
1377 CONF_HANDLE_BOOL(opt_tcache, "tcache")
1378 CONF_HANDLE_SIZE_T(opt_tcache_max, "tcache_max",
1379 0, TCACHE_MAXCLASS_LIMIT, CONF_DONT_CHECK_MIN,
1380 CONF_CHECK_MAX, /* clip */ true)
1381 if (CONF_MATCH("lg_tcache_max")) {
1382 size_t m;
1383 CONF_VALUE_READ(size_t, m)
1384 if (CONF_VALUE_READ_FAIL()) {
1385 CONF_ERROR("Invalid conf value",
1386 k, klen, v, vlen);
1387 } else {
1388 /* clip if necessary */
1389 if (m > TCACHE_LG_MAXCLASS_LIMIT) {
1390 m = TCACHE_LG_MAXCLASS_LIMIT;
1391 }
1392 opt_tcache_max = (size_t)1 << m;
1393 }
1394 CONF_CONTINUE;
1395 }
1396 /*
1397 * Anyone trying to set a value outside -16 to 16 is
1398 * deeply confused.
1399 */
1400 CONF_HANDLE_SSIZE_T(opt_lg_tcache_nslots_mul,
1401 "lg_tcache_nslots_mul", -16, 16)
1402 /* Ditto with values past 2048. */
1403 CONF_HANDLE_UNSIGNED(opt_tcache_nslots_small_min,
1404 "tcache_nslots_small_min", 1, 2048,
1405 CONF_CHECK_MIN, CONF_CHECK_MAX, /* clip */ true)
1406 CONF_HANDLE_UNSIGNED(opt_tcache_nslots_small_max,
1407 "tcache_nslots_small_max", 1, 2048,
1408 CONF_CHECK_MIN, CONF_CHECK_MAX, /* clip */ true)
1409 CONF_HANDLE_UNSIGNED(opt_tcache_nslots_large,
1410 "tcache_nslots_large", 1, 2048,
1411 CONF_CHECK_MIN, CONF_CHECK_MAX, /* clip */ true)
1412 CONF_HANDLE_SIZE_T(opt_tcache_gc_incr_bytes,
1413 "tcache_gc_incr_bytes", 1024, SIZE_T_MAX,
1414 CONF_CHECK_MIN, CONF_DONT_CHECK_MAX,
1415 /* clip */ true)
1416 CONF_HANDLE_SIZE_T(opt_tcache_gc_delay_bytes,
1417 "tcache_gc_delay_bytes", 0, SIZE_T_MAX,
1418 CONF_DONT_CHECK_MIN, CONF_DONT_CHECK_MAX,
1419 /* clip */ false)
1420 CONF_HANDLE_UNSIGNED(opt_lg_tcache_flush_small_div,
1421 "lg_tcache_flush_small_div", 1, 16,
1422 CONF_CHECK_MIN, CONF_CHECK_MAX, /* clip */ true)
1423 CONF_HANDLE_UNSIGNED(opt_lg_tcache_flush_large_div,
1424 "lg_tcache_flush_large_div", 1, 16,
1425 CONF_CHECK_MIN, CONF_CHECK_MAX, /* clip */ true)
1426
1427 /*
1428 * The runtime option of oversize_threshold remains
1429 * undocumented. It may be tweaked in the next major
1430 * release (6.0). The default value 8M is rather
1431 * conservative / safe. Tuning it further down may
1432 * improve fragmentation a bit more, but may also cause
1433 * contention on the huge arena.
1434 */
1435 CONF_HANDLE_SIZE_T(opt_oversize_threshold,
1436 "oversize_threshold", 0, SC_LARGE_MAXCLASS,
1437 CONF_DONT_CHECK_MIN, CONF_CHECK_MAX, false)
1438 CONF_HANDLE_SIZE_T(opt_lg_extent_max_active_fit,
1439 "lg_extent_max_active_fit", 0,
1440 (sizeof(size_t) << 3), CONF_DONT_CHECK_MIN,
1441 CONF_CHECK_MAX, false)
1442
1443 if (strncmp("percpu_arena", k, klen) == 0) {
1444 bool match = false;
1445 for (int m = percpu_arena_mode_names_base; m <
1446 percpu_arena_mode_names_limit; m++) {
1447 if (strncmp(percpu_arena_mode_names[m],
1448 v, vlen) == 0) {
1449 if (!have_percpu_arena) {
1450 CONF_ERROR(
1451 "No getcpu support",
1452 k, klen, v, vlen);
1453 }
1454 opt_percpu_arena = m;
1455 match = true;
1456 break;
1457 }
1458 }
1459 if (!match) {
1460 CONF_ERROR("Invalid conf value",
1461 k, klen, v, vlen);
1462 }
1463 CONF_CONTINUE;
1464 }
1465 CONF_HANDLE_BOOL(opt_background_thread,
1466 "background_thread");
1467 CONF_HANDLE_SIZE_T(opt_max_background_threads,
1468 "max_background_threads", 1,
1469 opt_max_background_threads,
1470 CONF_CHECK_MIN, CONF_CHECK_MAX,
1471 true);
1472 CONF_HANDLE_BOOL(opt_hpa, "hpa")
1473 CONF_HANDLE_SIZE_T(opt_hpa_opts.slab_max_alloc,
1474 "hpa_slab_max_alloc", PAGE, HUGEPAGE,
1475 CONF_CHECK_MIN, CONF_CHECK_MAX, true);
1476
1477 /*
1478 * Accept either a ratio-based or an exact hugification
1479 * threshold.
1480 */
1481 CONF_HANDLE_SIZE_T(opt_hpa_opts.hugification_threshold,
1482 "hpa_hugification_threshold", PAGE, HUGEPAGE,
1483 CONF_CHECK_MIN, CONF_CHECK_MAX, true);
1484 if (CONF_MATCH("hpa_hugification_threshold_ratio")) {
1485 fxp_t ratio;
1486 char *end;
1487 bool err = fxp_parse(&ratio, v,
1488 &end);
1489 if (err || (size_t)(end - v) != vlen
1490 || ratio > FXP_INIT_INT(1)) {
1491 CONF_ERROR("Invalid conf value",
1492 k, klen, v, vlen);
1493 } else {
1494 opt_hpa_opts.hugification_threshold =
1495 fxp_mul_frac(HUGEPAGE, ratio);
1496 }
1497 CONF_CONTINUE;
1498 }
1499
1500 CONF_HANDLE_UINT64_T(
1501 opt_hpa_opts.hugify_delay_ms, "hpa_hugify_delay_ms",
1502 0, 0, CONF_DONT_CHECK_MIN, CONF_DONT_CHECK_MAX,
1503 false);
1504
1505 CONF_HANDLE_UINT64_T(
1506 opt_hpa_opts.min_purge_interval_ms,
1507 "hpa_min_purge_interval_ms", 0, 0,
1508 CONF_DONT_CHECK_MIN, CONF_DONT_CHECK_MAX, false);
1509
1510 if (CONF_MATCH("hpa_dirty_mult")) {
1511 if (CONF_MATCH_VALUE("-1")) {
1512 opt_hpa_opts.dirty_mult = (fxp_t)-1;
1513 CONF_CONTINUE;
1514 }
1515 fxp_t ratio;
1516 char *end;
1517 bool err = fxp_parse(&ratio, v,
1518 &end);
1519 if (err || (size_t)(end - v) != vlen) {
1520 CONF_ERROR("Invalid conf value",
1521 k, klen, v, vlen);
1522 } else {
1523 opt_hpa_opts.dirty_mult = ratio;
1524 }
1525 CONF_CONTINUE;
1526 }
1527
1528 CONF_HANDLE_SIZE_T(opt_hpa_sec_opts.nshards,
1529 "hpa_sec_nshards", 0, 0, CONF_CHECK_MIN,
1530 CONF_DONT_CHECK_MAX, true);
1531 CONF_HANDLE_SIZE_T(opt_hpa_sec_opts.max_alloc,
1532 "hpa_sec_max_alloc", PAGE, 0, CONF_CHECK_MIN,
1533 CONF_DONT_CHECK_MAX, true);
1534 CONF_HANDLE_SIZE_T(opt_hpa_sec_opts.max_bytes,
1535 "hpa_sec_max_bytes", PAGE, 0, CONF_CHECK_MIN,
1536 CONF_DONT_CHECK_MAX, true);
1537 CONF_HANDLE_SIZE_T(opt_hpa_sec_opts.bytes_after_flush,
1538 "hpa_sec_bytes_after_flush", PAGE, 0,
1539 CONF_CHECK_MIN, CONF_DONT_CHECK_MAX, true);
1540 CONF_HANDLE_SIZE_T(opt_hpa_sec_opts.batch_fill_extra,
1541 "hpa_sec_batch_fill_extra", 0, HUGEPAGE_PAGES,
1542 CONF_CHECK_MIN, CONF_CHECK_MAX, true);
1543
1544 if (CONF_MATCH("slab_sizes")) {
1545 if (CONF_MATCH_VALUE("default")) {
1546 sc_data_init(sc_data);
1547 CONF_CONTINUE;
1548 }
1549 bool err;
1550 const char *slab_size_segment_cur = v;
1551 size_t vlen_left = vlen;
1552 do {
1553 size_t slab_start;
1554 size_t slab_end;
1555 size_t pgs;
1556 err = malloc_conf_multi_sizes_next(
1557 &slab_size_segment_cur,
1558 &vlen_left, &slab_start, &slab_end,
1559 &pgs);
1560 if (!err) {
1561 sc_data_update_slab_size(
1562 sc_data, slab_start,
1563 slab_end, (int)pgs);
1564 } else {
1565 CONF_ERROR("Invalid settings "
1566 "for slab_sizes",
1567 k, klen, v, vlen);
1568 }
1569 } while (!err && vlen_left > 0);
1570 CONF_CONTINUE;
1571 }
1572 if (config_prof) {
1573 CONF_HANDLE_BOOL(opt_prof, "prof")
1574 CONF_HANDLE_CHAR_P(opt_prof_prefix,
1575 "prof_prefix", "jeprof")
1576 CONF_HANDLE_BOOL(opt_prof_active, "prof_active")
1577 CONF_HANDLE_BOOL(opt_prof_thread_active_init,
1578 "prof_thread_active_init")
1579 CONF_HANDLE_SIZE_T(opt_lg_prof_sample,
1580 "lg_prof_sample", 0, (sizeof(uint64_t) << 3)
1581 - 1, CONF_DONT_CHECK_MIN, CONF_CHECK_MAX,
1582 true)
1583 CONF_HANDLE_BOOL(opt_prof_accum, "prof_accum")
1584 CONF_HANDLE_SSIZE_T(opt_lg_prof_interval,
1585 "lg_prof_interval", -1,
1586 (sizeof(uint64_t) << 3) - 1)
1587 CONF_HANDLE_BOOL(opt_prof_gdump, "prof_gdump")
1588 CONF_HANDLE_BOOL(opt_prof_final, "prof_final")
1589 CONF_HANDLE_BOOL(opt_prof_leak, "prof_leak")
1590 CONF_HANDLE_BOOL(opt_prof_leak_error,
1591 "prof_leak_error")
1592 CONF_HANDLE_BOOL(opt_prof_log, "prof_log")
1593 CONF_HANDLE_SSIZE_T(opt_prof_recent_alloc_max,
1594 "prof_recent_alloc_max", -1, SSIZE_MAX)
1595 CONF_HANDLE_BOOL(opt_prof_stats, "prof_stats")
1596 CONF_HANDLE_BOOL(opt_prof_sys_thread_name,
1597 "prof_sys_thread_name")
1598 if (CONF_MATCH("prof_time_resolution")) {
1599 if (CONF_MATCH_VALUE("default")) {
1600 opt_prof_time_res =
1601 prof_time_res_default;
1602 } else if (CONF_MATCH_VALUE("high")) {
1603 if (!config_high_res_timer) {
1604 CONF_ERROR(
1605 "No high resolution"
1606 " timer support",
1607 k, klen, v, vlen);
1608 } else {
1609 opt_prof_time_res =
1610 prof_time_res_high;
1611 }
1612 } else {
1613 CONF_ERROR("Invalid conf value",
1614 k, klen, v, vlen);
1615 }
1616 CONF_CONTINUE;
1617 }
1618 /*
1619 * Undocumented. When set to false, don't
1620 * correct for an unbiasing bug in jeprof
1621 * attribution. This can be handy if you want
1622 * to get consistent numbers from your binary
1623 * across different jemalloc versions, even if
1624 * those numbers are incorrect. The default is
1625 * true.
1626 */
1627 CONF_HANDLE_BOOL(opt_prof_unbias, "prof_unbias")
1628 }
1629 if (config_log) {
1630 if (CONF_MATCH("log")) {
1631 size_t cpylen = (
1632 vlen <= sizeof(log_var_names) ?
1633 vlen : sizeof(log_var_names) - 1);
1634 strncpy(log_var_names, v, cpylen);
1635 log_var_names[cpylen] = '\0';
1636 CONF_CONTINUE;
1637 }
1638 }
1639 if (CONF_MATCH("thp")) {
1640 bool match = false;
1641 for (int m = 0; m < thp_mode_names_limit; m++) {
1642 if (strncmp(thp_mode_names[m],v, vlen)
1643 == 0) {
1644 if (!have_madvise_huge && !have_memcntl) {
1645 CONF_ERROR(
1646 "No THP support",
1647 k, klen, v, vlen);
1648 }
1649 opt_thp = m;
1650 match = true;
1651 break;
1652 }
1653 }
1654 if (!match) {
1655 CONF_ERROR("Invalid conf value",
1656 k, klen, v, vlen);
1657 }
1658 CONF_CONTINUE;
1659 }
1660 if (CONF_MATCH("zero_realloc")) {
1661 if (CONF_MATCH_VALUE("alloc")) {
1662 opt_zero_realloc_action
1663 = zero_realloc_action_alloc;
1664 } else if (CONF_MATCH_VALUE("free")) {
1665 opt_zero_realloc_action
1666 = zero_realloc_action_free;
1667 } else if (CONF_MATCH_VALUE("abort")) {
1668 opt_zero_realloc_action
1669 = zero_realloc_action_abort;
1670 } else {
1671 CONF_ERROR("Invalid conf value",
1672 k, klen, v, vlen);
1673 }
1674 CONF_CONTINUE;
1675 }
1676 if (config_uaf_detection &&
1677 CONF_MATCH("lg_san_uaf_align")) {
1678 ssize_t a;
1679 CONF_VALUE_READ(ssize_t, a)
1680 if (CONF_VALUE_READ_FAIL() || a < -1) {
1681 CONF_ERROR("Invalid conf value",
1682 k, klen, v, vlen);
1683 }
1684 if (a == -1) {
1685 opt_lg_san_uaf_align = -1;
1686 CONF_CONTINUE;
1687 }
1688
1689 /* clip if necessary */
1690 ssize_t max_allowed = (sizeof(size_t) << 3) - 1;
1691 ssize_t min_allowed = LG_PAGE;
1692 if (a > max_allowed) {
1693 a = max_allowed;
1694 } else if (a < min_allowed) {
1695 a = min_allowed;
1696 }
1697
1698 opt_lg_san_uaf_align = a;
1699 CONF_CONTINUE;
1700 }
1701
1702 CONF_HANDLE_SIZE_T(opt_san_guard_small,
1703 "san_guard_small", 0, SIZE_T_MAX,
1704 CONF_DONT_CHECK_MIN, CONF_DONT_CHECK_MAX, false)
1705 CONF_HANDLE_SIZE_T(opt_san_guard_large,
1706 "san_guard_large", 0, SIZE_T_MAX,
1707 CONF_DONT_CHECK_MIN, CONF_DONT_CHECK_MAX, false)
1708
1709 CONF_ERROR("Invalid conf pair", k, klen, v, vlen);
1710 #undef CONF_ERROR
1711 #undef CONF_CONTINUE
1712 #undef CONF_MATCH
1713 #undef CONF_MATCH_VALUE
1714 #undef CONF_HANDLE_BOOL
1715 #undef CONF_DONT_CHECK_MIN
1716 #undef CONF_CHECK_MIN
1717 #undef CONF_DONT_CHECK_MAX
1718 #undef CONF_CHECK_MAX
1719 #undef CONF_HANDLE_T
1720 #undef CONF_HANDLE_T_U
1721 #undef CONF_HANDLE_T_SIGNED
1722 #undef CONF_HANDLE_UNSIGNED
1723 #undef CONF_HANDLE_SIZE_T
1724 #undef CONF_HANDLE_SSIZE_T
1725 #undef CONF_HANDLE_CHAR_P
1726 /* Re-enable diagnostic "-Wtype-limits" */
1727 JEMALLOC_DIAGNOSTIC_POP
1728 }
1729 if (opt_abort_conf && had_conf_error) {
1730 malloc_abort_invalid_conf();
1731 }
1732 }
1733 atomic_store_b(&log_init_done, true, ATOMIC_RELEASE);
1734 }
1735
1736 static bool
malloc_conf_init_check_deps(void)1737 malloc_conf_init_check_deps(void) {
1738 if (opt_prof_leak_error && !opt_prof_final) {
1739 malloc_printf("<jemalloc>: prof_leak_error is set w/o "
1740 "prof_final.\n");
1741 return true;
1742 }
1743
1744 return false;
1745 }
1746
1747 static void
malloc_conf_init(sc_data_t * sc_data,unsigned bin_shard_sizes[SC_NBINS])1748 malloc_conf_init(sc_data_t *sc_data, unsigned bin_shard_sizes[SC_NBINS]) {
1749 const char *opts_cache[MALLOC_CONF_NSOURCES] = {NULL, NULL, NULL, NULL,
1750 NULL};
1751 char buf[PATH_MAX + 1];
1752
1753 /* The first call only set the confirm_conf option and opts_cache */
1754 malloc_conf_init_helper(NULL, NULL, true, opts_cache, buf);
1755 malloc_conf_init_helper(sc_data, bin_shard_sizes, false, opts_cache,
1756 NULL);
1757 if (malloc_conf_init_check_deps()) {
1758 /* check_deps does warning msg only; abort below if needed. */
1759 if (opt_abort_conf) {
1760 malloc_abort_invalid_conf();
1761 }
1762 }
1763 }
1764
1765 #undef MALLOC_CONF_NSOURCES
1766
1767 static bool
malloc_init_hard_needed(void)1768 malloc_init_hard_needed(void) {
1769 if (malloc_initialized() || (IS_INITIALIZER && malloc_init_state ==
1770 malloc_init_recursible)) {
1771 /*
1772 * Another thread initialized the allocator before this one
1773 * acquired init_lock, or this thread is the initializing
1774 * thread, and it is recursively allocating.
1775 */
1776 return false;
1777 }
1778 #ifdef JEMALLOC_THREADED_INIT
1779 if (malloc_initializer != NO_INITIALIZER && !IS_INITIALIZER) {
1780 /* Busy-wait until the initializing thread completes. */
1781 spin_t spinner = SPIN_INITIALIZER;
1782 do {
1783 malloc_mutex_unlock(TSDN_NULL, &init_lock);
1784 spin_adaptive(&spinner);
1785 malloc_mutex_lock(TSDN_NULL, &init_lock);
1786 } while (!malloc_initialized());
1787 return false;
1788 }
1789 #endif
1790 return true;
1791 }
1792
1793 static bool
malloc_init_hard_a0_locked()1794 malloc_init_hard_a0_locked() {
1795 malloc_initializer = INITIALIZER;
1796
1797 JEMALLOC_DIAGNOSTIC_PUSH
1798 JEMALLOC_DIAGNOSTIC_IGNORE_MISSING_STRUCT_FIELD_INITIALIZERS
1799 sc_data_t sc_data = {0};
1800 JEMALLOC_DIAGNOSTIC_POP
1801
1802 /*
1803 * Ordering here is somewhat tricky; we need sc_boot() first, since that
1804 * determines what the size classes will be, and then
1805 * malloc_conf_init(), since any slab size tweaking will need to be done
1806 * before sz_boot and bin_info_boot, which assume that the values they
1807 * read out of sc_data_global are final.
1808 */
1809 sc_boot(&sc_data);
1810 unsigned bin_shard_sizes[SC_NBINS];
1811 bin_shard_sizes_boot(bin_shard_sizes);
1812 /*
1813 * prof_boot0 only initializes opt_prof_prefix. We need to do it before
1814 * we parse malloc_conf options, in case malloc_conf parsing overwrites
1815 * it.
1816 */
1817 if (config_prof) {
1818 prof_boot0();
1819 }
1820 malloc_conf_init(&sc_data, bin_shard_sizes);
1821 san_init(opt_lg_san_uaf_align);
1822 sz_boot(&sc_data, opt_cache_oblivious);
1823 bin_info_boot(&sc_data, bin_shard_sizes);
1824
1825 if (opt_stats_print) {
1826 /* Print statistics at exit. */
1827 if (atexit(stats_print_atexit) != 0) {
1828 malloc_write("<jemalloc>: Error in atexit()\n");
1829 if (opt_abort) {
1830 abort();
1831 }
1832 }
1833 }
1834
1835 if (stats_boot()) {
1836 return true;
1837 }
1838 if (pages_boot()) {
1839 return true;
1840 }
1841 if (base_boot(TSDN_NULL)) {
1842 return true;
1843 }
1844 /* emap_global is static, hence zeroed. */
1845 if (emap_init(&arena_emap_global, b0get(), /* zeroed */ true)) {
1846 return true;
1847 }
1848 if (extent_boot()) {
1849 return true;
1850 }
1851 if (ctl_boot()) {
1852 return true;
1853 }
1854 if (config_prof) {
1855 prof_boot1();
1856 }
1857 if (opt_hpa && !hpa_supported()) {
1858 malloc_printf("<jemalloc>: HPA not supported in the current "
1859 "configuration; %s.",
1860 opt_abort_conf ? "aborting" : "disabling");
1861 if (opt_abort_conf) {
1862 malloc_abort_invalid_conf();
1863 } else {
1864 opt_hpa = false;
1865 }
1866 }
1867 if (arena_boot(&sc_data, b0get(), opt_hpa)) {
1868 return true;
1869 }
1870 if (tcache_boot(TSDN_NULL, b0get())) {
1871 return true;
1872 }
1873 if (malloc_mutex_init(&arenas_lock, "arenas", WITNESS_RANK_ARENAS,
1874 malloc_mutex_rank_exclusive)) {
1875 return true;
1876 }
1877 hook_boot();
1878 /*
1879 * Create enough scaffolding to allow recursive allocation in
1880 * malloc_ncpus().
1881 */
1882 narenas_auto = 1;
1883 manual_arena_base = narenas_auto + 1;
1884 memset(arenas, 0, sizeof(arena_t *) * narenas_auto);
1885 /*
1886 * Initialize one arena here. The rest are lazily created in
1887 * arena_choose_hard().
1888 */
1889 if (arena_init(TSDN_NULL, 0, &arena_config_default) == NULL) {
1890 return true;
1891 }
1892 a0 = arena_get(TSDN_NULL, 0, false);
1893
1894 if (opt_hpa && !hpa_supported()) {
1895 malloc_printf("<jemalloc>: HPA not supported in the current "
1896 "configuration; %s.",
1897 opt_abort_conf ? "aborting" : "disabling");
1898 if (opt_abort_conf) {
1899 malloc_abort_invalid_conf();
1900 } else {
1901 opt_hpa = false;
1902 }
1903 } else if (opt_hpa) {
1904 hpa_shard_opts_t hpa_shard_opts = opt_hpa_opts;
1905 hpa_shard_opts.deferral_allowed = background_thread_enabled();
1906 if (pa_shard_enable_hpa(TSDN_NULL, &a0->pa_shard,
1907 &hpa_shard_opts, &opt_hpa_sec_opts)) {
1908 return true;
1909 }
1910 }
1911
1912 malloc_init_state = malloc_init_a0_initialized;
1913
1914 return false;
1915 }
1916
1917 static bool
malloc_init_hard_a0(void)1918 malloc_init_hard_a0(void) {
1919 bool ret;
1920
1921 malloc_mutex_lock(TSDN_NULL, &init_lock);
1922 ret = malloc_init_hard_a0_locked();
1923 malloc_mutex_unlock(TSDN_NULL, &init_lock);
1924 return ret;
1925 }
1926
1927 /* Initialize data structures which may trigger recursive allocation. */
1928 static bool
malloc_init_hard_recursible(void)1929 malloc_init_hard_recursible(void) {
1930 malloc_init_state = malloc_init_recursible;
1931
1932 ncpus = malloc_ncpus();
1933 if (opt_percpu_arena != percpu_arena_disabled) {
1934 bool cpu_count_is_deterministic =
1935 malloc_cpu_count_is_deterministic();
1936 if (!cpu_count_is_deterministic) {
1937 /*
1938 * If # of CPU is not deterministic, and narenas not
1939 * specified, disables per cpu arena since it may not
1940 * detect CPU IDs properly.
1941 */
1942 if (opt_narenas == 0) {
1943 opt_percpu_arena = percpu_arena_disabled;
1944 malloc_write("<jemalloc>: Number of CPUs "
1945 "detected is not deterministic. Per-CPU "
1946 "arena disabled.\n");
1947 if (opt_abort_conf) {
1948 malloc_abort_invalid_conf();
1949 }
1950 if (opt_abort) {
1951 abort();
1952 }
1953 }
1954 }
1955 }
1956
1957 #if (defined(JEMALLOC_HAVE_PTHREAD_ATFORK) && !defined(JEMALLOC_MUTEX_INIT_CB) \
1958 && !defined(JEMALLOC_ZONE) && !defined(_WIN32) && \
1959 !defined(__native_client__))
1960 /* LinuxThreads' pthread_atfork() allocates. */
1961 if (pthread_atfork(jemalloc_prefork, jemalloc_postfork_parent,
1962 jemalloc_postfork_child) != 0) {
1963 malloc_write("<jemalloc>: Error in pthread_atfork()\n");
1964 if (opt_abort) {
1965 abort();
1966 }
1967 return true;
1968 }
1969 #endif
1970
1971 if (background_thread_boot0()) {
1972 return true;
1973 }
1974
1975 return false;
1976 }
1977
1978 static unsigned
malloc_narenas_default(void)1979 malloc_narenas_default(void) {
1980 assert(ncpus > 0);
1981 /*
1982 * For SMP systems, create more than one arena per CPU by
1983 * default.
1984 */
1985 if (ncpus > 1) {
1986 fxp_t fxp_ncpus = FXP_INIT_INT(ncpus);
1987 fxp_t goal = fxp_mul(fxp_ncpus, opt_narenas_ratio);
1988 uint32_t int_goal = fxp_round_nearest(goal);
1989 if (int_goal == 0) {
1990 return 1;
1991 }
1992 return int_goal;
1993 } else {
1994 return 1;
1995 }
1996 }
1997
1998 static percpu_arena_mode_t
percpu_arena_as_initialized(percpu_arena_mode_t mode)1999 percpu_arena_as_initialized(percpu_arena_mode_t mode) {
2000 assert(!malloc_initialized());
2001 assert(mode <= percpu_arena_disabled);
2002
2003 if (mode != percpu_arena_disabled) {
2004 mode += percpu_arena_mode_enabled_base;
2005 }
2006
2007 return mode;
2008 }
2009
2010 static bool
malloc_init_narenas(void)2011 malloc_init_narenas(void) {
2012 assert(ncpus > 0);
2013
2014 if (opt_percpu_arena != percpu_arena_disabled) {
2015 if (!have_percpu_arena || malloc_getcpu() < 0) {
2016 opt_percpu_arena = percpu_arena_disabled;
2017 malloc_printf("<jemalloc>: perCPU arena getcpu() not "
2018 "available. Setting narenas to %u.\n", opt_narenas ?
2019 opt_narenas : malloc_narenas_default());
2020 if (opt_abort) {
2021 abort();
2022 }
2023 } else {
2024 if (ncpus >= MALLOCX_ARENA_LIMIT) {
2025 malloc_printf("<jemalloc>: narenas w/ percpu"
2026 "arena beyond limit (%d)\n", ncpus);
2027 if (opt_abort) {
2028 abort();
2029 }
2030 return true;
2031 }
2032 /* NB: opt_percpu_arena isn't fully initialized yet. */
2033 if (percpu_arena_as_initialized(opt_percpu_arena) ==
2034 per_phycpu_arena && ncpus % 2 != 0) {
2035 malloc_printf("<jemalloc>: invalid "
2036 "configuration -- per physical CPU arena "
2037 "with odd number (%u) of CPUs (no hyper "
2038 "threading?).\n", ncpus);
2039 if (opt_abort)
2040 abort();
2041 }
2042 unsigned n = percpu_arena_ind_limit(
2043 percpu_arena_as_initialized(opt_percpu_arena));
2044 if (opt_narenas < n) {
2045 /*
2046 * If narenas is specified with percpu_arena
2047 * enabled, actual narenas is set as the greater
2048 * of the two. percpu_arena_choose will be free
2049 * to use any of the arenas based on CPU
2050 * id. This is conservative (at a small cost)
2051 * but ensures correctness.
2052 *
2053 * If for some reason the ncpus determined at
2054 * boot is not the actual number (e.g. because
2055 * of affinity setting from numactl), reserving
2056 * narenas this way provides a workaround for
2057 * percpu_arena.
2058 */
2059 opt_narenas = n;
2060 }
2061 }
2062 }
2063 if (opt_narenas == 0) {
2064 opt_narenas = malloc_narenas_default();
2065 }
2066 assert(opt_narenas > 0);
2067
2068 narenas_auto = opt_narenas;
2069 /*
2070 * Limit the number of arenas to the indexing range of MALLOCX_ARENA().
2071 */
2072 if (narenas_auto >= MALLOCX_ARENA_LIMIT) {
2073 narenas_auto = MALLOCX_ARENA_LIMIT - 1;
2074 malloc_printf("<jemalloc>: Reducing narenas to limit (%d)\n",
2075 narenas_auto);
2076 }
2077 narenas_total_set(narenas_auto);
2078 if (arena_init_huge()) {
2079 narenas_total_inc();
2080 }
2081 manual_arena_base = narenas_total_get();
2082
2083 return false;
2084 }
2085
2086 static void
malloc_init_percpu(void)2087 malloc_init_percpu(void) {
2088 opt_percpu_arena = percpu_arena_as_initialized(opt_percpu_arena);
2089 }
2090
2091 static bool
malloc_init_hard_finish(void)2092 malloc_init_hard_finish(void) {
2093 if (malloc_mutex_boot()) {
2094 return true;
2095 }
2096
2097 malloc_init_state = malloc_init_initialized;
2098 malloc_slow_flag_init();
2099
2100 return false;
2101 }
2102
2103 static void
malloc_init_hard_cleanup(tsdn_t * tsdn,bool reentrancy_set)2104 malloc_init_hard_cleanup(tsdn_t *tsdn, bool reentrancy_set) {
2105 malloc_mutex_assert_owner(tsdn, &init_lock);
2106 malloc_mutex_unlock(tsdn, &init_lock);
2107 if (reentrancy_set) {
2108 assert(!tsdn_null(tsdn));
2109 tsd_t *tsd = tsdn_tsd(tsdn);
2110 assert(tsd_reentrancy_level_get(tsd) > 0);
2111 post_reentrancy(tsd);
2112 }
2113 }
2114
2115 static bool
malloc_init_hard(void)2116 malloc_init_hard(void) {
2117 tsd_t *tsd;
2118
2119 #if defined(_WIN32) && _WIN32_WINNT < 0x0600
2120 _init_init_lock();
2121 #endif
2122 malloc_mutex_lock(TSDN_NULL, &init_lock);
2123
2124 #define UNLOCK_RETURN(tsdn, ret, reentrancy) \
2125 malloc_init_hard_cleanup(tsdn, reentrancy); \
2126 return ret;
2127
2128 if (!malloc_init_hard_needed()) {
2129 UNLOCK_RETURN(TSDN_NULL, false, false)
2130 }
2131
2132 if (malloc_init_state != malloc_init_a0_initialized &&
2133 malloc_init_hard_a0_locked()) {
2134 UNLOCK_RETURN(TSDN_NULL, true, false)
2135 }
2136
2137 malloc_mutex_unlock(TSDN_NULL, &init_lock);
2138 /* Recursive allocation relies on functional tsd. */
2139 tsd = malloc_tsd_boot0();
2140 if (tsd == NULL) {
2141 return true;
2142 }
2143 if (malloc_init_hard_recursible()) {
2144 return true;
2145 }
2146
2147 malloc_mutex_lock(tsd_tsdn(tsd), &init_lock);
2148 /* Set reentrancy level to 1 during init. */
2149 pre_reentrancy(tsd, NULL);
2150 /* Initialize narenas before prof_boot2 (for allocation). */
2151 if (malloc_init_narenas()
2152 || background_thread_boot1(tsd_tsdn(tsd), b0get())) {
2153 UNLOCK_RETURN(tsd_tsdn(tsd), true, true)
2154 }
2155 if (config_prof && prof_boot2(tsd, b0get())) {
2156 UNLOCK_RETURN(tsd_tsdn(tsd), true, true)
2157 }
2158
2159 malloc_init_percpu();
2160
2161 if (malloc_init_hard_finish()) {
2162 UNLOCK_RETURN(tsd_tsdn(tsd), true, true)
2163 }
2164 post_reentrancy(tsd);
2165 malloc_mutex_unlock(tsd_tsdn(tsd), &init_lock);
2166
2167 witness_assert_lockless(witness_tsd_tsdn(
2168 tsd_witness_tsdp_get_unsafe(tsd)));
2169 malloc_tsd_boot1();
2170 /* Update TSD after tsd_boot1. */
2171 tsd = tsd_fetch();
2172 if (opt_background_thread) {
2173 assert(have_background_thread);
2174 /*
2175 * Need to finish init & unlock first before creating background
2176 * threads (pthread_create depends on malloc). ctl_init (which
2177 * sets isthreaded) needs to be called without holding any lock.
2178 */
2179 background_thread_ctl_init(tsd_tsdn(tsd));
2180 if (background_thread_create(tsd, 0)) {
2181 return true;
2182 }
2183 }
2184 #undef UNLOCK_RETURN
2185 return false;
2186 }
2187
2188 /*
2189 * End initialization functions.
2190 */
2191 /******************************************************************************/
2192 /*
2193 * Begin allocation-path internal functions and data structures.
2194 */
2195
2196 /*
2197 * Settings determined by the documented behavior of the allocation functions.
2198 */
2199 typedef struct static_opts_s static_opts_t;
2200 struct static_opts_s {
2201 /* Whether or not allocation size may overflow. */
2202 bool may_overflow;
2203
2204 /*
2205 * Whether or not allocations (with alignment) of size 0 should be
2206 * treated as size 1.
2207 */
2208 bool bump_empty_aligned_alloc;
2209 /*
2210 * Whether to assert that allocations are not of size 0 (after any
2211 * bumping).
2212 */
2213 bool assert_nonempty_alloc;
2214
2215 /*
2216 * Whether or not to modify the 'result' argument to malloc in case of
2217 * error.
2218 */
2219 bool null_out_result_on_error;
2220 /* Whether to set errno when we encounter an error condition. */
2221 bool set_errno_on_error;
2222
2223 /*
2224 * The minimum valid alignment for functions requesting aligned storage.
2225 */
2226 size_t min_alignment;
2227
2228 /* The error string to use if we oom. */
2229 const char *oom_string;
2230 /* The error string to use if the passed-in alignment is invalid. */
2231 const char *invalid_alignment_string;
2232
2233 /*
2234 * False if we're configured to skip some time-consuming operations.
2235 *
2236 * This isn't really a malloc "behavior", but it acts as a useful
2237 * summary of several other static (or at least, static after program
2238 * initialization) options.
2239 */
2240 bool slow;
2241 /*
2242 * Return size.
2243 */
2244 bool usize;
2245 };
2246
2247 JEMALLOC_ALWAYS_INLINE void
static_opts_init(static_opts_t * static_opts)2248 static_opts_init(static_opts_t *static_opts) {
2249 static_opts->may_overflow = false;
2250 static_opts->bump_empty_aligned_alloc = false;
2251 static_opts->assert_nonempty_alloc = false;
2252 static_opts->null_out_result_on_error = false;
2253 static_opts->set_errno_on_error = false;
2254 static_opts->min_alignment = 0;
2255 static_opts->oom_string = "";
2256 static_opts->invalid_alignment_string = "";
2257 static_opts->slow = false;
2258 static_opts->usize = false;
2259 }
2260
2261 /*
2262 * These correspond to the macros in jemalloc/jemalloc_macros.h. Broadly, we
2263 * should have one constant here per magic value there. Note however that the
2264 * representations need not be related.
2265 */
2266 #define TCACHE_IND_NONE ((unsigned)-1)
2267 #define TCACHE_IND_AUTOMATIC ((unsigned)-2)
2268 #define ARENA_IND_AUTOMATIC ((unsigned)-1)
2269
2270 typedef struct dynamic_opts_s dynamic_opts_t;
2271 struct dynamic_opts_s {
2272 void **result;
2273 size_t usize;
2274 size_t num_items;
2275 size_t item_size;
2276 size_t alignment;
2277 bool zero;
2278 unsigned tcache_ind;
2279 unsigned arena_ind;
2280 };
2281
2282 JEMALLOC_ALWAYS_INLINE void
dynamic_opts_init(dynamic_opts_t * dynamic_opts)2283 dynamic_opts_init(dynamic_opts_t *dynamic_opts) {
2284 dynamic_opts->result = NULL;
2285 dynamic_opts->usize = 0;
2286 dynamic_opts->num_items = 0;
2287 dynamic_opts->item_size = 0;
2288 dynamic_opts->alignment = 0;
2289 dynamic_opts->zero = false;
2290 dynamic_opts->tcache_ind = TCACHE_IND_AUTOMATIC;
2291 dynamic_opts->arena_ind = ARENA_IND_AUTOMATIC;
2292 }
2293
2294 /*
2295 * ind parameter is optional and is only checked and filled if alignment == 0;
2296 * return true if result is out of range.
2297 */
2298 JEMALLOC_ALWAYS_INLINE bool
aligned_usize_get(size_t size,size_t alignment,size_t * usize,szind_t * ind,bool bump_empty_aligned_alloc)2299 aligned_usize_get(size_t size, size_t alignment, size_t *usize, szind_t *ind,
2300 bool bump_empty_aligned_alloc) {
2301 assert(usize != NULL);
2302 if (alignment == 0) {
2303 if (ind != NULL) {
2304 *ind = sz_size2index(size);
2305 if (unlikely(*ind >= SC_NSIZES)) {
2306 return true;
2307 }
2308 *usize = sz_index2size(*ind);
2309 assert(*usize > 0 && *usize <= SC_LARGE_MAXCLASS);
2310 return false;
2311 }
2312 *usize = sz_s2u(size);
2313 } else {
2314 if (bump_empty_aligned_alloc && unlikely(size == 0)) {
2315 size = 1;
2316 }
2317 *usize = sz_sa2u(size, alignment);
2318 }
2319 if (unlikely(*usize == 0 || *usize > SC_LARGE_MAXCLASS)) {
2320 return true;
2321 }
2322 return false;
2323 }
2324
2325 JEMALLOC_ALWAYS_INLINE bool
zero_get(bool guarantee,bool slow)2326 zero_get(bool guarantee, bool slow) {
2327 if (config_fill && slow && unlikely(opt_zero)) {
2328 return true;
2329 } else {
2330 return guarantee;
2331 }
2332 }
2333
2334 JEMALLOC_ALWAYS_INLINE tcache_t *
tcache_get_from_ind(tsd_t * tsd,unsigned tcache_ind,bool slow,bool is_alloc)2335 tcache_get_from_ind(tsd_t *tsd, unsigned tcache_ind, bool slow, bool is_alloc) {
2336 tcache_t *tcache;
2337 if (tcache_ind == TCACHE_IND_AUTOMATIC) {
2338 if (likely(!slow)) {
2339 /* Getting tcache ptr unconditionally. */
2340 tcache = tsd_tcachep_get(tsd);
2341 assert(tcache == tcache_get(tsd));
2342 } else if (is_alloc ||
2343 likely(tsd_reentrancy_level_get(tsd) == 0)) {
2344 tcache = tcache_get(tsd);
2345 } else {
2346 tcache = NULL;
2347 }
2348 } else {
2349 /*
2350 * Should not specify tcache on deallocation path when being
2351 * reentrant.
2352 */
2353 assert(is_alloc || tsd_reentrancy_level_get(tsd) == 0 ||
2354 tsd_state_nocleanup(tsd));
2355 if (tcache_ind == TCACHE_IND_NONE) {
2356 tcache = NULL;
2357 } else {
2358 tcache = tcaches_get(tsd, tcache_ind);
2359 }
2360 }
2361 return tcache;
2362 }
2363
2364 /* Return true if a manual arena is specified and arena_get() OOMs. */
2365 JEMALLOC_ALWAYS_INLINE bool
arena_get_from_ind(tsd_t * tsd,unsigned arena_ind,arena_t ** arena_p)2366 arena_get_from_ind(tsd_t *tsd, unsigned arena_ind, arena_t **arena_p) {
2367 if (arena_ind == ARENA_IND_AUTOMATIC) {
2368 /*
2369 * In case of automatic arena management, we defer arena
2370 * computation until as late as we can, hoping to fill the
2371 * allocation out of the tcache.
2372 */
2373 *arena_p = NULL;
2374 } else {
2375 *arena_p = arena_get(tsd_tsdn(tsd), arena_ind, true);
2376 if (unlikely(*arena_p == NULL) && arena_ind >= narenas_auto) {
2377 return true;
2378 }
2379 }
2380 return false;
2381 }
2382
2383 /* ind is ignored if dopts->alignment > 0. */
2384 JEMALLOC_ALWAYS_INLINE void *
imalloc_no_sample(static_opts_t * sopts,dynamic_opts_t * dopts,tsd_t * tsd,size_t size,size_t usize,szind_t ind)2385 imalloc_no_sample(static_opts_t *sopts, dynamic_opts_t *dopts, tsd_t *tsd,
2386 size_t size, size_t usize, szind_t ind) {
2387 /* Fill in the tcache. */
2388 tcache_t *tcache = tcache_get_from_ind(tsd, dopts->tcache_ind,
2389 sopts->slow, /* is_alloc */ true);
2390
2391 /* Fill in the arena. */
2392 arena_t *arena;
2393 if (arena_get_from_ind(tsd, dopts->arena_ind, &arena)) {
2394 return NULL;
2395 }
2396
2397 if (unlikely(dopts->alignment != 0)) {
2398 return ipalloct(tsd_tsdn(tsd), usize, dopts->alignment,
2399 dopts->zero, tcache, arena);
2400 }
2401
2402 return iallocztm(tsd_tsdn(tsd), size, ind, dopts->zero, tcache, false,
2403 arena, sopts->slow);
2404 }
2405
2406 JEMALLOC_ALWAYS_INLINE void *
imalloc_sample(static_opts_t * sopts,dynamic_opts_t * dopts,tsd_t * tsd,size_t usize,szind_t ind)2407 imalloc_sample(static_opts_t *sopts, dynamic_opts_t *dopts, tsd_t *tsd,
2408 size_t usize, szind_t ind) {
2409 void *ret;
2410
2411 /*
2412 * For small allocations, sampling bumps the usize. If so, we allocate
2413 * from the ind_large bucket.
2414 */
2415 szind_t ind_large;
2416 size_t bumped_usize = usize;
2417
2418 dopts->alignment = prof_sample_align(dopts->alignment);
2419 if (usize <= SC_SMALL_MAXCLASS) {
2420 assert(((dopts->alignment == 0) ?
2421 sz_s2u(SC_LARGE_MINCLASS) :
2422 sz_sa2u(SC_LARGE_MINCLASS, dopts->alignment))
2423 == SC_LARGE_MINCLASS);
2424 ind_large = sz_size2index(SC_LARGE_MINCLASS);
2425 bumped_usize = sz_s2u(SC_LARGE_MINCLASS);
2426 ret = imalloc_no_sample(sopts, dopts, tsd, bumped_usize,
2427 bumped_usize, ind_large);
2428 if (unlikely(ret == NULL)) {
2429 return NULL;
2430 }
2431 arena_prof_promote(tsd_tsdn(tsd), ret, usize);
2432 } else {
2433 ret = imalloc_no_sample(sopts, dopts, tsd, usize, usize, ind);
2434 }
2435 assert(prof_sample_aligned(ret));
2436
2437 return ret;
2438 }
2439
2440 /*
2441 * Returns true if the allocation will overflow, and false otherwise. Sets
2442 * *size to the product either way.
2443 */
2444 JEMALLOC_ALWAYS_INLINE bool
compute_size_with_overflow(bool may_overflow,dynamic_opts_t * dopts,size_t * size)2445 compute_size_with_overflow(bool may_overflow, dynamic_opts_t *dopts,
2446 size_t *size) {
2447 /*
2448 * This function is just num_items * item_size, except that we may have
2449 * to check for overflow.
2450 */
2451
2452 if (!may_overflow) {
2453 assert(dopts->num_items == 1);
2454 *size = dopts->item_size;
2455 return false;
2456 }
2457
2458 /* A size_t with its high-half bits all set to 1. */
2459 static const size_t high_bits = SIZE_T_MAX << (sizeof(size_t) * 8 / 2);
2460
2461 *size = dopts->item_size * dopts->num_items;
2462
2463 if (unlikely(*size == 0)) {
2464 return (dopts->num_items != 0 && dopts->item_size != 0);
2465 }
2466
2467 /*
2468 * We got a non-zero size, but we don't know if we overflowed to get
2469 * there. To avoid having to do a divide, we'll be clever and note that
2470 * if both A and B can be represented in N/2 bits, then their product
2471 * can be represented in N bits (without the possibility of overflow).
2472 */
2473 if (likely((high_bits & (dopts->num_items | dopts->item_size)) == 0)) {
2474 return false;
2475 }
2476 if (likely(*size / dopts->item_size == dopts->num_items)) {
2477 return false;
2478 }
2479 return true;
2480 }
2481
2482 JEMALLOC_ALWAYS_INLINE int
imalloc_body(static_opts_t * sopts,dynamic_opts_t * dopts,tsd_t * tsd)2483 imalloc_body(static_opts_t *sopts, dynamic_opts_t *dopts, tsd_t *tsd) {
2484 /* Where the actual allocated memory will live. */
2485 void *allocation = NULL;
2486 /* Filled in by compute_size_with_overflow below. */
2487 size_t size = 0;
2488 /*
2489 * The zero initialization for ind is actually dead store, in that its
2490 * value is reset before any branch on its value is taken. Sometimes
2491 * though, it's convenient to pass it as arguments before this point.
2492 * To avoid undefined behavior then, we initialize it with dummy stores.
2493 */
2494 szind_t ind = 0;
2495 /* usize will always be properly initialized. */
2496 size_t usize;
2497
2498 /* Reentrancy is only checked on slow path. */
2499 int8_t reentrancy_level;
2500
2501 /* Compute the amount of memory the user wants. */
2502 if (unlikely(compute_size_with_overflow(sopts->may_overflow, dopts,
2503 &size))) {
2504 goto label_oom;
2505 }
2506
2507 if (unlikely(dopts->alignment < sopts->min_alignment
2508 || (dopts->alignment & (dopts->alignment - 1)) != 0)) {
2509 goto label_invalid_alignment;
2510 }
2511
2512 /* This is the beginning of the "core" algorithm. */
2513 dopts->zero = zero_get(dopts->zero, sopts->slow);
2514 if (aligned_usize_get(size, dopts->alignment, &usize, &ind,
2515 sopts->bump_empty_aligned_alloc)) {
2516 goto label_oom;
2517 }
2518 dopts->usize = usize;
2519 /* Validate the user input. */
2520 if (sopts->assert_nonempty_alloc) {
2521 assert (size != 0);
2522 }
2523
2524 check_entry_exit_locking(tsd_tsdn(tsd));
2525
2526 /*
2527 * If we need to handle reentrancy, we can do it out of a
2528 * known-initialized arena (i.e. arena 0).
2529 */
2530 reentrancy_level = tsd_reentrancy_level_get(tsd);
2531 if (sopts->slow && unlikely(reentrancy_level > 0)) {
2532 /*
2533 * We should never specify particular arenas or tcaches from
2534 * within our internal allocations.
2535 */
2536 assert(dopts->tcache_ind == TCACHE_IND_AUTOMATIC ||
2537 dopts->tcache_ind == TCACHE_IND_NONE);
2538 assert(dopts->arena_ind == ARENA_IND_AUTOMATIC);
2539 dopts->tcache_ind = TCACHE_IND_NONE;
2540 /* We know that arena 0 has already been initialized. */
2541 dopts->arena_ind = 0;
2542 }
2543
2544 /*
2545 * If dopts->alignment > 0, then ind is still 0, but usize was computed
2546 * in the previous if statement. Down the positive alignment path,
2547 * imalloc_no_sample and imalloc_sample will ignore ind.
2548 */
2549
2550 /* If profiling is on, get our profiling context. */
2551 if (config_prof && opt_prof) {
2552 bool prof_active = prof_active_get_unlocked();
2553 bool sample_event = te_prof_sample_event_lookahead(tsd, usize);
2554 prof_tctx_t *tctx = prof_alloc_prep(tsd, prof_active,
2555 sample_event);
2556
2557 emap_alloc_ctx_t alloc_ctx;
2558 if (likely((uintptr_t)tctx == (uintptr_t)1U)) {
2559 alloc_ctx.slab = (usize <= SC_SMALL_MAXCLASS);
2560 allocation = imalloc_no_sample(
2561 sopts, dopts, tsd, usize, usize, ind);
2562 } else if ((uintptr_t)tctx > (uintptr_t)1U) {
2563 allocation = imalloc_sample(
2564 sopts, dopts, tsd, usize, ind);
2565 alloc_ctx.slab = false;
2566 } else {
2567 allocation = NULL;
2568 }
2569
2570 if (unlikely(allocation == NULL)) {
2571 prof_alloc_rollback(tsd, tctx);
2572 goto label_oom;
2573 }
2574 prof_malloc(tsd, allocation, size, usize, &alloc_ctx, tctx);
2575 } else {
2576 assert(!opt_prof);
2577 allocation = imalloc_no_sample(sopts, dopts, tsd, size, usize,
2578 ind);
2579 if (unlikely(allocation == NULL)) {
2580 goto label_oom;
2581 }
2582 }
2583
2584 /*
2585 * Allocation has been done at this point. We still have some
2586 * post-allocation work to do though.
2587 */
2588
2589 thread_alloc_event(tsd, usize);
2590
2591 assert(dopts->alignment == 0
2592 || ((uintptr_t)allocation & (dopts->alignment - 1)) == ZU(0));
2593
2594 assert(usize == isalloc(tsd_tsdn(tsd), allocation));
2595
2596 if (config_fill && sopts->slow && !dopts->zero
2597 && unlikely(opt_junk_alloc)) {
2598 junk_alloc_callback(allocation, usize);
2599 }
2600
2601 if (sopts->slow) {
2602 UTRACE(0, size, allocation);
2603 }
2604
2605 /* Success! */
2606 check_entry_exit_locking(tsd_tsdn(tsd));
2607 *dopts->result = allocation;
2608 return 0;
2609
2610 label_oom:
2611 if (unlikely(sopts->slow) && config_xmalloc && unlikely(opt_xmalloc)) {
2612 malloc_write(sopts->oom_string);
2613 abort();
2614 }
2615
2616 if (sopts->slow) {
2617 UTRACE(NULL, size, NULL);
2618 }
2619
2620 check_entry_exit_locking(tsd_tsdn(tsd));
2621
2622 if (sopts->set_errno_on_error) {
2623 set_errno(ENOMEM);
2624 }
2625
2626 if (sopts->null_out_result_on_error) {
2627 *dopts->result = NULL;
2628 }
2629
2630 return ENOMEM;
2631
2632 /*
2633 * This label is only jumped to by one goto; we move it out of line
2634 * anyways to avoid obscuring the non-error paths, and for symmetry with
2635 * the oom case.
2636 */
2637 label_invalid_alignment:
2638 if (config_xmalloc && unlikely(opt_xmalloc)) {
2639 malloc_write(sopts->invalid_alignment_string);
2640 abort();
2641 }
2642
2643 if (sopts->set_errno_on_error) {
2644 set_errno(EINVAL);
2645 }
2646
2647 if (sopts->slow) {
2648 UTRACE(NULL, size, NULL);
2649 }
2650
2651 check_entry_exit_locking(tsd_tsdn(tsd));
2652
2653 if (sopts->null_out_result_on_error) {
2654 *dopts->result = NULL;
2655 }
2656
2657 return EINVAL;
2658 }
2659
2660 JEMALLOC_ALWAYS_INLINE bool
imalloc_init_check(static_opts_t * sopts,dynamic_opts_t * dopts)2661 imalloc_init_check(static_opts_t *sopts, dynamic_opts_t *dopts) {
2662 if (unlikely(!malloc_initialized()) && unlikely(malloc_init())) {
2663 if (config_xmalloc && unlikely(opt_xmalloc)) {
2664 malloc_write(sopts->oom_string);
2665 abort();
2666 }
2667 UTRACE(NULL, dopts->num_items * dopts->item_size, NULL);
2668 set_errno(ENOMEM);
2669 *dopts->result = NULL;
2670
2671 return false;
2672 }
2673
2674 return true;
2675 }
2676
2677 /* Returns the errno-style error code of the allocation. */
2678 JEMALLOC_ALWAYS_INLINE int
imalloc(static_opts_t * sopts,dynamic_opts_t * dopts)2679 imalloc(static_opts_t *sopts, dynamic_opts_t *dopts) {
2680 if (tsd_get_allocates() && !imalloc_init_check(sopts, dopts)) {
2681 return ENOMEM;
2682 }
2683
2684 /* We always need the tsd. Let's grab it right away. */
2685 tsd_t *tsd = tsd_fetch();
2686 assert(tsd);
2687 if (likely(tsd_fast(tsd))) {
2688 /* Fast and common path. */
2689 tsd_assert_fast(tsd);
2690 sopts->slow = false;
2691 return imalloc_body(sopts, dopts, tsd);
2692 } else {
2693 if (!tsd_get_allocates() && !imalloc_init_check(sopts, dopts)) {
2694 return ENOMEM;
2695 }
2696
2697 sopts->slow = true;
2698 return imalloc_body(sopts, dopts, tsd);
2699 }
2700 }
2701
2702 JEMALLOC_NOINLINE
2703 void *
malloc_default(size_t size)2704 malloc_default(size_t size) {
2705 void *ret;
2706 static_opts_t sopts;
2707 dynamic_opts_t dopts;
2708
2709 /*
2710 * This variant has logging hook on exit but not on entry. It's callled
2711 * only by je_malloc, below, which emits the entry one for us (and, if
2712 * it calls us, does so only via tail call).
2713 */
2714
2715 static_opts_init(&sopts);
2716 dynamic_opts_init(&dopts);
2717
2718 sopts.null_out_result_on_error = true;
2719 sopts.set_errno_on_error = true;
2720 sopts.oom_string = "<jemalloc>: Error in malloc(): out of memory\n";
2721
2722 dopts.result = &ret;
2723 dopts.num_items = 1;
2724 dopts.item_size = size;
2725
2726 imalloc(&sopts, &dopts);
2727 /*
2728 * Note that this branch gets optimized away -- it immediately follows
2729 * the check on tsd_fast that sets sopts.slow.
2730 */
2731 if (sopts.slow) {
2732 uintptr_t args[3] = {size};
2733 hook_invoke_alloc(hook_alloc_malloc, ret, (uintptr_t)ret, args);
2734 }
2735
2736 LOG("core.malloc.exit", "result: %p", ret);
2737
2738 return ret;
2739 }
2740
2741 /******************************************************************************/
2742 /*
2743 * Begin malloc(3)-compatible functions.
2744 */
2745
2746 JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
2747 void JEMALLOC_NOTHROW *
JEMALLOC_ATTR(malloc)2748 JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1)
2749 je_malloc(size_t size) {
2750 return imalloc_fastpath(size, &malloc_default);
2751 }
2752
2753 JEMALLOC_EXPORT int JEMALLOC_NOTHROW
2754 JEMALLOC_ATTR(nonnull(1))
je_posix_memalign(void ** memptr,size_t alignment,size_t size)2755 je_posix_memalign(void **memptr, size_t alignment, size_t size) {
2756 int ret;
2757 static_opts_t sopts;
2758 dynamic_opts_t dopts;
2759
2760 LOG("core.posix_memalign.entry", "mem ptr: %p, alignment: %zu, "
2761 "size: %zu", memptr, alignment, size);
2762
2763 static_opts_init(&sopts);
2764 dynamic_opts_init(&dopts);
2765
2766 sopts.bump_empty_aligned_alloc = true;
2767 sopts.min_alignment = sizeof(void *);
2768 sopts.oom_string =
2769 "<jemalloc>: Error allocating aligned memory: out of memory\n";
2770 sopts.invalid_alignment_string =
2771 "<jemalloc>: Error allocating aligned memory: invalid alignment\n";
2772
2773 dopts.result = memptr;
2774 dopts.num_items = 1;
2775 dopts.item_size = size;
2776 dopts.alignment = alignment;
2777
2778 ret = imalloc(&sopts, &dopts);
2779 if (sopts.slow) {
2780 uintptr_t args[3] = {(uintptr_t)memptr, (uintptr_t)alignment,
2781 (uintptr_t)size};
2782 hook_invoke_alloc(hook_alloc_posix_memalign, *memptr,
2783 (uintptr_t)ret, args);
2784 }
2785
2786 LOG("core.posix_memalign.exit", "result: %d, alloc ptr: %p", ret,
2787 *memptr);
2788
2789 return ret;
2790 }
2791
2792 JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
2793 void JEMALLOC_NOTHROW *
JEMALLOC_ATTR(malloc)2794 JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(2)
2795 je_aligned_alloc(size_t alignment, size_t size) {
2796 void *ret;
2797
2798 static_opts_t sopts;
2799 dynamic_opts_t dopts;
2800
2801 LOG("core.aligned_alloc.entry", "alignment: %zu, size: %zu\n",
2802 alignment, size);
2803
2804 static_opts_init(&sopts);
2805 dynamic_opts_init(&dopts);
2806
2807 sopts.bump_empty_aligned_alloc = true;
2808 sopts.null_out_result_on_error = true;
2809 sopts.set_errno_on_error = true;
2810 sopts.min_alignment = 1;
2811 sopts.oom_string =
2812 "<jemalloc>: Error allocating aligned memory: out of memory\n";
2813 sopts.invalid_alignment_string =
2814 "<jemalloc>: Error allocating aligned memory: invalid alignment\n";
2815
2816 dopts.result = &ret;
2817 dopts.num_items = 1;
2818 dopts.item_size = size;
2819 dopts.alignment = alignment;
2820
2821 imalloc(&sopts, &dopts);
2822 if (sopts.slow) {
2823 uintptr_t args[3] = {(uintptr_t)alignment, (uintptr_t)size};
2824 hook_invoke_alloc(hook_alloc_aligned_alloc, ret,
2825 (uintptr_t)ret, args);
2826 }
2827
2828 LOG("core.aligned_alloc.exit", "result: %p", ret);
2829
2830 return ret;
2831 }
2832
2833 JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
2834 void JEMALLOC_NOTHROW *
JEMALLOC_ATTR(malloc)2835 JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE2(1, 2)
2836 je_calloc(size_t num, size_t size) {
2837 void *ret;
2838 static_opts_t sopts;
2839 dynamic_opts_t dopts;
2840
2841 LOG("core.calloc.entry", "num: %zu, size: %zu\n", num, size);
2842
2843 static_opts_init(&sopts);
2844 dynamic_opts_init(&dopts);
2845
2846 sopts.may_overflow = true;
2847 sopts.null_out_result_on_error = true;
2848 sopts.set_errno_on_error = true;
2849 sopts.oom_string = "<jemalloc>: Error in calloc(): out of memory\n";
2850
2851 dopts.result = &ret;
2852 dopts.num_items = num;
2853 dopts.item_size = size;
2854 dopts.zero = true;
2855
2856 imalloc(&sopts, &dopts);
2857 if (sopts.slow) {
2858 uintptr_t args[3] = {(uintptr_t)num, (uintptr_t)size};
2859 hook_invoke_alloc(hook_alloc_calloc, ret, (uintptr_t)ret, args);
2860 }
2861
2862 LOG("core.calloc.exit", "result: %p", ret);
2863
2864 return ret;
2865 }
2866
2867 JEMALLOC_ALWAYS_INLINE void
ifree(tsd_t * tsd,void * ptr,tcache_t * tcache,bool slow_path)2868 ifree(tsd_t *tsd, void *ptr, tcache_t *tcache, bool slow_path) {
2869 if (!slow_path) {
2870 tsd_assert_fast(tsd);
2871 }
2872 check_entry_exit_locking(tsd_tsdn(tsd));
2873 if (tsd_reentrancy_level_get(tsd) != 0) {
2874 assert(slow_path);
2875 }
2876
2877 assert(ptr != NULL);
2878 assert(malloc_initialized() || IS_INITIALIZER);
2879
2880 emap_alloc_ctx_t alloc_ctx;
2881 emap_alloc_ctx_lookup(tsd_tsdn(tsd), &arena_emap_global, ptr,
2882 &alloc_ctx);
2883 assert(alloc_ctx.szind != SC_NSIZES);
2884
2885 size_t usize = sz_index2size(alloc_ctx.szind);
2886 if (config_prof && opt_prof) {
2887 prof_free(tsd, ptr, usize, &alloc_ctx);
2888 }
2889
2890 if (likely(!slow_path)) {
2891 idalloctm(tsd_tsdn(tsd), ptr, tcache, &alloc_ctx, false,
2892 false);
2893 } else {
2894 if (config_fill && slow_path && opt_junk_free) {
2895 junk_free_callback(ptr, usize);
2896 }
2897 idalloctm(tsd_tsdn(tsd), ptr, tcache, &alloc_ctx, false,
2898 true);
2899 }
2900 thread_dalloc_event(tsd, usize);
2901 }
2902
2903 JEMALLOC_ALWAYS_INLINE bool
maybe_check_alloc_ctx(tsd_t * tsd,void * ptr,emap_alloc_ctx_t * alloc_ctx)2904 maybe_check_alloc_ctx(tsd_t *tsd, void *ptr, emap_alloc_ctx_t *alloc_ctx) {
2905 if (config_opt_size_checks) {
2906 emap_alloc_ctx_t dbg_ctx;
2907 emap_alloc_ctx_lookup(tsd_tsdn(tsd), &arena_emap_global, ptr,
2908 &dbg_ctx);
2909 if (alloc_ctx->szind != dbg_ctx.szind) {
2910 safety_check_fail_sized_dealloc(
2911 /* current_dealloc */ true, ptr,
2912 /* true_size */ sz_size2index(dbg_ctx.szind),
2913 /* input_size */ sz_size2index(alloc_ctx->szind));
2914 return true;
2915 }
2916 if (alloc_ctx->slab != dbg_ctx.slab) {
2917 safety_check_fail(
2918 "Internal heap corruption detected: "
2919 "mismatch in slab bit");
2920 return true;
2921 }
2922 }
2923 return false;
2924 }
2925
2926 JEMALLOC_ALWAYS_INLINE void
isfree(tsd_t * tsd,void * ptr,size_t usize,tcache_t * tcache,bool slow_path)2927 isfree(tsd_t *tsd, void *ptr, size_t usize, tcache_t *tcache, bool slow_path) {
2928 if (!slow_path) {
2929 tsd_assert_fast(tsd);
2930 }
2931 check_entry_exit_locking(tsd_tsdn(tsd));
2932 if (tsd_reentrancy_level_get(tsd) != 0) {
2933 assert(slow_path);
2934 }
2935
2936 assert(ptr != NULL);
2937 assert(malloc_initialized() || IS_INITIALIZER);
2938
2939 emap_alloc_ctx_t alloc_ctx;
2940 if (!config_prof) {
2941 alloc_ctx.szind = sz_size2index(usize);
2942 alloc_ctx.slab = (alloc_ctx.szind < SC_NBINS);
2943 } else {
2944 if (likely(!prof_sample_aligned(ptr))) {
2945 /*
2946 * When the ptr is not page aligned, it was not sampled.
2947 * usize can be trusted to determine szind and slab.
2948 */
2949 alloc_ctx.szind = sz_size2index(usize);
2950 alloc_ctx.slab = (alloc_ctx.szind < SC_NBINS);
2951 } else if (opt_prof) {
2952 emap_alloc_ctx_lookup(tsd_tsdn(tsd), &arena_emap_global,
2953 ptr, &alloc_ctx);
2954
2955 if (config_opt_safety_checks) {
2956 /* Small alloc may have !slab (sampled). */
2957 if (unlikely(alloc_ctx.szind !=
2958 sz_size2index(usize))) {
2959 safety_check_fail_sized_dealloc(
2960 /* current_dealloc */ true, ptr,
2961 /* true_size */ sz_index2size(
2962 alloc_ctx.szind),
2963 /* input_size */ usize);
2964 }
2965 }
2966 } else {
2967 alloc_ctx.szind = sz_size2index(usize);
2968 alloc_ctx.slab = (alloc_ctx.szind < SC_NBINS);
2969 }
2970 }
2971 bool fail = maybe_check_alloc_ctx(tsd, ptr, &alloc_ctx);
2972 if (fail) {
2973 /*
2974 * This is a heap corruption bug. In real life we'll crash; for
2975 * the unit test we just want to avoid breaking anything too
2976 * badly to get a test result out. Let's leak instead of trying
2977 * to free.
2978 */
2979 return;
2980 }
2981
2982 if (config_prof && opt_prof) {
2983 prof_free(tsd, ptr, usize, &alloc_ctx);
2984 }
2985 if (likely(!slow_path)) {
2986 isdalloct(tsd_tsdn(tsd), ptr, usize, tcache, &alloc_ctx,
2987 false);
2988 } else {
2989 if (config_fill && slow_path && opt_junk_free) {
2990 junk_free_callback(ptr, usize);
2991 }
2992 isdalloct(tsd_tsdn(tsd), ptr, usize, tcache, &alloc_ctx,
2993 true);
2994 }
2995 thread_dalloc_event(tsd, usize);
2996 }
2997
2998 JEMALLOC_NOINLINE
2999 void
free_default(void * ptr)3000 free_default(void *ptr) {
3001 UTRACE(ptr, 0, 0);
3002 if (likely(ptr != NULL)) {
3003 /*
3004 * We avoid setting up tsd fully (e.g. tcache, arena binding)
3005 * based on only free() calls -- other activities trigger the
3006 * minimal to full transition. This is because free() may
3007 * happen during thread shutdown after tls deallocation: if a
3008 * thread never had any malloc activities until then, a
3009 * fully-setup tsd won't be destructed properly.
3010 */
3011 tsd_t *tsd = tsd_fetch_min();
3012 check_entry_exit_locking(tsd_tsdn(tsd));
3013
3014 if (likely(tsd_fast(tsd))) {
3015 tcache_t *tcache = tcache_get_from_ind(tsd,
3016 TCACHE_IND_AUTOMATIC, /* slow */ false,
3017 /* is_alloc */ false);
3018 ifree(tsd, ptr, tcache, /* slow */ false);
3019 } else {
3020 tcache_t *tcache = tcache_get_from_ind(tsd,
3021 TCACHE_IND_AUTOMATIC, /* slow */ true,
3022 /* is_alloc */ false);
3023 uintptr_t args_raw[3] = {(uintptr_t)ptr};
3024 hook_invoke_dalloc(hook_dalloc_free, ptr, args_raw);
3025 ifree(tsd, ptr, tcache, /* slow */ true);
3026 }
3027
3028 check_entry_exit_locking(tsd_tsdn(tsd));
3029 }
3030 }
3031
3032 JEMALLOC_ALWAYS_INLINE bool
free_fastpath_nonfast_aligned(void * ptr,bool check_prof)3033 free_fastpath_nonfast_aligned(void *ptr, bool check_prof) {
3034 /*
3035 * free_fastpath do not handle two uncommon cases: 1) sampled profiled
3036 * objects and 2) sampled junk & stash for use-after-free detection.
3037 * Both have special alignments which are used to escape the fastpath.
3038 *
3039 * prof_sample is page-aligned, which covers the UAF check when both
3040 * are enabled (the assertion below). Avoiding redundant checks since
3041 * this is on the fastpath -- at most one runtime branch from this.
3042 */
3043 if (config_debug && cache_bin_nonfast_aligned(ptr)) {
3044 assert(prof_sample_aligned(ptr));
3045 }
3046
3047 if (config_prof && check_prof) {
3048 /* When prof is enabled, the prof_sample alignment is enough. */
3049 if (prof_sample_aligned(ptr)) {
3050 return true;
3051 } else {
3052 return false;
3053 }
3054 }
3055
3056 if (config_uaf_detection) {
3057 if (cache_bin_nonfast_aligned(ptr)) {
3058 return true;
3059 } else {
3060 return false;
3061 }
3062 }
3063
3064 return false;
3065 }
3066
3067 /* Returns whether or not the free attempt was successful. */
3068 JEMALLOC_ALWAYS_INLINE
free_fastpath(void * ptr,size_t size,bool size_hint)3069 bool free_fastpath(void *ptr, size_t size, bool size_hint) {
3070 tsd_t *tsd = tsd_get(false);
3071 /* The branch gets optimized away unless tsd_get_allocates(). */
3072 if (unlikely(tsd == NULL)) {
3073 return false;
3074 }
3075 /*
3076 * The tsd_fast() / initialized checks are folded into the branch
3077 * testing (deallocated_after >= threshold) later in this function.
3078 * The threshold will be set to 0 when !tsd_fast.
3079 */
3080 assert(tsd_fast(tsd) ||
3081 *tsd_thread_deallocated_next_event_fastp_get_unsafe(tsd) == 0);
3082
3083 emap_alloc_ctx_t alloc_ctx;
3084 if (!size_hint) {
3085 bool err = emap_alloc_ctx_try_lookup_fast(tsd,
3086 &arena_emap_global, ptr, &alloc_ctx);
3087
3088 /* Note: profiled objects will have alloc_ctx.slab set */
3089 if (unlikely(err || !alloc_ctx.slab ||
3090 free_fastpath_nonfast_aligned(ptr,
3091 /* check_prof */ false))) {
3092 return false;
3093 }
3094 assert(alloc_ctx.szind != SC_NSIZES);
3095 } else {
3096 /*
3097 * Check for both sizes that are too large, and for sampled /
3098 * special aligned objects. The alignment check will also check
3099 * for null ptr.
3100 */
3101 if (unlikely(size > SC_LOOKUP_MAXCLASS ||
3102 free_fastpath_nonfast_aligned(ptr,
3103 /* check_prof */ true))) {
3104 return false;
3105 }
3106 alloc_ctx.szind = sz_size2index_lookup(size);
3107 /* Max lookup class must be small. */
3108 assert(alloc_ctx.szind < SC_NBINS);
3109 /* This is a dead store, except when opt size checking is on. */
3110 alloc_ctx.slab = true;
3111 }
3112 /*
3113 * Currently the fastpath only handles small sizes. The branch on
3114 * SC_LOOKUP_MAXCLASS makes sure of it. This lets us avoid checking
3115 * tcache szind upper limit (i.e. tcache_maxclass) as well.
3116 */
3117 assert(alloc_ctx.slab);
3118
3119 uint64_t deallocated, threshold;
3120 te_free_fastpath_ctx(tsd, &deallocated, &threshold);
3121
3122 size_t usize = sz_index2size(alloc_ctx.szind);
3123 uint64_t deallocated_after = deallocated + usize;
3124 /*
3125 * Check for events and tsd non-nominal (fast_threshold will be set to
3126 * 0) in a single branch. Note that this handles the uninitialized case
3127 * as well (TSD init will be triggered on the non-fastpath). Therefore
3128 * anything depends on a functional TSD (e.g. the alloc_ctx sanity check
3129 * below) needs to be after this branch.
3130 */
3131 if (unlikely(deallocated_after >= threshold)) {
3132 return false;
3133 }
3134 assert(tsd_fast(tsd));
3135 bool fail = maybe_check_alloc_ctx(tsd, ptr, &alloc_ctx);
3136 if (fail) {
3137 /* See the comment in isfree. */
3138 return true;
3139 }
3140
3141 tcache_t *tcache = tcache_get_from_ind(tsd, TCACHE_IND_AUTOMATIC,
3142 /* slow */ false, /* is_alloc */ false);
3143 cache_bin_t *bin = &tcache->bins[alloc_ctx.szind];
3144
3145 /*
3146 * If junking were enabled, this is where we would do it. It's not
3147 * though, since we ensured above that we're on the fast path. Assert
3148 * that to double-check.
3149 */
3150 assert(!opt_junk_free);
3151
3152 if (!cache_bin_dalloc_easy(bin, ptr)) {
3153 return false;
3154 }
3155
3156 *tsd_thread_deallocatedp_get(tsd) = deallocated_after;
3157
3158 return true;
3159 }
3160
3161 JEMALLOC_EXPORT void JEMALLOC_NOTHROW
je_free(void * ptr)3162 je_free(void *ptr) {
3163 LOG("core.free.entry", "ptr: %p", ptr);
3164
3165 if (!free_fastpath(ptr, 0, false)) {
3166 free_default(ptr);
3167 }
3168
3169 LOG("core.free.exit", "");
3170 }
3171
3172 /*
3173 * End malloc(3)-compatible functions.
3174 */
3175 /******************************************************************************/
3176 /*
3177 * Begin non-standard override functions.
3178 */
3179
3180 #ifdef JEMALLOC_OVERRIDE_MEMALIGN
3181 JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
3182 void JEMALLOC_NOTHROW *
JEMALLOC_ATTR(malloc)3183 JEMALLOC_ATTR(malloc)
3184 je_memalign(size_t alignment, size_t size) {
3185 void *ret;
3186 static_opts_t sopts;
3187 dynamic_opts_t dopts;
3188
3189 LOG("core.memalign.entry", "alignment: %zu, size: %zu\n", alignment,
3190 size);
3191
3192 static_opts_init(&sopts);
3193 dynamic_opts_init(&dopts);
3194
3195 sopts.min_alignment = 1;
3196 sopts.oom_string =
3197 "<jemalloc>: Error allocating aligned memory: out of memory\n";
3198 sopts.invalid_alignment_string =
3199 "<jemalloc>: Error allocating aligned memory: invalid alignment\n";
3200 sopts.null_out_result_on_error = true;
3201
3202 dopts.result = &ret;
3203 dopts.num_items = 1;
3204 dopts.item_size = size;
3205 dopts.alignment = alignment;
3206
3207 imalloc(&sopts, &dopts);
3208 if (sopts.slow) {
3209 uintptr_t args[3] = {alignment, size};
3210 hook_invoke_alloc(hook_alloc_memalign, ret, (uintptr_t)ret,
3211 args);
3212 }
3213
3214 LOG("core.memalign.exit", "result: %p", ret);
3215 return ret;
3216 }
3217 #endif
3218
3219 #ifdef JEMALLOC_OVERRIDE_VALLOC
3220 JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
3221 void JEMALLOC_NOTHROW *
JEMALLOC_ATTR(malloc)3222 JEMALLOC_ATTR(malloc)
3223 je_valloc(size_t size) {
3224 void *ret;
3225
3226 static_opts_t sopts;
3227 dynamic_opts_t dopts;
3228
3229 LOG("core.valloc.entry", "size: %zu\n", size);
3230
3231 static_opts_init(&sopts);
3232 dynamic_opts_init(&dopts);
3233
3234 sopts.null_out_result_on_error = true;
3235 sopts.min_alignment = PAGE;
3236 sopts.oom_string =
3237 "<jemalloc>: Error allocating aligned memory: out of memory\n";
3238 sopts.invalid_alignment_string =
3239 "<jemalloc>: Error allocating aligned memory: invalid alignment\n";
3240
3241 dopts.result = &ret;
3242 dopts.num_items = 1;
3243 dopts.item_size = size;
3244 dopts.alignment = PAGE;
3245
3246 imalloc(&sopts, &dopts);
3247 if (sopts.slow) {
3248 uintptr_t args[3] = {size};
3249 hook_invoke_alloc(hook_alloc_valloc, ret, (uintptr_t)ret, args);
3250 }
3251
3252 LOG("core.valloc.exit", "result: %p\n", ret);
3253 return ret;
3254 }
3255 #endif
3256
3257 #if defined(JEMALLOC_IS_MALLOC) && defined(JEMALLOC_GLIBC_MALLOC_HOOK)
3258 /*
3259 * glibc provides the RTLD_DEEPBIND flag for dlopen which can make it possible
3260 * to inconsistently reference libc's malloc(3)-compatible functions
3261 * (https://bugzilla.mozilla.org/show_bug.cgi?id=493541).
3262 *
3263 * These definitions interpose hooks in glibc. The functions are actually
3264 * passed an extra argument for the caller return address, which will be
3265 * ignored.
3266 */
3267 #include <features.h> // defines __GLIBC__ if we are compiling against glibc
3268
3269 JEMALLOC_EXPORT void (*__free_hook)(void *ptr) = je_free;
3270 JEMALLOC_EXPORT void *(*__malloc_hook)(size_t size) = je_malloc;
3271 JEMALLOC_EXPORT void *(*__realloc_hook)(void *ptr, size_t size) = je_realloc;
3272 # ifdef JEMALLOC_GLIBC_MEMALIGN_HOOK
3273 JEMALLOC_EXPORT void *(*__memalign_hook)(size_t alignment, size_t size) =
3274 je_memalign;
3275 # endif
3276
3277 # ifdef __GLIBC__
3278 /*
3279 * To enable static linking with glibc, the libc specific malloc interface must
3280 * be implemented also, so none of glibc's malloc.o functions are added to the
3281 * link.
3282 */
3283 # define ALIAS(je_fn) __attribute__((alias (#je_fn), used))
3284 /* To force macro expansion of je_ prefix before stringification. */
3285 # define PREALIAS(je_fn) ALIAS(je_fn)
3286 # ifdef JEMALLOC_OVERRIDE___LIBC_CALLOC
3287 void *__libc_calloc(size_t n, size_t size) PREALIAS(je_calloc);
3288 # endif
3289 # ifdef JEMALLOC_OVERRIDE___LIBC_FREE
3290 void __libc_free(void* ptr) PREALIAS(je_free);
3291 # endif
3292 # ifdef JEMALLOC_OVERRIDE___LIBC_MALLOC
3293 void *__libc_malloc(size_t size) PREALIAS(je_malloc);
3294 # endif
3295 # ifdef JEMALLOC_OVERRIDE___LIBC_MEMALIGN
3296 void *__libc_memalign(size_t align, size_t s) PREALIAS(je_memalign);
3297 # endif
3298 # ifdef JEMALLOC_OVERRIDE___LIBC_REALLOC
3299 void *__libc_realloc(void* ptr, size_t size) PREALIAS(je_realloc);
3300 # endif
3301 # ifdef JEMALLOC_OVERRIDE___LIBC_VALLOC
3302 void *__libc_valloc(size_t size) PREALIAS(je_valloc);
3303 # endif
3304 # ifdef JEMALLOC_OVERRIDE___POSIX_MEMALIGN
3305 int __posix_memalign(void** r, size_t a, size_t s) PREALIAS(je_posix_memalign);
3306 # endif
3307 # undef PREALIAS
3308 # undef ALIAS
3309 # endif
3310 #endif
3311
3312 /*
3313 * End non-standard override functions.
3314 */
3315 /******************************************************************************/
3316 /*
3317 * Begin non-standard functions.
3318 */
3319
3320 JEMALLOC_ALWAYS_INLINE unsigned
mallocx_tcache_get(int flags)3321 mallocx_tcache_get(int flags) {
3322 if (likely((flags & MALLOCX_TCACHE_MASK) == 0)) {
3323 return TCACHE_IND_AUTOMATIC;
3324 } else if ((flags & MALLOCX_TCACHE_MASK) == MALLOCX_TCACHE_NONE) {
3325 return TCACHE_IND_NONE;
3326 } else {
3327 return MALLOCX_TCACHE_GET(flags);
3328 }
3329 }
3330
3331 JEMALLOC_ALWAYS_INLINE unsigned
mallocx_arena_get(int flags)3332 mallocx_arena_get(int flags) {
3333 if (unlikely((flags & MALLOCX_ARENA_MASK) != 0)) {
3334 return MALLOCX_ARENA_GET(flags);
3335 } else {
3336 return ARENA_IND_AUTOMATIC;
3337 }
3338 }
3339
3340 #ifdef JEMALLOC_EXPERIMENTAL_SMALLOCX_API
3341
3342 #define JEMALLOC_SMALLOCX_CONCAT_HELPER(x, y) x ## y
3343 #define JEMALLOC_SMALLOCX_CONCAT_HELPER2(x, y) \
3344 JEMALLOC_SMALLOCX_CONCAT_HELPER(x, y)
3345
3346 typedef struct {
3347 void *ptr;
3348 size_t size;
3349 } smallocx_return_t;
3350
3351 JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
3352 smallocx_return_t JEMALLOC_NOTHROW
3353 /*
3354 * The attribute JEMALLOC_ATTR(malloc) cannot be used due to:
3355 * - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86488
3356 */
JEMALLOC_SMALLOCX_CONCAT_HELPER2(je_smallocx_,JEMALLOC_VERSION_GID_IDENT)3357 JEMALLOC_SMALLOCX_CONCAT_HELPER2(je_smallocx_, JEMALLOC_VERSION_GID_IDENT)
3358 (size_t size, int flags) {
3359 /*
3360 * Note: the attribute JEMALLOC_ALLOC_SIZE(1) cannot be
3361 * used here because it makes writing beyond the `size`
3362 * of the `ptr` undefined behavior, but the objective
3363 * of this function is to allow writing beyond `size`
3364 * up to `smallocx_return_t::size`.
3365 */
3366 smallocx_return_t ret;
3367 static_opts_t sopts;
3368 dynamic_opts_t dopts;
3369
3370 LOG("core.smallocx.entry", "size: %zu, flags: %d", size, flags);
3371
3372 static_opts_init(&sopts);
3373 dynamic_opts_init(&dopts);
3374
3375 sopts.assert_nonempty_alloc = true;
3376 sopts.null_out_result_on_error = true;
3377 sopts.oom_string = "<jemalloc>: Error in mallocx(): out of memory\n";
3378 sopts.usize = true;
3379
3380 dopts.result = &ret.ptr;
3381 dopts.num_items = 1;
3382 dopts.item_size = size;
3383 if (unlikely(flags != 0)) {
3384 dopts.alignment = MALLOCX_ALIGN_GET(flags);
3385 dopts.zero = MALLOCX_ZERO_GET(flags);
3386 dopts.tcache_ind = mallocx_tcache_get(flags);
3387 dopts.arena_ind = mallocx_arena_get(flags);
3388 }
3389
3390 imalloc(&sopts, &dopts);
3391 assert(dopts.usize == je_nallocx(size, flags));
3392 ret.size = dopts.usize;
3393
3394 LOG("core.smallocx.exit", "result: %p, size: %zu", ret.ptr, ret.size);
3395 return ret;
3396 }
3397 #undef JEMALLOC_SMALLOCX_CONCAT_HELPER
3398 #undef JEMALLOC_SMALLOCX_CONCAT_HELPER2
3399 #endif
3400
3401 JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
3402 void JEMALLOC_NOTHROW *
JEMALLOC_ATTR(malloc)3403 JEMALLOC_ATTR(malloc) JEMALLOC_ALLOC_SIZE(1)
3404 je_mallocx(size_t size, int flags) {
3405 void *ret;
3406 static_opts_t sopts;
3407 dynamic_opts_t dopts;
3408
3409 LOG("core.mallocx.entry", "size: %zu, flags: %d", size, flags);
3410
3411 static_opts_init(&sopts);
3412 dynamic_opts_init(&dopts);
3413
3414 sopts.assert_nonempty_alloc = true;
3415 sopts.null_out_result_on_error = true;
3416 sopts.oom_string = "<jemalloc>: Error in mallocx(): out of memory\n";
3417
3418 dopts.result = &ret;
3419 dopts.num_items = 1;
3420 dopts.item_size = size;
3421 if (unlikely(flags != 0)) {
3422 dopts.alignment = MALLOCX_ALIGN_GET(flags);
3423 dopts.zero = MALLOCX_ZERO_GET(flags);
3424 dopts.tcache_ind = mallocx_tcache_get(flags);
3425 dopts.arena_ind = mallocx_arena_get(flags);
3426 }
3427
3428 imalloc(&sopts, &dopts);
3429 if (sopts.slow) {
3430 uintptr_t args[3] = {size, flags};
3431 hook_invoke_alloc(hook_alloc_mallocx, ret, (uintptr_t)ret,
3432 args);
3433 }
3434
3435 LOG("core.mallocx.exit", "result: %p", ret);
3436 return ret;
3437 }
3438
3439 static void *
irallocx_prof_sample(tsdn_t * tsdn,void * old_ptr,size_t old_usize,size_t usize,size_t alignment,bool zero,tcache_t * tcache,arena_t * arena,prof_tctx_t * tctx,hook_ralloc_args_t * hook_args)3440 irallocx_prof_sample(tsdn_t *tsdn, void *old_ptr, size_t old_usize,
3441 size_t usize, size_t alignment, bool zero, tcache_t *tcache, arena_t *arena,
3442 prof_tctx_t *tctx, hook_ralloc_args_t *hook_args) {
3443 void *p;
3444
3445 if (tctx == NULL) {
3446 return NULL;
3447 }
3448
3449 alignment = prof_sample_align(alignment);
3450 if (usize <= SC_SMALL_MAXCLASS) {
3451 p = iralloct(tsdn, old_ptr, old_usize,
3452 SC_LARGE_MINCLASS, alignment, zero, tcache,
3453 arena, hook_args);
3454 if (p == NULL) {
3455 return NULL;
3456 }
3457 arena_prof_promote(tsdn, p, usize);
3458 } else {
3459 p = iralloct(tsdn, old_ptr, old_usize, usize, alignment, zero,
3460 tcache, arena, hook_args);
3461 }
3462 assert(prof_sample_aligned(p));
3463
3464 return p;
3465 }
3466
3467 JEMALLOC_ALWAYS_INLINE void *
irallocx_prof(tsd_t * tsd,void * old_ptr,size_t old_usize,size_t size,size_t alignment,size_t usize,bool zero,tcache_t * tcache,arena_t * arena,emap_alloc_ctx_t * alloc_ctx,hook_ralloc_args_t * hook_args)3468 irallocx_prof(tsd_t *tsd, void *old_ptr, size_t old_usize, size_t size,
3469 size_t alignment, size_t usize, bool zero, tcache_t *tcache,
3470 arena_t *arena, emap_alloc_ctx_t *alloc_ctx,
3471 hook_ralloc_args_t *hook_args) {
3472 prof_info_t old_prof_info;
3473 prof_info_get_and_reset_recent(tsd, old_ptr, alloc_ctx, &old_prof_info);
3474 bool prof_active = prof_active_get_unlocked();
3475 bool sample_event = te_prof_sample_event_lookahead(tsd, usize);
3476 prof_tctx_t *tctx = prof_alloc_prep(tsd, prof_active, sample_event);
3477 void *p;
3478 if (unlikely((uintptr_t)tctx != (uintptr_t)1U)) {
3479 p = irallocx_prof_sample(tsd_tsdn(tsd), old_ptr, old_usize,
3480 usize, alignment, zero, tcache, arena, tctx, hook_args);
3481 } else {
3482 p = iralloct(tsd_tsdn(tsd), old_ptr, old_usize, size, alignment,
3483 zero, tcache, arena, hook_args);
3484 }
3485 if (unlikely(p == NULL)) {
3486 prof_alloc_rollback(tsd, tctx);
3487 return NULL;
3488 }
3489 assert(usize == isalloc(tsd_tsdn(tsd), p));
3490 prof_realloc(tsd, p, size, usize, tctx, prof_active, old_ptr,
3491 old_usize, &old_prof_info, sample_event);
3492
3493 return p;
3494 }
3495
3496 static void *
do_rallocx(void * ptr,size_t size,int flags,bool is_realloc)3497 do_rallocx(void *ptr, size_t size, int flags, bool is_realloc) {
3498 void *p;
3499 tsd_t *tsd;
3500 size_t usize;
3501 size_t old_usize;
3502 size_t alignment = MALLOCX_ALIGN_GET(flags);
3503 arena_t *arena;
3504
3505 assert(ptr != NULL);
3506 assert(size != 0);
3507 assert(malloc_initialized() || IS_INITIALIZER);
3508 tsd = tsd_fetch();
3509 check_entry_exit_locking(tsd_tsdn(tsd));
3510
3511 bool zero = zero_get(MALLOCX_ZERO_GET(flags), /* slow */ true);
3512
3513 unsigned arena_ind = mallocx_arena_get(flags);
3514 if (arena_get_from_ind(tsd, arena_ind, &arena)) {
3515 goto label_oom;
3516 }
3517
3518 unsigned tcache_ind = mallocx_tcache_get(flags);
3519 tcache_t *tcache = tcache_get_from_ind(tsd, tcache_ind,
3520 /* slow */ true, /* is_alloc */ true);
3521
3522 emap_alloc_ctx_t alloc_ctx;
3523 emap_alloc_ctx_lookup(tsd_tsdn(tsd), &arena_emap_global, ptr,
3524 &alloc_ctx);
3525 assert(alloc_ctx.szind != SC_NSIZES);
3526 old_usize = sz_index2size(alloc_ctx.szind);
3527 assert(old_usize == isalloc(tsd_tsdn(tsd), ptr));
3528 if (aligned_usize_get(size, alignment, &usize, NULL, false)) {
3529 goto label_oom;
3530 }
3531
3532 hook_ralloc_args_t hook_args = {is_realloc, {(uintptr_t)ptr, size,
3533 flags, 0}};
3534 if (config_prof && opt_prof) {
3535 p = irallocx_prof(tsd, ptr, old_usize, size, alignment, usize,
3536 zero, tcache, arena, &alloc_ctx, &hook_args);
3537 if (unlikely(p == NULL)) {
3538 goto label_oom;
3539 }
3540 } else {
3541 p = iralloct(tsd_tsdn(tsd), ptr, old_usize, size, alignment,
3542 zero, tcache, arena, &hook_args);
3543 if (unlikely(p == NULL)) {
3544 goto label_oom;
3545 }
3546 assert(usize == isalloc(tsd_tsdn(tsd), p));
3547 }
3548 assert(alignment == 0 || ((uintptr_t)p & (alignment - 1)) == ZU(0));
3549 thread_alloc_event(tsd, usize);
3550 thread_dalloc_event(tsd, old_usize);
3551
3552 UTRACE(ptr, size, p);
3553 check_entry_exit_locking(tsd_tsdn(tsd));
3554
3555 if (config_fill && unlikely(opt_junk_alloc) && usize > old_usize
3556 && !zero) {
3557 size_t excess_len = usize - old_usize;
3558 void *excess_start = (void *)((uintptr_t)p + old_usize);
3559 junk_alloc_callback(excess_start, excess_len);
3560 }
3561
3562 return p;
3563 label_oom:
3564 if (is_realloc) {
3565 set_errno(ENOMEM);
3566 }
3567 if (config_xmalloc && unlikely(opt_xmalloc)) {
3568 malloc_write("<jemalloc>: Error in rallocx(): out of memory\n");
3569 abort();
3570 }
3571 UTRACE(ptr, size, 0);
3572 check_entry_exit_locking(tsd_tsdn(tsd));
3573
3574 return NULL;
3575 }
3576
3577 JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
3578 void JEMALLOC_NOTHROW *
3579 JEMALLOC_ALLOC_SIZE(2)
je_rallocx(void * ptr,size_t size,int flags)3580 je_rallocx(void *ptr, size_t size, int flags) {
3581 LOG("core.rallocx.entry", "ptr: %p, size: %zu, flags: %d", ptr,
3582 size, flags);
3583 void *ret = do_rallocx(ptr, size, flags, false);
3584 LOG("core.rallocx.exit", "result: %p", ret);
3585 return ret;
3586 }
3587
3588 static void *
do_realloc_nonnull_zero(void * ptr)3589 do_realloc_nonnull_zero(void *ptr) {
3590 if (config_stats) {
3591 atomic_fetch_add_zu(&zero_realloc_count, 1, ATOMIC_RELAXED);
3592 }
3593 if (opt_zero_realloc_action == zero_realloc_action_alloc) {
3594 /*
3595 * The user might have gotten an alloc setting while expecting a
3596 * free setting. If that's the case, we at least try to
3597 * reduce the harm, and turn off the tcache while allocating, so
3598 * that we'll get a true first fit.
3599 */
3600 return do_rallocx(ptr, 1, MALLOCX_TCACHE_NONE, true);
3601 } else if (opt_zero_realloc_action == zero_realloc_action_free) {
3602 UTRACE(ptr, 0, 0);
3603 tsd_t *tsd = tsd_fetch();
3604 check_entry_exit_locking(tsd_tsdn(tsd));
3605
3606 tcache_t *tcache = tcache_get_from_ind(tsd,
3607 TCACHE_IND_AUTOMATIC, /* slow */ true,
3608 /* is_alloc */ false);
3609 uintptr_t args[3] = {(uintptr_t)ptr, 0};
3610 hook_invoke_dalloc(hook_dalloc_realloc, ptr, args);
3611 ifree(tsd, ptr, tcache, true);
3612
3613 check_entry_exit_locking(tsd_tsdn(tsd));
3614 return NULL;
3615 } else {
3616 safety_check_fail("Called realloc(non-null-ptr, 0) with "
3617 "zero_realloc:abort set\n");
3618 /* In real code, this will never run; the safety check failure
3619 * will call abort. In the unit test, we just want to bail out
3620 * without corrupting internal state that the test needs to
3621 * finish.
3622 */
3623 return NULL;
3624 }
3625 }
3626
3627 JEMALLOC_EXPORT JEMALLOC_ALLOCATOR JEMALLOC_RESTRICT_RETURN
3628 void JEMALLOC_NOTHROW *
3629 JEMALLOC_ALLOC_SIZE(2)
je_realloc(void * ptr,size_t size)3630 je_realloc(void *ptr, size_t size) {
3631 LOG("core.realloc.entry", "ptr: %p, size: %zu\n", ptr, size);
3632
3633 if (likely(ptr != NULL && size != 0)) {
3634 void *ret = do_rallocx(ptr, size, 0, true);
3635 LOG("core.realloc.exit", "result: %p", ret);
3636 return ret;
3637 } else if (ptr != NULL && size == 0) {
3638 void *ret = do_realloc_nonnull_zero(ptr);
3639 LOG("core.realloc.exit", "result: %p", ret);
3640 return ret;
3641 } else {
3642 /* realloc(NULL, size) is equivalent to malloc(size). */
3643 void *ret;
3644
3645 static_opts_t sopts;
3646 dynamic_opts_t dopts;
3647
3648 static_opts_init(&sopts);
3649 dynamic_opts_init(&dopts);
3650
3651 sopts.null_out_result_on_error = true;
3652 sopts.set_errno_on_error = true;
3653 sopts.oom_string =
3654 "<jemalloc>: Error in realloc(): out of memory\n";
3655
3656 dopts.result = &ret;
3657 dopts.num_items = 1;
3658 dopts.item_size = size;
3659
3660 imalloc(&sopts, &dopts);
3661 if (sopts.slow) {
3662 uintptr_t args[3] = {(uintptr_t)ptr, size};
3663 hook_invoke_alloc(hook_alloc_realloc, ret,
3664 (uintptr_t)ret, args);
3665 }
3666 LOG("core.realloc.exit", "result: %p", ret);
3667 return ret;
3668 }
3669 }
3670
3671 JEMALLOC_ALWAYS_INLINE size_t
ixallocx_helper(tsdn_t * tsdn,void * ptr,size_t old_usize,size_t size,size_t extra,size_t alignment,bool zero)3672 ixallocx_helper(tsdn_t *tsdn, void *ptr, size_t old_usize, size_t size,
3673 size_t extra, size_t alignment, bool zero) {
3674 size_t newsize;
3675
3676 if (ixalloc(tsdn, ptr, old_usize, size, extra, alignment, zero,
3677 &newsize)) {
3678 return old_usize;
3679 }
3680
3681 return newsize;
3682 }
3683
3684 static size_t
ixallocx_prof_sample(tsdn_t * tsdn,void * ptr,size_t old_usize,size_t size,size_t extra,size_t alignment,bool zero,prof_tctx_t * tctx)3685 ixallocx_prof_sample(tsdn_t *tsdn, void *ptr, size_t old_usize, size_t size,
3686 size_t extra, size_t alignment, bool zero, prof_tctx_t *tctx) {
3687 /* Sampled allocation needs to be page aligned. */
3688 if (tctx == NULL || !prof_sample_aligned(ptr)) {
3689 return old_usize;
3690 }
3691
3692 return ixallocx_helper(tsdn, ptr, old_usize, size, extra, alignment,
3693 zero);
3694 }
3695
3696 JEMALLOC_ALWAYS_INLINE size_t
ixallocx_prof(tsd_t * tsd,void * ptr,size_t old_usize,size_t size,size_t extra,size_t alignment,bool zero,emap_alloc_ctx_t * alloc_ctx)3697 ixallocx_prof(tsd_t *tsd, void *ptr, size_t old_usize, size_t size,
3698 size_t extra, size_t alignment, bool zero, emap_alloc_ctx_t *alloc_ctx) {
3699 /*
3700 * old_prof_info is only used for asserting that the profiling info
3701 * isn't changed by the ixalloc() call.
3702 */
3703 prof_info_t old_prof_info;
3704 prof_info_get(tsd, ptr, alloc_ctx, &old_prof_info);
3705
3706 /*
3707 * usize isn't knowable before ixalloc() returns when extra is non-zero.
3708 * Therefore, compute its maximum possible value and use that in
3709 * prof_alloc_prep() to decide whether to capture a backtrace.
3710 * prof_realloc() will use the actual usize to decide whether to sample.
3711 */
3712 size_t usize_max;
3713 if (aligned_usize_get(size + extra, alignment, &usize_max, NULL,
3714 false)) {
3715 /*
3716 * usize_max is out of range, and chances are that allocation
3717 * will fail, but use the maximum possible value and carry on
3718 * with prof_alloc_prep(), just in case allocation succeeds.
3719 */
3720 usize_max = SC_LARGE_MAXCLASS;
3721 }
3722 bool prof_active = prof_active_get_unlocked();
3723 bool sample_event = te_prof_sample_event_lookahead(tsd, usize_max);
3724 prof_tctx_t *tctx = prof_alloc_prep(tsd, prof_active, sample_event);
3725
3726 size_t usize;
3727 if (unlikely((uintptr_t)tctx != (uintptr_t)1U)) {
3728 usize = ixallocx_prof_sample(tsd_tsdn(tsd), ptr, old_usize,
3729 size, extra, alignment, zero, tctx);
3730 } else {
3731 usize = ixallocx_helper(tsd_tsdn(tsd), ptr, old_usize, size,
3732 extra, alignment, zero);
3733 }
3734
3735 /*
3736 * At this point we can still safely get the original profiling
3737 * information associated with the ptr, because (a) the edata_t object
3738 * associated with the ptr still lives and (b) the profiling info
3739 * fields are not touched. "(a)" is asserted in the outer je_xallocx()
3740 * function, and "(b)" is indirectly verified below by checking that
3741 * the alloc_tctx field is unchanged.
3742 */
3743 prof_info_t prof_info;
3744 if (usize == old_usize) {
3745 prof_info_get(tsd, ptr, alloc_ctx, &prof_info);
3746 prof_alloc_rollback(tsd, tctx);
3747 } else {
3748 prof_info_get_and_reset_recent(tsd, ptr, alloc_ctx, &prof_info);
3749 assert(usize <= usize_max);
3750 sample_event = te_prof_sample_event_lookahead(tsd, usize);
3751 prof_realloc(tsd, ptr, size, usize, tctx, prof_active, ptr,
3752 old_usize, &prof_info, sample_event);
3753 }
3754
3755 assert(old_prof_info.alloc_tctx == prof_info.alloc_tctx);
3756 return usize;
3757 }
3758
3759 JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW
je_xallocx(void * ptr,size_t size,size_t extra,int flags)3760 je_xallocx(void *ptr, size_t size, size_t extra, int flags) {
3761 tsd_t *tsd;
3762 size_t usize, old_usize;
3763 size_t alignment = MALLOCX_ALIGN_GET(flags);
3764 bool zero = zero_get(MALLOCX_ZERO_GET(flags), /* slow */ true);
3765
3766 LOG("core.xallocx.entry", "ptr: %p, size: %zu, extra: %zu, "
3767 "flags: %d", ptr, size, extra, flags);
3768
3769 assert(ptr != NULL);
3770 assert(size != 0);
3771 assert(SIZE_T_MAX - size >= extra);
3772 assert(malloc_initialized() || IS_INITIALIZER);
3773 tsd = tsd_fetch();
3774 check_entry_exit_locking(tsd_tsdn(tsd));
3775
3776 /*
3777 * old_edata is only for verifying that xallocx() keeps the edata_t
3778 * object associated with the ptr (though the content of the edata_t
3779 * object can be changed).
3780 */
3781 edata_t *old_edata = emap_edata_lookup(tsd_tsdn(tsd),
3782 &arena_emap_global, ptr);
3783
3784 emap_alloc_ctx_t alloc_ctx;
3785 emap_alloc_ctx_lookup(tsd_tsdn(tsd), &arena_emap_global, ptr,
3786 &alloc_ctx);
3787 assert(alloc_ctx.szind != SC_NSIZES);
3788 old_usize = sz_index2size(alloc_ctx.szind);
3789 assert(old_usize == isalloc(tsd_tsdn(tsd), ptr));
3790 /*
3791 * The API explicitly absolves itself of protecting against (size +
3792 * extra) numerical overflow, but we may need to clamp extra to avoid
3793 * exceeding SC_LARGE_MAXCLASS.
3794 *
3795 * Ordinarily, size limit checking is handled deeper down, but here we
3796 * have to check as part of (size + extra) clamping, since we need the
3797 * clamped value in the above helper functions.
3798 */
3799 if (unlikely(size > SC_LARGE_MAXCLASS)) {
3800 usize = old_usize;
3801 goto label_not_resized;
3802 }
3803 if (unlikely(SC_LARGE_MAXCLASS - size < extra)) {
3804 extra = SC_LARGE_MAXCLASS - size;
3805 }
3806
3807 if (config_prof && opt_prof) {
3808 usize = ixallocx_prof(tsd, ptr, old_usize, size, extra,
3809 alignment, zero, &alloc_ctx);
3810 } else {
3811 usize = ixallocx_helper(tsd_tsdn(tsd), ptr, old_usize, size,
3812 extra, alignment, zero);
3813 }
3814
3815 /*
3816 * xallocx() should keep using the same edata_t object (though its
3817 * content can be changed).
3818 */
3819 assert(emap_edata_lookup(tsd_tsdn(tsd), &arena_emap_global, ptr)
3820 == old_edata);
3821
3822 if (unlikely(usize == old_usize)) {
3823 goto label_not_resized;
3824 }
3825 thread_alloc_event(tsd, usize);
3826 thread_dalloc_event(tsd, old_usize);
3827
3828 if (config_fill && unlikely(opt_junk_alloc) && usize > old_usize &&
3829 !zero) {
3830 size_t excess_len = usize - old_usize;
3831 void *excess_start = (void *)((uintptr_t)ptr + old_usize);
3832 junk_alloc_callback(excess_start, excess_len);
3833 }
3834 label_not_resized:
3835 if (unlikely(!tsd_fast(tsd))) {
3836 uintptr_t args[4] = {(uintptr_t)ptr, size, extra, flags};
3837 hook_invoke_expand(hook_expand_xallocx, ptr, old_usize,
3838 usize, (uintptr_t)usize, args);
3839 }
3840
3841 UTRACE(ptr, size, ptr);
3842 check_entry_exit_locking(tsd_tsdn(tsd));
3843
3844 LOG("core.xallocx.exit", "result: %zu", usize);
3845 return usize;
3846 }
3847
3848 JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW
JEMALLOC_ATTR(pure)3849 JEMALLOC_ATTR(pure)
3850 je_sallocx(const void *ptr, int flags) {
3851 size_t usize;
3852 tsdn_t *tsdn;
3853
3854 LOG("core.sallocx.entry", "ptr: %p, flags: %d", ptr, flags);
3855
3856 assert(malloc_initialized() || IS_INITIALIZER);
3857 assert(ptr != NULL);
3858
3859 tsdn = tsdn_fetch();
3860 check_entry_exit_locking(tsdn);
3861
3862 if (config_debug || force_ivsalloc) {
3863 usize = ivsalloc(tsdn, ptr);
3864 assert(force_ivsalloc || usize != 0);
3865 } else {
3866 usize = isalloc(tsdn, ptr);
3867 }
3868
3869 check_entry_exit_locking(tsdn);
3870
3871 LOG("core.sallocx.exit", "result: %zu", usize);
3872 return usize;
3873 }
3874
3875 JEMALLOC_EXPORT void JEMALLOC_NOTHROW
je_dallocx(void * ptr,int flags)3876 je_dallocx(void *ptr, int flags) {
3877 LOG("core.dallocx.entry", "ptr: %p, flags: %d", ptr, flags);
3878
3879 assert(ptr != NULL);
3880 assert(malloc_initialized() || IS_INITIALIZER);
3881
3882 tsd_t *tsd = tsd_fetch_min();
3883 bool fast = tsd_fast(tsd);
3884 check_entry_exit_locking(tsd_tsdn(tsd));
3885
3886 unsigned tcache_ind = mallocx_tcache_get(flags);
3887 tcache_t *tcache = tcache_get_from_ind(tsd, tcache_ind, !fast,
3888 /* is_alloc */ false);
3889
3890 UTRACE(ptr, 0, 0);
3891 if (likely(fast)) {
3892 tsd_assert_fast(tsd);
3893 ifree(tsd, ptr, tcache, false);
3894 } else {
3895 uintptr_t args_raw[3] = {(uintptr_t)ptr, flags};
3896 hook_invoke_dalloc(hook_dalloc_dallocx, ptr, args_raw);
3897 ifree(tsd, ptr, tcache, true);
3898 }
3899 check_entry_exit_locking(tsd_tsdn(tsd));
3900
3901 LOG("core.dallocx.exit", "");
3902 }
3903
3904 JEMALLOC_ALWAYS_INLINE size_t
inallocx(tsdn_t * tsdn,size_t size,int flags)3905 inallocx(tsdn_t *tsdn, size_t size, int flags) {
3906 check_entry_exit_locking(tsdn);
3907 size_t usize;
3908 /* In case of out of range, let the user see it rather than fail. */
3909 aligned_usize_get(size, MALLOCX_ALIGN_GET(flags), &usize, NULL, false);
3910 check_entry_exit_locking(tsdn);
3911 return usize;
3912 }
3913
3914 JEMALLOC_NOINLINE void
sdallocx_default(void * ptr,size_t size,int flags)3915 sdallocx_default(void *ptr, size_t size, int flags) {
3916 assert(ptr != NULL);
3917 assert(malloc_initialized() || IS_INITIALIZER);
3918
3919 tsd_t *tsd = tsd_fetch_min();
3920 bool fast = tsd_fast(tsd);
3921 size_t usize = inallocx(tsd_tsdn(tsd), size, flags);
3922 check_entry_exit_locking(tsd_tsdn(tsd));
3923
3924 unsigned tcache_ind = mallocx_tcache_get(flags);
3925 tcache_t *tcache = tcache_get_from_ind(tsd, tcache_ind, !fast,
3926 /* is_alloc */ false);
3927
3928 UTRACE(ptr, 0, 0);
3929 if (likely(fast)) {
3930 tsd_assert_fast(tsd);
3931 isfree(tsd, ptr, usize, tcache, false);
3932 } else {
3933 uintptr_t args_raw[3] = {(uintptr_t)ptr, size, flags};
3934 hook_invoke_dalloc(hook_dalloc_sdallocx, ptr, args_raw);
3935 isfree(tsd, ptr, usize, tcache, true);
3936 }
3937 check_entry_exit_locking(tsd_tsdn(tsd));
3938 }
3939
3940 JEMALLOC_EXPORT void JEMALLOC_NOTHROW
je_sdallocx(void * ptr,size_t size,int flags)3941 je_sdallocx(void *ptr, size_t size, int flags) {
3942 LOG("core.sdallocx.entry", "ptr: %p, size: %zu, flags: %d", ptr,
3943 size, flags);
3944
3945 if (flags != 0 || !free_fastpath(ptr, size, true)) {
3946 sdallocx_default(ptr, size, flags);
3947 }
3948
3949 LOG("core.sdallocx.exit", "");
3950 }
3951
3952 void JEMALLOC_NOTHROW
je_sdallocx_noflags(void * ptr,size_t size)3953 je_sdallocx_noflags(void *ptr, size_t size) {
3954 LOG("core.sdallocx.entry", "ptr: %p, size: %zu, flags: 0", ptr,
3955 size);
3956
3957 if (!free_fastpath(ptr, size, true)) {
3958 sdallocx_default(ptr, size, 0);
3959 }
3960
3961 LOG("core.sdallocx.exit", "");
3962 }
3963
3964 JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW
JEMALLOC_ATTR(pure)3965 JEMALLOC_ATTR(pure)
3966 je_nallocx(size_t size, int flags) {
3967 size_t usize;
3968 tsdn_t *tsdn;
3969
3970 assert(size != 0);
3971
3972 if (unlikely(malloc_init())) {
3973 LOG("core.nallocx.exit", "result: %zu", ZU(0));
3974 return 0;
3975 }
3976
3977 tsdn = tsdn_fetch();
3978 check_entry_exit_locking(tsdn);
3979
3980 usize = inallocx(tsdn, size, flags);
3981 if (unlikely(usize > SC_LARGE_MAXCLASS)) {
3982 LOG("core.nallocx.exit", "result: %zu", ZU(0));
3983 return 0;
3984 }
3985
3986 check_entry_exit_locking(tsdn);
3987 LOG("core.nallocx.exit", "result: %zu", usize);
3988 return usize;
3989 }
3990
3991 JEMALLOC_EXPORT int JEMALLOC_NOTHROW
je_mallctl(const char * name,void * oldp,size_t * oldlenp,void * newp,size_t newlen)3992 je_mallctl(const char *name, void *oldp, size_t *oldlenp, void *newp,
3993 size_t newlen) {
3994 int ret;
3995 tsd_t *tsd;
3996
3997 LOG("core.mallctl.entry", "name: %s", name);
3998
3999 if (unlikely(malloc_init())) {
4000 LOG("core.mallctl.exit", "result: %d", EAGAIN);
4001 return EAGAIN;
4002 }
4003
4004 tsd = tsd_fetch();
4005 check_entry_exit_locking(tsd_tsdn(tsd));
4006 ret = ctl_byname(tsd, name, oldp, oldlenp, newp, newlen);
4007 check_entry_exit_locking(tsd_tsdn(tsd));
4008
4009 LOG("core.mallctl.exit", "result: %d", ret);
4010 return ret;
4011 }
4012
4013 JEMALLOC_EXPORT int JEMALLOC_NOTHROW
je_mallctlnametomib(const char * name,size_t * mibp,size_t * miblenp)4014 je_mallctlnametomib(const char *name, size_t *mibp, size_t *miblenp) {
4015 int ret;
4016
4017 LOG("core.mallctlnametomib.entry", "name: %s", name);
4018
4019 if (unlikely(malloc_init())) {
4020 LOG("core.mallctlnametomib.exit", "result: %d", EAGAIN);
4021 return EAGAIN;
4022 }
4023
4024 tsd_t *tsd = tsd_fetch();
4025 check_entry_exit_locking(tsd_tsdn(tsd));
4026 ret = ctl_nametomib(tsd, name, mibp, miblenp);
4027 check_entry_exit_locking(tsd_tsdn(tsd));
4028
4029 LOG("core.mallctlnametomib.exit", "result: %d", ret);
4030 return ret;
4031 }
4032
4033 JEMALLOC_EXPORT int JEMALLOC_NOTHROW
je_mallctlbymib(const size_t * mib,size_t miblen,void * oldp,size_t * oldlenp,void * newp,size_t newlen)4034 je_mallctlbymib(const size_t *mib, size_t miblen, void *oldp, size_t *oldlenp,
4035 void *newp, size_t newlen) {
4036 int ret;
4037 tsd_t *tsd;
4038
4039 LOG("core.mallctlbymib.entry", "");
4040
4041 if (unlikely(malloc_init())) {
4042 LOG("core.mallctlbymib.exit", "result: %d", EAGAIN);
4043 return EAGAIN;
4044 }
4045
4046 tsd = tsd_fetch();
4047 check_entry_exit_locking(tsd_tsdn(tsd));
4048 ret = ctl_bymib(tsd, mib, miblen, oldp, oldlenp, newp, newlen);
4049 check_entry_exit_locking(tsd_tsdn(tsd));
4050 LOG("core.mallctlbymib.exit", "result: %d", ret);
4051 return ret;
4052 }
4053
4054 #define STATS_PRINT_BUFSIZE 65536
4055 JEMALLOC_EXPORT void JEMALLOC_NOTHROW
je_malloc_stats_print(void (* write_cb)(void *,const char *),void * cbopaque,const char * opts)4056 je_malloc_stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
4057 const char *opts) {
4058 tsdn_t *tsdn;
4059
4060 LOG("core.malloc_stats_print.entry", "");
4061
4062 tsdn = tsdn_fetch();
4063 check_entry_exit_locking(tsdn);
4064
4065 if (config_debug) {
4066 stats_print(write_cb, cbopaque, opts);
4067 } else {
4068 buf_writer_t buf_writer;
4069 buf_writer_init(tsdn, &buf_writer, write_cb, cbopaque, NULL,
4070 STATS_PRINT_BUFSIZE);
4071 stats_print(buf_writer_cb, &buf_writer, opts);
4072 buf_writer_terminate(tsdn, &buf_writer);
4073 }
4074
4075 check_entry_exit_locking(tsdn);
4076 LOG("core.malloc_stats_print.exit", "");
4077 }
4078 #undef STATS_PRINT_BUFSIZE
4079
4080 JEMALLOC_ALWAYS_INLINE size_t
je_malloc_usable_size_impl(JEMALLOC_USABLE_SIZE_CONST void * ptr)4081 je_malloc_usable_size_impl(JEMALLOC_USABLE_SIZE_CONST void *ptr) {
4082 assert(malloc_initialized() || IS_INITIALIZER);
4083
4084 tsdn_t *tsdn = tsdn_fetch();
4085 check_entry_exit_locking(tsdn);
4086
4087 size_t ret;
4088 if (unlikely(ptr == NULL)) {
4089 ret = 0;
4090 } else {
4091 if (config_debug || force_ivsalloc) {
4092 ret = ivsalloc(tsdn, ptr);
4093 assert(force_ivsalloc || ret != 0);
4094 } else {
4095 ret = isalloc(tsdn, ptr);
4096 }
4097 }
4098 check_entry_exit_locking(tsdn);
4099
4100 return ret;
4101 }
4102
4103 JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW
je_malloc_usable_size(JEMALLOC_USABLE_SIZE_CONST void * ptr)4104 je_malloc_usable_size(JEMALLOC_USABLE_SIZE_CONST void *ptr) {
4105 LOG("core.malloc_usable_size.entry", "ptr: %p", ptr);
4106
4107 size_t ret = je_malloc_usable_size_impl(ptr);
4108
4109 LOG("core.malloc_usable_size.exit", "result: %zu", ret);
4110 return ret;
4111 }
4112
4113 #ifdef JEMALLOC_HAVE_MALLOC_SIZE
4114 JEMALLOC_EXPORT size_t JEMALLOC_NOTHROW
je_malloc_size(const void * ptr)4115 je_malloc_size(const void *ptr) {
4116 LOG("core.malloc_size.entry", "ptr: %p", ptr);
4117
4118 size_t ret = je_malloc_usable_size_impl(ptr);
4119
4120 LOG("core.malloc_size.exit", "result: %zu", ret);
4121 return ret;
4122 }
4123 #endif
4124
4125 static void
batch_alloc_prof_sample_assert(tsd_t * tsd,size_t batch,size_t usize)4126 batch_alloc_prof_sample_assert(tsd_t *tsd, size_t batch, size_t usize) {
4127 assert(config_prof && opt_prof);
4128 bool prof_sample_event = te_prof_sample_event_lookahead(tsd,
4129 batch * usize);
4130 assert(!prof_sample_event);
4131 size_t surplus;
4132 prof_sample_event = te_prof_sample_event_lookahead_surplus(tsd,
4133 (batch + 1) * usize, &surplus);
4134 assert(prof_sample_event);
4135 assert(surplus < usize);
4136 }
4137
4138 size_t
batch_alloc(void ** ptrs,size_t num,size_t size,int flags)4139 batch_alloc(void **ptrs, size_t num, size_t size, int flags) {
4140 LOG("core.batch_alloc.entry",
4141 "ptrs: %p, num: %zu, size: %zu, flags: %d", ptrs, num, size, flags);
4142
4143 tsd_t *tsd = tsd_fetch();
4144 check_entry_exit_locking(tsd_tsdn(tsd));
4145
4146 size_t filled = 0;
4147
4148 if (unlikely(tsd == NULL || tsd_reentrancy_level_get(tsd) > 0)) {
4149 goto label_done;
4150 }
4151
4152 size_t alignment = MALLOCX_ALIGN_GET(flags);
4153 size_t usize;
4154 if (aligned_usize_get(size, alignment, &usize, NULL, false)) {
4155 goto label_done;
4156 }
4157 szind_t ind = sz_size2index(usize);
4158 bool zero = zero_get(MALLOCX_ZERO_GET(flags), /* slow */ true);
4159
4160 /*
4161 * The cache bin and arena will be lazily initialized; it's hard to
4162 * know in advance whether each of them needs to be initialized.
4163 */
4164 cache_bin_t *bin = NULL;
4165 arena_t *arena = NULL;
4166
4167 size_t nregs = 0;
4168 if (likely(ind < SC_NBINS)) {
4169 nregs = bin_infos[ind].nregs;
4170 assert(nregs > 0);
4171 }
4172
4173 while (filled < num) {
4174 size_t batch = num - filled;
4175 size_t surplus = SIZE_MAX; /* Dead store. */
4176 bool prof_sample_event = config_prof && opt_prof
4177 && prof_active_get_unlocked()
4178 && te_prof_sample_event_lookahead_surplus(tsd,
4179 batch * usize, &surplus);
4180
4181 if (prof_sample_event) {
4182 /*
4183 * Adjust so that the batch does not trigger prof
4184 * sampling.
4185 */
4186 batch -= surplus / usize + 1;
4187 batch_alloc_prof_sample_assert(tsd, batch, usize);
4188 }
4189
4190 size_t progress = 0;
4191
4192 if (likely(ind < SC_NBINS) && batch >= nregs) {
4193 if (arena == NULL) {
4194 unsigned arena_ind = mallocx_arena_get(flags);
4195 if (arena_get_from_ind(tsd, arena_ind,
4196 &arena)) {
4197 goto label_done;
4198 }
4199 if (arena == NULL) {
4200 arena = arena_choose(tsd, NULL);
4201 }
4202 if (unlikely(arena == NULL)) {
4203 goto label_done;
4204 }
4205 }
4206 size_t arena_batch = batch - batch % nregs;
4207 size_t n = arena_fill_small_fresh(tsd_tsdn(tsd), arena,
4208 ind, ptrs + filled, arena_batch, zero);
4209 progress += n;
4210 filled += n;
4211 }
4212
4213 if (likely(ind < nhbins) && progress < batch) {
4214 if (bin == NULL) {
4215 unsigned tcache_ind = mallocx_tcache_get(flags);
4216 tcache_t *tcache = tcache_get_from_ind(tsd,
4217 tcache_ind, /* slow */ true,
4218 /* is_alloc */ true);
4219 if (tcache != NULL) {
4220 bin = &tcache->bins[ind];
4221 }
4222 }
4223 /*
4224 * If we don't have a tcache bin, we don't want to
4225 * immediately give up, because there's the possibility
4226 * that the user explicitly requested to bypass the
4227 * tcache, or that the user explicitly turned off the
4228 * tcache; in such cases, we go through the slow path,
4229 * i.e. the mallocx() call at the end of the while loop.
4230 */
4231 if (bin != NULL) {
4232 size_t bin_batch = batch - progress;
4233 /*
4234 * n can be less than bin_batch, meaning that
4235 * the cache bin does not have enough memory.
4236 * In such cases, we rely on the slow path,
4237 * i.e. the mallocx() call at the end of the
4238 * while loop, to fill in the cache, and in the
4239 * next iteration of the while loop, the tcache
4240 * will contain a lot of memory, and we can
4241 * harvest them here. Compared to the
4242 * alternative approach where we directly go to
4243 * the arena bins here, the overhead of our
4244 * current approach should usually be minimal,
4245 * since we never try to fetch more memory than
4246 * what a slab contains via the tcache. An
4247 * additional benefit is that the tcache will
4248 * not be empty for the next allocation request.
4249 */
4250 size_t n = cache_bin_alloc_batch(bin, bin_batch,
4251 ptrs + filled);
4252 if (config_stats) {
4253 bin->tstats.nrequests += n;
4254 }
4255 if (zero) {
4256 for (size_t i = 0; i < n; ++i) {
4257 memset(ptrs[filled + i], 0,
4258 usize);
4259 }
4260 }
4261 if (config_prof && opt_prof
4262 && unlikely(ind >= SC_NBINS)) {
4263 for (size_t i = 0; i < n; ++i) {
4264 prof_tctx_reset_sampled(tsd,
4265 ptrs[filled + i]);
4266 }
4267 }
4268 progress += n;
4269 filled += n;
4270 }
4271 }
4272
4273 /*
4274 * For thread events other than prof sampling, trigger them as
4275 * if there's a single allocation of size (n * usize). This is
4276 * fine because:
4277 * (a) these events do not alter the allocation itself, and
4278 * (b) it's possible that some event would have been triggered
4279 * multiple times, instead of only once, if the allocations
4280 * were handled individually, but it would do no harm (or
4281 * even be beneficial) to coalesce the triggerings.
4282 */
4283 thread_alloc_event(tsd, progress * usize);
4284
4285 if (progress < batch || prof_sample_event) {
4286 void *p = je_mallocx(size, flags);
4287 if (p == NULL) { /* OOM */
4288 break;
4289 }
4290 if (progress == batch) {
4291 assert(prof_sampled(tsd, p));
4292 }
4293 ptrs[filled++] = p;
4294 }
4295 }
4296
4297 label_done:
4298 check_entry_exit_locking(tsd_tsdn(tsd));
4299 LOG("core.batch_alloc.exit", "result: %zu", filled);
4300 return filled;
4301 }
4302
4303 /*
4304 * End non-standard functions.
4305 */
4306 /******************************************************************************/
4307 /*
4308 * Begin compatibility functions.
4309 */
4310
4311 #define ALLOCM_LG_ALIGN(la) (la)
4312 #define ALLOCM_ALIGN(a) (ffsl(a)-1)
4313 #define ALLOCM_ZERO ((int)0x40)
4314 #define ALLOCM_NO_MOVE ((int)0x80)
4315
4316 #define ALLOCM_SUCCESS 0
4317 #define ALLOCM_ERR_OOM 1
4318 #define ALLOCM_ERR_NOT_MOVED 2
4319
4320 int
je_allocm(void ** ptr,size_t * rsize,size_t size,int flags)4321 je_allocm(void **ptr, size_t *rsize, size_t size, int flags) {
4322 assert(ptr != NULL);
4323
4324 void *p = je_mallocx(size, flags);
4325 if (p == NULL) {
4326 return (ALLOCM_ERR_OOM);
4327 }
4328 if (rsize != NULL) {
4329 *rsize = isalloc(tsdn_fetch(), p);
4330 }
4331 *ptr = p;
4332 return ALLOCM_SUCCESS;
4333 }
4334
4335 int
je_rallocm(void ** ptr,size_t * rsize,size_t size,size_t extra,int flags)4336 je_rallocm(void **ptr, size_t *rsize, size_t size, size_t extra, int flags) {
4337 assert(ptr != NULL);
4338 assert(*ptr != NULL);
4339 assert(size != 0);
4340 assert(SIZE_T_MAX - size >= extra);
4341
4342 int ret;
4343 bool no_move = flags & ALLOCM_NO_MOVE;
4344
4345 if (no_move) {
4346 size_t usize = je_xallocx(*ptr, size, extra, flags);
4347 ret = (usize >= size) ? ALLOCM_SUCCESS : ALLOCM_ERR_NOT_MOVED;
4348 if (rsize != NULL) {
4349 *rsize = usize;
4350 }
4351 } else {
4352 void *p = je_rallocx(*ptr, size+extra, flags);
4353 if (p != NULL) {
4354 *ptr = p;
4355 ret = ALLOCM_SUCCESS;
4356 } else {
4357 ret = ALLOCM_ERR_OOM;
4358 }
4359 if (rsize != NULL) {
4360 *rsize = isalloc(tsdn_fetch(), *ptr);
4361 }
4362 }
4363 return ret;
4364 }
4365
4366 int
je_sallocm(const void * ptr,size_t * rsize,int flags)4367 je_sallocm(const void *ptr, size_t *rsize, int flags) {
4368 assert(rsize != NULL);
4369 *rsize = je_sallocx(ptr, flags);
4370 return ALLOCM_SUCCESS;
4371 }
4372
4373 int
je_dallocm(void * ptr,int flags)4374 je_dallocm(void *ptr, int flags) {
4375 je_dallocx(ptr, flags);
4376 return ALLOCM_SUCCESS;
4377 }
4378
4379 int
je_nallocm(size_t * rsize,size_t size,int flags)4380 je_nallocm(size_t *rsize, size_t size, int flags) {
4381 size_t usize = je_nallocx(size, flags);
4382 if (usize == 0) {
4383 return ALLOCM_ERR_OOM;
4384 }
4385 if (rsize != NULL) {
4386 *rsize = usize;
4387 }
4388 return ALLOCM_SUCCESS;
4389 }
4390
4391 #undef ALLOCM_LG_ALIGN
4392 #undef ALLOCM_ALIGN
4393 #undef ALLOCM_ZERO
4394 #undef ALLOCM_NO_MOVE
4395
4396 #undef ALLOCM_SUCCESS
4397 #undef ALLOCM_ERR_OOM
4398 #undef ALLOCM_ERR_NOT_MOVED
4399
4400 /*
4401 * End compatibility functions.
4402 */
4403 /******************************************************************************/
4404 /*
4405 * The following functions are used by threading libraries for protection of
4406 * malloc during fork().
4407 */
4408
4409 /*
4410 * If an application creates a thread before doing any allocation in the main
4411 * thread, then calls fork(2) in the main thread followed by memory allocation
4412 * in the child process, a race can occur that results in deadlock within the
4413 * child: the main thread may have forked while the created thread had
4414 * partially initialized the allocator. Ordinarily jemalloc prevents
4415 * fork/malloc races via the following functions it registers during
4416 * initialization using pthread_atfork(), but of course that does no good if
4417 * the allocator isn't fully initialized at fork time. The following library
4418 * constructor is a partial solution to this problem. It may still be possible
4419 * to trigger the deadlock described above, but doing so would involve forking
4420 * via a library constructor that runs before jemalloc's runs.
4421 */
4422 #ifndef JEMALLOC_JET
JEMALLOC_ATTR(constructor)4423 JEMALLOC_ATTR(constructor)
4424 static void
4425 jemalloc_constructor(void) {
4426 malloc_init();
4427 }
4428 #endif
4429
4430 #ifndef JEMALLOC_MUTEX_INIT_CB
4431 void
jemalloc_prefork(void)4432 jemalloc_prefork(void)
4433 #else
4434 JEMALLOC_EXPORT void
4435 _malloc_prefork(void)
4436 #endif
4437 {
4438 tsd_t *tsd;
4439 unsigned i, j, narenas;
4440 arena_t *arena;
4441
4442 #ifdef JEMALLOC_MUTEX_INIT_CB
4443 if (!malloc_initialized()) {
4444 return;
4445 }
4446 #endif
4447 assert(malloc_initialized());
4448
4449 tsd = tsd_fetch();
4450
4451 narenas = narenas_total_get();
4452
4453 witness_prefork(tsd_witness_tsdp_get(tsd));
4454 /* Acquire all mutexes in a safe order. */
4455 ctl_prefork(tsd_tsdn(tsd));
4456 tcache_prefork(tsd_tsdn(tsd));
4457 malloc_mutex_prefork(tsd_tsdn(tsd), &arenas_lock);
4458 if (have_background_thread) {
4459 background_thread_prefork0(tsd_tsdn(tsd));
4460 }
4461 prof_prefork0(tsd_tsdn(tsd));
4462 if (have_background_thread) {
4463 background_thread_prefork1(tsd_tsdn(tsd));
4464 }
4465 /* Break arena prefork into stages to preserve lock order. */
4466 for (i = 0; i < 9; i++) {
4467 for (j = 0; j < narenas; j++) {
4468 if ((arena = arena_get(tsd_tsdn(tsd), j, false)) !=
4469 NULL) {
4470 switch (i) {
4471 case 0:
4472 arena_prefork0(tsd_tsdn(tsd), arena);
4473 break;
4474 case 1:
4475 arena_prefork1(tsd_tsdn(tsd), arena);
4476 break;
4477 case 2:
4478 arena_prefork2(tsd_tsdn(tsd), arena);
4479 break;
4480 case 3:
4481 arena_prefork3(tsd_tsdn(tsd), arena);
4482 break;
4483 case 4:
4484 arena_prefork4(tsd_tsdn(tsd), arena);
4485 break;
4486 case 5:
4487 arena_prefork5(tsd_tsdn(tsd), arena);
4488 break;
4489 case 6:
4490 arena_prefork6(tsd_tsdn(tsd), arena);
4491 break;
4492 case 7:
4493 arena_prefork7(tsd_tsdn(tsd), arena);
4494 break;
4495 case 8:
4496 arena_prefork8(tsd_tsdn(tsd), arena);
4497 break;
4498 default: not_reached();
4499 }
4500 }
4501 }
4502
4503 }
4504 prof_prefork1(tsd_tsdn(tsd));
4505 stats_prefork(tsd_tsdn(tsd));
4506 tsd_prefork(tsd);
4507 }
4508
4509 #ifndef JEMALLOC_MUTEX_INIT_CB
4510 void
jemalloc_postfork_parent(void)4511 jemalloc_postfork_parent(void)
4512 #else
4513 JEMALLOC_EXPORT void
4514 _malloc_postfork(void)
4515 #endif
4516 {
4517 tsd_t *tsd;
4518 unsigned i, narenas;
4519
4520 #ifdef JEMALLOC_MUTEX_INIT_CB
4521 if (!malloc_initialized()) {
4522 return;
4523 }
4524 #endif
4525 assert(malloc_initialized());
4526
4527 tsd = tsd_fetch();
4528
4529 tsd_postfork_parent(tsd);
4530
4531 witness_postfork_parent(tsd_witness_tsdp_get(tsd));
4532 /* Release all mutexes, now that fork() has completed. */
4533 stats_postfork_parent(tsd_tsdn(tsd));
4534 for (i = 0, narenas = narenas_total_get(); i < narenas; i++) {
4535 arena_t *arena;
4536
4537 if ((arena = arena_get(tsd_tsdn(tsd), i, false)) != NULL) {
4538 arena_postfork_parent(tsd_tsdn(tsd), arena);
4539 }
4540 }
4541 prof_postfork_parent(tsd_tsdn(tsd));
4542 if (have_background_thread) {
4543 background_thread_postfork_parent(tsd_tsdn(tsd));
4544 }
4545 malloc_mutex_postfork_parent(tsd_tsdn(tsd), &arenas_lock);
4546 tcache_postfork_parent(tsd_tsdn(tsd));
4547 ctl_postfork_parent(tsd_tsdn(tsd));
4548 }
4549
4550 void
jemalloc_postfork_child(void)4551 jemalloc_postfork_child(void) {
4552 tsd_t *tsd;
4553 unsigned i, narenas;
4554
4555 assert(malloc_initialized());
4556
4557 tsd = tsd_fetch();
4558
4559 tsd_postfork_child(tsd);
4560
4561 witness_postfork_child(tsd_witness_tsdp_get(tsd));
4562 /* Release all mutexes, now that fork() has completed. */
4563 stats_postfork_child(tsd_tsdn(tsd));
4564 for (i = 0, narenas = narenas_total_get(); i < narenas; i++) {
4565 arena_t *arena;
4566
4567 if ((arena = arena_get(tsd_tsdn(tsd), i, false)) != NULL) {
4568 arena_postfork_child(tsd_tsdn(tsd), arena);
4569 }
4570 }
4571 prof_postfork_child(tsd_tsdn(tsd));
4572 if (have_background_thread) {
4573 background_thread_postfork_child(tsd_tsdn(tsd));
4574 }
4575 malloc_mutex_postfork_child(tsd_tsdn(tsd), &arenas_lock);
4576 tcache_postfork_child(tsd_tsdn(tsd));
4577 ctl_postfork_child(tsd_tsdn(tsd));
4578 }
4579
4580 void
_malloc_first_thread(void)4581 _malloc_first_thread(void)
4582 {
4583
4584 (void)malloc_mutex_first_thread();
4585 }
4586
4587 /******************************************************************************/
4588