511 lines · cpp
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-exception6//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 28using namespace __sanitizer;29using namespace __tysan;30 31extern "C" SANITIZER_INTERFACE_ATTRIBUTE void32tysan_set_type_unknown(const void *addr, uptr size) {33 if (tysan_inited)34 internal_memset(shadow_for(addr), 0, size * sizeof(uptr));35}36 37extern "C" SANITIZER_INTERFACE_ATTRIBUTE void38tysan_copy_types(const void *daddr, const void *saddr, uptr size) {39 if (tysan_inited)40 internal_memmove(shadow_for(daddr), shadow_for(saddr), size * sizeof(uptr));41}42 43static 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 the48 // prefix from the generated string.49 const char *TIPrefix = "typeinfo name for ";50 size_t TIPrefixLen = strlen(TIPrefix);51 52 const char *DName = Symbolizer::GetOrInit()->Demangle(Name);53 if (!internal_strncmp(DName, TIPrefix, TIPrefixLen))54 DName += TIPrefixLen;55 56 return DName;57}58 59static void printTDName(tysan_type_descriptor *td) {60 if (((sptr)td) <= 0) {61 Printf("<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->Member.Access);71 if (td->Member.Access != td->Member.Base) {72 Printf(" (in ");73 printTDName(td->Member.Base);74 Printf(" at offset %zu)", td->Member.Offset);75 }76 break;77 case TYSAN_STRUCT_TD:78 Printf("%s", getDisplayName(79 (char *)(td->Struct.Members + td->Struct.MemberCount)));80 break;81 }82}83 84static 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 else94 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.107static 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 something126 // 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.148static 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 164static 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 assumed182 // that we are accessing a member in an object. This means that rather than183 // walk up the scalar access TDA to reach an object, we should walk up the184 // object TBD to reach the scalar we are accessing it with. The offsets will185 // still be checked at the end to make sure this alias is legal.186 return walkAliasTree(TDB, TDA, OffsetB, OffsetA);187}188 189static 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(TDA) != getRootTD(TDB))196 return true;197 198 // TDB may have been adjusted by offset TDAOffset in the caller to point to199 // the outer type. Check for aliasing with and without adjusting for this200 // offset.201 return isAliasingLegalUp(TDA, TDB) || isAliasingLegalUp(TDB, TDA) ||202 isAliasingLegalWithOffset(TDA, TDB, OffsetB);203}204 205namespace __tysan {206class Decorator : public __sanitizer::SanitizerCommonDecorator {207public:208 Decorator() : SanitizerCommonDecorator() {}209 const char *Warning() { return Red(); }210 const char *Name() { return Green(); }211 const char *End() { return Default(); }212};213} // namespace __tysan214 215ALWAYS_INLINE216static 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("%s", d.Warning());222 Report("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("%s", d.End());226 Printf("%s of size %d at %p with type ", AccessStr, Size, Addr);227 228 Printf("%s", d.Name());229 printTDName(TD);230 Printf("%s", d.End());231 232 Printf(" %s of type ", DescStr);233 234 Printf("%s", d.Name());235 printTDName(OldTD);236 Printf("%s", d.End());237 238 if (Offset != 0)239 Printf(" that starts at offset %d\n", Offset);240 else241 Printf("\n");242 243 if (pc) {244 uptr top = 0;245 uptr bottom = 0;246 if (flags().print_stacktrace)247 GetThreadStackTopAndBottom(false, &top, &bottom);248 249 bool request_fast = StackTrace::WillUseFastUnwind(true);250 BufferedStackTrace ST;251 ST.Unwind(kStackTraceMax, pc, bp, 0, top, bottom, request_fast);252 ST.Print();253 } else {254 Printf("\n");255 }256}257 258ALWAYS_INLINE259static 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 273ALWAYS_INLINE274static 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 284ALWAYS_INLINE285static 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 295extern "C" SANITIZER_INTERFACE_ATTRIBUTE void296__tysan_instrument_mem_inst(char *dest, char *src, uint64_t size,297 bool needsMemMove) {298 tysan_type_descriptor **destShadowDataPtr = shadow_for(dest);299 300 if (!src) {301 internal_memset((char *)destShadowDataPtr, 0, 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((char *)destShadowDataPtr, srcShadow, size << PtrShift());311 } else {312 internal_memcpy((char *)destShadowDataPtr, srcShadow, size << PtrShift());313 }314}315 316ALWAYS_INLINE317static 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 else328 AccessStr = "ATOMIC UPDATE";329 330 tysan_type_descriptor **OldTDPtr = shadow_for(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(td, OldTD, i))338 reportError(addr, size, td, OldTD, AccessStr,339 "accesses part of an existing object", -i, pc, bp, sp);340 341 return;342 }343 344 if (!isAliasingLegal(td, OldTD)) {345 reportError(addr, size, td, OldTD, AccessStr, "accesses an existing object",346 0, pc, bp, sp);347 return;348 }349 350 // These types are allowed to alias (or the stored type is unknown), report351 // an error if we find an interior type.352 353 for (int i = 0; i < size; ++i) {354 OldTDPtr = shadow_for((void *)(((uptr)addr) + i));355 OldTD = *OldTDPtr;356 if (((sptr)OldTD) >= 0 && !isAliasingLegal(td, OldTD))357 reportError(addr, size, td, OldTD, AccessStr,358 "partially accesses an object", i, pc, bp, sp);359 }360}361 362extern "C" SANITIZER_INTERFACE_ATTRIBUTE void363__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 368extern "C" SANITIZER_INTERFACE_ATTRIBUTE void369__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 split377 // into two different functions378 if (sanitizeFunction) {379 380 if (td != loadedTD) {381 382 // We now know that the types did not match (we're on the slow path). If383 // 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 value386 // are also of unknown type.387 bool isAllUnknownTD = GetNotAllUnkTD((uint64_t)shadowData, accessSize);388 if (isAllUnknownTD) {389 GET_CALLER_PC_BP_SP;390 __tysan_check_internal(ptr, accessSize, td, flags, pc, bp, sp);391 }392 SetShadowType(td, shadowData, accessSize);393 } else {394 GET_CALLER_PC_BP_SP;395 __tysan_check_internal(ptr, accessSize, td, flags, pc, bp, sp);396 }397 } else {398 // We appear to have the right type. Make sure that all other bytes in399 // the type are still marked as interior bytes. If not, call the runtime.400 bool isNotAllBadTD = GetNotAllBadTD((uint64_t)shadowData, accessSize);401 if (isNotAllBadTD) {402 GET_CALLER_PC_BP_SP;403 __tysan_check_internal(ptr, accessSize, td, flags, pc, bp, sp);404 }405 }406 } else if (shadowIsNull) {407 SetShadowType(td, shadowData, accessSize);408 }409}410 411extern "C" SANITIZER_INTERFACE_ATTRIBUTE void412__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 does415 // not also read), we just set the type.416 tysan_type_descriptor **shadow = shadow_for(ptr);417 SetShadowType(td, shadow, accessSize);418}419 420Flags __tysan::flags_data;421 422SANITIZER_INTERFACE_ATTRIBUTE uptr __tysan_shadow_memory_address;423SANITIZER_INTERFACE_ATTRIBUTE uptr __tysan_app_memory_mask;424 425#ifdef TYSAN_RUNTIME_VMA426// Runtime detected VMA size.427int __tysan::vmaSize;428#endif429 430void Flags::SetDefaults() {431#define TYSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;432#include "tysan_flags.inc"433#undef TYSAN_FLAG434}435 436static 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_FLAG441}442 443static void InitializeFlags() {444 SetCommonFlagsDefaults();445 {446 CommonFlags cf;447 cf.CopyFrom(*common_flags());448 cf.external_symbolizer_path = GetEnv("TYSAN_SYMBOLIZER_PATH");449 OverrideCommonFlags(cf);450 }451 452 flags().SetDefaults();453 454 FlagParser parser;455 RegisterCommonFlags(&parser);456 RegisterTySanFlags(&parser, &flags());457 parser.ParseString(GetEnv("TYSAN_OPTIONS"));458 InitializeCommonFlags();459 if (Verbosity())460 ReportUnrecognizedFlags();461 if (common_flags()->help)462 parser.PrintFlagDescriptions();463}464 465static void TySanInitializePlatformEarly() {466 AvoidCVE_2016_2143();467#ifdef TYSAN_RUNTIME_VMA468 vmaSize = (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1);469#if defined(__aarch64__) && !SANITIZER_APPLE470 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#endif476#endif477 478 __sanitizer::InitializePlatformEarly();479 480 __tysan_shadow_memory_address = ShadowAddr();481 __tysan_app_memory_mask = AppMask();482}483 484namespace __tysan {485bool tysan_inited = false;486bool tysan_init_is_running;487} // namespace __tysan488 489extern "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(ShadowAddr(), AppAddr() - ShadowAddr()))501 Die();502 503 tysan_init_is_running = false;504 tysan_inited = true;505}506 507#if SANITIZER_CAN_USE_PREINIT_ARRAY508__attribute__((section(".preinit_array"),509 used)) static void (*tysan_init_ptr)() = __tysan_init;510#endif511