1 | //===-- tysan.cpp ---------------------------------------------------------===// |
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 TypeSanitizer. |
10 | // |
11 | // TypeSanitizer runtime. |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "sanitizer_common/sanitizer_atomic.h" |
15 | #include "sanitizer_common/sanitizer_common.h" |
16 | #include "sanitizer_common/sanitizer_flag_parser.h" |
17 | #include "sanitizer_common/sanitizer_flags.h" |
18 | #include "sanitizer_common/sanitizer_libc.h" |
19 | #include "sanitizer_common/sanitizer_report_decorator.h" |
20 | #include "sanitizer_common/sanitizer_stacktrace.h" |
21 | #include "sanitizer_common/sanitizer_symbolizer.h" |
22 | |
23 | #include "tysan/tysan.h" |
24 | |
25 | #include <string.h> |
26 | |
27 | using namespace __sanitizer; |
28 | using namespace __tysan; |
29 | |
30 | extern "C" SANITIZER_INTERFACE_ATTRIBUTE void |
31 | tysan_set_type_unknown(const void *addr, uptr size) { |
32 | if (tysan_inited) |
33 | internal_memset(s: shadow_for(ptr: addr), c: 0, n: size * sizeof(uptr)); |
34 | } |
35 | |
36 | extern "C" SANITIZER_INTERFACE_ATTRIBUTE void |
37 | tysan_copy_types(const void *daddr, const void *saddr, uptr size) { |
38 | if (tysan_inited) |
39 | internal_memmove(dest: shadow_for(ptr: daddr), src: shadow_for(ptr: saddr), n: size * sizeof(uptr)); |
40 | } |
41 | |
42 | static const char *getDisplayName(const char *Name) { |
43 | if (Name[0] == '\0') |
44 | return "<anonymous type>" ; |
45 | |
46 | // Clang generates tags for C++ types that demangle as typeinfo. Remove the |
47 | // prefix from the generated string. |
48 | const char *TIPrefix = "typeinfo name for " ; |
49 | size_t TIPrefixLen = strlen(s: TIPrefix); |
50 | |
51 | const char *DName = Symbolizer::GetOrInit()->Demangle(name: Name); |
52 | if (!internal_strncmp(s1: DName, s2: TIPrefix, n: TIPrefixLen)) |
53 | DName += TIPrefixLen; |
54 | |
55 | return DName; |
56 | } |
57 | |
58 | static void printTDName(tysan_type_descriptor *td) { |
59 | if (((sptr)td) <= 0) { |
60 | Printf(format: "<unknown type>" ); |
61 | return; |
62 | } |
63 | |
64 | switch (td->Tag) { |
65 | default: |
66 | CHECK(false && "invalid enum value" ); |
67 | break; |
68 | case TYSAN_MEMBER_TD: |
69 | printTDName(td: td->Member.Access); |
70 | if (td->Member.Access != td->Member.Base) { |
71 | Printf(format: " (in " ); |
72 | printTDName(td: td->Member.Base); |
73 | Printf(format: " at offset %zu)" , td->Member.Offset); |
74 | } |
75 | break; |
76 | case TYSAN_STRUCT_TD: |
77 | Printf(format: "%s" , getDisplayName( |
78 | Name: (char *)(td->Struct.Members + td->Struct.MemberCount))); |
79 | break; |
80 | } |
81 | } |
82 | |
83 | static tysan_type_descriptor *getRootTD(tysan_type_descriptor *TD) { |
84 | tysan_type_descriptor *RootTD = TD; |
85 | |
86 | do { |
87 | RootTD = TD; |
88 | |
89 | if (TD->Tag == TYSAN_STRUCT_TD) { |
90 | if (TD->Struct.MemberCount > 0) |
91 | TD = TD->Struct.Members[0].Type; |
92 | else |
93 | TD = nullptr; |
94 | } else if (TD->Tag == TYSAN_MEMBER_TD) { |
95 | TD = TD->Member.Access; |
96 | } else { |
97 | CHECK(false && "invalid enum value" ); |
98 | break; |
99 | } |
100 | } while (TD); |
101 | |
102 | return RootTD; |
103 | } |
104 | |
105 | // Walk up TDA to see if it reaches TDB. |
106 | static bool walkAliasTree(tysan_type_descriptor *TDA, |
107 | tysan_type_descriptor *TDB, uptr OffsetA, |
108 | uptr OffsetB) { |
109 | do { |
110 | if (TDA == TDB) |
111 | return OffsetA == OffsetB; |
112 | |
113 | if (TDA->Tag == TYSAN_STRUCT_TD) { |
114 | // Reached root type descriptor. |
115 | if (!TDA->Struct.MemberCount) |
116 | break; |
117 | |
118 | uptr Idx = 0; |
119 | for (; Idx < TDA->Struct.MemberCount - 1; ++Idx) { |
120 | if (TDA->Struct.Members[Idx].Offset >= OffsetA) |
121 | break; |
122 | } |
123 | |
124 | // This offset can't be negative. Therefore we must be accessing something |
125 | // before the current type (not legal) or partially inside the last type. |
126 | // In the latter case, we adjust Idx. |
127 | if (TDA->Struct.Members[Idx].Offset > OffsetA) { |
128 | // Trying to access something before the current type. |
129 | if (!Idx) |
130 | return false; |
131 | |
132 | Idx -= 1; |
133 | } |
134 | |
135 | OffsetA -= TDA->Struct.Members[Idx].Offset; |
136 | TDA = TDA->Struct.Members[Idx].Type; |
137 | } else { |
138 | CHECK(false && "invalid enum value" ); |
139 | break; |
140 | } |
141 | } while (TDA); |
142 | |
143 | return false; |
144 | } |
145 | |
146 | // Walk up the tree starting with TDA to see if we reach TDB. |
147 | static bool isAliasingLegalUp(tysan_type_descriptor *TDA, |
148 | tysan_type_descriptor *TDB) { |
149 | uptr OffsetA = 0, OffsetB = 0; |
150 | if (TDB->Tag == TYSAN_MEMBER_TD) { |
151 | OffsetB = TDB->Member.Offset; |
152 | TDB = TDB->Member.Base; |
153 | } |
154 | |
155 | if (TDA->Tag == TYSAN_MEMBER_TD) { |
156 | OffsetA = TDA->Member.Offset; |
157 | TDA = TDA->Member.Base; |
158 | } |
159 | |
160 | return walkAliasTree(TDA, TDB, OffsetA, OffsetB); |
161 | } |
162 | |
163 | static bool isAliasingLegalWithOffset(tysan_type_descriptor *TDA, |
164 | tysan_type_descriptor *TDB, |
165 | uptr OffsetB) { |
166 | // This is handled by calls to isAliasingLegalUp. |
167 | if (OffsetB == 0) |
168 | return false; |
169 | |
170 | // You can't have an offset into a member. |
171 | if (TDB->Tag == TYSAN_MEMBER_TD) |
172 | return false; |
173 | |
174 | uptr OffsetA = 0; |
175 | if (TDA->Tag == TYSAN_MEMBER_TD) { |
176 | OffsetA = TDA->Member.Offset; |
177 | TDA = TDA->Member.Base; |
178 | } |
179 | |
180 | // Since the access was partially inside TDB (the shadow), it can be assumed |
181 | // that we are accessing a member in an object. This means that rather than |
182 | // walk up the scalar access TDA to reach an object, we should walk up the |
183 | // object TBD to reach the scalar we are accessing it with. The offsets will |
184 | // still be checked at the end to make sure this alias is legal. |
185 | return walkAliasTree(TDA: TDB, TDB: TDA, OffsetA: OffsetB, OffsetB: OffsetA); |
186 | } |
187 | |
188 | static bool isAliasingLegal(tysan_type_descriptor *TDA, |
189 | tysan_type_descriptor *TDB, uptr OffsetB = 0) { |
190 | if (TDA == TDB || !TDB || !TDA) |
191 | return true; |
192 | |
193 | // Aliasing is legal is the two types have different root nodes. |
194 | if (getRootTD(TD: TDA) != getRootTD(TD: TDB)) |
195 | return true; |
196 | |
197 | // TDB may have been adjusted by offset TDAOffset in the caller to point to |
198 | // the outer type. Check for aliasing with and without adjusting for this |
199 | // offset. |
200 | return isAliasingLegalUp(TDA, TDB) || isAliasingLegalUp(TDA: TDB, TDB: TDA) || |
201 | isAliasingLegalWithOffset(TDA, TDB, OffsetB); |
202 | } |
203 | |
204 | namespace __tysan { |
205 | class Decorator : public __sanitizer::SanitizerCommonDecorator { |
206 | public: |
207 | Decorator() : SanitizerCommonDecorator() {} |
208 | const char *Warning() { return Red(); } |
209 | const char *Name() { return Green(); } |
210 | const char *End() { return Default(); } |
211 | }; |
212 | } // namespace __tysan |
213 | |
214 | ALWAYS_INLINE |
215 | static void reportError(void *Addr, int Size, tysan_type_descriptor *TD, |
216 | tysan_type_descriptor *OldTD, const char *AccessStr, |
217 | const char *DescStr, int Offset, uptr pc, uptr bp, |
218 | uptr sp) { |
219 | Decorator d; |
220 | Printf(format: "%s" , d.Warning()); |
221 | Report(format: "ERROR: TypeSanitizer: type-aliasing-violation on address %p" |
222 | " (pc %p bp %p sp %p tid %llu)\n" , |
223 | Addr, (void *)pc, (void *)bp, (void *)sp, GetTid()); |
224 | Printf(format: "%s" , d.End()); |
225 | Printf(format: "%s of size %d at %p with type " , AccessStr, Size, Addr); |
226 | |
227 | Printf(format: "%s" , d.Name()); |
228 | printTDName(td: TD); |
229 | Printf(format: "%s" , d.End()); |
230 | |
231 | Printf(format: " %s of type " , DescStr); |
232 | |
233 | Printf(format: "%s" , d.Name()); |
234 | printTDName(td: OldTD); |
235 | Printf(format: "%s" , d.End()); |
236 | |
237 | if (Offset != 0) |
238 | Printf(format: " that starts at offset %d\n" , Offset); |
239 | else |
240 | Printf(format: "\n" ); |
241 | |
242 | if (pc) { |
243 | uptr top = 0; |
244 | uptr bottom = 0; |
245 | if (flags().print_stacktrace) |
246 | GetThreadStackTopAndBottom(at_initialization: false, stack_top: &top, stack_bottom: &bottom); |
247 | |
248 | bool request_fast = StackTrace::WillUseFastUnwind(request_fast_unwind: true); |
249 | BufferedStackTrace ST; |
250 | ST.Unwind(max_depth: kStackTraceMax, pc, bp, context: 0, stack_top: top, stack_bottom: bottom, request_fast_unwind: request_fast); |
251 | ST.Print(); |
252 | } else { |
253 | Printf(format: "\n" ); |
254 | } |
255 | } |
256 | |
257 | extern "C" SANITIZER_INTERFACE_ATTRIBUTE void |
258 | __tysan_check(void *addr, int size, tysan_type_descriptor *td, int flags) { |
259 | GET_CALLER_PC_BP_SP; |
260 | |
261 | bool IsRead = flags & 1; |
262 | bool IsWrite = flags & 2; |
263 | const char *AccessStr; |
264 | if (IsRead && !IsWrite) |
265 | AccessStr = "READ" ; |
266 | else if (!IsRead && IsWrite) |
267 | AccessStr = "WRITE" ; |
268 | else |
269 | AccessStr = "ATOMIC UPDATE" ; |
270 | |
271 | tysan_type_descriptor **OldTDPtr = shadow_for(ptr: addr); |
272 | tysan_type_descriptor *OldTD = *OldTDPtr; |
273 | if (((sptr)OldTD) < 0) { |
274 | int i = -((sptr)OldTD); |
275 | OldTDPtr -= i; |
276 | OldTD = *OldTDPtr; |
277 | |
278 | if (!isAliasingLegal(TDA: td, TDB: OldTD, OffsetB: i)) |
279 | reportError(Addr: addr, Size: size, TD: td, OldTD, AccessStr, |
280 | DescStr: "accesses part of an existing object" , Offset: -i, pc, bp, sp); |
281 | |
282 | return; |
283 | } |
284 | |
285 | if (!isAliasingLegal(TDA: td, TDB: OldTD)) { |
286 | reportError(Addr: addr, Size: size, TD: td, OldTD, AccessStr, DescStr: "accesses an existing object" , |
287 | Offset: 0, pc, bp, sp); |
288 | return; |
289 | } |
290 | |
291 | // These types are allowed to alias (or the stored type is unknown), report |
292 | // an error if we find an interior type. |
293 | |
294 | for (int i = 0; i < size; ++i) { |
295 | OldTDPtr = shadow_for(ptr: (void *)(((uptr)addr) + i)); |
296 | OldTD = *OldTDPtr; |
297 | if (((sptr)OldTD) >= 0 && !isAliasingLegal(TDA: td, TDB: OldTD)) |
298 | reportError(Addr: addr, Size: size, TD: td, OldTD, AccessStr, |
299 | DescStr: "partially accesses an object" , Offset: i, pc, bp, sp); |
300 | } |
301 | } |
302 | |
303 | Flags __tysan::flags_data; |
304 | |
305 | SANITIZER_INTERFACE_ATTRIBUTE uptr __tysan_shadow_memory_address; |
306 | SANITIZER_INTERFACE_ATTRIBUTE uptr __tysan_app_memory_mask; |
307 | |
308 | #ifdef TYSAN_RUNTIME_VMA |
309 | // Runtime detected VMA size. |
310 | int __tysan::vmaSize; |
311 | #endif |
312 | |
313 | void Flags::SetDefaults() { |
314 | #define TYSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue; |
315 | #include "tysan_flags.inc" |
316 | #undef TYSAN_FLAG |
317 | } |
318 | |
319 | static void RegisterTySanFlags(FlagParser *parser, Flags *f) { |
320 | #define TYSAN_FLAG(Type, Name, DefaultValue, Description) \ |
321 | RegisterFlag(parser, #Name, Description, &f->Name); |
322 | #include "tysan_flags.inc" |
323 | #undef TYSAN_FLAG |
324 | } |
325 | |
326 | static void InitializeFlags() { |
327 | SetCommonFlagsDefaults(); |
328 | { |
329 | CommonFlags cf; |
330 | cf.CopyFrom(other: *common_flags()); |
331 | cf.external_symbolizer_path = GetEnv(name: "TYSAN_SYMBOLIZER_PATH" ); |
332 | OverrideCommonFlags(cf); |
333 | } |
334 | |
335 | flags().SetDefaults(); |
336 | |
337 | FlagParser parser; |
338 | RegisterCommonFlags(parser: &parser); |
339 | RegisterTySanFlags(parser: &parser, f: &flags()); |
340 | parser.ParseString(s: GetEnv(name: "TYSAN_OPTIONS" )); |
341 | InitializeCommonFlags(); |
342 | if (Verbosity()) |
343 | ReportUnrecognizedFlags(); |
344 | if (common_flags()->help) |
345 | parser.PrintFlagDescriptions(); |
346 | } |
347 | |
348 | static void TySanInitializePlatformEarly() { |
349 | AvoidCVE_2016_2143(); |
350 | #ifdef TYSAN_RUNTIME_VMA |
351 | vmaSize = (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1); |
352 | #if defined(__aarch64__) && !SANITIZER_APPLE |
353 | if (vmaSize != 39 && vmaSize != 42 && vmaSize != 48) { |
354 | Printf("FATAL: TypeSanitizer: unsupported VMA range\n" ); |
355 | Printf("FATAL: Found %d - Supported 39, 42 and 48\n" , vmaSize); |
356 | Die(); |
357 | } |
358 | #endif |
359 | #endif |
360 | |
361 | __sanitizer::InitializePlatformEarly(); |
362 | |
363 | __tysan_shadow_memory_address = ShadowAddr(); |
364 | __tysan_app_memory_mask = AppMask(); |
365 | } |
366 | |
367 | namespace __tysan { |
368 | bool tysan_inited = false; |
369 | bool tysan_init_is_running; |
370 | } // namespace __tysan |
371 | |
372 | extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __tysan_init() { |
373 | CHECK(!tysan_init_is_running); |
374 | if (tysan_inited) |
375 | return; |
376 | tysan_init_is_running = true; |
377 | |
378 | InitializeFlags(); |
379 | TySanInitializePlatformEarly(); |
380 | |
381 | InitializeInterceptors(); |
382 | |
383 | if (!MmapFixedNoReserve(fixed_addr: ShadowAddr(), size: AppAddr() - ShadowAddr())) |
384 | Die(); |
385 | |
386 | tysan_init_is_running = false; |
387 | tysan_inited = true; |
388 | } |
389 | |
390 | #if SANITIZER_CAN_USE_PREINIT_ARRAY |
391 | __attribute__((section(".preinit_array" ), |
392 | used)) static void (*tysan_init_ptr)() = __tysan_init; |
393 | #endif |
394 | |