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