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
27using namespace __sanitizer;
28using namespace __tysan;
29
30extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
31tysan_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
36extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
37tysan_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
42static 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
58static 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
83static 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.
106static 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.
147static 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
163static 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
188static 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
204namespace __tysan {
205class Decorator : public __sanitizer::SanitizerCommonDecorator {
206public:
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
214ALWAYS_INLINE
215static 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
257extern "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
303Flags __tysan::flags_data;
304
305SANITIZER_INTERFACE_ATTRIBUTE uptr __tysan_shadow_memory_address;
306SANITIZER_INTERFACE_ATTRIBUTE uptr __tysan_app_memory_mask;
307
308#ifdef TYSAN_RUNTIME_VMA
309// Runtime detected VMA size.
310int __tysan::vmaSize;
311#endif
312
313void Flags::SetDefaults() {
314#define TYSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
315#include "tysan_flags.inc"
316#undef TYSAN_FLAG
317}
318
319static 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
326static 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
348static 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
367namespace __tysan {
368bool tysan_inited = false;
369bool tysan_init_is_running;
370} // namespace __tysan
371
372extern "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