double_free.c revision 1.1.1.2 1 #include "test/jemalloc_test.h"
2 #include "test/san.h"
3
4 #include "jemalloc/internal/safety_check.h"
5
6 bool fake_abort_called;
7 void
8 fake_abort(const char *message) {
9 (void)message;
10 fake_abort_called = true;
11 }
12
13 static void
14 test_double_free_pre(void) {
15 safety_check_set_abort(&fake_abort);
16 fake_abort_called = false;
17 }
18
19 static void
20 test_double_free_post(void) {
21 expect_b_eq(fake_abort_called, true, "Double-free check didn't fire.");
22 safety_check_set_abort(NULL);
23 }
24
25 static bool
26 tcache_enabled(void) {
27 bool enabled;
28 size_t sz = sizeof(enabled);
29 assert_d_eq(mallctl("thread.tcache.enabled", &enabled, &sz, NULL, 0), 0,
30 "Unexpected mallctl failure");
31 return enabled;
32 }
33
34 TEST_BEGIN(test_large_double_free_tcache) {
35 test_skip_if(!config_opt_safety_checks);
36 /*
37 * Skip debug builds, since too many assertions will be triggered with
38 * double-free before hitting the one we are interested in.
39 */
40 test_skip_if(config_debug);
41
42 test_double_free_pre();
43 char *ptr = malloc(SC_LARGE_MINCLASS);
44 bool guarded = extent_is_guarded(tsdn_fetch(), ptr);
45 free(ptr);
46 if (!guarded) {
47 free(ptr);
48 } else {
49 /*
50 * Skip because guarded extents may unguard immediately on
51 * deallocation, in which case the second free will crash before
52 * reaching the intended safety check.
53 */
54 fake_abort_called = true;
55 }
56 mallctl("thread.tcache.flush", NULL, NULL, NULL, 0);
57 test_double_free_post();
58 }
59 TEST_END
60
61 TEST_BEGIN(test_large_double_free_no_tcache) {
62 test_skip_if(!config_opt_safety_checks);
63 test_skip_if(config_debug);
64
65 test_double_free_pre();
66 char *ptr = mallocx(SC_LARGE_MINCLASS, MALLOCX_TCACHE_NONE);
67 bool guarded = extent_is_guarded(tsdn_fetch(), ptr);
68 dallocx(ptr, MALLOCX_TCACHE_NONE);
69 if (!guarded) {
70 dallocx(ptr, MALLOCX_TCACHE_NONE);
71 } else {
72 /*
73 * Skip because guarded extents may unguard immediately on
74 * deallocation, in which case the second free will crash before
75 * reaching the intended safety check.
76 */
77 fake_abort_called = true;
78 }
79 test_double_free_post();
80 }
81 TEST_END
82
83 TEST_BEGIN(test_small_double_free_tcache) {
84 test_skip_if(!config_debug);
85 test_skip_if(opt_debug_double_free_max_scan == 0);
86 test_skip_if(!tcache_enabled());
87
88 test_double_free_pre();
89 char *ptr = malloc(1);
90 bool guarded = extent_is_guarded(tsdn_fetch(), ptr);
91 free(ptr);
92 if (!guarded) {
93 free(ptr);
94 } else {
95 /*
96 * Skip because guarded extents may unguard immediately on
97 * deallocation, in which case the second free will crash before
98 * reaching the intended safety check.
99 */
100 fake_abort_called = true;
101 }
102 mallctl("thread.tcache.flush", NULL, NULL, NULL, 0);
103 test_double_free_post();
104 }
105 TEST_END
106
107 TEST_BEGIN(test_small_double_free_arena) {
108 test_skip_if(!config_debug);
109 test_skip_if(!tcache_enabled());
110
111 test_double_free_pre();
112 /*
113 * Allocate one more pointer to keep the slab partially used after
114 * flushing the cache.
115 */
116 char *ptr1 = malloc(1);
117 char *ptr = malloc(1);
118 bool guarded = extent_is_guarded(tsdn_fetch(), ptr);
119 free(ptr);
120 if (!guarded) {
121 mallctl("thread.tcache.flush", NULL, NULL, NULL, 0);
122 free(ptr);
123 } else {
124 /*
125 * Skip because guarded extents may unguard immediately on
126 * deallocation, in which case the second free will crash before
127 * reaching the intended safety check.
128 */
129 fake_abort_called = true;
130 }
131 test_double_free_post();
132 free(ptr1);
133 }
134 TEST_END
135
136 int
137 main(void) {
138 return test(test_large_double_free_no_tcache,
139 test_large_double_free_tcache, test_small_double_free_tcache,
140 test_small_double_free_arena);
141 }
142