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