xref: /llvm-project/compiler-rt/lib/tysan/tysan.cpp (revision 09a8b7cbc29d8704c343197d4b33b6972366c682)
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(shadow_for(addr), 0, 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(shadow_for(daddr), shadow_for(saddr), 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(TIPrefix);
50 
51   const char *DName = Symbolizer::GetOrInit()->Demangle(Name);
52   if (!internal_strncmp(DName, TIPrefix, 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("<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->Member.Access);
70     if (td->Member.Access != td->Member.Base) {
71       Printf(" (in ");
72       printTDName(td->Member.Base);
73       Printf(" at offset %zu)", td->Member.Offset);
74     }
75     break;
76   case TYSAN_STRUCT_TD:
77     Printf("%s", getDisplayName(
78                      (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 static bool isAliasingLegalUp(tysan_type_descriptor *TDA,
106                               tysan_type_descriptor *TDB, int TDAOffset) {
107   // Walk up the tree starting with TDA to see if we reach TDB.
108   uptr OffsetA = 0, OffsetB = 0;
109   if (TDB->Tag == TYSAN_MEMBER_TD) {
110     OffsetB = TDB->Member.Offset;
111     TDB = TDB->Member.Base;
112   }
113 
114   if (TDA->Tag == TYSAN_MEMBER_TD) {
115     OffsetA = TDA->Member.Offset - TDAOffset;
116     TDA = TDA->Member.Base;
117   }
118 
119   do {
120     if (TDA == TDB)
121       return OffsetA == OffsetB;
122 
123     if (TDA->Tag == TYSAN_STRUCT_TD) {
124       // Reached root type descriptor.
125       if (!TDA->Struct.MemberCount)
126         break;
127 
128       uptr Idx = 0;
129       for (; Idx < TDA->Struct.MemberCount - 1; ++Idx) {
130         if (TDA->Struct.Members[Idx].Offset >= OffsetA)
131           break;
132       }
133 
134       // This offset can't be negative. Therefore we must be accessing something
135       // before the current type (not legal) or partially inside the last type.
136       // In the latter case, we adjust Idx.
137       if (TDA->Struct.Members[Idx].Offset > OffsetA) {
138         // Trying to access something before the current type.
139         if (!Idx)
140           return false;
141 
142         Idx -= 1;
143       }
144 
145       OffsetA -= TDA->Struct.Members[Idx].Offset;
146       TDA = TDA->Struct.Members[Idx].Type;
147     } else {
148       CHECK(false && "invalid enum value");
149       break;
150     }
151   } while (TDA);
152 
153   return false;
154 }
155 
156 static bool isAliasingLegal(tysan_type_descriptor *TDA,
157                             tysan_type_descriptor *TDB, int TDAOffset = 0) {
158   if (TDA == TDB || !TDB || !TDA)
159     return true;
160 
161   // Aliasing is legal is the two types have different root nodes.
162   if (getRootTD(TDA) != getRootTD(TDB))
163     return true;
164 
165   // TDB may have been adjusted by offset TDAOffset in the caller to point to
166   // the outer type. Check for aliasing with and without adjusting for this
167   // offset.
168   return isAliasingLegalUp(TDA, TDB, 0) || isAliasingLegalUp(TDB, TDA, 0) ||
169          isAliasingLegalUp(TDA, TDB, TDAOffset);
170 }
171 
172 namespace __tysan {
173 class Decorator : public __sanitizer::SanitizerCommonDecorator {
174 public:
175   Decorator() : SanitizerCommonDecorator() {}
176   const char *Warning() { return Red(); }
177   const char *Name() { return Green(); }
178   const char *End() { return Default(); }
179 };
180 } // namespace __tysan
181 
182 ALWAYS_INLINE
183 static void reportError(void *Addr, int Size, tysan_type_descriptor *TD,
184                         tysan_type_descriptor *OldTD, const char *AccessStr,
185                         const char *DescStr, int Offset, uptr pc, uptr bp,
186                         uptr sp) {
187   Decorator d;
188   Printf("%s", d.Warning());
189   Report("ERROR: TypeSanitizer: type-aliasing-violation on address %p"
190          " (pc %p bp %p sp %p tid %llu)\n",
191          Addr, (void *)pc, (void *)bp, (void *)sp, GetTid());
192   Printf("%s", d.End());
193   Printf("%s of size %d at %p with type ", AccessStr, Size, Addr);
194 
195   Printf("%s", d.Name());
196   printTDName(TD);
197   Printf("%s", d.End());
198 
199   Printf(" %s of type ", DescStr);
200 
201   Printf("%s", d.Name());
202   printTDName(OldTD);
203   Printf("%s", d.End());
204 
205   if (Offset != 0)
206     Printf(" that starts at offset %d\n", Offset);
207   else
208     Printf("\n");
209 
210   if (pc) {
211     uptr top = 0;
212     uptr bottom = 0;
213     if (flags().print_stacktrace)
214       GetThreadStackTopAndBottom(false, &top, &bottom);
215 
216     bool request_fast = StackTrace::WillUseFastUnwind(true);
217     BufferedStackTrace ST;
218     ST.Unwind(kStackTraceMax, pc, bp, 0, top, bottom, request_fast);
219     ST.Print();
220   } else {
221     Printf("\n");
222   }
223 }
224 
225 extern "C" SANITIZER_INTERFACE_ATTRIBUTE void
226 __tysan_check(void *addr, int size, tysan_type_descriptor *td, int flags) {
227   GET_CALLER_PC_BP_SP;
228 
229   bool IsRead = flags & 1;
230   bool IsWrite = flags & 2;
231   const char *AccessStr;
232   if (IsRead && !IsWrite)
233     AccessStr = "READ";
234   else if (!IsRead && IsWrite)
235     AccessStr = "WRITE";
236   else
237     AccessStr = "ATOMIC UPDATE";
238 
239   tysan_type_descriptor **OldTDPtr = shadow_for(addr);
240   tysan_type_descriptor *OldTD = *OldTDPtr;
241   if (((sptr)OldTD) < 0) {
242     int i = -((sptr)OldTD);
243     OldTDPtr -= i;
244     OldTD = *OldTDPtr;
245 
246     if (!isAliasingLegal(td, OldTD, i))
247       reportError(addr, size, td, OldTD, AccessStr,
248                   "accesses part of an existing object", -i, pc, bp, sp);
249 
250     return;
251   }
252 
253   if (!isAliasingLegal(td, OldTD)) {
254     reportError(addr, size, td, OldTD, AccessStr, "accesses an existing object",
255                 0, pc, bp, sp);
256     return;
257   }
258 
259   // These types are allowed to alias (or the stored type is unknown), report
260   // an error if we find an interior type.
261 
262   for (int i = 0; i < size; ++i) {
263     OldTDPtr = shadow_for((void *)(((uptr)addr) + i));
264     OldTD = *OldTDPtr;
265     if (((sptr)OldTD) >= 0 && !isAliasingLegal(td, OldTD))
266       reportError(addr, size, td, OldTD, AccessStr,
267                   "partially accesses an object", i, pc, bp, sp);
268   }
269 }
270 
271 Flags __tysan::flags_data;
272 
273 SANITIZER_INTERFACE_ATTRIBUTE uptr __tysan_shadow_memory_address;
274 SANITIZER_INTERFACE_ATTRIBUTE uptr __tysan_app_memory_mask;
275 
276 #ifdef TYSAN_RUNTIME_VMA
277 // Runtime detected VMA size.
278 int __tysan::vmaSize;
279 #endif
280 
281 void Flags::SetDefaults() {
282 #define TYSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;
283 #include "tysan_flags.inc"
284 #undef TYSAN_FLAG
285 }
286 
287 static void RegisterTySanFlags(FlagParser *parser, Flags *f) {
288 #define TYSAN_FLAG(Type, Name, DefaultValue, Description)                      \
289   RegisterFlag(parser, #Name, Description, &f->Name);
290 #include "tysan_flags.inc"
291 #undef TYSAN_FLAG
292 }
293 
294 static void InitializeFlags() {
295   SetCommonFlagsDefaults();
296   {
297     CommonFlags cf;
298     cf.CopyFrom(*common_flags());
299     cf.external_symbolizer_path = GetEnv("TYSAN_SYMBOLIZER_PATH");
300     OverrideCommonFlags(cf);
301   }
302 
303   flags().SetDefaults();
304 
305   FlagParser parser;
306   RegisterCommonFlags(&parser);
307   RegisterTySanFlags(&parser, &flags());
308   parser.ParseString(GetEnv("TYSAN_OPTIONS"));
309   InitializeCommonFlags();
310   if (Verbosity())
311     ReportUnrecognizedFlags();
312   if (common_flags()->help)
313     parser.PrintFlagDescriptions();
314 }
315 
316 static void TySanInitializePlatformEarly() {
317   AvoidCVE_2016_2143();
318 #ifdef TYSAN_RUNTIME_VMA
319   vmaSize = (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1);
320 #if defined(__aarch64__) && !SANITIZER_APPLE
321   if (vmaSize != 39 && vmaSize != 42 && vmaSize != 48) {
322     Printf("FATAL: TypeSanitizer: unsupported VMA range\n");
323     Printf("FATAL: Found %d - Supported 39, 42 and 48\n", vmaSize);
324     Die();
325   }
326 #endif
327 #endif
328 
329   __sanitizer::InitializePlatformEarly();
330 
331   __tysan_shadow_memory_address = ShadowAddr();
332   __tysan_app_memory_mask = AppMask();
333 }
334 
335 namespace __tysan {
336 bool tysan_inited = false;
337 bool tysan_init_is_running;
338 } // namespace __tysan
339 
340 extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __tysan_init() {
341   CHECK(!tysan_init_is_running);
342   if (tysan_inited)
343     return;
344   tysan_init_is_running = true;
345 
346   InitializeFlags();
347   TySanInitializePlatformEarly();
348 
349   InitializeInterceptors();
350 
351   if (!MmapFixedNoReserve(ShadowAddr(), AppAddr() - ShadowAddr()))
352     Die();
353 
354   tysan_init_is_running = false;
355   tysan_inited = true;
356 }
357 
358 #if SANITIZER_CAN_USE_PREINIT_ARRAY
359 __attribute__((section(".preinit_array"),
360                used)) static void (*tysan_init_ptr)() = __tysan_init;
361 #endif
362