asan_fake_stack.cc revision 7a0bba457ee05ced3adf37a0c0790d0ed23a5446
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
20bool FakeStack::AddrIsInSizeClass(uptr addr, uptr size_class) {
21  uptr mem = allocated_size_classes_[size_class];
22  uptr size = ClassMmapSize(size_class);
23  bool res = mem && addr >= mem && addr < mem + size;
24  return res;
25}
26
27uptr FakeStack::AddrIsInFakeStack(uptr addr) {
28  for (uptr size_class = 0; size_class < kNumberOfSizeClasses; size_class++) {
29    if (!AddrIsInSizeClass(addr, size_class)) continue;
30    uptr size_class_first_ptr = allocated_size_classes_[size_class];
31    uptr size = ClassSize(size_class);
32    CHECK_LE(size_class_first_ptr, addr);
33    CHECK_GT(size_class_first_ptr + ClassMmapSize(size_class), addr);
34    return size_class_first_ptr + ((addr - size_class_first_ptr) / size) * size;
35  }
36  return 0;
37}
38
39// We may want to compute this during compilation.
40ALWAYS_INLINE uptr FakeStack::ComputeSizeClass(uptr alloc_size) {
41  uptr rounded_size = RoundUpToPowerOfTwo(alloc_size);
42  uptr log = Log2(rounded_size);
43  CHECK_LE(alloc_size, (1UL << log));
44  CHECK_GT(alloc_size, (1UL << (log-1)));
45  uptr res = log < kMinStackFrameSizeLog ? 0 : log - kMinStackFrameSizeLog;
46  CHECK_LT(res, kNumberOfSizeClasses);
47  CHECK_GE(ClassSize(res), rounded_size);
48  return res;
49}
50
51void FakeFrameFifo::FifoPush(FakeFrame *node) {
52  CHECK(node);
53  node->next = 0;
54  if (first_ == 0 && last_ == 0) {
55    first_ = last_ = node;
56  } else {
57    CHECK(first_);
58    CHECK(last_);
59    last_->next = node;
60    last_ = node;
61  }
62}
63
64FakeFrame *FakeFrameFifo::FifoPop() {
65  CHECK(first_ && last_ && "Exhausted fake stack");
66  FakeFrame *res = 0;
67  if (first_ == last_) {
68    res = first_;
69    first_ = last_ = 0;
70  } else {
71    res = first_;
72    first_ = first_->next;
73  }
74  return res;
75}
76
77void FakeStack::Init(uptr stack_size) {
78  stack_size_ = stack_size;
79  alive_ = true;
80}
81
82void FakeStack::Cleanup() {
83  alive_ = false;
84  for (uptr i = 0; i < kNumberOfSizeClasses; i++) {
85    uptr mem = allocated_size_classes_[i];
86    if (mem) {
87      PoisonShadow(mem, ClassMmapSize(i), 0);
88      allocated_size_classes_[i] = 0;
89      UnmapOrDie((void*)mem, ClassMmapSize(i));
90    }
91  }
92}
93
94uptr FakeStack::ClassMmapSize(uptr size_class) {
95  return RoundUpToPowerOfTwo(stack_size_);
96}
97
98void FakeStack::AllocateOneSizeClass(uptr size_class) {
99  CHECK(ClassMmapSize(size_class) >= GetPageSizeCached());
100  uptr new_mem = (uptr)MmapOrDie(
101      ClassMmapSize(size_class), __FUNCTION__);
102  // Printf("T%d new_mem[%zu]: %p-%p mmap %zu\n",
103  //       GetCurrentThread()->tid(),
104  //       size_class, new_mem, new_mem + ClassMmapSize(size_class),
105  //       ClassMmapSize(size_class));
106  uptr i;
107  for (i = 0; i < ClassMmapSize(size_class);
108       i += ClassSize(size_class)) {
109    size_classes_[size_class].FifoPush((FakeFrame*)(new_mem + i));
110  }
111  CHECK(i == ClassMmapSize(size_class));
112  allocated_size_classes_[size_class] = new_mem;
113}
114
115ALWAYS_INLINE uptr FakeStack::AllocateStack(uptr size, uptr real_stack) {
116  if (!alive_) return real_stack;
117  CHECK(size <= kMaxStackMallocSize && size > 1);
118  uptr size_class = ComputeSizeClass(size);
119  if (!allocated_size_classes_[size_class]) {
120    AllocateOneSizeClass(size_class);
121  }
122  FakeFrame *fake_frame = size_classes_[size_class].FifoPop();
123  CHECK(fake_frame);
124  fake_frame->size_minus_one = size - 1;
125  fake_frame->real_stack = real_stack;
126  while (FakeFrame *top = call_stack_.top()) {
127    if (top->real_stack > real_stack) break;
128    call_stack_.LifoPop();
129    DeallocateFrame(top);
130  }
131  call_stack_.LifoPush(fake_frame);
132  uptr ptr = (uptr)fake_frame;
133  PoisonShadow(ptr, size, 0);
134  return ptr;
135}
136
137ALWAYS_INLINE void FakeStack::DeallocateFrame(FakeFrame *fake_frame) {
138  CHECK(alive_);
139  uptr size = static_cast<uptr>(fake_frame->size_minus_one + 1);
140  uptr size_class = ComputeSizeClass(size);
141  CHECK(allocated_size_classes_[size_class]);
142  uptr ptr = (uptr)fake_frame;
143  CHECK(AddrIsInSizeClass(ptr, size_class));
144  CHECK(AddrIsInSizeClass(ptr + size - 1, size_class));
145  size_classes_[size_class].FifoPush(fake_frame);
146}
147
148ALWAYS_INLINE void FakeStack::OnFree(uptr ptr, uptr size, uptr real_stack) {
149  FakeFrame *fake_frame = (FakeFrame*)ptr;
150  CHECK_EQ(fake_frame->magic, kRetiredStackFrameMagic);
151  CHECK_NE(fake_frame->descr, 0);
152  CHECK_EQ(fake_frame->size_minus_one, size - 1);
153  PoisonShadow(ptr, size, kAsanStackAfterReturnMagic);
154}
155
156}  // namespace __asan
157
158// ---------------------- Interface ---------------- {{{1
159using namespace __asan;  // NOLINT
160
161uptr __asan_stack_malloc(uptr size, uptr real_stack) {
162  if (!flags()->use_fake_stack) return real_stack;
163  AsanThread *t = GetCurrentThread();
164  if (!t) {
165    // TSD is gone, use the real stack.
166    return real_stack;
167  }
168  t->LazyInitFakeStack();
169  uptr ptr = t->fake_stack()->AllocateStack(size, real_stack);
170  // Printf("__asan_stack_malloc %p %zu %p\n", ptr, size, real_stack);
171  return ptr;
172}
173
174void __asan_stack_free(uptr ptr, uptr size, uptr real_stack) {
175  if (!flags()->use_fake_stack) return;
176  if (ptr != real_stack) {
177    FakeStack::OnFree(ptr, size, real_stack);
178  }
179}
180