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