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