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