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