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