brintos

brintos / llvm-project-archived public Read only

0
0
Text · 15.2 KiB · 1c67ade Raw
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