asan_fake_stack.cc revision 34e3ed1db94c5ce9784d7ffb8d66a54cf523e09c
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 class_id) { 21 uptr mem = allocated_size_classes_[class_id]; 22 uptr size = ClassMmapSize(class_id); 23 bool res = mem && addr >= mem && addr < mem + size; 24 return res; 25} 26 27uptr FakeStack::AddrIsInFakeStack(uptr addr) { 28 for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++) { 29 if (!AddrIsInSizeClass(addr, class_id)) continue; 30 uptr size_class_first_ptr = allocated_size_classes_[class_id]; 31 uptr size = ClassSize(class_id); 32 CHECK_LE(size_class_first_ptr, addr); 33 CHECK_GT(size_class_first_ptr + ClassMmapSize(class_id), 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 class_id) { 95 // Limit allocation size to ClassSize * MaxDepth when running with unlimited 96 // stack. 97 return RoundUpTo(Min(ClassSize(class_id) * kMaxRecursionDepth, stack_size_), 98 GetPageSizeCached()); 99} 100 101void FakeStack::AllocateOneSizeClass(uptr class_id) { 102 CHECK(ClassMmapSize(class_id) >= GetPageSizeCached()); 103 uptr new_mem = (uptr)MmapOrDie( 104 ClassMmapSize(class_id), __FUNCTION__); 105 if (0) { 106 Printf("T%d new_mem[%zu]: %p-%p mmap %zu\n", 107 GetCurrentThread()->tid(), 108 class_id, new_mem, new_mem + ClassMmapSize(class_id), 109 ClassMmapSize(class_id)); 110 } 111 uptr i; 112 uptr size = ClassSize(class_id); 113 for (i = 0; i + size <= ClassMmapSize(class_id); i += size) { 114 size_classes_[class_id].FifoPush((FakeFrame*)(new_mem + i)); 115 } 116 CHECK_LE(i, ClassMmapSize(class_id)); 117 allocated_size_classes_[class_id] = new_mem; 118} 119 120ALWAYS_INLINE uptr 121FakeStack::AllocateStack(uptr class_id, uptr size, uptr real_stack) { 122 CHECK(size <= kMaxStackMallocSize && size > 1); 123 if (!alive_) return real_stack; 124 if (!allocated_size_classes_[class_id]) { 125 AllocateOneSizeClass(class_id); 126 } 127 FakeFrame *fake_frame = size_classes_[class_id].FifoPop(); 128 CHECK(fake_frame); 129 fake_frame->class_id = class_id; 130 fake_frame->real_stack = real_stack; 131 while (FakeFrame *top = call_stack_.top()) { 132 if (top->real_stack > real_stack) break; 133 call_stack_.LifoPop(); 134 DeallocateFrame(top); 135 } 136 call_stack_.LifoPush(fake_frame); 137 uptr ptr = (uptr)fake_frame; 138 PoisonShadow(ptr, size, 0); 139 return ptr; 140} 141 142ALWAYS_INLINE void FakeStack::DeallocateFrame(FakeFrame *fake_frame) { 143 CHECK(alive_); 144 uptr class_id = static_cast<uptr>(fake_frame->class_id); 145 CHECK(allocated_size_classes_[class_id]); 146 uptr ptr = (uptr)fake_frame; 147 CHECK(AddrIsInSizeClass(ptr, class_id)); 148 size_classes_[class_id].FifoPush(fake_frame); 149} 150 151ALWAYS_INLINE void FakeStack::OnFree(uptr ptr, uptr class_id, uptr size, 152 uptr real_stack) { 153 FakeFrame *fake_frame = (FakeFrame*)ptr; 154 CHECK_EQ(fake_frame->magic, kRetiredStackFrameMagic); 155 CHECK_NE(fake_frame->descr, 0); 156 CHECK_EQ(fake_frame->class_id, class_id); 157 PoisonShadow(ptr, size, kAsanStackAfterReturnMagic); 158} 159 160ALWAYS_INLINE uptr OnMalloc(uptr class_id, uptr size, uptr real_stack) { 161 if (!flags()->use_fake_stack) return real_stack; 162 AsanThread *t = GetCurrentThread(); 163 if (!t) { 164 // TSD is gone, use the real stack. 165 return real_stack; 166 } 167 t->LazyInitFakeStack(); 168 uptr ptr = t->fake_stack()->AllocateStack(class_id, size, real_stack); 169 // Printf("__asan_stack_malloc %p %zu %p\n", ptr, size, real_stack); 170 return ptr; 171} 172 173ALWAYS_INLINE void OnFree(uptr ptr, uptr class_id, uptr size, uptr real_stack) { 174 if (!flags()->use_fake_stack) return; 175 if (ptr != real_stack) { 176 FakeStack::OnFree(ptr, class_id, size, real_stack); 177 } 178} 179 180} // namespace __asan 181 182// ---------------------- Interface ---------------- {{{1 183#define DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(class_id) \ 184 extern "C" SANITIZER_INTERFACE_ATTRIBUTE uptr \ 185 __asan_stack_malloc_##class_id(uptr size, uptr real_stack) { \ 186 return __asan::OnMalloc(class_id, size, real_stack); \ 187 } \ 188 extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __asan_stack_free_##class_id( \ 189 uptr ptr, uptr size, uptr real_stack) { \ 190 __asan::OnFree(ptr, class_id, size, real_stack); \ 191 } 192 193DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(0) 194DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(1) 195DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(2) 196DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(3) 197DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(4) 198DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(5) 199DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(6) 200DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(7) 201DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(8) 202DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(9) 203DEFINE_STACK_MALLOC_FREE_WITH_CLASS_ID(10) 204