1 1.1 christos #ifndef JEMALLOC_INTERNAL_GUARD_H 2 1.1 christos #define JEMALLOC_INTERNAL_GUARD_H 3 1.1 christos 4 1.1 christos #include "jemalloc/internal/ehooks.h" 5 1.1 christos #include "jemalloc/internal/emap.h" 6 1.1 christos 7 1.1 christos #define SAN_PAGE_GUARD PAGE 8 1.1 christos #define SAN_PAGE_GUARDS_SIZE (SAN_PAGE_GUARD * 2) 9 1.1 christos 10 1.1 christos #define SAN_GUARD_LARGE_EVERY_N_EXTENTS_DEFAULT 0 11 1.1 christos #define SAN_GUARD_SMALL_EVERY_N_EXTENTS_DEFAULT 0 12 1.1 christos 13 1.1 christos #define SAN_LG_UAF_ALIGN_DEFAULT (-1) 14 1.1 christos #define SAN_CACHE_BIN_NONFAST_MASK_DEFAULT (uintptr_t)(-1) 15 1.1 christos 16 1.1 christos static const uintptr_t uaf_detect_junk = (uintptr_t)0x5b5b5b5b5b5b5b5bULL; 17 1.1 christos 18 1.1 christos /* 0 means disabled, i.e. never guarded. */ 19 1.1 christos extern size_t opt_san_guard_large; 20 1.1 christos extern size_t opt_san_guard_small; 21 1.1 christos /* -1 means disabled, i.e. never check for use-after-free. */ 22 1.1 christos extern ssize_t opt_lg_san_uaf_align; 23 1.1 christos 24 1.1 christos void san_guard_pages(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata, 25 1.1 christos emap_t *emap, bool left, bool right, bool remap); 26 1.1 christos void san_unguard_pages(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata, 27 1.1 christos emap_t *emap, bool left, bool right); 28 1.1 christos /* 29 1.1 christos * Unguard the extent, but don't modify emap boundaries. Must be called on an 30 1.1 christos * extent that has been erased from emap and shouldn't be placed back. 31 1.1 christos */ 32 1.1 christos void san_unguard_pages_pre_destroy(tsdn_t *tsdn, ehooks_t *ehooks, 33 1.1 christos edata_t *edata, emap_t *emap); 34 1.1 christos void san_check_stashed_ptrs(void **ptrs, size_t nstashed, size_t usize); 35 1.1 christos 36 1.1 christos void tsd_san_init(tsd_t *tsd); 37 1.1 christos void san_init(ssize_t lg_san_uaf_align); 38 1.1 christos 39 1.1 christos static inline void 40 1.1 christos san_guard_pages_two_sided(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata, 41 1.1 christos emap_t *emap, bool remap) { 42 1.1 christos san_guard_pages(tsdn, ehooks, edata, emap, true, true, remap); 43 1.1 christos } 44 1.1 christos 45 1.1 christos static inline void 46 1.1 christos san_unguard_pages_two_sided(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata, 47 1.1 christos emap_t *emap) { 48 1.1 christos san_unguard_pages(tsdn, ehooks, edata, emap, true, true); 49 1.1 christos } 50 1.1 christos 51 1.1 christos static inline size_t 52 1.1 christos san_two_side_unguarded_sz(size_t size) { 53 1.1 christos assert(size % PAGE == 0); 54 1.1 christos assert(size >= SAN_PAGE_GUARDS_SIZE); 55 1.1 christos return size - SAN_PAGE_GUARDS_SIZE; 56 1.1 christos } 57 1.1 christos 58 1.1 christos static inline size_t 59 1.1 christos san_two_side_guarded_sz(size_t size) { 60 1.1 christos assert(size % PAGE == 0); 61 1.1 christos return size + SAN_PAGE_GUARDS_SIZE; 62 1.1 christos } 63 1.1 christos 64 1.1 christos static inline size_t 65 1.1 christos san_one_side_unguarded_sz(size_t size) { 66 1.1 christos assert(size % PAGE == 0); 67 1.1 christos assert(size >= SAN_PAGE_GUARD); 68 1.1 christos return size - SAN_PAGE_GUARD; 69 1.1 christos } 70 1.1 christos 71 1.1 christos static inline size_t 72 1.1 christos san_one_side_guarded_sz(size_t size) { 73 1.1 christos assert(size % PAGE == 0); 74 1.1 christos return size + SAN_PAGE_GUARD; 75 1.1 christos } 76 1.1 christos 77 1.1 christos static inline bool 78 1.1 christos san_guard_enabled(void) { 79 1.1 christos return (opt_san_guard_large != 0 || opt_san_guard_small != 0); 80 1.1 christos } 81 1.1 christos 82 1.1 christos static inline bool 83 1.1 christos san_large_extent_decide_guard(tsdn_t *tsdn, ehooks_t *ehooks, size_t size, 84 1.1 christos size_t alignment) { 85 1.1 christos if (opt_san_guard_large == 0 || ehooks_guard_will_fail(ehooks) || 86 1.1 christos tsdn_null(tsdn)) { 87 1.1 christos return false; 88 1.1 christos } 89 1.1 christos 90 1.1 christos tsd_t *tsd = tsdn_tsd(tsdn); 91 1.1 christos uint64_t n = tsd_san_extents_until_guard_large_get(tsd); 92 1.1 christos assert(n >= 1); 93 1.1 christos if (n > 1) { 94 1.1 christos /* 95 1.1 christos * Subtract conditionally because the guard may not happen due 96 1.1 christos * to alignment or size restriction below. 97 1.1 christos */ 98 1.1 christos *tsd_san_extents_until_guard_largep_get(tsd) = n - 1; 99 1.1 christos } 100 1.1 christos 101 1.1 christos if (n == 1 && (alignment <= PAGE) && 102 1.1 christos (san_two_side_guarded_sz(size) <= SC_LARGE_MAXCLASS)) { 103 1.1 christos *tsd_san_extents_until_guard_largep_get(tsd) = 104 1.1 christos opt_san_guard_large; 105 1.1 christos return true; 106 1.1 christos } else { 107 1.1 christos assert(tsd_san_extents_until_guard_large_get(tsd) >= 1); 108 1.1 christos return false; 109 1.1 christos } 110 1.1 christos } 111 1.1 christos 112 1.1 christos static inline bool 113 1.1 christos san_slab_extent_decide_guard(tsdn_t *tsdn, ehooks_t *ehooks) { 114 1.1 christos if (opt_san_guard_small == 0 || ehooks_guard_will_fail(ehooks) || 115 1.1 christos tsdn_null(tsdn)) { 116 1.1 christos return false; 117 1.1 christos } 118 1.1 christos 119 1.1 christos tsd_t *tsd = tsdn_tsd(tsdn); 120 1.1 christos uint64_t n = tsd_san_extents_until_guard_small_get(tsd); 121 1.1 christos assert(n >= 1); 122 1.1 christos if (n == 1) { 123 1.1 christos *tsd_san_extents_until_guard_smallp_get(tsd) = 124 1.1 christos opt_san_guard_small; 125 1.1 christos return true; 126 1.1 christos } else { 127 1.1 christos *tsd_san_extents_until_guard_smallp_get(tsd) = n - 1; 128 1.1 christos assert(tsd_san_extents_until_guard_small_get(tsd) >= 1); 129 1.1 christos return false; 130 1.1 christos } 131 1.1 christos } 132 1.1 christos 133 1.1 christos static inline void 134 1.1 christos san_junk_ptr_locations(void *ptr, size_t usize, void **first, void **mid, 135 1.1 christos void **last) { 136 1.1 christos size_t ptr_sz = sizeof(void *); 137 1.1 christos 138 1.1 christos *first = ptr; 139 1.1 christos 140 1.1 christos *mid = (void *)((uintptr_t)ptr + ((usize >> 1) & ~(ptr_sz - 1))); 141 1.1 christos assert(*first != *mid || usize == ptr_sz); 142 1.1 christos assert((uintptr_t)*first <= (uintptr_t)*mid); 143 1.1 christos 144 1.1 christos /* 145 1.1 christos * When usize > 32K, the gap between requested_size and usize might be 146 1.1 christos * greater than 4K -- this means the last write may access an 147 1.1 christos * likely-untouched page (default settings w/ 4K pages). However by 148 1.1 christos * default the tcache only goes up to the 32K size class, and is usually 149 1.1 christos * tuned lower instead of higher, which makes it less of a concern. 150 1.1 christos */ 151 1.1 christos *last = (void *)((uintptr_t)ptr + usize - sizeof(uaf_detect_junk)); 152 1.1 christos assert(*first != *last || usize == ptr_sz); 153 1.1 christos assert(*mid != *last || usize <= ptr_sz * 2); 154 1.1 christos assert((uintptr_t)*mid <= (uintptr_t)*last); 155 1.1 christos } 156 1.1 christos 157 1.1 christos static inline bool 158 1.1 christos san_junk_ptr_should_slow(void) { 159 1.1 christos /* 160 1.1 christos * The latter condition (pointer size greater than the min size class) 161 1.1 christos * is not expected -- fall back to the slow path for simplicity. 162 1.1 christos */ 163 1.1 christos return config_debug || (LG_SIZEOF_PTR > SC_LG_TINY_MIN); 164 1.1 christos } 165 1.1 christos 166 1.1 christos static inline void 167 1.1 christos san_junk_ptr(void *ptr, size_t usize) { 168 1.1 christos if (san_junk_ptr_should_slow()) { 169 1.1 christos memset(ptr, (char)uaf_detect_junk, usize); 170 1.1 christos return; 171 1.1 christos } 172 1.1 christos 173 1.1 christos void *first, *mid, *last; 174 1.1 christos san_junk_ptr_locations(ptr, usize, &first, &mid, &last); 175 1.1 christos *(uintptr_t *)first = uaf_detect_junk; 176 1.1 christos *(uintptr_t *)mid = uaf_detect_junk; 177 1.1 christos *(uintptr_t *)last = uaf_detect_junk; 178 1.1 christos } 179 1.1 christos 180 1.1 christos static inline bool 181 1.1 christos san_uaf_detection_enabled(void) { 182 1.1 christos bool ret = config_uaf_detection && (opt_lg_san_uaf_align != -1); 183 1.1 christos if (config_uaf_detection && ret) { 184 1.1 christos assert(san_cache_bin_nonfast_mask == ((uintptr_t)1 << 185 1.1 christos opt_lg_san_uaf_align) - 1); 186 1.1 christos } 187 1.1 christos 188 1.1 christos return ret; 189 1.1 christos } 190 1.1 christos 191 1.1 christos #endif /* JEMALLOC_INTERNAL_GUARD_H */ 192