Home | History | Annotate | Line # | Download | only in unit
      1  1.1  christos #include "test/jemalloc_test.h"
      2  1.1  christos #include "test/arena_util.h"
      3  1.1  christos #include "test/san.h"
      4  1.1  christos 
      5  1.1  christos #include "jemalloc/internal/san.h"
      6  1.1  christos 
      7  1.1  christos static void
      8  1.1  christos verify_extent_guarded(tsdn_t *tsdn, void *ptr) {
      9  1.1  christos 	expect_true(extent_is_guarded(tsdn, ptr),
     10  1.1  christos 	    "All extents should be guarded.");
     11  1.1  christos }
     12  1.1  christos 
     13  1.1  christos #define MAX_SMALL_ALLOCATIONS 4096
     14  1.1  christos void *small_alloc[MAX_SMALL_ALLOCATIONS];
     15  1.1  christos 
     16  1.1  christos /*
     17  1.1  christos  * This test allocates page sized slabs and checks that every two slabs have
     18  1.1  christos  * at least one page in between them. That page is supposed to be the guard
     19  1.1  christos  * page.
     20  1.1  christos  */
     21  1.1  christos TEST_BEGIN(test_guarded_small) {
     22  1.1  christos 	test_skip_if(opt_prof);
     23  1.1  christos 
     24  1.1  christos 	tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
     25  1.1  christos 	unsigned npages = 16, pages_found = 0, ends_found = 0;
     26  1.1  christos 	VARIABLE_ARRAY(uintptr_t, pages, npages);
     27  1.1  christos 
     28  1.1  christos 	/* Allocate to get sanitized pointers. */
     29  1.1  christos 	size_t slab_sz = PAGE;
     30  1.1  christos 	size_t sz = slab_sz / 8;
     31  1.1  christos 	unsigned n_alloc = 0;
     32  1.1  christos 	while (n_alloc < MAX_SMALL_ALLOCATIONS) {
     33  1.1  christos 		void *ptr = malloc(sz);
     34  1.1  christos 		expect_ptr_not_null(ptr, "Unexpected malloc() failure");
     35  1.1  christos 		small_alloc[n_alloc] = ptr;
     36  1.1  christos 		verify_extent_guarded(tsdn, ptr);
     37  1.1  christos 		if ((uintptr_t)ptr % PAGE == 0) {
     38  1.1  christos 			assert_u_lt(pages_found, npages,
     39  1.1  christos 			    "Unexpectedly large number of page aligned allocs");
     40  1.1  christos 			pages[pages_found++] = (uintptr_t)ptr;
     41  1.1  christos 		}
     42  1.1  christos 		if (((uintptr_t)ptr + (uintptr_t)sz) % PAGE == 0) {
     43  1.1  christos 			ends_found++;
     44  1.1  christos 		}
     45  1.1  christos 		n_alloc++;
     46  1.1  christos 		if (pages_found == npages && ends_found == npages) {
     47  1.1  christos 			break;
     48  1.1  christos 		}
     49  1.1  christos 	}
     50  1.1  christos 	/* Should found the ptrs being checked for overflow and underflow. */
     51  1.1  christos 	expect_u_eq(pages_found, npages, "Could not found the expected pages.");
     52  1.1  christos 	expect_u_eq(ends_found, npages, "Could not found the expected pages.");
     53  1.1  christos 
     54  1.1  christos 	/* Verify the pages are not continuous, i.e. separated by guards. */
     55  1.1  christos 	for (unsigned i = 0; i < npages - 1; i++) {
     56  1.1  christos 		for (unsigned j = i + 1; j < npages; j++) {
     57  1.1  christos 			uintptr_t ptr_diff = pages[i] > pages[j] ?
     58  1.1  christos 			    pages[i] - pages[j] : pages[j] - pages[i];
     59  1.1  christos 			expect_zu_ge((size_t)ptr_diff, slab_sz + PAGE,
     60  1.1  christos 			    "There should be at least one pages between "
     61  1.1  christos 			    "guarded slabs");
     62  1.1  christos 		}
     63  1.1  christos 	}
     64  1.1  christos 
     65  1.1  christos 	for (unsigned i = 0; i < n_alloc + 1; i++) {
     66  1.1  christos 		free(small_alloc[i]);
     67  1.1  christos 	}
     68  1.1  christos }
     69  1.1  christos TEST_END
     70  1.1  christos 
     71  1.1  christos TEST_BEGIN(test_guarded_large) {
     72  1.1  christos 	tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
     73  1.1  christos 	unsigned nlarge = 32;
     74  1.1  christos 	VARIABLE_ARRAY(uintptr_t, large, nlarge);
     75  1.1  christos 
     76  1.1  christos 	/* Allocate to get sanitized pointers. */
     77  1.1  christos 	size_t large_sz = SC_LARGE_MINCLASS;
     78  1.1  christos 	for (unsigned i = 0; i < nlarge; i++) {
     79  1.1  christos 		void *ptr = malloc(large_sz);
     80  1.1  christos 		verify_extent_guarded(tsdn, ptr);
     81  1.1  christos 		expect_ptr_not_null(ptr, "Unexpected malloc() failure");
     82  1.1  christos 		large[i] = (uintptr_t)ptr;
     83  1.1  christos 	}
     84  1.1  christos 
     85  1.1  christos 	/* Verify the pages are not continuous, i.e. separated by guards. */
     86  1.1  christos 	for (unsigned i = 0; i < nlarge; i++) {
     87  1.1  christos 		for (unsigned j = i + 1; j < nlarge; j++) {
     88  1.1  christos 			uintptr_t ptr_diff = large[i] > large[j] ?
     89  1.1  christos 			    large[i] - large[j] : large[j] - large[i];
     90  1.1  christos 			expect_zu_ge((size_t)ptr_diff, large_sz + 2 * PAGE,
     91  1.1  christos 			    "There should be at least two pages between "
     92  1.1  christos 			    " guarded large allocations");
     93  1.1  christos 		}
     94  1.1  christos 	}
     95  1.1  christos 
     96  1.1  christos 	for (unsigned i = 0; i < nlarge; i++) {
     97  1.1  christos 		free((void *)large[i]);
     98  1.1  christos 	}
     99  1.1  christos }
    100  1.1  christos TEST_END
    101  1.1  christos 
    102  1.1  christos static void
    103  1.1  christos verify_pdirty(unsigned arena_ind, uint64_t expected) {
    104  1.1  christos 	uint64_t pdirty = get_arena_pdirty(arena_ind);
    105  1.1  christos 	expect_u64_eq(pdirty, expected / PAGE,
    106  1.1  christos 	    "Unexpected dirty page amount.");
    107  1.1  christos }
    108  1.1  christos 
    109  1.1  christos static void
    110  1.1  christos verify_pmuzzy(unsigned arena_ind, uint64_t expected) {
    111  1.1  christos 	uint64_t pmuzzy = get_arena_pmuzzy(arena_ind);
    112  1.1  christos 	expect_u64_eq(pmuzzy, expected / PAGE,
    113  1.1  christos 	    "Unexpected muzzy page amount.");
    114  1.1  christos }
    115  1.1  christos 
    116  1.1  christos TEST_BEGIN(test_guarded_decay) {
    117  1.1  christos 	unsigned arena_ind = do_arena_create(-1, -1);
    118  1.1  christos 	do_decay(arena_ind);
    119  1.1  christos 	do_purge(arena_ind);
    120  1.1  christos 
    121  1.1  christos 	verify_pdirty(arena_ind, 0);
    122  1.1  christos 	verify_pmuzzy(arena_ind, 0);
    123  1.1  christos 
    124  1.1  christos 	/* Verify that guarded extents as dirty. */
    125  1.1  christos 	size_t sz1 = PAGE, sz2 = PAGE * 2;
    126  1.1  christos 	/* W/o maps_coalesce, guarded extents are unguarded eagerly. */
    127  1.1  christos 	size_t add_guard_size = maps_coalesce ? 0 : SAN_PAGE_GUARDS_SIZE;
    128  1.1  christos 	generate_dirty(arena_ind, sz1);
    129  1.1  christos 	verify_pdirty(arena_ind, sz1 + add_guard_size);
    130  1.1  christos 	verify_pmuzzy(arena_ind, 0);
    131  1.1  christos 
    132  1.1  christos 	/* Should reuse the first extent. */
    133  1.1  christos 	generate_dirty(arena_ind, sz1);
    134  1.1  christos 	verify_pdirty(arena_ind, sz1 + add_guard_size);
    135  1.1  christos 	verify_pmuzzy(arena_ind, 0);
    136  1.1  christos 
    137  1.1  christos 	/* Should not reuse; expect new dirty pages. */
    138  1.1  christos 	generate_dirty(arena_ind, sz2);
    139  1.1  christos 	verify_pdirty(arena_ind, sz1 + sz2 + 2 * add_guard_size);
    140  1.1  christos 	verify_pmuzzy(arena_ind, 0);
    141  1.1  christos 
    142  1.1  christos 	tsdn_t *tsdn = tsd_tsdn(tsd_fetch());
    143  1.1  christos 	int flags = MALLOCX_ARENA(arena_ind) | MALLOCX_TCACHE_NONE;
    144  1.1  christos 
    145  1.1  christos 	/* Should reuse dirty extents for the two mallocx. */
    146  1.1  christos 	void *p1 = do_mallocx(sz1, flags);
    147  1.1  christos 	verify_extent_guarded(tsdn, p1);
    148  1.1  christos 	verify_pdirty(arena_ind, sz2 + add_guard_size);
    149  1.1  christos 
    150  1.1  christos 	void *p2 = do_mallocx(sz2, flags);
    151  1.1  christos 	verify_extent_guarded(tsdn, p2);
    152  1.1  christos 	verify_pdirty(arena_ind, 0);
    153  1.1  christos 	verify_pmuzzy(arena_ind, 0);
    154  1.1  christos 
    155  1.1  christos 	dallocx(p1, flags);
    156  1.1  christos 	verify_pdirty(arena_ind, sz1 + add_guard_size);
    157  1.1  christos 	dallocx(p2, flags);
    158  1.1  christos 	verify_pdirty(arena_ind, sz1 + sz2 + 2 * add_guard_size);
    159  1.1  christos 	verify_pmuzzy(arena_ind, 0);
    160  1.1  christos 
    161  1.1  christos 	do_purge(arena_ind);
    162  1.1  christos 	verify_pdirty(arena_ind, 0);
    163  1.1  christos 	verify_pmuzzy(arena_ind, 0);
    164  1.1  christos 
    165  1.1  christos 	if (config_stats) {
    166  1.1  christos 		expect_u64_eq(get_arena_npurge(arena_ind), 1,
    167  1.1  christos 		    "Expected purging to occur");
    168  1.1  christos 		expect_u64_eq(get_arena_dirty_npurge(arena_ind), 1,
    169  1.1  christos 		    "Expected purging to occur");
    170  1.1  christos 		expect_u64_eq(get_arena_dirty_purged(arena_ind),
    171  1.1  christos 		    (sz1 + sz2 + 2 * add_guard_size) / PAGE,
    172  1.1  christos 		    "Expected purging to occur");
    173  1.1  christos 		expect_u64_eq(get_arena_muzzy_npurge(arena_ind), 0,
    174  1.1  christos 		    "Expected purging to occur");
    175  1.1  christos 	}
    176  1.1  christos 
    177  1.1  christos 	if (opt_retain) {
    178  1.1  christos 		/*
    179  1.1  christos 		 * With retain, guarded extents are not mergable and will be
    180  1.1  christos 		 * cached in ecache_retained.  They should be reused.
    181  1.1  christos 		 */
    182  1.1  christos 		void *new_p1 = do_mallocx(sz1, flags);
    183  1.1  christos 		verify_extent_guarded(tsdn, p1);
    184  1.1  christos 		expect_ptr_eq(p1, new_p1, "Expect to reuse p1");
    185  1.1  christos 
    186  1.1  christos 		void *new_p2 = do_mallocx(sz2, flags);
    187  1.1  christos 		verify_extent_guarded(tsdn, p2);
    188  1.1  christos 		expect_ptr_eq(p2, new_p2, "Expect to reuse p2");
    189  1.1  christos 
    190  1.1  christos 		dallocx(new_p1, flags);
    191  1.1  christos 		verify_pdirty(arena_ind, sz1 + add_guard_size);
    192  1.1  christos 		dallocx(new_p2, flags);
    193  1.1  christos 		verify_pdirty(arena_ind, sz1 + sz2 + 2 * add_guard_size);
    194  1.1  christos 		verify_pmuzzy(arena_ind, 0);
    195  1.1  christos 	}
    196  1.1  christos 
    197  1.1  christos 	do_arena_destroy(arena_ind);
    198  1.1  christos }
    199  1.1  christos TEST_END
    200  1.1  christos 
    201  1.1  christos int
    202  1.1  christos main(void) {
    203  1.1  christos 	return test(
    204  1.1  christos 	    test_guarded_small,
    205  1.1  christos 	    test_guarded_large,
    206  1.1  christos 	    test_guarded_decay);
    207  1.1  christos }
    208