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