1 | //===-- msan_poisoning.cpp --------------------------------------*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file is a part of MemorySanitizer. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "msan_poisoning.h" |
14 | |
15 | #include "interception/interception.h" |
16 | #include "msan_origin.h" |
17 | #include "msan_thread.h" |
18 | #include "sanitizer_common/sanitizer_common.h" |
19 | |
20 | DECLARE_REAL(void *, memset, void *dest, int c, uptr n) |
21 | DECLARE_REAL(void *, memcpy, void *dest, const void *src, uptr n) |
22 | DECLARE_REAL(void *, memmove, void *dest, const void *src, uptr n) |
23 | |
24 | namespace __msan { |
25 | |
26 | u32 GetOriginIfPoisoned(uptr addr, uptr size) { |
27 | unsigned char *s = (unsigned char *)MEM_TO_SHADOW(addr); |
28 | for (uptr i = 0; i < size; ++i) |
29 | if (s[i]) return *(u32 *)SHADOW_TO_ORIGIN(((uptr)s + i) & ~3UL); |
30 | return 0; |
31 | } |
32 | |
33 | void SetOriginIfPoisoned(uptr addr, uptr src_shadow, uptr size, |
34 | u32 src_origin) { |
35 | uptr dst_s = MEM_TO_SHADOW(addr); |
36 | uptr src_s = src_shadow; |
37 | uptr src_s_end = src_s + size; |
38 | |
39 | for (; src_s < src_s_end; ++dst_s, ++src_s) |
40 | if (*(u8 *)src_s) *(u32 *)SHADOW_TO_ORIGIN(dst_s & ~3UL) = src_origin; |
41 | } |
42 | |
43 | void CopyOrigin(const void *dst, const void *src, uptr size, |
44 | StackTrace *stack) { |
45 | if (!MEM_IS_APP(dst) || !MEM_IS_APP(src)) return; |
46 | |
47 | uptr d = (uptr)dst; |
48 | uptr beg = d & ~3UL; |
49 | // Copy left unaligned origin if that memory is poisoned. |
50 | if (beg < d) { |
51 | u32 o = GetOriginIfPoisoned(addr: (uptr)src, size: beg + 4 - d); |
52 | if (o) { |
53 | if (__msan_get_track_origins() > 1) o = ChainOrigin(id: o, stack); |
54 | *(u32 *)MEM_TO_ORIGIN(beg) = o; |
55 | } |
56 | beg += 4; |
57 | } |
58 | |
59 | uptr end = (d + size) & ~3UL; |
60 | // If both ends fall into the same 4-byte slot, we are done. |
61 | if (end < beg) return; |
62 | |
63 | // Copy right unaligned origin if that memory is poisoned. |
64 | if (end < d + size) { |
65 | u32 o = GetOriginIfPoisoned(addr: (uptr)src + (end - d), size: (d + size) - end); |
66 | if (o) { |
67 | if (__msan_get_track_origins() > 1) o = ChainOrigin(id: o, stack); |
68 | *(u32 *)MEM_TO_ORIGIN(end) = o; |
69 | } |
70 | } |
71 | |
72 | if (beg < end) { |
73 | // Align src up. |
74 | uptr s = ((uptr)src + 3) & ~3UL; |
75 | // FIXME: factor out to msan_copy_origin_aligned |
76 | if (__msan_get_track_origins() > 1) { |
77 | u32 *src = (u32 *)MEM_TO_ORIGIN(s); |
78 | u32 *src_s = (u32 *)MEM_TO_SHADOW(s); |
79 | u32 *src_end = (u32 *)MEM_TO_ORIGIN(s + (end - beg)); |
80 | u32 *dst = (u32 *)MEM_TO_ORIGIN(beg); |
81 | u32 src_o = 0; |
82 | u32 dst_o = 0; |
83 | for (; src < src_end; ++src, ++src_s, ++dst) { |
84 | if (!*src_s) continue; |
85 | if (*src != src_o) { |
86 | src_o = *src; |
87 | dst_o = ChainOrigin(id: src_o, stack); |
88 | } |
89 | *dst = dst_o; |
90 | } |
91 | } else { |
92 | REAL(memcpy)((void *)MEM_TO_ORIGIN(beg), (void *)MEM_TO_ORIGIN(s), |
93 | end - beg); |
94 | } |
95 | } |
96 | } |
97 | |
98 | void ReverseCopyOrigin(const void *dst, const void *src, uptr size, |
99 | StackTrace *stack) { |
100 | if (!MEM_IS_APP(dst) || !MEM_IS_APP(src)) |
101 | return; |
102 | |
103 | uptr d = (uptr)dst; |
104 | uptr end = (d + size) & ~3UL; |
105 | |
106 | // Copy right unaligned origin if that memory is poisoned. |
107 | if (end < d + size) { |
108 | u32 o = GetOriginIfPoisoned(addr: (uptr)src + (end - d), size: (d + size) - end); |
109 | if (o) { |
110 | if (__msan_get_track_origins() > 1) |
111 | o = ChainOrigin(id: o, stack); |
112 | *(u32 *)MEM_TO_ORIGIN(end) = o; |
113 | } |
114 | } |
115 | |
116 | uptr beg = d & ~3UL; |
117 | |
118 | if (beg + 4 < end) { |
119 | // Align src up. |
120 | uptr s = ((uptr)src + 3) & ~3UL; |
121 | if (__msan_get_track_origins() > 1) { |
122 | u32 *src = (u32 *)MEM_TO_ORIGIN(s + end - beg - 4); |
123 | u32 *src_s = (u32 *)MEM_TO_SHADOW(s + end - beg - 4); |
124 | u32 *src_begin = (u32 *)MEM_TO_ORIGIN(s); |
125 | u32 *dst = (u32 *)MEM_TO_ORIGIN(end - 4); |
126 | u32 src_o = 0; |
127 | u32 dst_o = 0; |
128 | for (; src >= src_begin; --src, --src_s, --dst) { |
129 | if (!*src_s) |
130 | continue; |
131 | if (*src != src_o) { |
132 | src_o = *src; |
133 | dst_o = ChainOrigin(id: src_o, stack); |
134 | } |
135 | *dst = dst_o; |
136 | } |
137 | } else { |
138 | REAL(memmove) |
139 | ((void *)MEM_TO_ORIGIN(beg), (void *)MEM_TO_ORIGIN(s), end - beg - 4); |
140 | } |
141 | } |
142 | |
143 | // Copy left unaligned origin if that memory is poisoned. |
144 | if (beg < d) { |
145 | u32 o = GetOriginIfPoisoned(addr: (uptr)src, size: beg + 4 - d); |
146 | if (o) { |
147 | if (__msan_get_track_origins() > 1) |
148 | o = ChainOrigin(id: o, stack); |
149 | *(u32 *)MEM_TO_ORIGIN(beg) = o; |
150 | } |
151 | } |
152 | } |
153 | |
154 | void MoveOrigin(const void *dst, const void *src, uptr size, |
155 | StackTrace *stack) { |
156 | // If destination origin range overlaps with source origin range, move |
157 | // origins by coping origins in a reverse order; otherwise, copy origins in |
158 | // a normal order. |
159 | uptr src_aligned_beg = reinterpret_cast<uptr>(src) & ~3UL; |
160 | uptr src_aligned_end = (reinterpret_cast<uptr>(src) + size) & ~3UL; |
161 | uptr dst_aligned_beg = reinterpret_cast<uptr>(dst) & ~3UL; |
162 | if (dst_aligned_beg < src_aligned_end && dst_aligned_beg >= src_aligned_beg) |
163 | return ReverseCopyOrigin(dst, src, size, stack); |
164 | return CopyOrigin(dst, src, size, stack); |
165 | } |
166 | |
167 | void MoveShadowAndOrigin(const void *dst, const void *src, uptr size, |
168 | StackTrace *stack) { |
169 | if (!MEM_IS_APP(dst)) return; |
170 | if (!MEM_IS_APP(src)) return; |
171 | if (src == dst) return; |
172 | // MoveOrigin transfers origins by refering to their shadows. So we |
173 | // need to move origins before moving shadows. |
174 | if (__msan_get_track_origins()) |
175 | MoveOrigin(dst, src, size, stack); |
176 | REAL(memmove)((void *)MEM_TO_SHADOW((uptr)dst), |
177 | (void *)MEM_TO_SHADOW((uptr)src), size); |
178 | } |
179 | |
180 | void CopyShadowAndOrigin(const void *dst, const void *src, uptr size, |
181 | StackTrace *stack) { |
182 | if (!MEM_IS_APP(dst)) return; |
183 | if (!MEM_IS_APP(src)) return; |
184 | // Because origin's range is slightly larger than app range, memcpy may also |
185 | // cause overlapped origin ranges. |
186 | REAL(memcpy)((void *)MEM_TO_SHADOW((uptr)dst), |
187 | (void *)MEM_TO_SHADOW((uptr)src), size); |
188 | if (__msan_get_track_origins()) |
189 | MoveOrigin(dst, src, size, stack); |
190 | } |
191 | |
192 | void CopyMemory(void *dst, const void *src, uptr size, StackTrace *stack) { |
193 | REAL(memcpy)(dst, src, size); |
194 | CopyShadowAndOrigin(dst, src, size, stack); |
195 | } |
196 | |
197 | void SetShadow(const void *ptr, uptr size, u8 value) { |
198 | uptr PageSize = GetPageSizeCached(); |
199 | uptr shadow_beg = MEM_TO_SHADOW(ptr); |
200 | uptr shadow_end = shadow_beg + size; |
201 | if (value || |
202 | shadow_end - shadow_beg < common_flags()->clear_shadow_mmap_threshold) { |
203 | REAL(memset)((void *)shadow_beg, value, shadow_end - shadow_beg); |
204 | } else { |
205 | uptr page_beg = RoundUpTo(size: shadow_beg, boundary: PageSize); |
206 | uptr page_end = RoundDownTo(x: shadow_end, boundary: PageSize); |
207 | |
208 | if (page_beg >= page_end) { |
209 | REAL(memset)((void *)shadow_beg, 0, shadow_end - shadow_beg); |
210 | } else { |
211 | if (page_beg != shadow_beg) { |
212 | REAL(memset)((void *)shadow_beg, 0, page_beg - shadow_beg); |
213 | } |
214 | if (page_end != shadow_end) { |
215 | REAL(memset)((void *)page_end, 0, shadow_end - page_end); |
216 | } |
217 | if (!MmapFixedSuperNoReserve(fixed_addr: page_beg, size: page_end - page_beg)) |
218 | Die(); |
219 | |
220 | if (__msan_get_track_origins()) { |
221 | // No need to set origin for zero shadow, but we can release pages. |
222 | uptr origin_beg = RoundUpTo(MEM_TO_ORIGIN(ptr), boundary: PageSize); |
223 | if (!MmapFixedSuperNoReserve(fixed_addr: origin_beg, size: page_end - page_beg)) |
224 | Die(); |
225 | } |
226 | } |
227 | } |
228 | } |
229 | |
230 | void SetOrigin(const void *dst, uptr size, u32 origin) { |
231 | // Origin mapping is 4 bytes per 4 bytes of application memory. |
232 | // Here we extend the range such that its left and right bounds are both |
233 | // 4 byte aligned. |
234 | uptr x = MEM_TO_ORIGIN((uptr)dst); |
235 | uptr beg = x & ~3UL; // align down. |
236 | uptr end = (x + size + 3) & ~3UL; // align up. |
237 | u64 origin64 = ((u64)origin << 32) | origin; |
238 | // This is like memset, but the value is 32-bit. We unroll by 2 to write |
239 | // 64 bits at once. May want to unroll further to get 128-bit stores. |
240 | if (beg & 7ULL) { |
241 | *(u32 *)beg = origin; |
242 | beg += 4; |
243 | } |
244 | for (uptr addr = beg; addr < (end & ~7UL); addr += 8) *(u64 *)addr = origin64; |
245 | if (end & 7ULL) *(u32 *)(end - 4) = origin; |
246 | } |
247 | |
248 | void PoisonMemory(const void *dst, uptr size, StackTrace *stack) { |
249 | SetShadow(ptr: dst, size, value: (u8)-1); |
250 | |
251 | if (__msan_get_track_origins()) { |
252 | MsanThread *t = GetCurrentThread(); |
253 | if (t && t->InSignalHandler()) |
254 | return; |
255 | Origin o = Origin::CreateHeapOrigin(stack); |
256 | SetOrigin(dst, size, origin: o.raw_id()); |
257 | } |
258 | } |
259 | |
260 | } // namespace __msan |
261 | |