1 #include "test/jemalloc_test.h"
2 
3 /*
4  * Use a separate arena for xallocx() extension/contraction tests so that
5  * internal allocation e.g. by heap profiling can't interpose allocations where
6  * xallocx() would ordinarily be able to extend.
7  */
8 static unsigned
arena_ind(void)9 arena_ind(void) {
10 	static unsigned ind = 0;
11 
12 	if (ind == 0) {
13 		size_t sz = sizeof(ind);
14 		assert_d_eq(mallctl("arenas.create", (void *)&ind, &sz, NULL,
15 		    0), 0, "Unexpected mallctl failure creating arena");
16 	}
17 
18 	return ind;
19 }
20 
TEST_BEGIN(test_same_size)21 TEST_BEGIN(test_same_size) {
22 	void *p;
23 	size_t sz, tsz;
24 
25 	p = mallocx(42, 0);
26 	assert_ptr_not_null(p, "Unexpected mallocx() error");
27 	sz = sallocx(p, 0);
28 
29 	tsz = xallocx(p, sz, 0, 0);
30 	assert_zu_eq(tsz, sz, "Unexpected size change: %zu --> %zu", sz, tsz);
31 
32 	dallocx(p, 0);
33 }
34 TEST_END
35 
TEST_BEGIN(test_extra_no_move)36 TEST_BEGIN(test_extra_no_move) {
37 	void *p;
38 	size_t sz, tsz;
39 
40 	p = mallocx(42, 0);
41 	assert_ptr_not_null(p, "Unexpected mallocx() error");
42 	sz = sallocx(p, 0);
43 
44 	tsz = xallocx(p, sz, sz-42, 0);
45 	assert_zu_eq(tsz, sz, "Unexpected size change: %zu --> %zu", sz, tsz);
46 
47 	dallocx(p, 0);
48 }
49 TEST_END
50 
TEST_BEGIN(test_no_move_fail)51 TEST_BEGIN(test_no_move_fail) {
52 	void *p;
53 	size_t sz, tsz;
54 
55 	p = mallocx(42, 0);
56 	assert_ptr_not_null(p, "Unexpected mallocx() error");
57 	sz = sallocx(p, 0);
58 
59 	tsz = xallocx(p, sz + 5, 0, 0);
60 	assert_zu_eq(tsz, sz, "Unexpected size change: %zu --> %zu", sz, tsz);
61 
62 	dallocx(p, 0);
63 }
64 TEST_END
65 
66 static unsigned
get_nsizes_impl(const char * cmd)67 get_nsizes_impl(const char *cmd) {
68 	unsigned ret;
69 	size_t z;
70 
71 	z = sizeof(unsigned);
72 	assert_d_eq(mallctl(cmd, (void *)&ret, &z, NULL, 0), 0,
73 	    "Unexpected mallctl(\"%s\", ...) failure", cmd);
74 
75 	return ret;
76 }
77 
78 static unsigned
get_nsmall(void)79 get_nsmall(void) {
80 	return get_nsizes_impl("arenas.nbins");
81 }
82 
83 static unsigned
get_nlarge(void)84 get_nlarge(void) {
85 	return get_nsizes_impl("arenas.nlextents");
86 }
87 
88 static size_t
get_size_impl(const char * cmd,size_t ind)89 get_size_impl(const char *cmd, size_t ind) {
90 	size_t ret;
91 	size_t z;
92 	size_t mib[4];
93 	size_t miblen = 4;
94 
95 	z = sizeof(size_t);
96 	assert_d_eq(mallctlnametomib(cmd, mib, &miblen),
97 	    0, "Unexpected mallctlnametomib(\"%s\", ...) failure", cmd);
98 	mib[2] = ind;
99 	z = sizeof(size_t);
100 	assert_d_eq(mallctlbymib(mib, miblen, (void *)&ret, &z, NULL, 0),
101 	    0, "Unexpected mallctlbymib([\"%s\", %zu], ...) failure", cmd, ind);
102 
103 	return ret;
104 }
105 
106 static size_t
get_small_size(size_t ind)107 get_small_size(size_t ind) {
108 	return get_size_impl("arenas.bin.0.size", ind);
109 }
110 
111 static size_t
get_large_size(size_t ind)112 get_large_size(size_t ind) {
113 	return get_size_impl("arenas.lextent.0.size", ind);
114 }
115 
TEST_BEGIN(test_size)116 TEST_BEGIN(test_size) {
117 	size_t small0, largemax;
118 	void *p;
119 
120 	/* Get size classes. */
121 	small0 = get_small_size(0);
122 	largemax = get_large_size(get_nlarge()-1);
123 
124 	p = mallocx(small0, 0);
125 	assert_ptr_not_null(p, "Unexpected mallocx() error");
126 
127 	/* Test smallest supported size. */
128 	assert_zu_eq(xallocx(p, 1, 0, 0), small0,
129 	    "Unexpected xallocx() behavior");
130 
131 	/* Test largest supported size. */
132 	assert_zu_le(xallocx(p, largemax, 0, 0), largemax,
133 	    "Unexpected xallocx() behavior");
134 
135 	/* Test size overflow. */
136 	assert_zu_le(xallocx(p, largemax+1, 0, 0), largemax,
137 	    "Unexpected xallocx() behavior");
138 	assert_zu_le(xallocx(p, SIZE_T_MAX, 0, 0), largemax,
139 	    "Unexpected xallocx() behavior");
140 
141 	dallocx(p, 0);
142 }
143 TEST_END
144 
TEST_BEGIN(test_size_extra_overflow)145 TEST_BEGIN(test_size_extra_overflow) {
146 	size_t small0, largemax;
147 	void *p;
148 
149 	/* Get size classes. */
150 	small0 = get_small_size(0);
151 	largemax = get_large_size(get_nlarge()-1);
152 
153 	p = mallocx(small0, 0);
154 	assert_ptr_not_null(p, "Unexpected mallocx() error");
155 
156 	/* Test overflows that can be resolved by clamping extra. */
157 	assert_zu_le(xallocx(p, largemax-1, 2, 0), largemax,
158 	    "Unexpected xallocx() behavior");
159 	assert_zu_le(xallocx(p, largemax, 1, 0), largemax,
160 	    "Unexpected xallocx() behavior");
161 
162 	/* Test overflow such that largemax-size underflows. */
163 	assert_zu_le(xallocx(p, largemax+1, 2, 0), largemax,
164 	    "Unexpected xallocx() behavior");
165 	assert_zu_le(xallocx(p, largemax+2, 3, 0), largemax,
166 	    "Unexpected xallocx() behavior");
167 	assert_zu_le(xallocx(p, SIZE_T_MAX-2, 2, 0), largemax,
168 	    "Unexpected xallocx() behavior");
169 	assert_zu_le(xallocx(p, SIZE_T_MAX-1, 1, 0), largemax,
170 	    "Unexpected xallocx() behavior");
171 
172 	dallocx(p, 0);
173 }
174 TEST_END
175 
TEST_BEGIN(test_extra_small)176 TEST_BEGIN(test_extra_small) {
177 	size_t small0, small1, largemax;
178 	void *p;
179 
180 	/* Get size classes. */
181 	small0 = get_small_size(0);
182 	small1 = get_small_size(1);
183 	largemax = get_large_size(get_nlarge()-1);
184 
185 	p = mallocx(small0, 0);
186 	assert_ptr_not_null(p, "Unexpected mallocx() error");
187 
188 	assert_zu_eq(xallocx(p, small1, 0, 0), small0,
189 	    "Unexpected xallocx() behavior");
190 
191 	assert_zu_eq(xallocx(p, small1, 0, 0), small0,
192 	    "Unexpected xallocx() behavior");
193 
194 	assert_zu_eq(xallocx(p, small0, small1 - small0, 0), small0,
195 	    "Unexpected xallocx() behavior");
196 
197 	/* Test size+extra overflow. */
198 	assert_zu_eq(xallocx(p, small0, largemax - small0 + 1, 0), small0,
199 	    "Unexpected xallocx() behavior");
200 	assert_zu_eq(xallocx(p, small0, SIZE_T_MAX - small0, 0), small0,
201 	    "Unexpected xallocx() behavior");
202 
203 	dallocx(p, 0);
204 }
205 TEST_END
206 
TEST_BEGIN(test_extra_large)207 TEST_BEGIN(test_extra_large) {
208 	int flags = MALLOCX_ARENA(arena_ind());
209 	size_t smallmax, large1, large2, large3, largemax;
210 	void *p;
211 
212 	/* Get size classes. */
213 	smallmax = get_small_size(get_nsmall()-1);
214 	large1 = get_large_size(1);
215 	large2 = get_large_size(2);
216 	large3 = get_large_size(3);
217 	largemax = get_large_size(get_nlarge()-1);
218 
219 	p = mallocx(large3, flags);
220 	assert_ptr_not_null(p, "Unexpected mallocx() error");
221 
222 	assert_zu_eq(xallocx(p, large3, 0, flags), large3,
223 	    "Unexpected xallocx() behavior");
224 	/* Test size decrease with zero extra. */
225 	assert_zu_ge(xallocx(p, large1, 0, flags), large1,
226 	    "Unexpected xallocx() behavior");
227 	assert_zu_ge(xallocx(p, smallmax, 0, flags), large1,
228 	    "Unexpected xallocx() behavior");
229 
230 	if (xallocx(p, large3, 0, flags) != large3) {
231 		p = rallocx(p, large3, flags);
232 		assert_ptr_not_null(p, "Unexpected rallocx() failure");
233 	}
234 	/* Test size decrease with non-zero extra. */
235 	assert_zu_eq(xallocx(p, large1, large3 - large1, flags), large3,
236 	    "Unexpected xallocx() behavior");
237 	assert_zu_eq(xallocx(p, large2, large3 - large2, flags), large3,
238 	    "Unexpected xallocx() behavior");
239 	assert_zu_ge(xallocx(p, large1, large2 - large1, flags), large2,
240 	    "Unexpected xallocx() behavior");
241 	assert_zu_ge(xallocx(p, smallmax, large1 - smallmax, flags), large1,
242 	    "Unexpected xallocx() behavior");
243 
244 	assert_zu_ge(xallocx(p, large1, 0, flags), large1,
245 	    "Unexpected xallocx() behavior");
246 	/* Test size increase with zero extra. */
247 	assert_zu_le(xallocx(p, large3, 0, flags), large3,
248 	    "Unexpected xallocx() behavior");
249 	assert_zu_le(xallocx(p, largemax+1, 0, flags), large3,
250 	    "Unexpected xallocx() behavior");
251 
252 	assert_zu_ge(xallocx(p, large1, 0, flags), large1,
253 	    "Unexpected xallocx() behavior");
254 	/* Test size increase with non-zero extra. */
255 	assert_zu_le(xallocx(p, large1, SIZE_T_MAX - large1, flags), largemax,
256 	    "Unexpected xallocx() behavior");
257 
258 	assert_zu_ge(xallocx(p, large1, 0, flags), large1,
259 	    "Unexpected xallocx() behavior");
260 	/* Test size increase with non-zero extra. */
261 	assert_zu_le(xallocx(p, large1, large3 - large1, flags), large3,
262 	    "Unexpected xallocx() behavior");
263 
264 	if (xallocx(p, large3, 0, flags) != large3) {
265 		p = rallocx(p, large3, flags);
266 		assert_ptr_not_null(p, "Unexpected rallocx() failure");
267 	}
268 	/* Test size+extra overflow. */
269 	assert_zu_le(xallocx(p, large3, largemax - large3 + 1, flags), largemax,
270 	    "Unexpected xallocx() behavior");
271 
272 	dallocx(p, flags);
273 }
274 TEST_END
275 
276 static void
print_filled_extents(const void * p,uint8_t c,size_t len)277 print_filled_extents(const void *p, uint8_t c, size_t len) {
278 	const uint8_t *pc = (const uint8_t *)p;
279 	size_t i, range0;
280 	uint8_t c0;
281 
282 	malloc_printf("  p=%p, c=%#x, len=%zu:", p, c, len);
283 	range0 = 0;
284 	c0 = pc[0];
285 	for (i = 0; i < len; i++) {
286 		if (pc[i] != c0) {
287 			malloc_printf(" %#x[%zu..%zu)", c0, range0, i);
288 			range0 = i;
289 			c0 = pc[i];
290 		}
291 	}
292 	malloc_printf(" %#x[%zu..%zu)\n", c0, range0, i);
293 }
294 
295 static bool
validate_fill(const void * p,uint8_t c,size_t offset,size_t len)296 validate_fill(const void *p, uint8_t c, size_t offset, size_t len) {
297 	const uint8_t *pc = (const uint8_t *)p;
298 	bool err;
299 	size_t i;
300 
301 	for (i = offset, err = false; i < offset+len; i++) {
302 		if (pc[i] != c) {
303 			err = true;
304 		}
305 	}
306 
307 	if (err) {
308 		print_filled_extents(p, c, offset + len);
309 	}
310 
311 	return err;
312 }
313 
314 static void
test_zero(size_t szmin,size_t szmax)315 test_zero(size_t szmin, size_t szmax) {
316 	int flags = MALLOCX_ARENA(arena_ind()) | MALLOCX_ZERO;
317 	size_t sz, nsz;
318 	void *p;
319 #define FILL_BYTE 0x7aU
320 
321 	sz = szmax;
322 	p = mallocx(sz, flags);
323 	assert_ptr_not_null(p, "Unexpected mallocx() error");
324 	assert_false(validate_fill(p, 0x00, 0, sz), "Memory not filled: sz=%zu",
325 	    sz);
326 
327 	/*
328 	 * Fill with non-zero so that non-debug builds are more likely to detect
329 	 * errors.
330 	 */
331 	memset(p, FILL_BYTE, sz);
332 	assert_false(validate_fill(p, FILL_BYTE, 0, sz),
333 	    "Memory not filled: sz=%zu", sz);
334 
335 	/* Shrink in place so that we can expect growing in place to succeed. */
336 	sz = szmin;
337 	if (xallocx(p, sz, 0, flags) != sz) {
338 		p = rallocx(p, sz, flags);
339 		assert_ptr_not_null(p, "Unexpected rallocx() failure");
340 	}
341 	assert_false(validate_fill(p, FILL_BYTE, 0, sz),
342 	    "Memory not filled: sz=%zu", sz);
343 
344 	for (sz = szmin; sz < szmax; sz = nsz) {
345 		nsz = nallocx(sz+1, flags);
346 		if (xallocx(p, sz+1, 0, flags) != nsz) {
347 			p = rallocx(p, sz+1, flags);
348 			assert_ptr_not_null(p, "Unexpected rallocx() failure");
349 		}
350 		assert_false(validate_fill(p, FILL_BYTE, 0, sz),
351 		    "Memory not filled: sz=%zu", sz);
352 		assert_false(validate_fill(p, 0x00, sz, nsz-sz),
353 		    "Memory not filled: sz=%zu, nsz-sz=%zu", sz, nsz-sz);
354 		memset((void *)((uintptr_t)p + sz), FILL_BYTE, nsz-sz);
355 		assert_false(validate_fill(p, FILL_BYTE, 0, nsz),
356 		    "Memory not filled: nsz=%zu", nsz);
357 	}
358 
359 	dallocx(p, flags);
360 }
361 
TEST_BEGIN(test_zero_large)362 TEST_BEGIN(test_zero_large) {
363 	size_t large0, large1;
364 
365 	/* Get size classes. */
366 	large0 = get_large_size(0);
367 	large1 = get_large_size(1);
368 
369 	test_zero(large1, large0 * 2);
370 }
371 TEST_END
372 
373 int
main(void)374 main(void) {
375 	return test(
376 	    test_same_size,
377 	    test_extra_no_move,
378 	    test_no_move_fail,
379 	    test_size,
380 	    test_size_extra_overflow,
381 	    test_extra_small,
382 	    test_extra_large,
383 	    test_zero_large);
384 }
385