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