1 #include <stdlib.h>
2 #include <stdint.h>
3 #include <limits.h>
4 #include <string.h>
5 #include <sys/mman.h>
6 #ifndef __LITEOS__
7 #include <sys/prctl.h>
8 #endif
9 #include <errno.h>
10
11 #include "meta.h"
12
13 #ifdef USE_JEMALLOC
14 #ifdef USE_JEMALLOC_DFX_INTF
15 extern void je_malloc_disable();
16 extern void je_malloc_enable();
17 extern int je_iterate(uintptr_t base, size_t size,
18 void (*callback)(uintptr_t ptr, size_t size, void* arg), void* arg);
19 extern int je_mallopt(int param, int value);
20 #endif
21 extern int je_malloc_check_from_ptr(void *ptr);
22 #endif
23
24 #ifdef MALLOC_SECURE_ALL
25 #include <fcntl.h>
26 #define RANDOM_BUFFER_LEN 512
27 static uint8_t buffer[RANDOM_BUFFER_LEN] = { 0 };
28 static size_t ri = RANDOM_BUFFER_LEN;
29
get_random8()30 static uint8_t get_random8()
31 {
32 uint8_t num;
33 if ((ri >= RANDOM_BUFFER_LEN) || (buffer[0] == 0)) {
34 int fd = open("/dev/urandom", O_RDONLY);
35 if (fd < 0) {
36 num = (uint8_t)get_random_secret();
37 return num;
38 }
39
40 read(fd, buffer, RANDOM_BUFFER_LEN);
41 close(fd);
42 ri = 0;
43 }
44 num = buffer[ri];
45 ri++;
46 return num;
47 }
48
get_randomIdx(int avail_mask,int last_idx)49 static int get_randomIdx(int avail_mask, int last_idx)
50 {
51 uint32_t mask;
52 uint32_t r;
53 uint32_t cmask;
54 int idx;
55
56 mask = avail_mask;
57 r = get_random8() % last_idx;
58 cmask = ~((2u << (last_idx - r)) - 1);
59
60 if (mask & cmask) {
61 idx = 31 - a_clz_32(mask & cmask);
62 } else {
63 idx = a_ctz_32(mask);
64 }
65
66 return idx;
67 }
68 #endif
69
70 LOCK_OBJ_DEF;
71
72 const uint16_t size_classes[] = {
73 1, 2, 3, 4, 5, 6, 7, 8,
74 9, 10, 12, 15,
75 18, 20, 25, 31,
76 36, 42, 50, 63,
77 72, 84, 102, 127,
78 146, 170, 204, 255,
79 292, 340, 409, 511,
80 584, 682, 818, 1023,
81 1169, 1364, 1637, 2047,
82 2340, 2730, 3276, 4095,
83 4680, 5460, 6552, 8191,
84 };
85
86 static const uint8_t small_cnt_tab[][3] = {
87 { 30, 30, 30 },
88 { 31, 15, 15 },
89 { 20, 10, 10 },
90 { 31, 15, 7 },
91 { 25, 12, 6 },
92 { 21, 10, 5 },
93 { 18, 8, 4 },
94 { 31, 15, 7 },
95 { 28, 14, 6 },
96 };
97
98 static const uint8_t med_cnt_tab[4] = { 28, 24, 20, 32 };
99
100 struct malloc_context ctx = { 0 };
101
alloc_meta(void)102 struct meta *alloc_meta(void)
103 {
104 struct meta *m;
105 unsigned char *p;
106 if (!ctx.init_done) {
107 #ifndef PAGESIZE
108 ctx.pagesize = get_page_size();
109 #endif
110 ctx.secret = get_random_secret();
111 ctx.init_done = 1;
112 }
113 size_t pagesize = PGSZ;
114 if (pagesize < 4096) pagesize = 4096;
115 if ((m = dequeue_head(&ctx.free_meta_head))) return m;
116 if (!ctx.avail_meta_count) {
117 int need_unprotect = 1;
118 if (!ctx.avail_meta_area_count && ctx.brk!=-1) {
119 uintptr_t new = ctx.brk + pagesize;
120 int need_guard = 0;
121 if (!ctx.brk) {
122 need_guard = 1;
123 ctx.brk = brk(0);
124 // some ancient kernels returned _ebss
125 // instead of next page as initial brk.
126 ctx.brk += -ctx.brk & (pagesize-1);
127 new = ctx.brk + 2*pagesize;
128 }
129 if (brk(new) != new) {
130 ctx.brk = -1;
131 } else {
132 #ifndef __LITEOS__
133 prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, ctx.brk, new - ctx.brk, "native_heap:meta");
134 #endif
135 if (need_guard) mmap((void *)ctx.brk, pagesize,
136 PROT_NONE, MAP_ANON|MAP_PRIVATE|MAP_FIXED, -1, 0);
137 ctx.brk = new;
138 ctx.avail_meta_areas = (void *)(new - pagesize);
139 ctx.avail_meta_area_count = pagesize>>12;
140 need_unprotect = 0;
141 }
142 }
143 if (!ctx.avail_meta_area_count) {
144 size_t n = 2UL << ctx.meta_alloc_shift;
145 p = mmap(0, n*pagesize, PROT_NONE,
146 MAP_PRIVATE|MAP_ANON, -1, 0);
147 if (p==MAP_FAILED) return 0;
148 ctx.avail_meta_areas = p + pagesize;
149 ctx.avail_meta_area_count = (n-1)*(pagesize>>12);
150 ctx.meta_alloc_shift++;
151 }
152 p = ctx.avail_meta_areas;
153 if ((uintptr_t)p & (pagesize-1)) need_unprotect = 0;
154 if (need_unprotect)
155 if (mprotect(p, pagesize, PROT_READ|PROT_WRITE)
156 && errno != ENOSYS)
157 return 0;
158 ctx.avail_meta_area_count--;
159 ctx.avail_meta_areas = p + 4096;
160 if (ctx.meta_area_tail) {
161 ctx.meta_area_tail->next = (void *)p;
162 } else {
163 ctx.meta_area_head = (void *)p;
164 }
165 ctx.meta_area_tail = (void *)p;
166 ctx.meta_area_tail->check = ctx.secret;
167 ctx.avail_meta_count = ctx.meta_area_tail->nslots
168 = (4096-sizeof(struct meta_area))/sizeof *m;
169 ctx.avail_meta = ctx.meta_area_tail->slots;
170 }
171 ctx.avail_meta_count--;
172 m = ctx.avail_meta++;
173 m->prev = m->next = 0;
174 return m;
175 }
176
try_avail(struct meta ** pm)177 static uint32_t try_avail(struct meta **pm)
178 {
179 struct meta *m = *pm;
180 uint32_t first;
181 if (!m) return 0;
182 uint32_t mask = m->avail_mask;
183 if (!mask) {
184 if (!m) return 0;
185 if (!m->freed_mask) {
186 dequeue(pm, m);
187 m = *pm;
188 if (!m) return 0;
189 } else {
190 m = m->next;
191 *pm = m;
192 }
193
194 mask = m->freed_mask;
195
196 // skip fully-free group unless it's the only one
197 // or it's a permanently non-freeable group
198 if (mask == (2u<<m->last_idx)-1 && m->freeable) {
199 m = m->next;
200 *pm = m;
201 mask = m->freed_mask;
202 }
203
204 // activate more slots in a not-fully-active group
205 // if needed, but only as a last resort. prefer using
206 // any other group with free slots. this avoids
207 // touching & dirtying as-yet-unused pages.
208 if (!(mask & ((2u<<m->mem->active_idx)-1))) {
209 if (m->next != m) {
210 m = m->next;
211 *pm = m;
212 } else {
213 int cnt = m->mem->active_idx + 2;
214 int size = size_classes[m->sizeclass]*UNIT;
215 int span = UNIT + size*cnt;
216 // activate up to next 4k boundary
217 while ((span^(span+size-1)) < 4096) {
218 cnt++;
219 span += size;
220 }
221 if (cnt > m->last_idx+1)
222 cnt = m->last_idx+1;
223 m->mem->active_idx = cnt-1;
224 }
225 }
226 mask = activate_group(m);
227 assert(mask);
228 decay_bounces(m->sizeclass);
229 }
230 #ifdef MALLOC_SECURE_ALL
231 int idx = get_randomIdx(mask, m->last_idx);
232 first = 1 << idx;
233 #else
234 first = mask&-mask;
235 #endif
236 m->avail_mask = mask-first;
237 return first;
238 }
239
240 static int alloc_slot(int, size_t);
241
alloc_group(int sc,size_t req)242 static struct meta *alloc_group(int sc, size_t req)
243 {
244 size_t size = UNIT*size_classes[sc];
245 int i = 0, cnt;
246 unsigned char *p;
247 struct meta *m = alloc_meta();
248 if (!m) return 0;
249 size_t usage = ctx.usage_by_class[sc];
250 size_t pagesize = PGSZ;
251 int active_idx;
252 if (sc < 9) {
253 while (i<2 && 4*small_cnt_tab[sc][i] > usage)
254 i++;
255 cnt = small_cnt_tab[sc][i];
256 } else {
257 // lookup max number of slots fitting in power-of-two size
258 // from a table, along with number of factors of two we
259 // can divide out without a remainder or reaching 1.
260 cnt = med_cnt_tab[sc&3];
261
262 // reduce cnt to avoid excessive eagar allocation.
263 while (!(cnt&1) && 4*cnt > usage)
264 cnt >>= 1;
265
266 // data structures don't support groups whose slot offsets
267 // in units don't fit in 16 bits.
268 while (size*cnt >= 65536*UNIT)
269 cnt >>= 1;
270 }
271
272 // If we selected a count of 1 above but it's not sufficient to use
273 // mmap, increase to 2. Then it might be; if not it will nest.
274 if (cnt==1 && size*cnt+UNIT <= pagesize/2) cnt = 2;
275
276 // All choices of size*cnt are "just below" a power of two, so anything
277 // larger than half the page size should be allocated as whole pages.
278 if (size*cnt+UNIT > pagesize/2) {
279 // check/update bounce counter to start/increase retention
280 // of freed maps, and inhibit use of low-count, odd-size
281 // small mappings and single-slot groups if activated.
282 int nosmall = is_bouncing(sc);
283 account_bounce(sc);
284 step_seq();
285
286 // since the following count reduction opportunities have
287 // an absolute memory usage cost, don't overdo them. count
288 // coarse usage as part of usage.
289 if (!(sc&1) && sc<32) usage += ctx.usage_by_class[sc+1];
290
291 // try to drop to a lower count if the one found above
292 // increases usage by more than 25%. these reduced counts
293 // roughly fill an integral number of pages, just not a
294 // power of two, limiting amount of unusable space.
295 if (4*cnt > usage && !nosmall) {
296 if (0);
297 else if ((sc&3)==1 && size*cnt>8*pagesize) cnt = 2;
298 else if ((sc&3)==2 && size*cnt>4*pagesize) cnt = 3;
299 else if ((sc&3)==0 && size*cnt>8*pagesize) cnt = 3;
300 else if ((sc&3)==0 && size*cnt>2*pagesize) cnt = 5;
301 }
302 size_t needed = size*cnt + UNIT;
303 needed += -needed & (pagesize-1);
304
305 // produce an individually-mmapped allocation if usage is low,
306 // bounce counter hasn't triggered, and either it saves memory
307 // or it avoids eagar slot allocation without wasting too much.
308 if (!nosmall && cnt<=7) {
309 req += IB + UNIT;
310 req += -req & (pagesize-1);
311 if (req<size+UNIT || (req>=4*pagesize && 2*cnt>usage)) {
312 cnt = 1;
313 needed = req;
314 }
315 }
316
317 p = mmap(0, needed, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
318 if (p==MAP_FAILED) {
319 free_meta(m);
320 return 0;
321 }
322 #ifndef __LITEOS__
323 prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, p, needed, "native_heap:brk");
324 #endif
325 m->maplen = needed>>12;
326 ctx.mmap_counter++;
327 active_idx = (4096-UNIT)/size-1;
328 if (active_idx > cnt-1) active_idx = cnt-1;
329 if (active_idx < 0) active_idx = 0;
330 } else {
331 int j = size_to_class(UNIT+cnt*size-IB);
332 int idx = alloc_slot(j, UNIT+cnt*size-IB);
333 if (idx < 0) {
334 free_meta(m);
335 return 0;
336 }
337 struct meta *g = ctx.active[j];
338 p = enframe(g, idx, UNIT*size_classes[j]-IB, ctx.mmap_counter);
339 m->maplen = 0;
340 p[-3] = (p[-3]&31) | (6<<5);
341 for (int i=0; i<=cnt; i++)
342 p[UNIT+i*size-4] = 0;
343 active_idx = cnt-1;
344 }
345 ctx.usage_by_class[sc] += cnt;
346 m->avail_mask = (2u<<active_idx)-1;
347 m->freed_mask = (2u<<(cnt-1))-1 - m->avail_mask;
348 m->mem = (void *)p;
349 m->mem->meta = encode_ptr(m, ctx.secret);
350 m->mem->active_idx = active_idx;
351 m->last_idx = cnt-1;
352 m->freeable = 1;
353 m->sizeclass = sc;
354 return m;
355 }
356
alloc_slot(int sc,size_t req)357 static int alloc_slot(int sc, size_t req)
358 {
359 uint32_t first = try_avail(&ctx.active[sc]);
360 if (first) return a_ctz_32(first);
361
362 struct meta *g = alloc_group(sc, req);
363 if (!g) return -1;
364
365 g->avail_mask--;
366 queue(&ctx.active[sc], g);
367 return 0;
368 }
369
malloc(size_t n)370 void *malloc(size_t n)
371 {
372 if (size_overflows(n)) return 0;
373 struct meta *g;
374 uint32_t mask, first;
375 int sc;
376 int idx;
377 int ctr;
378
379 if (n >= MMAP_THRESHOLD) {
380 size_t needed = n + IB + UNIT;
381 void *p = mmap(0, needed, PROT_READ|PROT_WRITE,
382 MAP_PRIVATE|MAP_ANON, -1, 0);
383 if (p==MAP_FAILED) return 0;
384 wrlock();
385 #ifndef __LITEOS__
386 prctl(PR_SET_VMA, PR_SET_VMA_ANON_NAME, p, needed, "native_heap:mmap");
387 #endif
388 step_seq();
389 g = alloc_meta();
390 if (!g) {
391 unlock();
392 munmap(p, needed);
393 return 0;
394 }
395 g->mem = p;
396 g->mem->meta = encode_ptr(g, ctx.secret);
397 g->last_idx = 0;
398 g->freeable = 1;
399 g->sizeclass = 63;
400 g->maplen = (needed+4095)/4096;
401 g->avail_mask = g->freed_mask = 0;
402 // use a global counter to cycle offset in
403 // individually-mmapped allocations.
404 ctx.mmap_counter++;
405 idx = 0;
406 goto success;
407 }
408
409 sc = size_to_class(n);
410
411 rdlock();
412 g = ctx.active[sc];
413
414 // use coarse size classes initially when there are not yet
415 // any groups of desired size. this allows counts of 2 or 3
416 // to be allocated at first rather than having to start with
417 // 7 or 5, the min counts for even size classes.
418 if (!g && sc>=4 && sc<32 && sc!=6 && !(sc&1) && !ctx.usage_by_class[sc]) {
419 size_t usage = ctx.usage_by_class[sc|1];
420 // if a new group may be allocated, count it toward
421 // usage in deciding if we can use coarse class.
422 if (!ctx.active[sc|1] || (!ctx.active[sc|1]->avail_mask
423 && !ctx.active[sc|1]->freed_mask))
424 usage += 3;
425 if (usage <= 12)
426 sc |= 1;
427 g = ctx.active[sc];
428 }
429
430 for (;;) {
431 mask = g ? g->avail_mask : 0;
432 #ifdef MALLOC_SECURE_ALL
433 if (!mask) break;
434 idx = get_randomIdx(mask, g->last_idx);
435 first = 1u << idx;
436
437 if (RDLOCK_IS_EXCLUSIVE || !MT)
438 g->avail_mask = mask-first;
439 else if (a_cas(&g->avail_mask, mask, mask-first)!=mask)
440 continue;
441 #else
442 first = mask&-mask;
443 if (!first) break;
444 if (RDLOCK_IS_EXCLUSIVE || !MT)
445 g->avail_mask = mask-first;
446 else if (a_cas(&g->avail_mask, mask, mask-first)!=mask)
447 continue;
448 idx = a_ctz_32(first);
449 #endif
450 goto success;
451 }
452 upgradelock();
453
454 idx = alloc_slot(sc, n);
455 if (idx < 0) {
456 unlock();
457 return 0;
458 }
459 g = ctx.active[sc];
460
461 success:
462 ctr = ctx.mmap_counter;
463 unlock();
464 return enframe(g, idx, n, ctr);
465 }
466
is_allzero(void * p)467 int is_allzero(void *p)
468 {
469 struct meta *g = get_meta(p);
470 return g->sizeclass >= 48 ||
471 get_stride(g) < UNIT*size_classes[g->sizeclass];
472 }
473
mallopt(int param,int value)474 int mallopt(int param, int value)
475 {
476 #ifdef USE_JEMALLOC_DFX_INTF
477 return je_mallopt(param, value);
478 #endif
479 return 0;
480 }
481
malloc_disable(void)482 void malloc_disable(void)
483 {
484 #ifdef USE_JEMALLOC_DFX_INTF
485 je_malloc_disable();
486 #endif
487 }
488
malloc_enable(void)489 void malloc_enable(void)
490 {
491 #ifdef USE_JEMALLOC_DFX_INTF
492 je_malloc_enable();
493 #endif
494 }
495
malloc_iterate(void * base,size_t size,void (* callback)(void * base,size_t size,void * arg),void * arg)496 int malloc_iterate(void* base, size_t size, void (*callback)(void* base, size_t size, void* arg), void* arg)
497 {
498 #ifdef USE_JEMALLOC_DFX_INTF
499 return je_iterate(base, size, callback, arg);
500 #endif
501 return 0;
502 }
503
malloc_backtrace(void * pointer,uintptr_t * frames,size_t frame_count)504 ssize_t malloc_backtrace(void* pointer, uintptr_t* frames, size_t frame_count)
505 {
506 return 0;
507 }
508
509 /**
510 * @brief This function determines whether a given memory block was allocated using
511 * Standard C library Memory Allocator.
512 * This function is MT-Safe(multi-thread safe) but not signal-safe.
513 * @param {void *} ptr A pointer to the memory block to be checked.
514 * @return 1 - The memory block was allocated using Standard C library Memory Allocator.
515 * 0 - The memory block was not allocated using Standard C library Memory Allocator.
516 * -1 - The function is not implemented or other error:
517 * ENOSYS: Indicates that the function is not implemented.
518 * EINVAL: Indicates that an invalid argument was passed to the function.
519 */
__malloc_check_from_ptr(void * ptr)520 int __malloc_check_from_ptr(void *ptr)
521 {
522 #ifdef USE_JEMALLOC
523 return je_malloc_check_from_ptr(ptr);
524 #endif
525 errno = ENOSYS;
526 return -1;
527 }
528
529 weak_alias(__malloc_check_from_ptr, malloc_check_from_ptr);