1*c43cad87SWarner Losh #include "jemalloc/internal/jemalloc_preamble.h" 2*c43cad87SWarner Losh #include "jemalloc/internal/jemalloc_internal_includes.h" 3*c43cad87SWarner Losh 4*c43cad87SWarner Losh #include "jemalloc/internal/assert.h" 5*c43cad87SWarner Losh #include "jemalloc/internal/ehooks.h" 6*c43cad87SWarner Losh #include "jemalloc/internal/san.h" 7*c43cad87SWarner Losh #include "jemalloc/internal/tsd.h" 8*c43cad87SWarner Losh 9*c43cad87SWarner Losh /* The sanitizer options. */ 10*c43cad87SWarner Losh size_t opt_san_guard_large = SAN_GUARD_LARGE_EVERY_N_EXTENTS_DEFAULT; 11*c43cad87SWarner Losh size_t opt_san_guard_small = SAN_GUARD_SMALL_EVERY_N_EXTENTS_DEFAULT; 12*c43cad87SWarner Losh 13*c43cad87SWarner Losh /* Aligned (-1 is off) ptrs will be junked & stashed on dealloc. */ 14*c43cad87SWarner Losh ssize_t opt_lg_san_uaf_align = SAN_LG_UAF_ALIGN_DEFAULT; 15*c43cad87SWarner Losh 16*c43cad87SWarner Losh /* 17*c43cad87SWarner Losh * Initialized in san_init(). When disabled, the mask is set to (uintptr_t)-1 18*c43cad87SWarner Losh * to always fail the nonfast_align check. 19*c43cad87SWarner Losh */ 20*c43cad87SWarner Losh uintptr_t san_cache_bin_nonfast_mask = SAN_CACHE_BIN_NONFAST_MASK_DEFAULT; 21*c43cad87SWarner Losh 22*c43cad87SWarner Losh static inline void 23*c43cad87SWarner Losh san_find_guarded_addr(edata_t *edata, uintptr_t *guard1, uintptr_t *guard2, 24*c43cad87SWarner Losh uintptr_t *addr, size_t size, bool left, bool right) { 25*c43cad87SWarner Losh assert(!edata_guarded_get(edata)); 26*c43cad87SWarner Losh assert(size % PAGE == 0); 27*c43cad87SWarner Losh *addr = (uintptr_t)edata_base_get(edata); 28*c43cad87SWarner Losh if (left) { 29*c43cad87SWarner Losh *guard1 = *addr; 30*c43cad87SWarner Losh *addr += SAN_PAGE_GUARD; 31*c43cad87SWarner Losh } else { 32*c43cad87SWarner Losh *guard1 = 0; 33*c43cad87SWarner Losh } 34*c43cad87SWarner Losh 35*c43cad87SWarner Losh if (right) { 36*c43cad87SWarner Losh *guard2 = *addr + size; 37*c43cad87SWarner Losh } else { 38*c43cad87SWarner Losh *guard2 = 0; 39*c43cad87SWarner Losh } 40*c43cad87SWarner Losh } 41*c43cad87SWarner Losh 42*c43cad87SWarner Losh static inline void 43*c43cad87SWarner Losh san_find_unguarded_addr(edata_t *edata, uintptr_t *guard1, uintptr_t *guard2, 44*c43cad87SWarner Losh uintptr_t *addr, size_t size, bool left, bool right) { 45*c43cad87SWarner Losh assert(edata_guarded_get(edata)); 46*c43cad87SWarner Losh assert(size % PAGE == 0); 47*c43cad87SWarner Losh *addr = (uintptr_t)edata_base_get(edata); 48*c43cad87SWarner Losh if (right) { 49*c43cad87SWarner Losh *guard2 = *addr + size; 50*c43cad87SWarner Losh } else { 51*c43cad87SWarner Losh *guard2 = 0; 52*c43cad87SWarner Losh } 53*c43cad87SWarner Losh 54*c43cad87SWarner Losh if (left) { 55*c43cad87SWarner Losh *guard1 = *addr - SAN_PAGE_GUARD; 56*c43cad87SWarner Losh assert(*guard1 != 0); 57*c43cad87SWarner Losh *addr = *guard1; 58*c43cad87SWarner Losh } else { 59*c43cad87SWarner Losh *guard1 = 0; 60*c43cad87SWarner Losh } 61*c43cad87SWarner Losh } 62*c43cad87SWarner Losh 63*c43cad87SWarner Losh void 64*c43cad87SWarner Losh san_guard_pages(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata, emap_t *emap, 65*c43cad87SWarner Losh bool left, bool right, bool remap) { 66*c43cad87SWarner Losh assert(left || right); 67*c43cad87SWarner Losh if (remap) { 68*c43cad87SWarner Losh emap_deregister_boundary(tsdn, emap, edata); 69*c43cad87SWarner Losh } 70*c43cad87SWarner Losh 71*c43cad87SWarner Losh size_t size_with_guards = edata_size_get(edata); 72*c43cad87SWarner Losh size_t usize = (left && right) 73*c43cad87SWarner Losh ? san_two_side_unguarded_sz(size_with_guards) 74*c43cad87SWarner Losh : san_one_side_unguarded_sz(size_with_guards); 75*c43cad87SWarner Losh 76*c43cad87SWarner Losh uintptr_t guard1, guard2, addr; 77*c43cad87SWarner Losh san_find_guarded_addr(edata, &guard1, &guard2, &addr, usize, left, 78*c43cad87SWarner Losh right); 79*c43cad87SWarner Losh 80*c43cad87SWarner Losh assert(edata_state_get(edata) == extent_state_active); 81*c43cad87SWarner Losh ehooks_guard(tsdn, ehooks, (void *)guard1, (void *)guard2); 82*c43cad87SWarner Losh 83*c43cad87SWarner Losh /* Update the guarded addr and usable size of the edata. */ 84*c43cad87SWarner Losh edata_size_set(edata, usize); 85*c43cad87SWarner Losh edata_addr_set(edata, (void *)addr); 86*c43cad87SWarner Losh edata_guarded_set(edata, true); 87*c43cad87SWarner Losh 88*c43cad87SWarner Losh if (remap) { 89*c43cad87SWarner Losh emap_register_boundary(tsdn, emap, edata, SC_NSIZES, 90*c43cad87SWarner Losh /* slab */ false); 91*c43cad87SWarner Losh } 92*c43cad87SWarner Losh } 93*c43cad87SWarner Losh 94*c43cad87SWarner Losh static void 95*c43cad87SWarner Losh san_unguard_pages_impl(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata, 96*c43cad87SWarner Losh emap_t *emap, bool left, bool right, bool remap) { 97*c43cad87SWarner Losh assert(left || right); 98*c43cad87SWarner Losh /* Remove the inner boundary which no longer exists. */ 99*c43cad87SWarner Losh if (remap) { 100*c43cad87SWarner Losh assert(edata_state_get(edata) == extent_state_active); 101*c43cad87SWarner Losh emap_deregister_boundary(tsdn, emap, edata); 102*c43cad87SWarner Losh } else { 103*c43cad87SWarner Losh assert(edata_state_get(edata) == extent_state_retained); 104*c43cad87SWarner Losh } 105*c43cad87SWarner Losh 106*c43cad87SWarner Losh size_t size = edata_size_get(edata); 107*c43cad87SWarner Losh size_t size_with_guards = (left && right) 108*c43cad87SWarner Losh ? san_two_side_guarded_sz(size) 109*c43cad87SWarner Losh : san_one_side_guarded_sz(size); 110*c43cad87SWarner Losh 111*c43cad87SWarner Losh uintptr_t guard1, guard2, addr; 112*c43cad87SWarner Losh san_find_unguarded_addr(edata, &guard1, &guard2, &addr, size, left, 113*c43cad87SWarner Losh right); 114*c43cad87SWarner Losh 115*c43cad87SWarner Losh ehooks_unguard(tsdn, ehooks, (void *)guard1, (void *)guard2); 116*c43cad87SWarner Losh 117*c43cad87SWarner Losh /* Update the true addr and usable size of the edata. */ 118*c43cad87SWarner Losh edata_size_set(edata, size_with_guards); 119*c43cad87SWarner Losh edata_addr_set(edata, (void *)addr); 120*c43cad87SWarner Losh edata_guarded_set(edata, false); 121*c43cad87SWarner Losh 122*c43cad87SWarner Losh /* 123*c43cad87SWarner Losh * Then re-register the outer boundary including the guards, if 124*c43cad87SWarner Losh * requested. 125*c43cad87SWarner Losh */ 126*c43cad87SWarner Losh if (remap) { 127*c43cad87SWarner Losh emap_register_boundary(tsdn, emap, edata, SC_NSIZES, 128*c43cad87SWarner Losh /* slab */ false); 129*c43cad87SWarner Losh } 130*c43cad87SWarner Losh } 131*c43cad87SWarner Losh 132*c43cad87SWarner Losh void 133*c43cad87SWarner Losh san_unguard_pages(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata, 134*c43cad87SWarner Losh emap_t *emap, bool left, bool right) { 135*c43cad87SWarner Losh san_unguard_pages_impl(tsdn, ehooks, edata, emap, left, right, 136*c43cad87SWarner Losh /* remap */ true); 137*c43cad87SWarner Losh } 138*c43cad87SWarner Losh 139*c43cad87SWarner Losh void 140*c43cad87SWarner Losh san_unguard_pages_pre_destroy(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata, 141*c43cad87SWarner Losh emap_t *emap) { 142*c43cad87SWarner Losh emap_assert_not_mapped(tsdn, emap, edata); 143*c43cad87SWarner Losh /* 144*c43cad87SWarner Losh * We don't want to touch the emap of about to be destroyed extents, as 145*c43cad87SWarner Losh * they have been unmapped upon eviction from the retained ecache. Also, 146*c43cad87SWarner Losh * we unguard the extents to the right, because retained extents only 147*c43cad87SWarner Losh * own their right guard page per san_bump_alloc's logic. 148*c43cad87SWarner Losh */ 149*c43cad87SWarner Losh san_unguard_pages_impl(tsdn, ehooks, edata, emap, /* left */ false, 150*c43cad87SWarner Losh /* right */ true, /* remap */ false); 151*c43cad87SWarner Losh } 152*c43cad87SWarner Losh 153*c43cad87SWarner Losh static bool 154*c43cad87SWarner Losh san_stashed_corrupted(void *ptr, size_t size) { 155*c43cad87SWarner Losh if (san_junk_ptr_should_slow()) { 156*c43cad87SWarner Losh for (size_t i = 0; i < size; i++) { 157*c43cad87SWarner Losh if (((char *)ptr)[i] != (char)uaf_detect_junk) { 158*c43cad87SWarner Losh return true; 159*c43cad87SWarner Losh } 160*c43cad87SWarner Losh } 161*c43cad87SWarner Losh return false; 162*c43cad87SWarner Losh } 163*c43cad87SWarner Losh 164*c43cad87SWarner Losh void *first, *mid, *last; 165*c43cad87SWarner Losh san_junk_ptr_locations(ptr, size, &first, &mid, &last); 166*c43cad87SWarner Losh if (*(uintptr_t *)first != uaf_detect_junk || 167*c43cad87SWarner Losh *(uintptr_t *)mid != uaf_detect_junk || 168*c43cad87SWarner Losh *(uintptr_t *)last != uaf_detect_junk) { 169*c43cad87SWarner Losh return true; 170*c43cad87SWarner Losh } 171*c43cad87SWarner Losh 172*c43cad87SWarner Losh return false; 173*c43cad87SWarner Losh } 174*c43cad87SWarner Losh 175*c43cad87SWarner Losh void 176*c43cad87SWarner Losh san_check_stashed_ptrs(void **ptrs, size_t nstashed, size_t usize) { 177*c43cad87SWarner Losh /* 178*c43cad87SWarner Losh * Verify that the junked-filled & stashed pointers remain unchanged, to 179*c43cad87SWarner Losh * detect write-after-free. 180*c43cad87SWarner Losh */ 181*c43cad87SWarner Losh for (size_t n = 0; n < nstashed; n++) { 182*c43cad87SWarner Losh void *stashed = ptrs[n]; 183*c43cad87SWarner Losh assert(stashed != NULL); 184*c43cad87SWarner Losh assert(cache_bin_nonfast_aligned(stashed)); 185*c43cad87SWarner Losh if (unlikely(san_stashed_corrupted(stashed, usize))) { 186*c43cad87SWarner Losh safety_check_fail("<jemalloc>: Write-after-free " 187*c43cad87SWarner Losh "detected on deallocated pointer %p (size %zu).\n", 188*c43cad87SWarner Losh stashed, usize); 189*c43cad87SWarner Losh } 190*c43cad87SWarner Losh } 191*c43cad87SWarner Losh } 192*c43cad87SWarner Losh 193*c43cad87SWarner Losh void 194*c43cad87SWarner Losh tsd_san_init(tsd_t *tsd) { 195*c43cad87SWarner Losh *tsd_san_extents_until_guard_smallp_get(tsd) = opt_san_guard_small; 196*c43cad87SWarner Losh *tsd_san_extents_until_guard_largep_get(tsd) = opt_san_guard_large; 197*c43cad87SWarner Losh } 198*c43cad87SWarner Losh 199*c43cad87SWarner Losh void 200*c43cad87SWarner Losh san_init(ssize_t lg_san_uaf_align) { 201*c43cad87SWarner Losh assert(lg_san_uaf_align == -1 || lg_san_uaf_align >= LG_PAGE); 202*c43cad87SWarner Losh if (lg_san_uaf_align == -1) { 203*c43cad87SWarner Losh san_cache_bin_nonfast_mask = (uintptr_t)-1; 204*c43cad87SWarner Losh return; 205*c43cad87SWarner Losh } 206*c43cad87SWarner Losh 207*c43cad87SWarner Losh san_cache_bin_nonfast_mask = ((uintptr_t)1 << lg_san_uaf_align) - 1; 208*c43cad87SWarner Losh } 209