brintos

brintos / llvm-project-archived public Read only

0
0
Text · 14.1 KiB · d3fa953 Raw
366 lines · cpp
1//===-- asan_fake_stack.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 AddressSanitizer, an address sanity checker.10//11// FakeStack is used to detect use-after-return bugs.12//===----------------------------------------------------------------------===//13 14#include "asan_allocator.h"15#include "asan_poisoning.h"16#include "asan_thread.h"17 18namespace __asan {19 20static const u64 kMagic1 = kAsanStackAfterReturnMagic;21static const u64 kMagic2 = (kMagic1 << 8) | kMagic1;22static const u64 kMagic4 = (kMagic2 << 16) | kMagic2;23static const u64 kMagic8 = (kMagic4 << 32) | kMagic4;24 25static const u64 kAllocaRedzoneSize = 32UL;26static const u64 kAllocaRedzoneMask = 31UL;27 28// For small size classes inline PoisonShadow for better performance.29ALWAYS_INLINE void SetShadow(uptr ptr, uptr size, uptr class_id, u64 magic) {30  CHECK(AddrIsAlignedByGranularity(ptr + size));31  u64* shadow = reinterpret_cast<u64*>(MemToShadow(ptr));32  if (ASAN_SHADOW_SCALE == 3 && class_id <= 6) {33    // This code expects ASAN_SHADOW_SCALE=3.34    for (uptr i = 0; i < (((uptr)1) << class_id); i++) {35      shadow[i] = magic;36      // Make sure this does not become memset.37      SanitizerBreakOptimization(nullptr);38    }39  } else {40    // The size class is too big, it's cheaper to poison only size bytes.41    PoisonShadow(ptr, size, static_cast<u8>(magic));42  }43 44  if (magic == 0) {45    uptr redzone_size = FakeStack::BytesInSizeClass(class_id) - size;46    PoisonShadow(ptr + size, redzone_size, kAsanStackRightRedzoneMagic);47  }48}49 50FakeStack* FakeStack::Create(uptr stack_size_log) {51  static uptr kMinStackSizeLog = 16;52  static uptr kMaxStackSizeLog = FIRST_32_SECOND_64(24, 28);53  if (stack_size_log < kMinStackSizeLog)54    stack_size_log = kMinStackSizeLog;55  if (stack_size_log > kMaxStackSizeLog)56    stack_size_log = kMaxStackSizeLog;57  CHECK_LE(kMaxStackFrameSizeLog, stack_size_log);58  uptr size = RequiredSize(stack_size_log);59  uptr padded_size = size + kMaxStackFrameSize;60  void* true_res = reinterpret_cast<void*>(61      flags()->uar_noreserve ? MmapNoReserveOrDie(padded_size, "FakeStack")62                             : MmapOrDie(padded_size, "FakeStack"));63  // GetFrame() requires the property that64  // (res + kFlagsOffset + SizeRequiredForFlags(stack_size_log)) is aligned to65  // kMaxStackFrameSize.66  // We didn't use MmapAlignedOrDieOnFatalError, because it requires that the67  // *size* is a power of 2, which is an overly strong condition.68  static_assert(alignof(FakeStack) <= kMaxStackFrameSize);69  FakeStack* res = reinterpret_cast<FakeStack*>(70      RoundUpTo(71          (uptr)true_res + kFlagsOffset + SizeRequiredForFlags(stack_size_log),72          kMaxStackFrameSize) -73      kFlagsOffset - SizeRequiredForFlags(stack_size_log));74  res->true_start = true_res;75  res->stack_size_log_ = stack_size_log;76  u8* p = reinterpret_cast<u8*>(res);77  VReport(1,78          "T%d: FakeStack created: %p -- %p stack_size_log: %zd; "79          "mmapped %zdK, noreserve=%d, true_start: %p, start of first frame: "80          "0x%zx\n",81          GetCurrentTidOrInvalid(), (void*)p,82          (void*)(p + FakeStack::RequiredSize(stack_size_log)), stack_size_log,83          size >> 10, flags()->uar_noreserve, res->true_start,84          res->GetFrame(stack_size_log, /*class_id*/ 0, /*pos*/ 0));85  return res;86}87 88void FakeStack::Destroy(int tid) {89  PoisonAll(0);90  if (Verbosity() >= 2) {91    InternalScopedString str;92    for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++)93      str.AppendF("%zd: %zd/%zd; ", class_id, hint_position_[class_id],94                  NumberOfFrames(stack_size_log(), class_id));95    Report("T%d: FakeStack destroyed: %s\n", tid, str.data());96  }97  uptr size = RequiredSize(stack_size_log_);98  uptr padded_size = size + kMaxStackFrameSize;99  FlushUnneededASanShadowMemory(reinterpret_cast<uptr>(true_start),100                                padded_size);101  UnmapOrDie(true_start, padded_size);102}103 104void FakeStack::PoisonAll(u8 magic) {105  PoisonShadow(reinterpret_cast<uptr>(this), RequiredSize(stack_size_log()),106               magic);107}108 109#if !defined(_MSC_VER) || defined(__clang__)110ALWAYS_INLINE USED111#endif112    FakeFrame* FakeStack::Allocate(uptr stack_size_log, uptr class_id,113                                   uptr real_stack) {114  CHECK_LT(class_id, kNumberOfSizeClasses);115  if (needs_gc_)116    GC(real_stack);117  uptr& hint_position = hint_position_[class_id];118  const int num_iter = NumberOfFrames(stack_size_log, class_id);119  u8* flags = GetFlags(stack_size_log, class_id);120  for (int i = 0; i < num_iter; i++) {121    uptr pos = ModuloNumberOfFrames(stack_size_log, class_id, hint_position++);122    // This part is tricky. On one hand, checking and setting flags[pos]123    // should be atomic to ensure async-signal safety. But on the other hand,124    // if the signal arrives between checking and setting flags[pos], the125    // signal handler's fake stack will start from a different hint_position126    // and so will not touch this particular byte. So, it is safe to do this127    // with regular non-atomic load and store (at least I was not able to make128    // this code crash).129    if (flags[pos])130      continue;131    flags[pos] = 1;132    FakeFrame* res =133        reinterpret_cast<FakeFrame*>(GetFrame(stack_size_log, class_id, pos));134    res->real_stack = real_stack;135    *SavedFlagPtr(reinterpret_cast<uptr>(res), class_id) = &flags[pos];136    return res;137  }138  return nullptr;  // We are out of fake stack.139}140 141uptr FakeStack::AddrIsInFakeStack(uptr ptr, uptr* frame_beg, uptr* frame_end) {142  uptr stack_size_log = this->stack_size_log();143  uptr beg = reinterpret_cast<uptr>(GetFrame(stack_size_log, 0, 0));144  uptr end = reinterpret_cast<uptr>(this) + RequiredSize(stack_size_log);145  if (ptr < beg || ptr >= end)146    return 0;147  uptr class_id = (ptr - beg) >> stack_size_log;148  uptr base = beg + (class_id << stack_size_log);149  CHECK_LE(base, ptr);150  CHECK_LT(ptr, base + (((uptr)1) << stack_size_log));151  uptr pos = (ptr - base) >> (kMinStackFrameSizeLog + class_id);152  uptr res = base + pos * BytesInSizeClass(class_id);153  *frame_end = res + BytesInSizeClass(class_id);154  *frame_beg = res + sizeof(FakeFrame);155  return res;156}157 158void FakeStack::HandleNoReturn() { needs_gc_ = true; }159 160// Hack: The statement below is not true if we take into account sigaltstack or161// makecontext. It should be possible to make GC to discard wrong stack frame if162// we use these tools. For now, let's support the simplest case and allow GC to163// discard only frames from the default stack, assuming there is no buffer on164// the stack which is used for makecontext or sigaltstack.165//166// When throw, longjmp or some such happens we don't call OnFree() and167// as the result may leak one or more fake frames, but the good news is that168// we are notified about all such events by HandleNoReturn().169// If we recently had such no-return event we need to collect garbage frames.170// We do it based on their 'real_stack' values -- everything that is lower171// than the current real_stack is garbage.172NOINLINE void FakeStack::GC(uptr real_stack) {173  AsanThread* curr_thread = GetCurrentThread();174  if (!curr_thread)175    return;  // Try again when we have a thread.176  auto top = curr_thread->stack_top();177  auto bottom = curr_thread->stack_bottom();178  if (real_stack < bottom || real_stack > top)179    return;  // Not the default stack.180 181  for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++) {182    u8* flags = GetFlags(stack_size_log(), class_id);183    for (uptr i = 0, n = NumberOfFrames(stack_size_log(), class_id); i < n;184         i++) {185      if (flags[i] == 0)186        continue;  // not allocated.187      FakeFrame* ff =188          reinterpret_cast<FakeFrame*>(GetFrame(stack_size_log(), class_id, i));189      // GC only on the default stack.190      if (bottom < ff->real_stack && ff->real_stack < real_stack) {191        flags[i] = 0;192        // Poison the frame, so the any access will be reported as UAR.193        SetShadow(reinterpret_cast<uptr>(ff), BytesInSizeClass(class_id),194                  class_id, kMagic8);195      }196    }197  }198  needs_gc_ = false;199}200 201void FakeStack::ForEachFakeFrame(RangeIteratorCallback callback, void* arg) {202  for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++) {203    u8* flags = GetFlags(stack_size_log(), class_id);204    for (uptr i = 0, n = NumberOfFrames(stack_size_log(), class_id); i < n;205         i++) {206      if (flags[i] == 0)207        continue;  // not allocated.208      FakeFrame* ff =209          reinterpret_cast<FakeFrame*>(GetFrame(stack_size_log(), class_id, i));210      uptr begin = reinterpret_cast<uptr>(ff);211      callback(begin, begin + FakeStack::BytesInSizeClass(class_id), arg);212    }213  }214}215 216#if (SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_FUCHSIA217static THREADLOCAL FakeStack* fake_stack_tls;218 219static FakeStack* GetTLSFakeStack() { return fake_stack_tls; }220static void SetTLSFakeStack(FakeStack* fs) { fake_stack_tls = fs; }221void ResetTLSFakeStack() { fake_stack_tls = nullptr; }222#else223static FakeStack* GetTLSFakeStack() { return nullptr; }224static void SetTLSFakeStack(FakeStack*) {}225void ResetTLSFakeStack() {}226#endif  // (SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_FUCHSIA227 228static FakeStack* GetFakeStack() {229  AsanThread* t = GetCurrentThread();230  if (!t)231    return nullptr;232  return t->get_or_create_fake_stack();233}234 235static FakeStack* GetFakeStackFast() {236  FakeStack* fs = GetTLSFakeStack();237  if (LIKELY(fs))238    return fs;239  if (!__asan_option_detect_stack_use_after_return)240    return nullptr;241  fs = GetFakeStack();242  if (LIKELY(fs))243    SetTLSFakeStack(fs);244  return fs;245}246 247static FakeStack* GetFakeStackFastAlways() {248  FakeStack* fs = GetTLSFakeStack();249  if (LIKELY(fs))250    return fs;251  fs = GetFakeStack();252  if (LIKELY(fs))253    SetTLSFakeStack(fs);254  return fs;255}256 257static ALWAYS_INLINE uptr OnMalloc(uptr class_id, uptr size) {258  FakeStack* fs = GetFakeStackFast();259  if (!fs)260    return 0;261  FakeFrame* ff =262      fs->Allocate(fs->stack_size_log(), class_id, GET_CURRENT_FRAME());263  if (!ff)264    return 0;  // Out of fake stack.265  uptr ptr = reinterpret_cast<uptr>(ff);266  SetShadow(ptr, size, class_id, 0);267  return ptr;268}269 270static ALWAYS_INLINE uptr OnMallocAlways(uptr class_id, uptr size) {271  FakeStack* fs = GetFakeStackFastAlways();272  if (!fs)273    return 0;274  FakeFrame* ff =275      fs->Allocate(fs->stack_size_log(), class_id, GET_CURRENT_FRAME());276  if (!ff)277    return 0;  // Out of fake stack.278  uptr ptr = reinterpret_cast<uptr>(ff);279  SetShadow(ptr, size, class_id, 0);280  return ptr;281}282 283static ALWAYS_INLINE void OnFree(uptr ptr, uptr class_id, uptr size) {284  FakeStack::Deallocate(ptr, class_id);285  SetShadow(ptr, size, class_id, kMagic8);286}287 288}  // namespace __asan289 290// ---------------------- Interface ---------------- {{{1291using namespace __asan;292#define DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(class_id)                      \293  extern "C" SANITIZER_INTERFACE_ATTRIBUTE uptr                               \294  __asan_stack_malloc_##class_id(uptr size) {                                 \295    return OnMalloc(class_id, size);                                          \296  }                                                                           \297  extern "C" SANITIZER_INTERFACE_ATTRIBUTE uptr                               \298  __asan_stack_malloc_always_##class_id(uptr size) {                          \299    return OnMallocAlways(class_id, size);                                    \300  }                                                                           \301  extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __asan_stack_free_##class_id( \302      uptr ptr, uptr size) {                                                  \303    OnFree(ptr, class_id, size);                                              \304  }305 306DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(0)307DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(1)308DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(2)309DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(3)310DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(4)311DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(5)312DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(6)313DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(7)314DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(8)315DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(9)316DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(10)317 318extern "C" {319// TODO: remove this method and fix tests that use it by setting320// -asan-use-after-return=never, after modal UAR flag lands321// (https://github.com/google/sanitizers/issues/1394)322SANITIZER_INTERFACE_ATTRIBUTE323void* __asan_get_current_fake_stack() { return GetFakeStackFast(); }324 325SANITIZER_INTERFACE_ATTRIBUTE326void* __asan_addr_is_in_fake_stack(void* fake_stack, void* addr, void** beg,327                                   void** end) {328  FakeStack* fs = reinterpret_cast<FakeStack*>(fake_stack);329  if (!fs)330    return nullptr;331  uptr frame_beg, frame_end;332  FakeFrame* frame = reinterpret_cast<FakeFrame*>(fs->AddrIsInFakeStack(333      reinterpret_cast<uptr>(addr), &frame_beg, &frame_end));334  if (!frame)335    return nullptr;336  if (frame->magic != kCurrentStackFrameMagic)337    return nullptr;338  if (beg)339    *beg = reinterpret_cast<void*>(frame_beg);340  if (end)341    *end = reinterpret_cast<void*>(frame_end);342  return reinterpret_cast<void*>(frame->real_stack);343}344 345SANITIZER_INTERFACE_ATTRIBUTE346void __asan_alloca_poison(uptr addr, uptr size) {347  uptr LeftRedzoneAddr = addr - kAllocaRedzoneSize;348  uptr PartialRzAddr = addr + size;349  uptr RightRzAddr = (PartialRzAddr + kAllocaRedzoneMask) & ~kAllocaRedzoneMask;350  uptr PartialRzAligned = PartialRzAddr & ~(ASAN_SHADOW_GRANULARITY - 1);351  FastPoisonShadow(LeftRedzoneAddr, kAllocaRedzoneSize, kAsanAllocaLeftMagic);352  FastPoisonShadowPartialRightRedzone(353      PartialRzAligned, PartialRzAddr % ASAN_SHADOW_GRANULARITY,354      RightRzAddr - PartialRzAligned, kAsanAllocaRightMagic);355  FastPoisonShadow(RightRzAddr, kAllocaRedzoneSize, kAsanAllocaRightMagic);356}357 358SANITIZER_INTERFACE_ATTRIBUTE359void __asan_allocas_unpoison(uptr top, uptr bottom) {360  if ((!top) || (top > bottom))361    return;362  REAL(memset)(reinterpret_cast<void*>(MemToShadow(top)), 0,363               (bottom - top) / ASAN_SHADOW_GRANULARITY);364}365}  // extern "C"366