xref: /netbsd-src/external/gpl3/gcc.old/dist/libsanitizer/asan/asan_fake_stack.cc (revision fa28c6faa16e0b00edee7acdcaf4899797043def)
1 //===-- asan_fake_stack.cc ------------------------------------------------===//
2 //
3 // This file is distributed under the University of Illinois Open Source
4 // License. See LICENSE.TXT for details.
5 //
6 //===----------------------------------------------------------------------===//
7 //
8 // This file is a part of AddressSanitizer, an address sanity checker.
9 //
10 // FakeStack is used to detect use-after-return bugs.
11 //===----------------------------------------------------------------------===//
12 #include "asan_allocator.h"
13 #include "asan_thread.h"
14 #include "asan_thread_registry.h"
15 
16 namespace __asan {
17 
18 FakeStack::FakeStack() {
19   CHECK(REAL(memset) != 0);
20   REAL(memset)(this, 0, sizeof(*this));
21 }
22 
23 bool FakeStack::AddrIsInSizeClass(uptr addr, uptr size_class) {
24   uptr mem = allocated_size_classes_[size_class];
25   uptr size = ClassMmapSize(size_class);
26   bool res = mem && addr >= mem && addr < mem + size;
27   return res;
28 }
29 
30 uptr FakeStack::AddrIsInFakeStack(uptr addr) {
31   for (uptr i = 0; i < kNumberOfSizeClasses; i++) {
32     if (AddrIsInSizeClass(addr, i)) return allocated_size_classes_[i];
33   }
34   return 0;
35 }
36 
37 // We may want to compute this during compilation.
38 inline uptr FakeStack::ComputeSizeClass(uptr alloc_size) {
39   uptr rounded_size = RoundUpToPowerOfTwo(alloc_size);
40   uptr log = Log2(rounded_size);
41   CHECK(alloc_size <= (1UL << log));
42   if (!(alloc_size > (1UL << (log-1)))) {
43     Printf("alloc_size %zu log %zu\n", alloc_size, log);
44   }
45   CHECK(alloc_size > (1UL << (log-1)));
46   uptr res = log < kMinStackFrameSizeLog ? 0 : log - kMinStackFrameSizeLog;
47   CHECK(res < kNumberOfSizeClasses);
48   CHECK(ClassSize(res) >= rounded_size);
49   return res;
50 }
51 
52 void FakeFrameFifo::FifoPush(FakeFrame *node) {
53   CHECK(node);
54   node->next = 0;
55   if (first_ == 0 && last_ == 0) {
56     first_ = last_ = node;
57   } else {
58     CHECK(first_);
59     CHECK(last_);
60     last_->next = node;
61     last_ = node;
62   }
63 }
64 
65 FakeFrame *FakeFrameFifo::FifoPop() {
66   CHECK(first_ && last_ && "Exhausted fake stack");
67   FakeFrame *res = 0;
68   if (first_ == last_) {
69     res = first_;
70     first_ = last_ = 0;
71   } else {
72     res = first_;
73     first_ = first_->next;
74   }
75   return res;
76 }
77 
78 void FakeStack::Init(uptr stack_size) {
79   stack_size_ = stack_size;
80   alive_ = true;
81 }
82 
83 void FakeStack::Cleanup() {
84   alive_ = false;
85   for (uptr i = 0; i < kNumberOfSizeClasses; i++) {
86     uptr mem = allocated_size_classes_[i];
87     if (mem) {
88       PoisonShadow(mem, ClassMmapSize(i), 0);
89       allocated_size_classes_[i] = 0;
90       UnmapOrDie((void*)mem, ClassMmapSize(i));
91     }
92   }
93 }
94 
95 uptr FakeStack::ClassMmapSize(uptr size_class) {
96   return RoundUpToPowerOfTwo(stack_size_);
97 }
98 
99 void FakeStack::AllocateOneSizeClass(uptr size_class) {
100   CHECK(ClassMmapSize(size_class) >= GetPageSizeCached());
101   uptr new_mem = (uptr)MmapOrDie(
102       ClassMmapSize(size_class), __FUNCTION__);
103   // Printf("T%d new_mem[%zu]: %p-%p mmap %zu\n",
104   //       asanThreadRegistry().GetCurrent()->tid(),
105   //       size_class, new_mem, new_mem + ClassMmapSize(size_class),
106   //       ClassMmapSize(size_class));
107   uptr i;
108   for (i = 0; i < ClassMmapSize(size_class);
109        i += ClassSize(size_class)) {
110     size_classes_[size_class].FifoPush((FakeFrame*)(new_mem + i));
111   }
112   CHECK(i == ClassMmapSize(size_class));
113   allocated_size_classes_[size_class] = new_mem;
114 }
115 
116 uptr FakeStack::AllocateStack(uptr size, uptr real_stack) {
117   if (!alive_) return real_stack;
118   CHECK(size <= kMaxStackMallocSize && size > 1);
119   uptr size_class = ComputeSizeClass(size);
120   if (!allocated_size_classes_[size_class]) {
121     AllocateOneSizeClass(size_class);
122   }
123   FakeFrame *fake_frame = size_classes_[size_class].FifoPop();
124   CHECK(fake_frame);
125   fake_frame->size_minus_one = size - 1;
126   fake_frame->real_stack = real_stack;
127   while (FakeFrame *top = call_stack_.top()) {
128     if (top->real_stack > real_stack) break;
129     call_stack_.LifoPop();
130     DeallocateFrame(top);
131   }
132   call_stack_.LifoPush(fake_frame);
133   uptr ptr = (uptr)fake_frame;
134   PoisonShadow(ptr, size, 0);
135   return ptr;
136 }
137 
138 void FakeStack::DeallocateFrame(FakeFrame *fake_frame) {
139   CHECK(alive_);
140   uptr size = fake_frame->size_minus_one + 1;
141   uptr size_class = ComputeSizeClass(size);
142   CHECK(allocated_size_classes_[size_class]);
143   uptr ptr = (uptr)fake_frame;
144   CHECK(AddrIsInSizeClass(ptr, size_class));
145   CHECK(AddrIsInSizeClass(ptr + size - 1, size_class));
146   size_classes_[size_class].FifoPush(fake_frame);
147 }
148 
149 void FakeStack::OnFree(uptr ptr, uptr size, uptr real_stack) {
150   FakeFrame *fake_frame = (FakeFrame*)ptr;
151   CHECK(fake_frame->magic = kRetiredStackFrameMagic);
152   CHECK(fake_frame->descr != 0);
153   CHECK(fake_frame->size_minus_one == size - 1);
154   PoisonShadow(ptr, size, kAsanStackAfterReturnMagic);
155 }
156 
157 }  // namespace __asan
158 
159 // ---------------------- Interface ---------------- {{{1
160 using namespace __asan;  // NOLINT
161 
162 uptr __asan_stack_malloc(uptr size, uptr real_stack) {
163   if (!flags()->use_fake_stack) return real_stack;
164   AsanThread *t = asanThreadRegistry().GetCurrent();
165   if (!t) {
166     // TSD is gone, use the real stack.
167     return real_stack;
168   }
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 
174 void __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