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 {
11 	static unsigned ind = 0;
12 
13 	if (ind == 0) {
14 		size_t sz = sizeof(ind);
15 		assert_d_eq(mallctl("arenas.extend", &ind, &sz, NULL, 0), 0,
16 		    "Unexpected mallctl failure creating arena");
17 	}
18 
19 	return (ind);
20 }
21 
TEST_BEGIN(test_same_size)22 TEST_BEGIN(test_same_size)
23 {
24 	void *p;
25 	size_t sz, tsz;
26 
27 	p = mallocx(42, 0);
28 	assert_ptr_not_null(p, "Unexpected mallocx() error");
29 	sz = sallocx(p, 0);
30 
31 	tsz = xallocx(p, sz, 0, 0);
32 	assert_zu_eq(tsz, sz, "Unexpected size change: %zu --> %zu", sz, tsz);
33 
34 	dallocx(p, 0);
35 }
36 TEST_END
37 
TEST_BEGIN(test_extra_no_move)38 TEST_BEGIN(test_extra_no_move)
39 {
40 	void *p;
41 	size_t sz, tsz;
42 
43 	p = mallocx(42, 0);
44 	assert_ptr_not_null(p, "Unexpected mallocx() error");
45 	sz = sallocx(p, 0);
46 
47 	tsz = xallocx(p, sz, sz-42, 0);
48 	assert_zu_eq(tsz, sz, "Unexpected size change: %zu --> %zu", sz, tsz);
49 
50 	dallocx(p, 0);
51 }
52 TEST_END
53 
TEST_BEGIN(test_no_move_fail)54 TEST_BEGIN(test_no_move_fail)
55 {
56 	void *p;
57 	size_t sz, tsz;
58 
59 	p = mallocx(42, 0);
60 	assert_ptr_not_null(p, "Unexpected mallocx() error");
61 	sz = sallocx(p, 0);
62 
63 	tsz = xallocx(p, sz + 5, 0, 0);
64 	assert_zu_eq(tsz, sz, "Unexpected size change: %zu --> %zu", sz, tsz);
65 
66 	dallocx(p, 0);
67 }
68 TEST_END
69 
70 static unsigned
get_nsizes_impl(const char * cmd)71 get_nsizes_impl(const char *cmd)
72 {
73 	unsigned ret;
74 	size_t z;
75 
76 	z = sizeof(unsigned);
77 	assert_d_eq(mallctl(cmd, &ret, &z, NULL, 0), 0,
78 	    "Unexpected mallctl(\"%s\", ...) failure", cmd);
79 
80 	return (ret);
81 }
82 
83 static unsigned
get_nsmall(void)84 get_nsmall(void)
85 {
86 
87 	return (get_nsizes_impl("arenas.nbins"));
88 }
89 
90 static unsigned
get_nlarge(void)91 get_nlarge(void)
92 {
93 
94 	return (get_nsizes_impl("arenas.nlruns"));
95 }
96 
97 static unsigned
get_nhuge(void)98 get_nhuge(void)
99 {
100 
101 	return (get_nsizes_impl("arenas.nhchunks"));
102 }
103 
104 static size_t
get_size_impl(const char * cmd,size_t ind)105 get_size_impl(const char *cmd, size_t ind)
106 {
107 	size_t ret;
108 	size_t z;
109 	size_t mib[4];
110 	size_t miblen = 4;
111 
112 	z = sizeof(size_t);
113 	assert_d_eq(mallctlnametomib(cmd, mib, &miblen),
114 	    0, "Unexpected mallctlnametomib(\"%s\", ...) failure", cmd);
115 	mib[2] = ind;
116 	z = sizeof(size_t);
117 	assert_d_eq(mallctlbymib(mib, miblen, &ret, &z, NULL, 0),
118 	    0, "Unexpected mallctlbymib([\"%s\", %zu], ...) failure", cmd, ind);
119 
120 	return (ret);
121 }
122 
123 static size_t
get_small_size(size_t ind)124 get_small_size(size_t ind)
125 {
126 
127 	return (get_size_impl("arenas.bin.0.size", ind));
128 }
129 
130 static size_t
get_large_size(size_t ind)131 get_large_size(size_t ind)
132 {
133 
134 	return (get_size_impl("arenas.lrun.0.size", ind));
135 }
136 
137 static size_t
get_huge_size(size_t ind)138 get_huge_size(size_t ind)
139 {
140 
141 	return (get_size_impl("arenas.hchunk.0.size", ind));
142 }
143 
TEST_BEGIN(test_size)144 TEST_BEGIN(test_size)
145 {
146 	size_t small0, hugemax;
147 	void *p;
148 
149 	/* Get size classes. */
150 	small0 = get_small_size(0);
151 	hugemax = get_huge_size(get_nhuge()-1);
152 
153 	p = mallocx(small0, 0);
154 	assert_ptr_not_null(p, "Unexpected mallocx() error");
155 
156 	/* Test smallest supported size. */
157 	assert_zu_eq(xallocx(p, 1, 0, 0), small0,
158 	    "Unexpected xallocx() behavior");
159 
160 	/* Test largest supported size. */
161 	assert_zu_le(xallocx(p, hugemax, 0, 0), hugemax,
162 	    "Unexpected xallocx() behavior");
163 
164 	/* Test size overflow. */
165 	assert_zu_le(xallocx(p, hugemax+1, 0, 0), hugemax,
166 	    "Unexpected xallocx() behavior");
167 	assert_zu_le(xallocx(p, SIZE_T_MAX, 0, 0), hugemax,
168 	    "Unexpected xallocx() behavior");
169 
170 	dallocx(p, 0);
171 }
172 TEST_END
173 
TEST_BEGIN(test_size_extra_overflow)174 TEST_BEGIN(test_size_extra_overflow)
175 {
176 	size_t small0, hugemax;
177 	void *p;
178 
179 	/* Get size classes. */
180 	small0 = get_small_size(0);
181 	hugemax = get_huge_size(get_nhuge()-1);
182 
183 	p = mallocx(small0, 0);
184 	assert_ptr_not_null(p, "Unexpected mallocx() error");
185 
186 	/* Test overflows that can be resolved by clamping extra. */
187 	assert_zu_le(xallocx(p, hugemax-1, 2, 0), hugemax,
188 	    "Unexpected xallocx() behavior");
189 	assert_zu_le(xallocx(p, hugemax, 1, 0), hugemax,
190 	    "Unexpected xallocx() behavior");
191 
192 	/* Test overflow such that hugemax-size underflows. */
193 	assert_zu_le(xallocx(p, hugemax+1, 2, 0), hugemax,
194 	    "Unexpected xallocx() behavior");
195 	assert_zu_le(xallocx(p, hugemax+2, 3, 0), hugemax,
196 	    "Unexpected xallocx() behavior");
197 	assert_zu_le(xallocx(p, SIZE_T_MAX-2, 2, 0), hugemax,
198 	    "Unexpected xallocx() behavior");
199 	assert_zu_le(xallocx(p, SIZE_T_MAX-1, 1, 0), hugemax,
200 	    "Unexpected xallocx() behavior");
201 
202 	dallocx(p, 0);
203 }
204 TEST_END
205 
TEST_BEGIN(test_extra_small)206 TEST_BEGIN(test_extra_small)
207 {
208 	size_t small0, small1, hugemax;
209 	void *p;
210 
211 	/* Get size classes. */
212 	small0 = get_small_size(0);
213 	small1 = get_small_size(1);
214 	hugemax = get_huge_size(get_nhuge()-1);
215 
216 	p = mallocx(small0, 0);
217 	assert_ptr_not_null(p, "Unexpected mallocx() error");
218 
219 	assert_zu_eq(xallocx(p, small1, 0, 0), small0,
220 	    "Unexpected xallocx() behavior");
221 
222 	assert_zu_eq(xallocx(p, small1, 0, 0), small0,
223 	    "Unexpected xallocx() behavior");
224 
225 	assert_zu_eq(xallocx(p, small0, small1 - small0, 0), small0,
226 	    "Unexpected xallocx() behavior");
227 
228 	/* Test size+extra overflow. */
229 	assert_zu_eq(xallocx(p, small0, hugemax - small0 + 1, 0), small0,
230 	    "Unexpected xallocx() behavior");
231 	assert_zu_eq(xallocx(p, small0, SIZE_T_MAX - small0, 0), small0,
232 	    "Unexpected xallocx() behavior");
233 
234 	dallocx(p, 0);
235 }
236 TEST_END
237 
TEST_BEGIN(test_extra_large)238 TEST_BEGIN(test_extra_large)
239 {
240 	int flags = MALLOCX_ARENA(arena_ind());
241 	size_t smallmax, large0, large1, large2, huge0, hugemax;
242 	void *p;
243 
244 	/* Get size classes. */
245 	smallmax = get_small_size(get_nsmall()-1);
246 	large0 = get_large_size(0);
247 	large1 = get_large_size(1);
248 	large2 = get_large_size(2);
249 	huge0 = get_huge_size(0);
250 	hugemax = get_huge_size(get_nhuge()-1);
251 
252 	p = mallocx(large2, flags);
253 	assert_ptr_not_null(p, "Unexpected mallocx() error");
254 
255 	assert_zu_eq(xallocx(p, large2, 0, flags), large2,
256 	    "Unexpected xallocx() behavior");
257 	/* Test size decrease with zero extra. */
258 	assert_zu_eq(xallocx(p, large0, 0, flags), large0,
259 	    "Unexpected xallocx() behavior");
260 	assert_zu_eq(xallocx(p, smallmax, 0, flags), large0,
261 	    "Unexpected xallocx() behavior");
262 
263 	assert_zu_eq(xallocx(p, large2, 0, flags), large2,
264 	    "Unexpected xallocx() behavior");
265 	/* Test size decrease with non-zero extra. */
266 	assert_zu_eq(xallocx(p, large0, large2 - large0, flags), large2,
267 	    "Unexpected xallocx() behavior");
268 	assert_zu_eq(xallocx(p, large1, large2 - large1, flags), large2,
269 	    "Unexpected xallocx() behavior");
270 	assert_zu_eq(xallocx(p, large0, large1 - large0, flags), large1,
271 	    "Unexpected xallocx() behavior");
272 	assert_zu_eq(xallocx(p, smallmax, large0 - smallmax, flags), large0,
273 	    "Unexpected xallocx() behavior");
274 
275 	assert_zu_eq(xallocx(p, large0, 0, flags), large0,
276 	    "Unexpected xallocx() behavior");
277 	/* Test size increase with zero extra. */
278 	assert_zu_eq(xallocx(p, large2, 0, flags), large2,
279 	    "Unexpected xallocx() behavior");
280 	assert_zu_eq(xallocx(p, huge0, 0, flags), large2,
281 	    "Unexpected xallocx() behavior");
282 
283 	assert_zu_eq(xallocx(p, large0, 0, flags), large0,
284 	    "Unexpected xallocx() behavior");
285 	/* Test size increase with non-zero extra. */
286 	assert_zu_lt(xallocx(p, large0, huge0 - large0, flags), huge0,
287 	    "Unexpected xallocx() behavior");
288 
289 	assert_zu_eq(xallocx(p, large0, 0, flags), large0,
290 	    "Unexpected xallocx() behavior");
291 	/* Test size increase with non-zero extra. */
292 	assert_zu_eq(xallocx(p, large0, large2 - large0, flags), large2,
293 	    "Unexpected xallocx() behavior");
294 
295 	assert_zu_eq(xallocx(p, large2, 0, flags), large2,
296 	    "Unexpected xallocx() behavior");
297 	/* Test size+extra overflow. */
298 	assert_zu_lt(xallocx(p, large2, hugemax - large2 + 1, flags), huge0,
299 	    "Unexpected xallocx() behavior");
300 
301 	dallocx(p, flags);
302 }
303 TEST_END
304 
TEST_BEGIN(test_extra_huge)305 TEST_BEGIN(test_extra_huge)
306 {
307 	int flags = MALLOCX_ARENA(arena_ind());
308 	size_t largemax, huge1, huge2, huge3, hugemax;
309 	void *p;
310 
311 	/* Get size classes. */
312 	largemax = get_large_size(get_nlarge()-1);
313 	huge1 = get_huge_size(1);
314 	huge2 = get_huge_size(2);
315 	huge3 = get_huge_size(3);
316 	hugemax = get_huge_size(get_nhuge()-1);
317 
318 	p = mallocx(huge3, flags);
319 	assert_ptr_not_null(p, "Unexpected mallocx() error");
320 
321 	assert_zu_eq(xallocx(p, huge3, 0, flags), huge3,
322 	    "Unexpected xallocx() behavior");
323 	/* Test size decrease with zero extra. */
324 	assert_zu_ge(xallocx(p, huge1, 0, flags), huge1,
325 	    "Unexpected xallocx() behavior");
326 	assert_zu_ge(xallocx(p, largemax, 0, flags), huge1,
327 	    "Unexpected xallocx() behavior");
328 
329 	assert_zu_eq(xallocx(p, huge3, 0, flags), huge3,
330 	    "Unexpected xallocx() behavior");
331 	/* Test size decrease with non-zero extra. */
332 	assert_zu_eq(xallocx(p, huge1, huge3 - huge1, flags), huge3,
333 	    "Unexpected xallocx() behavior");
334 	assert_zu_eq(xallocx(p, huge2, huge3 - huge2, flags), huge3,
335 	    "Unexpected xallocx() behavior");
336 	assert_zu_eq(xallocx(p, huge1, huge2 - huge1, flags), huge2,
337 	    "Unexpected xallocx() behavior");
338 	assert_zu_ge(xallocx(p, largemax, huge1 - largemax, flags), huge1,
339 	    "Unexpected xallocx() behavior");
340 
341 	assert_zu_ge(xallocx(p, huge1, 0, flags), huge1,
342 	    "Unexpected xallocx() behavior");
343 	/* Test size increase with zero extra. */
344 	assert_zu_le(xallocx(p, huge3, 0, flags), huge3,
345 	    "Unexpected xallocx() behavior");
346 	assert_zu_le(xallocx(p, hugemax+1, 0, flags), huge3,
347 	    "Unexpected xallocx() behavior");
348 
349 	assert_zu_ge(xallocx(p, huge1, 0, flags), huge1,
350 	    "Unexpected xallocx() behavior");
351 	/* Test size increase with non-zero extra. */
352 	assert_zu_le(xallocx(p, huge1, SIZE_T_MAX - huge1, flags), hugemax,
353 	    "Unexpected xallocx() behavior");
354 
355 	assert_zu_ge(xallocx(p, huge1, 0, flags), huge1,
356 	    "Unexpected xallocx() behavior");
357 	/* Test size increase with non-zero extra. */
358 	assert_zu_le(xallocx(p, huge1, huge3 - huge1, flags), huge3,
359 	    "Unexpected xallocx() behavior");
360 
361 	assert_zu_eq(xallocx(p, huge3, 0, flags), huge3,
362 	    "Unexpected xallocx() behavior");
363 	/* Test size+extra overflow. */
364 	assert_zu_le(xallocx(p, huge3, hugemax - huge3 + 1, flags), hugemax,
365 	    "Unexpected xallocx() behavior");
366 
367 	dallocx(p, flags);
368 }
369 TEST_END
370 
371 static void
print_filled_extents(const void * p,uint8_t c,size_t len)372 print_filled_extents(const void *p, uint8_t c, size_t len)
373 {
374 	const uint8_t *pc = (const uint8_t *)p;
375 	size_t i, range0;
376 	uint8_t c0;
377 
378 	malloc_printf("  p=%p, c=%#x, len=%zu:", p, c, len);
379 	range0 = 0;
380 	c0 = pc[0];
381 	for (i = 0; i < len; i++) {
382 		if (pc[i] != c0) {
383 			malloc_printf(" %#x[%zu..%zu)", c0, range0, i);
384 			range0 = i;
385 			c0 = pc[i];
386 		}
387 	}
388 	malloc_printf(" %#x[%zu..%zu)\n", c0, range0, i);
389 }
390 
391 static bool
validate_fill(const void * p,uint8_t c,size_t offset,size_t len)392 validate_fill(const void *p, uint8_t c, size_t offset, size_t len)
393 {
394 	const uint8_t *pc = (const uint8_t *)p;
395 	bool err;
396 	size_t i;
397 
398 	for (i = offset, err = false; i < offset+len; i++) {
399 		if (pc[i] != c)
400 			err = true;
401 	}
402 
403 	if (err)
404 		print_filled_extents(p, c, offset + len);
405 
406 	return (err);
407 }
408 
409 static void
test_zero(size_t szmin,size_t szmax)410 test_zero(size_t szmin, size_t szmax)
411 {
412 	int flags = MALLOCX_ARENA(arena_ind()) | MALLOCX_ZERO;
413 	size_t sz, nsz;
414 	void *p;
415 #define	FILL_BYTE 0x7aU
416 
417 	sz = szmax;
418 	p = mallocx(sz, flags);
419 	assert_ptr_not_null(p, "Unexpected mallocx() error");
420 	assert_false(validate_fill(p, 0x00, 0, sz), "Memory not filled: sz=%zu",
421 	    sz);
422 
423 	/*
424 	 * Fill with non-zero so that non-debug builds are more likely to detect
425 	 * errors.
426 	 */
427 	memset(p, FILL_BYTE, sz);
428 	assert_false(validate_fill(p, FILL_BYTE, 0, sz),
429 	    "Memory not filled: sz=%zu", sz);
430 
431 	/* Shrink in place so that we can expect growing in place to succeed. */
432 	sz = szmin;
433 	assert_zu_eq(xallocx(p, sz, 0, flags), sz,
434 	    "Unexpected xallocx() error");
435 	assert_false(validate_fill(p, FILL_BYTE, 0, sz),
436 	    "Memory not filled: sz=%zu", sz);
437 
438 	for (sz = szmin; sz < szmax; sz = nsz) {
439 		nsz = nallocx(sz+1, flags);
440 		assert_zu_eq(xallocx(p, sz+1, 0, flags), nsz,
441 		    "Unexpected xallocx() failure");
442 		assert_false(validate_fill(p, FILL_BYTE, 0, sz),
443 		    "Memory not filled: sz=%zu", sz);
444 		assert_false(validate_fill(p, 0x00, sz, nsz-sz),
445 		    "Memory not filled: sz=%zu, nsz-sz=%zu", sz, nsz-sz);
446 		memset((void *)((uintptr_t)p + sz), FILL_BYTE, nsz-sz);
447 		assert_false(validate_fill(p, FILL_BYTE, 0, nsz),
448 		    "Memory not filled: nsz=%zu", nsz);
449 	}
450 
451 	dallocx(p, flags);
452 }
453 
TEST_BEGIN(test_zero_large)454 TEST_BEGIN(test_zero_large)
455 {
456 	size_t large0, largemax;
457 
458 	/* Get size classes. */
459 	large0 = get_large_size(0);
460 	largemax = get_large_size(get_nlarge()-1);
461 
462 	test_zero(large0, largemax);
463 }
464 TEST_END
465 
TEST_BEGIN(test_zero_huge)466 TEST_BEGIN(test_zero_huge)
467 {
468 	size_t huge0, huge1;
469 
470 	/* Get size classes. */
471 	huge0 = get_huge_size(0);
472 	huge1 = get_huge_size(1);
473 
474 	test_zero(huge1, huge0 * 2);
475 }
476 TEST_END
477 
478 int
main(void)479 main(void)
480 {
481 
482 	return (test(
483 	    test_same_size,
484 	    test_extra_no_move,
485 	    test_no_move_fail,
486 	    test_size,
487 	    test_size_extra_overflow,
488 	    test_extra_small,
489 	    test_extra_large,
490 	    test_extra_huge,
491 	    test_zero_large,
492 	    test_zero_huge));
493 }
494