• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
queue(struct meta ** phead,struct meta * m)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 
dequeue(struct meta ** phead,struct meta * m)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 
dequeue_head(struct meta ** phead)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 
free_meta(struct meta * m)109 static inline void free_meta(struct meta *m)
110 {
111 	*m = (struct meta){0};
112 	queue(&ctx.free_meta_head, m);
113 }
114 
activate_group(struct meta * m)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 
get_slot_index(const unsigned char * p)124 static inline int get_slot_index(const unsigned char *p)
125 {
126 	return p[-3] & 31;
127 }
128 
get_meta(const unsigned char * p)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 
get_nominal_size(const unsigned char * p,const unsigned char * end)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 
get_stride(const struct meta * g)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 
set_size(unsigned char * p,unsigned char * end,size_t n)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 
enframe(struct meta * g,int idx,size_t n,int ctr)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 
size_to_class(size_t n)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 
size_overflows(size_t n)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 
step_seq(void)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 
record_seq(int sc)259 static inline void record_seq(int sc)
260 {
261 	if (sc-7U < 32) ctx.unmap_seq[sc-7] = ctx.seq;
262 }
263 
account_bounce(int sc)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 
decay_bounces(int sc)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 
is_bouncing(int sc)283 static inline int is_bouncing(int sc)
284 {
285 	return (sc-7U < 32 && ctx.bounces[sc-7] >= 100);
286 }
287 
288 #endif
289