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