Home | History | Annotate | Line # | Download | only in unit
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