| 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 | |
| 28 | using namespace __sanitizer; |
| 29 | using namespace __tysan; |
| 30 | |
| 31 | extern "C" SANITIZER_INTERFACE_ATTRIBUTE void |
| 32 | tysan_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 | |
| 37 | extern "C" SANITIZER_INTERFACE_ATTRIBUTE void |
| 38 | tysan_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 | |
| 43 | static 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 | |
| 59 | static 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 | |
| 84 | static 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. |
| 107 | static 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. |
| 148 | static 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 | |
| 164 | static 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 | |
| 189 | static 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 | |
| 205 | namespace __tysan { |
| 206 | class Decorator : public __sanitizer::SanitizerCommonDecorator { |
| 207 | public: |
| 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 | |
| 215 | ALWAYS_INLINE |
| 216 | static 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 | |
| 258 | ALWAYS_INLINE |
| 259 | static void SetShadowType(tysan_type_descriptor *td, |
| 260 | tysan_type_descriptor **shadowData, |
| 261 | uint64_t AccessSize) { |
| 262 | *shadowData = td; |
| 263 | uint64_t shadowDataInt = (uint64_t)shadowData; |
| 264 | |
| 265 | for (uint64_t i = 1; i < AccessSize; ++i) { |
| 266 | int64_t dataOffset = i << PtrShift(); |
| 267 | int64_t *badShadowData = (int64_t *)(shadowDataInt + dataOffset); |
| 268 | int64_t badTD = int64_t(i) * -1; |
| 269 | *badShadowData = badTD; |
| 270 | } |
| 271 | } |
| 272 | |
| 273 | ALWAYS_INLINE |
| 274 | static bool GetNotAllBadTD(uint64_t ShadowDataInt, uint64_t AccessSize) { |
| 275 | bool notAllBadTD = false; |
| 276 | for (uint64_t i = 1; i < AccessSize; ++i) { |
| 277 | int64_t **unkShadowData = (int64_t **)(ShadowDataInt + (i << PtrShift())); |
| 278 | int64_t *ILdTD = *unkShadowData; |
| 279 | notAllBadTD = notAllBadTD || (ILdTD != nullptr); |
| 280 | } |
| 281 | return notAllBadTD; |
| 282 | } |
| 283 | |
| 284 | ALWAYS_INLINE |
| 285 | static bool GetNotAllUnkTD(uint64_t ShadowDataInt, uint64_t AccessSize) { |
| 286 | bool notAllBadTD = false; |
| 287 | for (uint64_t i = 1; i < AccessSize; ++i) { |
| 288 | int64_t *badShadowData = (int64_t *)(ShadowDataInt + (i << PtrShift())); |
| 289 | int64_t ILdTD = *badShadowData; |
| 290 | notAllBadTD = notAllBadTD || (ILdTD >= 0); |
| 291 | } |
| 292 | return notAllBadTD; |
| 293 | } |
| 294 | |
| 295 | extern "C" SANITIZER_INTERFACE_ATTRIBUTE void |
| 296 | __tysan_instrument_mem_inst(char *dest, char *src, uint64_t size, |
| 297 | bool needsMemMove) { |
| 298 | tysan_type_descriptor **destShadowDataPtr = shadow_for(ptr: dest); |
| 299 | |
| 300 | if (!src) { |
| 301 | internal_memset(s: (char *)destShadowDataPtr, c: 0, n: size << PtrShift()); |
| 302 | return; |
| 303 | } |
| 304 | |
| 305 | uint64_t srcInt = (uint64_t)src; |
| 306 | uint64_t srcShadowInt = ((srcInt & AppMask()) << PtrShift()) + ShadowAddr(); |
| 307 | uint64_t *srcShadow = (uint64_t *)srcShadowInt; |
| 308 | |
| 309 | if (needsMemMove) { |
| 310 | internal_memmove(dest: (char *)destShadowDataPtr, src: srcShadow, n: size << PtrShift()); |
| 311 | } else { |
| 312 | internal_memcpy(dest: (char *)destShadowDataPtr, src: srcShadow, n: size << PtrShift()); |
| 313 | } |
| 314 | } |
| 315 | |
| 316 | ALWAYS_INLINE |
| 317 | static void __tysan_check_internal(void *addr, int size, |
| 318 | tysan_type_descriptor *td, int flags, |
| 319 | uptr pc, uptr bp, uptr sp) { |
| 320 | bool IsRead = flags & 1; |
| 321 | bool IsWrite = flags & 2; |
| 322 | const char *AccessStr; |
| 323 | if (IsRead && !IsWrite) |
| 324 | AccessStr = "READ" ; |
| 325 | else if (!IsRead && IsWrite) |
| 326 | AccessStr = "WRITE" ; |
| 327 | else |
| 328 | AccessStr = "ATOMIC UPDATE" ; |
| 329 | |
| 330 | tysan_type_descriptor **OldTDPtr = shadow_for(ptr: addr); |
| 331 | tysan_type_descriptor *OldTD = *OldTDPtr; |
| 332 | if (((sptr)OldTD) < 0) { |
| 333 | int i = -((sptr)OldTD); |
| 334 | OldTDPtr -= i; |
| 335 | OldTD = *OldTDPtr; |
| 336 | |
| 337 | if (!isAliasingLegal(TDA: td, TDB: OldTD, OffsetB: i)) |
| 338 | reportError(Addr: addr, Size: size, TD: td, OldTD, AccessStr, |
| 339 | DescStr: "accesses part of an existing object" , Offset: -i, pc, bp, sp); |
| 340 | |
| 341 | return; |
| 342 | } |
| 343 | |
| 344 | if (!isAliasingLegal(TDA: td, TDB: OldTD)) { |
| 345 | reportError(Addr: addr, Size: size, TD: td, OldTD, AccessStr, DescStr: "accesses an existing object" , |
| 346 | Offset: 0, pc, bp, sp); |
| 347 | return; |
| 348 | } |
| 349 | |
| 350 | // These types are allowed to alias (or the stored type is unknown), report |
| 351 | // an error if we find an interior type. |
| 352 | |
| 353 | for (int i = 0; i < size; ++i) { |
| 354 | OldTDPtr = shadow_for(ptr: (void *)(((uptr)addr) + i)); |
| 355 | OldTD = *OldTDPtr; |
| 356 | if (((sptr)OldTD) >= 0 && !isAliasingLegal(TDA: td, TDB: OldTD)) |
| 357 | reportError(Addr: addr, Size: size, TD: td, OldTD, AccessStr, |
| 358 | DescStr: "partially accesses an object" , Offset: i, pc, bp, sp); |
| 359 | } |
| 360 | } |
| 361 | |
| 362 | extern "C" SANITIZER_INTERFACE_ATTRIBUTE void |
| 363 | __tysan_check(void *addr, int size, tysan_type_descriptor *td, int flags) { |
| 364 | GET_CALLER_PC_BP_SP; |
| 365 | __tysan_check_internal(addr, size, td, flags, pc, bp, sp); |
| 366 | } |
| 367 | |
| 368 | extern "C" SANITIZER_INTERFACE_ATTRIBUTE void |
| 369 | __tysan_instrument_with_shadow_update(void *ptr, tysan_type_descriptor *td, |
| 370 | bool sanitizeFunction, |
| 371 | uint64_t accessSize, int flags) { |
| 372 | tysan_type_descriptor **shadowData = shadow_for(ptr); |
| 373 | tysan_type_descriptor *loadedTD = *shadowData; |
| 374 | bool shadowIsNull = loadedTD == nullptr; |
| 375 | |
| 376 | // TODO, sanitizeFunction is known at compile time, so maybe this is split |
| 377 | // into two different functions |
| 378 | if (sanitizeFunction) { |
| 379 | |
| 380 | if (td != loadedTD) { |
| 381 | |
| 382 | // We now know that the types did not match (we're on the slow path). If |
| 383 | // the type is unknown, then set it. |
| 384 | if (shadowIsNull) { |
| 385 | // We're about to set the type. Make sure that all bytes in the value |
| 386 | // are also of unknown type. |
| 387 | bool isAllUnknownTD = GetNotAllUnkTD(ShadowDataInt: (uint64_t)shadowData, AccessSize: accessSize); |
| 388 | if (isAllUnknownTD) { |
| 389 | GET_CALLER_PC_BP_SP; |
| 390 | __tysan_check_internal(addr: ptr, size: accessSize, td, flags, pc, bp, sp); |
| 391 | } |
| 392 | SetShadowType(td, shadowData, AccessSize: accessSize); |
| 393 | } else { |
| 394 | GET_CALLER_PC_BP_SP; |
| 395 | __tysan_check_internal(addr: ptr, size: accessSize, td, flags, pc, bp, sp); |
| 396 | } |
| 397 | } else { |
| 398 | // We appear to have the right type. Make sure that all other bytes in |
| 399 | // the type are still marked as interior bytes. If not, call the runtime. |
| 400 | bool isNotAllBadTD = GetNotAllBadTD(ShadowDataInt: (uint64_t)shadowData, AccessSize: accessSize); |
| 401 | if (isNotAllBadTD) { |
| 402 | GET_CALLER_PC_BP_SP; |
| 403 | __tysan_check_internal(addr: ptr, size: accessSize, td, flags, pc, bp, sp); |
| 404 | } |
| 405 | } |
| 406 | } else if (shadowIsNull) { |
| 407 | SetShadowType(td, shadowData, AccessSize: accessSize); |
| 408 | } |
| 409 | } |
| 410 | |
| 411 | extern "C" SANITIZER_INTERFACE_ATTRIBUTE void |
| 412 | __tysan_set_shadow_type(void *ptr, tysan_type_descriptor *td, |
| 413 | uint64_t accessSize) { |
| 414 | // In the mode where writes always set the type, for a write (which does |
| 415 | // not also read), we just set the type. |
| 416 | tysan_type_descriptor **shadow = shadow_for(ptr); |
| 417 | SetShadowType(td, shadowData: shadow, AccessSize: accessSize); |
| 418 | } |
| 419 | |
| 420 | Flags __tysan::flags_data; |
| 421 | |
| 422 | SANITIZER_INTERFACE_ATTRIBUTE uptr __tysan_shadow_memory_address; |
| 423 | SANITIZER_INTERFACE_ATTRIBUTE uptr __tysan_app_memory_mask; |
| 424 | |
| 425 | #ifdef TYSAN_RUNTIME_VMA |
| 426 | // Runtime detected VMA size. |
| 427 | int __tysan::vmaSize; |
| 428 | #endif |
| 429 | |
| 430 | void Flags::SetDefaults() { |
| 431 | #define TYSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue; |
| 432 | #include "tysan_flags.inc" |
| 433 | #undef TYSAN_FLAG |
| 434 | } |
| 435 | |
| 436 | static void RegisterTySanFlags(FlagParser *parser, Flags *f) { |
| 437 | #define TYSAN_FLAG(Type, Name, DefaultValue, Description) \ |
| 438 | RegisterFlag(parser, #Name, Description, &f->Name); |
| 439 | #include "tysan_flags.inc" |
| 440 | #undef TYSAN_FLAG |
| 441 | } |
| 442 | |
| 443 | static void InitializeFlags() { |
| 444 | SetCommonFlagsDefaults(); |
| 445 | { |
| 446 | CommonFlags cf; |
| 447 | cf.CopyFrom(other: *common_flags()); |
| 448 | cf.external_symbolizer_path = GetEnv(name: "TYSAN_SYMBOLIZER_PATH" ); |
| 449 | OverrideCommonFlags(cf); |
| 450 | } |
| 451 | |
| 452 | flags().SetDefaults(); |
| 453 | |
| 454 | FlagParser parser; |
| 455 | RegisterCommonFlags(parser: &parser); |
| 456 | RegisterTySanFlags(parser: &parser, f: &flags()); |
| 457 | parser.ParseString(s: GetEnv(name: "TYSAN_OPTIONS" )); |
| 458 | InitializeCommonFlags(); |
| 459 | if (Verbosity()) |
| 460 | ReportUnrecognizedFlags(); |
| 461 | if (common_flags()->help) |
| 462 | parser.PrintFlagDescriptions(); |
| 463 | } |
| 464 | |
| 465 | static void TySanInitializePlatformEarly() { |
| 466 | AvoidCVE_2016_2143(); |
| 467 | #ifdef TYSAN_RUNTIME_VMA |
| 468 | vmaSize = (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1); |
| 469 | #if defined(__aarch64__) && !SANITIZER_APPLE |
| 470 | if (vmaSize != 39 && vmaSize != 42 && vmaSize != 48) { |
| 471 | Printf("FATAL: TypeSanitizer: unsupported VMA range\n" ); |
| 472 | Printf("FATAL: Found %d - Supported 39, 42 and 48\n" , vmaSize); |
| 473 | Die(); |
| 474 | } |
| 475 | #endif |
| 476 | #endif |
| 477 | |
| 478 | __sanitizer::InitializePlatformEarly(); |
| 479 | |
| 480 | __tysan_shadow_memory_address = ShadowAddr(); |
| 481 | __tysan_app_memory_mask = AppMask(); |
| 482 | } |
| 483 | |
| 484 | namespace __tysan { |
| 485 | bool tysan_inited = false; |
| 486 | bool tysan_init_is_running; |
| 487 | } // namespace __tysan |
| 488 | |
| 489 | extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __tysan_init() { |
| 490 | CHECK(!tysan_init_is_running); |
| 491 | if (tysan_inited) |
| 492 | return; |
| 493 | tysan_init_is_running = true; |
| 494 | |
| 495 | InitializeFlags(); |
| 496 | TySanInitializePlatformEarly(); |
| 497 | |
| 498 | InitializeInterceptors(); |
| 499 | |
| 500 | if (!MmapFixedNoReserve(fixed_addr: ShadowAddr(), size: AppAddr() - ShadowAddr())) |
| 501 | Die(); |
| 502 | |
| 503 | tysan_init_is_running = false; |
| 504 | tysan_inited = true; |
| 505 | } |
| 506 | |
| 507 | #if SANITIZER_CAN_USE_PREINIT_ARRAY |
| 508 | __attribute__((section(".preinit_array" ), |
| 509 | used)) static void (*tysan_init_ptr)() = __tysan_init; |
| 510 | #endif |
| 511 | |