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