asan_fake_stack.cc revision c98fc1f8e52812cfaf5b19a29db5ed56acb0a682
1//===-- asan_fake_stack.cc ------------------------------------------------===//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This file is a part of AddressSanitizer, an address sanity checker.
11//
12// FakeStack is used to detect use-after-return bugs.
13//===----------------------------------------------------------------------===//
14#include "asan_allocator.h"
15#include "asan_poisoning.h"
16#include "asan_thread.h"
17
18namespace __asan {
19
20void FakeStack::PoisonAll(u8 magic) {
21  PoisonShadow(reinterpret_cast<uptr>(this), RequiredSize(stack_size_log()),
22               magic);
23}
24
25FakeFrame *FakeStack::Allocate(uptr stack_size_log, uptr class_id,
26                               uptr real_stack) {
27  CHECK_LT(class_id, kNumberOfSizeClasses);
28  uptr &hint_position = hint_position_[class_id];
29  const int num_iter = NumberOfFrames(stack_size_log, class_id);
30  u8 *flags = GetFlags(stack_size_log, class_id);
31  for (int i = 0; i < num_iter; i++) {
32    uptr pos = ModuloNumberOfFrames(stack_size_log, class_id, hint_position++);
33    if (flags[pos]) continue;
34    // FIXME: this does not have to be thread-safe, just async-signal-safe.
35    if (0 == atomic_exchange((atomic_uint8_t *)&flags[pos], 1,
36                             memory_order_relaxed)) {
37      FakeFrame *res = reinterpret_cast<FakeFrame *>(
38          GetFrame(stack_size_log, class_id, pos));
39      res->real_stack = real_stack;
40      res->class_id = class_id;
41      return res;
42    }
43  }
44  CHECK(0 && "Failed to allocate a fake stack frame");
45  return 0;
46}
47
48void FakeStack::Deallocate(FakeFrame *ff, uptr stack_size_log, uptr class_id,
49                           uptr real_stack) {
50  u8 *base = GetFrame(stack_size_log, class_id, 0);
51  u8 *cur = reinterpret_cast<u8 *>(ff);
52  CHECK_LE(base, cur);
53  CHECK_LT(cur, base + (1UL << stack_size_log));
54  uptr pos = (cur - base) >> (kMinStackFrameSizeLog + class_id);
55  u8 *flags = GetFlags(stack_size_log, class_id);
56  CHECK_EQ(flags[pos], 1);
57  flags[pos] = 0;
58}
59
60uptr FakeStack::AddrIsInFakeStack(uptr ptr) {
61  uptr stack_size_log = this->stack_size_log();
62  uptr beg = reinterpret_cast<uptr>(GetFrame(stack_size_log, 0, 0));
63  uptr end = reinterpret_cast<uptr>(this) + RequiredSize(stack_size_log);
64  if (ptr < beg || ptr >= end) return 0;
65  uptr class_id = (ptr - beg) >> stack_size_log;
66  uptr base = beg + (class_id << stack_size_log);
67  CHECK_LE(base, ptr);
68  CHECK_LT(ptr, base + (1UL << stack_size_log));
69  uptr pos = (ptr - base) >> (kMinStackFrameSizeLog + class_id);
70  return base + pos * BytesInSizeClass(class_id);
71}
72
73ALWAYS_INLINE uptr OnMalloc(uptr class_id, uptr size, uptr real_stack) {
74  AsanThread *t = GetCurrentThread();
75  if (!t) return real_stack;
76  FakeStack *fs = t->fake_stack();
77  if (!fs) return real_stack;
78  FakeFrame *ff = fs->Allocate(fs->stack_size_log(), class_id, real_stack);
79  uptr ptr = reinterpret_cast<uptr>(ff);
80  PoisonShadow(ptr, size, 0);
81  return ptr;
82}
83
84ALWAYS_INLINE void OnFree(uptr ptr, uptr class_id, uptr size, uptr real_stack) {
85  if (ptr == real_stack)
86    return;
87  AsanThread *t = GetCurrentThread();
88  if (!t) return;
89  FakeStack *fs = t->fake_stack();
90  FakeFrame *ff = reinterpret_cast<FakeFrame *>(ptr);
91  fs->Deallocate(ff, fs->stack_size_log(), class_id, real_stack);
92  PoisonShadow(ptr, size, kAsanStackAfterReturnMagic);
93}
94
95}  // namespace __asan
96
97// ---------------------- Interface ---------------- {{{1
98#define DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(class_id)                       \
99  extern "C" SANITIZER_INTERFACE_ATTRIBUTE uptr                                \
100  __asan_stack_malloc_##class_id(uptr size, uptr real_stack) {                 \
101    return __asan::OnMalloc(class_id, size, real_stack);                       \
102  }                                                                            \
103  extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __asan_stack_free_##class_id(  \
104      uptr ptr, uptr size, uptr real_stack) {                                  \
105    __asan::OnFree(ptr, class_id, size, real_stack);                           \
106  }
107
108DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(0)
109DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(1)
110DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(2)
111DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(3)
112DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(4)
113DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(5)
114DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(6)
115DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(7)
116DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(8)
117DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(9)
118DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(10)
119