Path: blob/main/contrib/llvm-project/compiler-rt/lib/tysan/tysan.cpp
213766 views
//===-- tysan.cpp ---------------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file is a part of TypeSanitizer.9//10// TypeSanitizer runtime.11//===----------------------------------------------------------------------===//1213#include "sanitizer_common/sanitizer_atomic.h"14#include "sanitizer_common/sanitizer_common.h"15#include "sanitizer_common/sanitizer_flag_parser.h"16#include "sanitizer_common/sanitizer_flags.h"17#include "sanitizer_common/sanitizer_libc.h"18#include "sanitizer_common/sanitizer_report_decorator.h"19#include "sanitizer_common/sanitizer_stacktrace.h"20#include "sanitizer_common/sanitizer_symbolizer.h"2122#include "tysan/tysan.h"2324#include <string.h>2526using namespace __sanitizer;27using namespace __tysan;2829extern "C" SANITIZER_INTERFACE_ATTRIBUTE void30tysan_set_type_unknown(const void *addr, uptr size) {31if (tysan_inited)32internal_memset(shadow_for(addr), 0, size * sizeof(uptr));33}3435extern "C" SANITIZER_INTERFACE_ATTRIBUTE void36tysan_copy_types(const void *daddr, const void *saddr, uptr size) {37if (tysan_inited)38internal_memmove(shadow_for(daddr), shadow_for(saddr), size * sizeof(uptr));39}4041static const char *getDisplayName(const char *Name) {42if (Name[0] == '\0')43return "<anonymous type>";4445// Clang generates tags for C++ types that demangle as typeinfo. Remove the46// prefix from the generated string.47const char *TIPrefix = "typeinfo name for ";48size_t TIPrefixLen = strlen(TIPrefix);4950const char *DName = Symbolizer::GetOrInit()->Demangle(Name);51if (!internal_strncmp(DName, TIPrefix, TIPrefixLen))52DName += TIPrefixLen;5354return DName;55}5657static void printTDName(tysan_type_descriptor *td) {58if (((sptr)td) <= 0) {59Printf("<unknown type>");60return;61}6263switch (td->Tag) {64default:65CHECK(false && "invalid enum value");66break;67case TYSAN_MEMBER_TD:68printTDName(td->Member.Access);69if (td->Member.Access != td->Member.Base) {70Printf(" (in ");71printTDName(td->Member.Base);72Printf(" at offset %zu)", td->Member.Offset);73}74break;75case TYSAN_STRUCT_TD:76Printf("%s", getDisplayName(77(char *)(td->Struct.Members + td->Struct.MemberCount)));78break;79}80}8182static tysan_type_descriptor *getRootTD(tysan_type_descriptor *TD) {83tysan_type_descriptor *RootTD = TD;8485do {86RootTD = TD;8788if (TD->Tag == TYSAN_STRUCT_TD) {89if (TD->Struct.MemberCount > 0)90TD = TD->Struct.Members[0].Type;91else92TD = nullptr;93} else if (TD->Tag == TYSAN_MEMBER_TD) {94TD = TD->Member.Access;95} else {96CHECK(false && "invalid enum value");97break;98}99} while (TD);100101return RootTD;102}103104// Walk up TDA to see if it reaches TDB.105static bool walkAliasTree(tysan_type_descriptor *TDA,106tysan_type_descriptor *TDB, uptr OffsetA,107uptr OffsetB) {108do {109if (TDA == TDB)110return OffsetA == OffsetB;111112if (TDA->Tag == TYSAN_STRUCT_TD) {113// Reached root type descriptor.114if (!TDA->Struct.MemberCount)115break;116117uptr Idx = 0;118for (; Idx < TDA->Struct.MemberCount - 1; ++Idx) {119if (TDA->Struct.Members[Idx].Offset >= OffsetA)120break;121}122123// This offset can't be negative. Therefore we must be accessing something124// before the current type (not legal) or partially inside the last type.125// In the latter case, we adjust Idx.126if (TDA->Struct.Members[Idx].Offset > OffsetA) {127// Trying to access something before the current type.128if (!Idx)129return false;130131Idx -= 1;132}133134OffsetA -= TDA->Struct.Members[Idx].Offset;135TDA = TDA->Struct.Members[Idx].Type;136} else {137CHECK(false && "invalid enum value");138break;139}140} while (TDA);141142return false;143}144145// Walk up the tree starting with TDA to see if we reach TDB.146static bool isAliasingLegalUp(tysan_type_descriptor *TDA,147tysan_type_descriptor *TDB) {148uptr OffsetA = 0, OffsetB = 0;149if (TDB->Tag == TYSAN_MEMBER_TD) {150OffsetB = TDB->Member.Offset;151TDB = TDB->Member.Base;152}153154if (TDA->Tag == TYSAN_MEMBER_TD) {155OffsetA = TDA->Member.Offset;156TDA = TDA->Member.Base;157}158159return walkAliasTree(TDA, TDB, OffsetA, OffsetB);160}161162static bool isAliasingLegalWithOffset(tysan_type_descriptor *TDA,163tysan_type_descriptor *TDB,164uptr OffsetB) {165// This is handled by calls to isAliasingLegalUp.166if (OffsetB == 0)167return false;168169// You can't have an offset into a member.170if (TDB->Tag == TYSAN_MEMBER_TD)171return false;172173uptr OffsetA = 0;174if (TDA->Tag == TYSAN_MEMBER_TD) {175OffsetA = TDA->Member.Offset;176TDA = TDA->Member.Base;177}178179// Since the access was partially inside TDB (the shadow), it can be assumed180// that we are accessing a member in an object. This means that rather than181// walk up the scalar access TDA to reach an object, we should walk up the182// object TBD to reach the scalar we are accessing it with. The offsets will183// still be checked at the end to make sure this alias is legal.184return walkAliasTree(TDB, TDA, OffsetB, OffsetA);185}186187static bool isAliasingLegal(tysan_type_descriptor *TDA,188tysan_type_descriptor *TDB, uptr OffsetB = 0) {189if (TDA == TDB || !TDB || !TDA)190return true;191192// Aliasing is legal is the two types have different root nodes.193if (getRootTD(TDA) != getRootTD(TDB))194return true;195196// TDB may have been adjusted by offset TDAOffset in the caller to point to197// the outer type. Check for aliasing with and without adjusting for this198// offset.199return isAliasingLegalUp(TDA, TDB) || isAliasingLegalUp(TDB, TDA) ||200isAliasingLegalWithOffset(TDA, TDB, OffsetB);201}202203namespace __tysan {204class Decorator : public __sanitizer::SanitizerCommonDecorator {205public:206Decorator() : SanitizerCommonDecorator() {}207const char *Warning() { return Red(); }208const char *Name() { return Green(); }209const char *End() { return Default(); }210};211} // namespace __tysan212213ALWAYS_INLINE214static void reportError(void *Addr, int Size, tysan_type_descriptor *TD,215tysan_type_descriptor *OldTD, const char *AccessStr,216const char *DescStr, int Offset, uptr pc, uptr bp,217uptr sp) {218Decorator d;219Printf("%s", d.Warning());220Report("ERROR: TypeSanitizer: type-aliasing-violation on address %p"221" (pc %p bp %p sp %p tid %llu)\n",222Addr, (void *)pc, (void *)bp, (void *)sp, GetTid());223Printf("%s", d.End());224Printf("%s of size %d at %p with type ", AccessStr, Size, Addr);225226Printf("%s", d.Name());227printTDName(TD);228Printf("%s", d.End());229230Printf(" %s of type ", DescStr);231232Printf("%s", d.Name());233printTDName(OldTD);234Printf("%s", d.End());235236if (Offset != 0)237Printf(" that starts at offset %d\n", Offset);238else239Printf("\n");240241if (pc) {242uptr top = 0;243uptr bottom = 0;244if (flags().print_stacktrace)245GetThreadStackTopAndBottom(false, &top, &bottom);246247bool request_fast = StackTrace::WillUseFastUnwind(true);248BufferedStackTrace ST;249ST.Unwind(kStackTraceMax, pc, bp, 0, top, bottom, request_fast);250ST.Print();251} else {252Printf("\n");253}254}255256extern "C" SANITIZER_INTERFACE_ATTRIBUTE void257__tysan_check(void *addr, int size, tysan_type_descriptor *td, int flags) {258GET_CALLER_PC_BP_SP;259260bool IsRead = flags & 1;261bool IsWrite = flags & 2;262const char *AccessStr;263if (IsRead && !IsWrite)264AccessStr = "READ";265else if (!IsRead && IsWrite)266AccessStr = "WRITE";267else268AccessStr = "ATOMIC UPDATE";269270tysan_type_descriptor **OldTDPtr = shadow_for(addr);271tysan_type_descriptor *OldTD = *OldTDPtr;272if (((sptr)OldTD) < 0) {273int i = -((sptr)OldTD);274OldTDPtr -= i;275OldTD = *OldTDPtr;276277if (!isAliasingLegal(td, OldTD, i))278reportError(addr, size, td, OldTD, AccessStr,279"accesses part of an existing object", -i, pc, bp, sp);280281return;282}283284if (!isAliasingLegal(td, OldTD)) {285reportError(addr, size, td, OldTD, AccessStr, "accesses an existing object",2860, pc, bp, sp);287return;288}289290// These types are allowed to alias (or the stored type is unknown), report291// an error if we find an interior type.292293for (int i = 0; i < size; ++i) {294OldTDPtr = shadow_for((void *)(((uptr)addr) + i));295OldTD = *OldTDPtr;296if (((sptr)OldTD) >= 0 && !isAliasingLegal(td, OldTD))297reportError(addr, size, td, OldTD, AccessStr,298"partially accesses an object", i, pc, bp, sp);299}300}301302Flags __tysan::flags_data;303304SANITIZER_INTERFACE_ATTRIBUTE uptr __tysan_shadow_memory_address;305SANITIZER_INTERFACE_ATTRIBUTE uptr __tysan_app_memory_mask;306307#ifdef TYSAN_RUNTIME_VMA308// Runtime detected VMA size.309int __tysan::vmaSize;310#endif311312void Flags::SetDefaults() {313#define TYSAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue;314#include "tysan_flags.inc"315#undef TYSAN_FLAG316}317318static void RegisterTySanFlags(FlagParser *parser, Flags *f) {319#define TYSAN_FLAG(Type, Name, DefaultValue, Description) \320RegisterFlag(parser, #Name, Description, &f->Name);321#include "tysan_flags.inc"322#undef TYSAN_FLAG323}324325static void InitializeFlags() {326SetCommonFlagsDefaults();327{328CommonFlags cf;329cf.CopyFrom(*common_flags());330cf.external_symbolizer_path = GetEnv("TYSAN_SYMBOLIZER_PATH");331OverrideCommonFlags(cf);332}333334flags().SetDefaults();335336FlagParser parser;337RegisterCommonFlags(&parser);338RegisterTySanFlags(&parser, &flags());339parser.ParseString(GetEnv("TYSAN_OPTIONS"));340InitializeCommonFlags();341if (Verbosity())342ReportUnrecognizedFlags();343if (common_flags()->help)344parser.PrintFlagDescriptions();345}346347static void TySanInitializePlatformEarly() {348AvoidCVE_2016_2143();349#ifdef TYSAN_RUNTIME_VMA350vmaSize = (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1);351#if defined(__aarch64__) && !SANITIZER_APPLE352if (vmaSize != 39 && vmaSize != 42 && vmaSize != 48) {353Printf("FATAL: TypeSanitizer: unsupported VMA range\n");354Printf("FATAL: Found %d - Supported 39, 42 and 48\n", vmaSize);355Die();356}357#endif358#endif359360__sanitizer::InitializePlatformEarly();361362__tysan_shadow_memory_address = ShadowAddr();363__tysan_app_memory_mask = AppMask();364}365366namespace __tysan {367bool tysan_inited = false;368bool tysan_init_is_running;369} // namespace __tysan370371extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __tysan_init() {372CHECK(!tysan_init_is_running);373if (tysan_inited)374return;375tysan_init_is_running = true;376377InitializeFlags();378TySanInitializePlatformEarly();379380InitializeInterceptors();381382if (!MmapFixedNoReserve(ShadowAddr(), AppAddr() - ShadowAddr()))383Die();384385tysan_init_is_running = false;386tysan_inited = true;387}388389#if SANITIZER_CAN_USE_PREINIT_ARRAY390__attribute__((section(".preinit_array"),391used)) static void (*tysan_init_ptr)() = __tysan_init;392#endif393394395