github.com/afumu/libc@v0.0.6/musl/src/malloc/mallocng/meta.h (about) 1 #ifndef MALLOC_META_H 2 #define MALLOC_META_H 3 4 #include <stdint.h> 5 #include <errno.h> 6 #include <limits.h> 7 #include "glue.h" 8 9 __attribute__((__visibility__("hidden"))) 10 extern const uint16_t size_classes[]; 11 12 #define MMAP_THRESHOLD 131052 13 14 #define UNIT 16 15 #define IB 4 16 17 struct group { 18 struct meta *meta; 19 unsigned char active_idx:5; 20 char pad[UNIT - sizeof(struct meta *) - 1]; 21 unsigned char storage[]; 22 }; 23 24 struct meta { 25 struct meta *prev, *next; 26 struct group *mem; 27 volatile int avail_mask, freed_mask; 28 uintptr_t last_idx:5; 29 uintptr_t freeable:1; 30 uintptr_t sizeclass:6; 31 uintptr_t maplen:8*sizeof(uintptr_t)-12; 32 }; 33 34 struct meta_area { 35 uint64_t check; 36 struct meta_area *next; 37 int nslots; 38 struct meta slots[]; 39 }; 40 41 struct malloc_context { 42 uint64_t secret; 43 #ifndef PAGESIZE 44 size_t pagesize; 45 #endif 46 int init_done; 47 unsigned mmap_counter; 48 struct meta *free_meta_head; 49 struct meta *avail_meta; 50 size_t avail_meta_count, avail_meta_area_count, meta_alloc_shift; 51 struct meta_area *meta_area_head, *meta_area_tail; 52 unsigned char *avail_meta_areas; 53 struct meta *active[48]; 54 size_t usage_by_class[48]; 55 uint8_t unmap_seq[32], bounces[32]; 56 uint8_t seq; 57 uintptr_t brk; 58 }; 59 60 __attribute__((__visibility__("hidden"))) 61 extern struct malloc_context ctx; 62 63 #ifdef PAGESIZE 64 #define PGSZ PAGESIZE 65 #else 66 #define PGSZ ctx.pagesize 67 #endif 68 69 __attribute__((__visibility__("hidden"))) 70 struct meta *alloc_meta(void); 71 72 __attribute__((__visibility__("hidden"))) 73 int is_allzero(void *); 74 75 static inline void queue(struct meta **phead, struct meta *m) 76 { 77 assert(!m->next); 78 assert(!m->prev); 79 if (*phead) { 80 struct meta *head = *phead; 81 m->next = head; 82 m->prev = head->prev; 83 m->next->prev = m->prev->next = m; 84 } else { 85 m->prev = m->next = m; 86 *phead = m; 87 } 88 } 89 90 static inline void dequeue(struct meta **phead, struct meta *m) 91 { 92 if (m->next != m) { 93 m->prev->next = m->next; 94 m->next->prev = m->prev; 95 if (*phead == m) *phead = m->next; 96 } else { 97 *phead = 0; 98 } 99 m->prev = m->next = 0; 100 } 101 102 static inline struct meta *dequeue_head(struct meta **phead) 103 { 104 struct meta *m = *phead; 105 if (m) dequeue(phead, m); 106 return m; 107 } 108 109 static inline void free_meta(struct meta *m) 110 { 111 *m = (struct meta){0}; 112 queue(&ctx.free_meta_head, m); 113 } 114 115 static inline uint32_t activate_group(struct meta *m) 116 { 117 assert(!m->avail_mask); 118 uint32_t mask, act = (2u<<m->mem->active_idx)-1; 119 do mask = m->freed_mask; 120 while (a_cas(&m->freed_mask, mask, mask&~act)!=mask); 121 return m->avail_mask = mask & act; 122 } 123 124 static inline int get_slot_index(const unsigned char *p) 125 { 126 return p[-3] & 31; 127 } 128 129 static inline struct meta *get_meta(const unsigned char *p) 130 { 131 assert(!((uintptr_t)p & 15)); 132 int offset = *(const uint16_t *)(p - 2); 133 int index = get_slot_index(p); 134 if (p[-4]) { 135 assert(!offset); 136 offset = *(uint32_t *)(p - 8); 137 assert(offset > 0xffff); 138 } 139 const struct group *base = (const void *)(p - UNIT*offset - UNIT); 140 const struct meta *meta = base->meta; 141 assert(meta->mem == base); 142 assert(index <= meta->last_idx); 143 assert(!(meta->avail_mask & (1u<<index))); 144 assert(!(meta->freed_mask & (1u<<index))); 145 const struct meta_area *area = (void *)((uintptr_t)meta & -4096); 146 assert(area->check == ctx.secret); 147 if (meta->sizeclass < 48) { 148 assert(offset >= size_classes[meta->sizeclass]*index); 149 assert(offset < size_classes[meta->sizeclass]*(index+1)); 150 } else { 151 assert(meta->sizeclass == 63); 152 } 153 if (meta->maplen) { 154 assert(offset <= meta->maplen*4096UL/UNIT - 1); 155 } 156 return (struct meta *)meta; 157 } 158 159 static inline size_t get_nominal_size(const unsigned char *p, const unsigned char *end) 160 { 161 size_t reserved = p[-3] >> 5; 162 if (reserved >= 5) { 163 assert(reserved == 5); 164 reserved = *(const uint32_t *)(end-4); 165 assert(reserved >= 5); 166 assert(!end[-5]); 167 } 168 assert(reserved <= end-p); 169 assert(!*(end-reserved)); 170 // also check the slot's overflow byte 171 assert(!*end); 172 return end-reserved-p; 173 } 174 175 static inline size_t get_stride(const struct meta *g) 176 { 177 if (!g->last_idx && g->maplen) { 178 return g->maplen*4096UL - UNIT; 179 } else { 180 return UNIT*size_classes[g->sizeclass]; 181 } 182 } 183 184 static inline void set_size(unsigned char *p, unsigned char *end, size_t n) 185 { 186 int reserved = end-p-n; 187 if (reserved) end[-reserved] = 0; 188 if (reserved >= 5) { 189 *(uint32_t *)(end-4) = reserved; 190 end[-5] = 0; 191 reserved = 5; 192 } 193 p[-3] = (p[-3]&31) + (reserved<<5); 194 } 195 196 static inline void *enframe(struct meta *g, int idx, size_t n, int ctr) 197 { 198 size_t stride = get_stride(g); 199 size_t slack = (stride-IB-n)/UNIT; 200 unsigned char *p = g->mem->storage + stride*idx; 201 unsigned char *end = p+stride-IB; 202 // cycle offset within slot to increase interval to address 203 // reuse, facilitate trapping double-free. 204 int off = (p[-3] ? *(uint16_t *)(p-2) + 1 : ctr) & 255; 205 assert(!p[-4]); 206 if (off > slack) { 207 size_t m = slack; 208 m |= m>>1; m |= m>>2; m |= m>>4; 209 off &= m; 210 if (off > slack) off -= slack+1; 211 assert(off <= slack); 212 } 213 if (off) { 214 // store offset in unused header at offset zero 215 // if enframing at non-zero offset. 216 *(uint16_t *)(p-2) = off; 217 p[-3] = 7<<5; 218 p += UNIT*off; 219 // for nonzero offset there is no permanent check 220 // byte, so make one. 221 p[-4] = 0; 222 } 223 *(uint16_t *)(p-2) = (size_t)(p-g->mem->storage)/UNIT; 224 p[-3] = idx; 225 set_size(p, end, n); 226 return p; 227 } 228 229 static inline int size_to_class(size_t n) 230 { 231 n = (n+IB-1)>>4; 232 if (n<10) return n; 233 n++; 234 int i = (28-a_clz_32(n))*4 + 8; 235 if (n>size_classes[i+1]) i+=2; 236 if (n>size_classes[i]) i++; 237 return i; 238 } 239 240 static inline int size_overflows(size_t n) 241 { 242 if (n >= SIZE_MAX/2 - 4096) { 243 errno = ENOMEM; 244 return 1; 245 } 246 return 0; 247 } 248 249 static inline void step_seq(void) 250 { 251 if (ctx.seq==255) { 252 for (int i=0; i<32; i++) ctx.unmap_seq[i] = 0; 253 ctx.seq = 1; 254 } else { 255 ctx.seq++; 256 } 257 } 258 259 static inline void record_seq(int sc) 260 { 261 if (sc-7U < 32) ctx.unmap_seq[sc-7] = ctx.seq; 262 } 263 264 static inline void account_bounce(int sc) 265 { 266 if (sc-7U < 32) { 267 int seq = ctx.unmap_seq[sc-7]; 268 if (seq && ctx.seq-seq < 10) { 269 if (ctx.bounces[sc-7]+1 < 100) 270 ctx.bounces[sc-7]++; 271 else 272 ctx.bounces[sc-7] = 150; 273 } 274 } 275 } 276 277 static inline void decay_bounces(int sc) 278 { 279 if (sc-7U < 32 && ctx.bounces[sc-7]) 280 ctx.bounces[sc-7]--; 281 } 282 283 static inline int is_bouncing(int sc) 284 { 285 return (sc-7U < 32 && ctx.bounces[sc-7] >= 100); 286 } 287 288 #endif