xref: /netbsd-src/external/bsd/jemalloc/dist/test/unit/double_free.c (revision 7bdf38e5b7a28439665f2fdeff81e36913eef7dd)
1*7bdf38e5Schristos #include "test/jemalloc_test.h"
2*7bdf38e5Schristos #include "test/san.h"
3*7bdf38e5Schristos 
4*7bdf38e5Schristos #include "jemalloc/internal/safety_check.h"
5*7bdf38e5Schristos 
6*7bdf38e5Schristos bool fake_abort_called;
7*7bdf38e5Schristos void fake_abort(const char *message) {
8*7bdf38e5Schristos 	(void)message;
9*7bdf38e5Schristos 	fake_abort_called = true;
10*7bdf38e5Schristos }
11*7bdf38e5Schristos 
12*7bdf38e5Schristos void
13*7bdf38e5Schristos test_large_double_free_pre(void) {
14*7bdf38e5Schristos 	safety_check_set_abort(&fake_abort);
15*7bdf38e5Schristos 	fake_abort_called = false;
16*7bdf38e5Schristos }
17*7bdf38e5Schristos 
18*7bdf38e5Schristos void
19*7bdf38e5Schristos test_large_double_free_post() {
20*7bdf38e5Schristos 	expect_b_eq(fake_abort_called, true, "Double-free check didn't fire.");
21*7bdf38e5Schristos 	safety_check_set_abort(NULL);
22*7bdf38e5Schristos }
23*7bdf38e5Schristos 
24*7bdf38e5Schristos TEST_BEGIN(test_large_double_free_tcache) {
25*7bdf38e5Schristos 	test_skip_if(!config_opt_safety_checks);
26*7bdf38e5Schristos 	/*
27*7bdf38e5Schristos 	 * Skip debug builds, since too many assertions will be triggered with
28*7bdf38e5Schristos 	 * double-free before hitting the one we are interested in.
29*7bdf38e5Schristos 	 */
30*7bdf38e5Schristos 	test_skip_if(config_debug);
31*7bdf38e5Schristos 
32*7bdf38e5Schristos 	test_large_double_free_pre();
33*7bdf38e5Schristos 	char *ptr = malloc(SC_LARGE_MINCLASS);
34*7bdf38e5Schristos 	bool guarded = extent_is_guarded(tsdn_fetch(), ptr);
35*7bdf38e5Schristos 	free(ptr);
36*7bdf38e5Schristos 	if (!guarded) {
37*7bdf38e5Schristos 		free(ptr);
38*7bdf38e5Schristos 	} else {
39*7bdf38e5Schristos 		/*
40*7bdf38e5Schristos 		 * Skip because guarded extents may unguard immediately on
41*7bdf38e5Schristos 		 * deallocation, in which case the second free will crash before
42*7bdf38e5Schristos 		 * reaching the intended safety check.
43*7bdf38e5Schristos 		 */
44*7bdf38e5Schristos 		fake_abort_called = true;
45*7bdf38e5Schristos 	}
46*7bdf38e5Schristos 	mallctl("thread.tcache.flush", NULL, NULL, NULL, 0);
47*7bdf38e5Schristos 	test_large_double_free_post();
48*7bdf38e5Schristos }
49*7bdf38e5Schristos TEST_END
50*7bdf38e5Schristos 
51*7bdf38e5Schristos TEST_BEGIN(test_large_double_free_no_tcache) {
52*7bdf38e5Schristos 	test_skip_if(!config_opt_safety_checks);
53*7bdf38e5Schristos 	test_skip_if(config_debug);
54*7bdf38e5Schristos 
55*7bdf38e5Schristos 	test_large_double_free_pre();
56*7bdf38e5Schristos 	char *ptr = mallocx(SC_LARGE_MINCLASS, MALLOCX_TCACHE_NONE);
57*7bdf38e5Schristos 	bool guarded = extent_is_guarded(tsdn_fetch(), ptr);
58*7bdf38e5Schristos 	dallocx(ptr, MALLOCX_TCACHE_NONE);
59*7bdf38e5Schristos 	if (!guarded) {
60*7bdf38e5Schristos 		dallocx(ptr, MALLOCX_TCACHE_NONE);
61*7bdf38e5Schristos 	} else {
62*7bdf38e5Schristos 		/*
63*7bdf38e5Schristos 		 * Skip because guarded extents may unguard immediately on
64*7bdf38e5Schristos 		 * deallocation, in which case the second free will crash before
65*7bdf38e5Schristos 		 * reaching the intended safety check.
66*7bdf38e5Schristos 		 */
67*7bdf38e5Schristos 		fake_abort_called = true;
68*7bdf38e5Schristos 	}
69*7bdf38e5Schristos 	test_large_double_free_post();
70*7bdf38e5Schristos }
71*7bdf38e5Schristos TEST_END
72*7bdf38e5Schristos 
73*7bdf38e5Schristos int
74*7bdf38e5Schristos main(void) {
75*7bdf38e5Schristos 	return test(test_large_double_free_no_tcache,
76*7bdf38e5Schristos 	    test_large_double_free_tcache);
77*7bdf38e5Schristos }
78