mirror of
https://github.com/jemalloc/jemalloc.git
synced 2026-07-23 21:23:06 +00:00
Implement use-after-free detection using junk and stash.
On deallocation, sampled pointers (specially aligned) get junked and stashed into tcache (to prevent immediate reuse). The expected behavior is to have read-after-free corrupted and stopped by the junk-filling, while write-after-free is checked when flushing the stashed pointers.
This commit is contained in:
60
src/san.c
60
src/san.c
@@ -10,6 +10,15 @@
|
||||
size_t opt_san_guard_large = SAN_GUARD_LARGE_EVERY_N_EXTENTS_DEFAULT;
|
||||
size_t opt_san_guard_small = SAN_GUARD_SMALL_EVERY_N_EXTENTS_DEFAULT;
|
||||
|
||||
/* Aligned (-1 is off) ptrs will be junked & stashed on dealloc. */
|
||||
ssize_t opt_lg_san_uaf_align = SAN_LG_UAF_ALIGN_DEFAULT;
|
||||
|
||||
/*
|
||||
* Initialized in san_init(). When disabled, the mask is set to (uintptr_t)-1
|
||||
* to always fail the nonfast_align check.
|
||||
*/
|
||||
uintptr_t san_cache_bin_nonfast_mask = SAN_CACHE_BIN_NONFAST_MASK_DEFAULT;
|
||||
|
||||
static inline void
|
||||
san_find_guarded_addr(edata_t *edata, uintptr_t *guard1, uintptr_t *guard2,
|
||||
uintptr_t *addr, size_t size, bool left, bool right) {
|
||||
@@ -141,8 +150,59 @@ san_unguard_pages_pre_destroy(tsdn_t *tsdn, ehooks_t *ehooks, edata_t *edata,
|
||||
/* right */ true, /* remap */ false);
|
||||
}
|
||||
|
||||
static bool
|
||||
san_stashed_corrupted(void *ptr, size_t size) {
|
||||
if (san_junk_ptr_should_slow()) {
|
||||
for (size_t i = 0; i < size; i++) {
|
||||
if (((char *)ptr)[i] != (char)uaf_detect_junk) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void *first, *mid, *last;
|
||||
san_junk_ptr_locations(ptr, size, &first, &mid, &last);
|
||||
if (*(uintptr_t *)first != uaf_detect_junk ||
|
||||
*(uintptr_t *)mid != uaf_detect_junk ||
|
||||
*(uintptr_t *)last != uaf_detect_junk) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
san_check_stashed_ptrs(void **ptrs, size_t nstashed, size_t usize) {
|
||||
/*
|
||||
* Verify that the junked-filled & stashed pointers remain unchanged, to
|
||||
* detect write-after-free.
|
||||
*/
|
||||
for (size_t n = 0; n < nstashed; n++) {
|
||||
void *stashed = ptrs[n];
|
||||
assert(stashed != NULL);
|
||||
assert(cache_bin_nonfast_aligned(stashed));
|
||||
if (unlikely(san_stashed_corrupted(stashed, usize))) {
|
||||
safety_check_fail("<jemalloc>: Write-after-free "
|
||||
"detected on deallocated pointer %p (size %zu).\n",
|
||||
stashed, usize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
tsd_san_init(tsd_t *tsd) {
|
||||
*tsd_san_extents_until_guard_smallp_get(tsd) = opt_san_guard_small;
|
||||
*tsd_san_extents_until_guard_largep_get(tsd) = opt_san_guard_large;
|
||||
}
|
||||
|
||||
void
|
||||
san_init(ssize_t lg_san_uaf_align) {
|
||||
assert(lg_san_uaf_align == -1 || lg_san_uaf_align >= LG_PAGE);
|
||||
if (lg_san_uaf_align == -1) {
|
||||
san_cache_bin_nonfast_mask = (uintptr_t)-1;
|
||||
return;
|
||||
}
|
||||
|
||||
san_cache_bin_nonfast_mask = ((uintptr_t)1 << lg_san_uaf_align) - 1;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user