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