| 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(s: shadow_for(ptr: addr), c: 0, n: 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(dest: shadow_for(ptr: daddr), src: shadow_for(ptr: saddr), n: 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(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 | |
| 58 | static 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 | |
| 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 | // Walk up TDA to see if it reaches TDB. |
| 106 | static 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. |
| 147 | static 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 | |
| 163 | static 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 | |
| 188 | static 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 | |
| 204 | namespace __tysan { |
| 205 | class Decorator : public __sanitizer::SanitizerCommonDecorator { |
| 206 | public: |
| 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 | |
| 214 | ALWAYS_INLINE |
| 215 | static 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 | |
| 257 | extern "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 | |
| 303 | Flags __tysan::flags_data; |
| 304 | |
| 305 | SANITIZER_INTERFACE_ATTRIBUTE uptr __tysan_shadow_memory_address; |
| 306 | SANITIZER_INTERFACE_ATTRIBUTE uptr __tysan_app_memory_mask; |
| 307 | |
| 308 | #ifdef TYSAN_RUNTIME_VMA |
| 309 | // Runtime detected VMA size. |
| 310 | int __tysan::vmaSize; |
| 311 | #endif |
| 312 | |
| 313 | void Flags::SetDefaults() { |
| 314 | #define TYSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue; |
| 315 | #include "tysan_flags.inc" |
| 316 | #undef TYSAN_FLAG |
| 317 | } |
| 318 | |
| 319 | static 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 | |
| 326 | static 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 | |
| 348 | static 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 | |
| 367 | namespace __tysan { |
| 368 | bool tysan_inited = false; |
| 369 | bool tysan_init_is_running; |
| 370 | } // namespace __tysan |
| 371 | |
| 372 | extern "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 | |