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_interface_internal.h"
19#include "sanitizer_common/sanitizer_libc.h"
20#include "sanitizer_common/sanitizer_report_decorator.h"
21#include "sanitizer_common/sanitizer_stacktrace.h"
22#include "sanitizer_common/sanitizer_symbolizer.h"
23
24#include "tysan/tysan.h"
25
26#include <stdint.h>
27#include <string.h>
28
29using namespace __sanitizer;
30using namespace __tysan;
31
32extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
33__tysan_set_type_unknown(const void *addr, uptr size) {
34 if (tysan_inited)
35 internal_memset(s: shadow_for(ptr: addr), c: 0, n: size * sizeof(uptr));
36}
37
38extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
39tysan_copy_types(const void *daddr, const void *saddr, uptr size) {
40 if (tysan_inited)
41 internal_memmove(dest: shadow_for(ptr: daddr), src: shadow_for(ptr: saddr), n: size * sizeof(uptr));
42}
43
44static void getStackTrace(bool fullStacktrace, uptr pc, uptr bp,
45 BufferedStackTrace *ST) {
46 uptr top = 0;
47 uptr bottom = 0;
48 if (fullStacktrace)
49 GetThreadStackTopAndBottom(at_initialization: false, stack_top: &top, stack_bottom: &bottom);
50 bool request_fast = StackTrace::WillUseFastUnwind(request_fast_unwind: true);
51 ST->Unwind(max_depth: kStackTraceMax, pc, bp, context: 0, stack_top: top, stack_bottom: bottom, request_fast_unwind: request_fast);
52}
53
54namespace __tysan {
55void OnStackUnwind(const SignalContext &sig, const void *,
56 BufferedStackTrace *stack) {
57 getStackTrace(fullStacktrace: true, pc: StackTrace::GetNextInstructionPc(pc: sig.pc), bp: sig.bp, ST: stack);
58}
59} // namespace __tysan
60
61extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_print_stack_trace() {
62 GET_CURRENT_PC_BP;
63 UNINITIALIZED BufferedStackTrace stack;
64 getStackTrace(fullStacktrace: true, pc, bp, ST: &stack);
65 stack.Print();
66}
67
68static const char *getDisplayName(const char *Name) {
69 if (Name[0] == '\0')
70 return "<anonymous type>";
71
72 // Clang generates tags for C++ types that demangle as typeinfo. Remove the
73 // prefix from the generated string.
74 const char *TIPrefix = "typeinfo name for ";
75 size_t TIPrefixLen = strlen(s: TIPrefix);
76
77 const char *DName = Symbolizer::GetOrInit()->Demangle(name: Name);
78 if (!internal_strncmp(s1: DName, s2: TIPrefix, n: TIPrefixLen))
79 DName += TIPrefixLen;
80
81 return DName;
82}
83
84static void printTDName(tysan_type_descriptor *td) {
85 if (((sptr)td) <= 0) {
86 Printf(format: "<unknown type>");
87 return;
88 }
89
90 switch (td->Tag) {
91 default:
92 CHECK(false && "invalid enum value");
93 break;
94 case TYSAN_MEMBER_TD:
95 printTDName(td: td->Member.Access);
96 if (td->Member.Access != td->Member.Base) {
97 Printf(format: " (in ");
98 printTDName(td: td->Member.Base);
99 Printf(format: " at offset %zu)", td->Member.Offset);
100 }
101 break;
102 case TYSAN_STRUCT_TD:
103 Printf(format: "%s", getDisplayName(
104 Name: (char *)(td->Struct.Members + td->Struct.MemberCount)));
105 break;
106 }
107}
108
109static tysan_type_descriptor *getRootTD(tysan_type_descriptor *TD) {
110 tysan_type_descriptor *RootTD = TD;
111
112 do {
113 RootTD = TD;
114
115 if (TD->Tag == TYSAN_STRUCT_TD) {
116 if (TD->Struct.MemberCount > 0)
117 TD = TD->Struct.Members[0].Type;
118 else
119 TD = nullptr;
120 } else if (TD->Tag == TYSAN_MEMBER_TD) {
121 TD = TD->Member.Access;
122 } else {
123 CHECK(false && "invalid enum value");
124 break;
125 }
126 } while (TD);
127
128 return RootTD;
129}
130
131// Walk up TDA to see if it reaches TDB.
132static bool walkAliasTree(tysan_type_descriptor *TDA,
133 tysan_type_descriptor *TDB, uptr OffsetA,
134 uptr OffsetB) {
135 do {
136 if (TDA == TDB)
137 return OffsetA == OffsetB;
138
139 if (TDA->Tag == TYSAN_STRUCT_TD) {
140 // Reached root type descriptor.
141 if (!TDA->Struct.MemberCount)
142 break;
143
144 uptr Idx = 0;
145 for (; Idx < TDA->Struct.MemberCount - 1; ++Idx) {
146 if (TDA->Struct.Members[Idx].Offset >= OffsetA)
147 break;
148 }
149
150 // This offset can't be negative. Therefore we must be accessing something
151 // before the current type (not legal) or partially inside the last type.
152 // In the latter case, we adjust Idx.
153 if (TDA->Struct.Members[Idx].Offset > OffsetA) {
154 // Trying to access something before the current type.
155 if (!Idx)
156 return false;
157
158 Idx -= 1;
159 }
160
161 OffsetA -= TDA->Struct.Members[Idx].Offset;
162 TDA = TDA->Struct.Members[Idx].Type;
163 } else {
164 CHECK(false && "invalid enum value");
165 break;
166 }
167 } while (TDA);
168
169 return false;
170}
171
172// Walk up the tree starting with TDA to see if we reach TDB.
173static bool isAliasingLegalUp(tysan_type_descriptor *TDA,
174 tysan_type_descriptor *TDB) {
175 uptr OffsetA = 0, OffsetB = 0;
176 if (TDB->Tag == TYSAN_MEMBER_TD) {
177 OffsetB = TDB->Member.Offset;
178 TDB = TDB->Member.Base;
179 }
180
181 if (TDA->Tag == TYSAN_MEMBER_TD) {
182 OffsetA = TDA->Member.Offset;
183 TDA = TDA->Member.Base;
184 }
185
186 return walkAliasTree(TDA, TDB, OffsetA, OffsetB);
187}
188
189static bool isAliasingLegalWithOffset(tysan_type_descriptor *TDA,
190 tysan_type_descriptor *TDB,
191 uptr OffsetB) {
192 // This is handled by calls to isAliasingLegalUp.
193 if (OffsetB == 0)
194 return false;
195
196 // You can't have an offset into a member.
197 if (TDB->Tag == TYSAN_MEMBER_TD)
198 return false;
199
200 uptr OffsetA = 0;
201 if (TDA->Tag == TYSAN_MEMBER_TD) {
202 OffsetA = TDA->Member.Offset;
203 TDA = TDA->Member.Base;
204 }
205
206 // Since the access was partially inside TDB (the shadow), it can be assumed
207 // that we are accessing a member in an object. This means that rather than
208 // walk up the scalar access TDA to reach an object, we should walk up the
209 // object TBD to reach the scalar we are accessing it with. The offsets will
210 // still be checked at the end to make sure this alias is legal.
211 return walkAliasTree(TDA: TDB, TDB: TDA, OffsetA: OffsetB, OffsetB: OffsetA);
212}
213
214static bool isAliasingLegal(tysan_type_descriptor *TDA,
215 tysan_type_descriptor *TDB, uptr OffsetB = 0) {
216 if (TDA == TDB || !TDB || !TDA)
217 return true;
218
219 // Aliasing is legal is the two types have different root nodes.
220 if (getRootTD(TD: TDA) != getRootTD(TD: TDB))
221 return true;
222
223 // TDB may have been adjusted by offset TDAOffset in the caller to point to
224 // the outer type. Check for aliasing with and without adjusting for this
225 // offset.
226 return isAliasingLegalUp(TDA, TDB) || isAliasingLegalUp(TDA: TDB, TDB: TDA) ||
227 isAliasingLegalWithOffset(TDA, TDB, OffsetB);
228}
229
230namespace __tysan {
231class Decorator : public __sanitizer::SanitizerCommonDecorator {
232public:
233 Decorator() : SanitizerCommonDecorator() {}
234 const char *Warning() { return Red(); }
235 const char *Name() { return Green(); }
236 const char *End() { return Default(); }
237};
238} // namespace __tysan
239
240ALWAYS_INLINE
241static void reportError(void *Addr, int Size, tysan_type_descriptor *TD,
242 tysan_type_descriptor *OldTD, const char *AccessStr,
243 const char *DescStr, int Offset, uptr pc, uptr bp,
244 uptr sp) {
245 Decorator d;
246 Printf(format: "%s", d.Warning());
247 Report(format: "ERROR: TypeSanitizer: type-aliasing-violation on address %p"
248 " (pc %p bp %p sp %p tid %llu)\n",
249 Addr, (void *)pc, (void *)bp, (void *)sp, GetTid());
250 Printf(format: "%s", d.End());
251 Printf(format: "%s of size %d at %p with type ", AccessStr, Size, Addr);
252
253 Printf(format: "%s", d.Name());
254 printTDName(td: TD);
255 Printf(format: "%s", d.End());
256
257 Printf(format: " %s of type ", DescStr);
258
259 Printf(format: "%s", d.Name());
260 printTDName(td: OldTD);
261 Printf(format: "%s", d.End());
262
263 if (Offset != 0)
264 Printf(format: " that starts at offset %d\n", Offset);
265 else
266 Printf(format: "\n");
267
268 if (pc) {
269 BufferedStackTrace ST;
270 getStackTrace(fullStacktrace: flags().print_stacktrace, pc, bp, ST: &ST);
271 ST.Print();
272 } else {
273 Printf(format: "\n");
274 }
275
276 if (flags().halt_on_error) {
277 Report(format: "ABORTING\n");
278 Die();
279 }
280}
281
282ALWAYS_INLINE
283static void SetShadowType(tysan_type_descriptor *td,
284 tysan_type_descriptor **shadowData,
285 uint64_t AccessSize) {
286 *shadowData = td;
287 uptr shadowDataInt = (uptr)shadowData;
288
289 for (uint64_t i = 1; i < AccessSize; ++i) {
290 uptr dataOffset = i << PtrShift();
291 sptr *badShadowData = (sptr *)(shadowDataInt + dataOffset);
292 sptr badTD = sptr(i) * -1;
293 *badShadowData = badTD;
294 }
295}
296
297ALWAYS_INLINE
298static bool GetNotAllBadTD(uptr ShadowDataInt, uint64_t AccessSize) {
299 bool notAllBadTD = false;
300 for (uint64_t i = 1; i < AccessSize; ++i) {
301 sptr **unkShadowData = (sptr **)(ShadowDataInt + (i << PtrShift()));
302 sptr *ILdTD = *unkShadowData;
303 notAllBadTD = notAllBadTD || (ILdTD != nullptr);
304 }
305 return notAllBadTD;
306}
307
308ALWAYS_INLINE
309static bool GetNotAllUnkTD(uptr ShadowDataInt, uint64_t AccessSize) {
310 bool notAllBadTD = false;
311 for (uint64_t i = 1; i < AccessSize; ++i) {
312 sptr *badShadowData = (sptr *)(ShadowDataInt + (i << PtrShift()));
313 sptr ILdTD = *badShadowData;
314 notAllBadTD = notAllBadTD || (ILdTD >= 0);
315 }
316 return notAllBadTD;
317}
318
319extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
320__tysan_instrument_mem_inst(char *dest, char *src, uint64_t size,
321 bool needsMemMove) {
322 tysan_type_descriptor **destShadowDataPtr = shadow_for(ptr: dest);
323
324 if (!src) {
325 internal_memset(s: (char *)destShadowDataPtr, c: 0, n: size << PtrShift());
326 return;
327 }
328
329 uptr srcShadowInt = ((((uptr)src) & AppMask()) << PtrShift()) + ShadowAddr();
330 void *srcShadow = (void *)srcShadowInt;
331
332 if (needsMemMove) {
333 internal_memmove(dest: (char *)destShadowDataPtr, src: srcShadow, n: size << PtrShift());
334 }
335}
336
337ALWAYS_INLINE
338static void __tysan_check_internal(void *addr, int size,
339 tysan_type_descriptor *td, int flags,
340 uptr pc, uptr bp, uptr sp) {
341 bool IsRead = flags & 1;
342 bool IsWrite = flags & 2;
343 const char *AccessStr;
344 if (IsRead && !IsWrite)
345 AccessStr = "READ";
346 else if (!IsRead && IsWrite)
347 AccessStr = "WRITE";
348 else
349 AccessStr = "ATOMIC UPDATE";
350
351 tysan_type_descriptor **OldTDPtr = shadow_for(ptr: addr);
352 tysan_type_descriptor *OldTD = *OldTDPtr;
353 if (((sptr)OldTD) < 0) {
354 int i = -((sptr)OldTD);
355 OldTDPtr -= i;
356 OldTD = *OldTDPtr;
357
358 if (!isAliasingLegal(TDA: td, TDB: OldTD, OffsetB: i))
359 reportError(Addr: addr, Size: size, TD: td, OldTD, AccessStr,
360 DescStr: "accesses part of an existing object", Offset: -i, pc, bp, sp);
361
362 return;
363 }
364
365 if (!isAliasingLegal(TDA: td, TDB: OldTD)) {
366 reportError(Addr: addr, Size: size, TD: td, OldTD, AccessStr, DescStr: "accesses an existing object",
367 Offset: 0, pc, bp, sp);
368 return;
369 }
370
371 // These types are allowed to alias (or the stored type is unknown), report
372 // an error if we find an interior type.
373
374 for (int i = 0; i < size; ++i) {
375 OldTDPtr = shadow_for(ptr: (void *)(((uptr)addr) + i));
376 OldTD = *OldTDPtr;
377 if (((sptr)OldTD) >= 0 && !isAliasingLegal(TDA: td, TDB: OldTD))
378 reportError(Addr: addr, Size: size, TD: td, OldTD, AccessStr,
379 DescStr: "partially accesses an object", Offset: i, pc, bp, sp);
380 }
381}
382
383extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
384__tysan_check(void *addr, int size, tysan_type_descriptor *td, int flags) {
385 GET_CALLER_PC_BP_SP;
386 __tysan_check_internal(addr, size, td, flags, pc, bp, sp);
387}
388
389extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
390__tysan_instrument_with_shadow_update(void *ptr, tysan_type_descriptor *td,
391 bool sanitizeFunction,
392 uint64_t accessSize, int flags) {
393 tysan_type_descriptor **shadowData = shadow_for(ptr);
394 tysan_type_descriptor *loadedTD = *shadowData;
395 bool shadowIsNull = loadedTD == nullptr;
396
397 // TODO, sanitizeFunction is known at compile time, so maybe this is split
398 // into two different functions
399 if (sanitizeFunction) {
400
401 if (td != loadedTD) {
402
403 // We now know that the types did not match (we're on the slow path). If
404 // the type is unknown, then set it.
405 if (shadowIsNull) {
406 // We're about to set the type. Make sure that all bytes in the value
407 // are also of unknown type.
408 bool isAllUnknownTD = GetNotAllUnkTD(ShadowDataInt: (uptr)shadowData, AccessSize: accessSize);
409 if (isAllUnknownTD) {
410 GET_CALLER_PC_BP_SP;
411 __tysan_check_internal(addr: ptr, size: accessSize, td, flags, pc, bp, sp);
412 }
413 SetShadowType(td, shadowData, AccessSize: accessSize);
414 } else {
415 GET_CALLER_PC_BP_SP;
416 __tysan_check_internal(addr: ptr, size: accessSize, td, flags, pc, bp, sp);
417 }
418 } else {
419 // We appear to have the right type. Make sure that all other bytes in
420 // the type are still marked as interior bytes. If not, call the runtime.
421 bool isNotAllBadTD = GetNotAllBadTD(ShadowDataInt: (uptr)shadowData, AccessSize: accessSize);
422 if (isNotAllBadTD) {
423 GET_CALLER_PC_BP_SP;
424 __tysan_check_internal(addr: ptr, size: accessSize, td, flags, pc, bp, sp);
425 }
426 }
427 } else if (shadowIsNull) {
428 SetShadowType(td, shadowData, AccessSize: accessSize);
429 }
430}
431
432extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
433__tysan_set_shadow_type(void *ptr, tysan_type_descriptor *td,
434 uint64_t accessSize) {
435 // In the mode where writes always set the type, for a write (which does
436 // not also read), we just set the type.
437 tysan_type_descriptor **shadow = shadow_for(ptr);
438 SetShadowType(td, shadowData: shadow, AccessSize: accessSize);
439}
440
441Flags __tysan::flags_data;
442
443SANITIZER_INTERFACE_ATTRIBUTE uptr __tysan_shadow_memory_address;
444SANITIZER_INTERFACE_ATTRIBUTE uptr __tysan_app_memory_mask;
445
446#ifdef TYSAN_RUNTIME_VMA
447// Runtime detected VMA size.
448int __tysan::vmaSize;
449#endif
450
451void Flags::SetDefaults() {
452#define TYSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
453#include "tysan_flags.inc"
454#undef TYSAN_FLAG
455}
456
457static void RegisterTySanFlags(FlagParser *parser, Flags *f) {
458#define TYSAN_FLAG(Type, Name, DefaultValue, Description) \
459 RegisterFlag(parser, #Name, Description, &f->Name);
460#include "tysan_flags.inc"
461#undef TYSAN_FLAG
462}
463
464static void InitializeFlags() {
465 SetCommonFlagsDefaults();
466 {
467 CommonFlags cf;
468 cf.CopyFrom(other: *common_flags());
469 cf.external_symbolizer_path = GetEnv(name: "TYSAN_SYMBOLIZER_PATH");
470 OverrideCommonFlags(cf);
471 }
472
473 flags().SetDefaults();
474
475 FlagParser parser;
476 RegisterCommonFlags(parser: &parser);
477 RegisterTySanFlags(parser: &parser, f: &flags());
478 parser.ParseString(s: GetEnv(name: "TYSAN_OPTIONS"));
479 InitializeCommonFlags();
480 if (Verbosity())
481 ReportUnrecognizedFlags();
482 if (common_flags()->help)
483 parser.PrintFlagDescriptions();
484
485 __sanitizer_set_report_path(path: common_flags()->log_path);
486}
487
488static void TySanInitializePlatformEarly() {
489 AvoidCVE_2016_2143();
490#ifdef TYSAN_RUNTIME_VMA
491 vmaSize = (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1);
492#if defined(__aarch64__) && !SANITIZER_APPLE
493 if (vmaSize != 39 && vmaSize != 42 && vmaSize != 48) {
494 Printf("FATAL: TypeSanitizer: unsupported VMA range\n");
495 Printf("FATAL: Found %d - Supported 39, 42 and 48\n", vmaSize);
496 Die();
497 }
498#endif
499#endif
500
501 __sanitizer::InitializePlatformEarly();
502
503 __tysan_shadow_memory_address = ShadowAddr();
504 __tysan_app_memory_mask = AppMask();
505}
506
507namespace __tysan {
508bool tysan_inited = false;
509bool tysan_init_is_running;
510} // namespace __tysan
511
512extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __tysan_init() {
513 SanitizerToolName = "TypeSanitizer";
514 CacheBinaryName();
515
516 CHECK(!tysan_init_is_running);
517 if (tysan_inited)
518 return;
519 tysan_init_is_running = true;
520
521 InitializeFlags();
522 TySanInitializePlatformEarly();
523
524 InitializeInterceptors();
525
526 if (!MmapFixedNoReserve(fixed_addr: ShadowAddr(), size: AppAddr() - ShadowAddr()))
527 Die();
528
529 tysan_init_is_running = false;
530 tysan_inited = true;
531}
532
533#if SANITIZER_CAN_USE_PREINIT_ARRAY
534__attribute__((section(".preinit_array"),
535 used)) static void (*tysan_init_ptr)() = __tysan_init;
536#endif
537