aboutsummaryrefslogtreecommitdiff
path: root/lib/asan/asan_fake_stack.cc
diff options
context:
space:
mode:
Diffstat (limited to 'lib/asan/asan_fake_stack.cc')
-rw-r--r--lib/asan/asan_fake_stack.cc182
1 files changed, 182 insertions, 0 deletions
diff --git a/lib/asan/asan_fake_stack.cc b/lib/asan/asan_fake_stack.cc
new file mode 100644
index 000000000000..7c5a16312d46
--- /dev/null
+++ b/lib/asan/asan_fake_stack.cc
@@ -0,0 +1,182 @@
+//===-- asan_fake_stack.cc ------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// FakeStack is used to detect use-after-return bugs.
+//===----------------------------------------------------------------------===//
+#include "asan_allocator.h"
+#include "asan_thread.h"
+#include "asan_thread_registry.h"
+#include "sanitizer/asan_interface.h"
+
+namespace __asan {
+
+FakeStack::FakeStack() {
+ CHECK(REAL(memset) != 0);
+ REAL(memset)(this, 0, sizeof(*this));
+}
+
+bool FakeStack::AddrIsInSizeClass(uptr addr, uptr size_class) {
+ uptr mem = allocated_size_classes_[size_class];
+ uptr size = ClassMmapSize(size_class);
+ bool res = mem && addr >= mem && addr < mem + size;
+ return res;
+}
+
+uptr FakeStack::AddrIsInFakeStack(uptr addr) {
+ for (uptr i = 0; i < kNumberOfSizeClasses; i++) {
+ if (AddrIsInSizeClass(addr, i)) return allocated_size_classes_[i];
+ }
+ return 0;
+}
+
+// We may want to compute this during compilation.
+inline uptr FakeStack::ComputeSizeClass(uptr alloc_size) {
+ uptr rounded_size = RoundUpToPowerOfTwo(alloc_size);
+ uptr log = Log2(rounded_size);
+ CHECK(alloc_size <= (1UL << log));
+ if (!(alloc_size > (1UL << (log-1)))) {
+ Printf("alloc_size %zu log %zu\n", alloc_size, log);
+ }
+ CHECK(alloc_size > (1UL << (log-1)));
+ uptr res = log < kMinStackFrameSizeLog ? 0 : log - kMinStackFrameSizeLog;
+ CHECK(res < kNumberOfSizeClasses);
+ CHECK(ClassSize(res) >= rounded_size);
+ return res;
+}
+
+void FakeFrameFifo::FifoPush(FakeFrame *node) {
+ CHECK(node);
+ node->next = 0;
+ if (first_ == 0 && last_ == 0) {
+ first_ = last_ = node;
+ } else {
+ CHECK(first_);
+ CHECK(last_);
+ last_->next = node;
+ last_ = node;
+ }
+}
+
+FakeFrame *FakeFrameFifo::FifoPop() {
+ CHECK(first_ && last_ && "Exhausted fake stack");
+ FakeFrame *res = 0;
+ if (first_ == last_) {
+ res = first_;
+ first_ = last_ = 0;
+ } else {
+ res = first_;
+ first_ = first_->next;
+ }
+ return res;
+}
+
+void FakeStack::Init(uptr stack_size) {
+ stack_size_ = stack_size;
+ alive_ = true;
+}
+
+void FakeStack::Cleanup() {
+ alive_ = false;
+ for (uptr i = 0; i < kNumberOfSizeClasses; i++) {
+ uptr mem = allocated_size_classes_[i];
+ if (mem) {
+ PoisonShadow(mem, ClassMmapSize(i), 0);
+ allocated_size_classes_[i] = 0;
+ UnmapOrDie((void*)mem, ClassMmapSize(i));
+ }
+ }
+}
+
+uptr FakeStack::ClassMmapSize(uptr size_class) {
+ return RoundUpToPowerOfTwo(stack_size_);
+}
+
+void FakeStack::AllocateOneSizeClass(uptr size_class) {
+ CHECK(ClassMmapSize(size_class) >= GetPageSizeCached());
+ uptr new_mem = (uptr)MmapOrDie(
+ ClassMmapSize(size_class), __FUNCTION__);
+ // Printf("T%d new_mem[%zu]: %p-%p mmap %zu\n",
+ // asanThreadRegistry().GetCurrent()->tid(),
+ // size_class, new_mem, new_mem + ClassMmapSize(size_class),
+ // ClassMmapSize(size_class));
+ uptr i;
+ for (i = 0; i < ClassMmapSize(size_class);
+ i += ClassSize(size_class)) {
+ size_classes_[size_class].FifoPush((FakeFrame*)(new_mem + i));
+ }
+ CHECK(i == ClassMmapSize(size_class));
+ allocated_size_classes_[size_class] = new_mem;
+}
+
+uptr FakeStack::AllocateStack(uptr size, uptr real_stack) {
+ if (!alive_) return real_stack;
+ CHECK(size <= kMaxStackMallocSize && size > 1);
+ uptr size_class = ComputeSizeClass(size);
+ if (!allocated_size_classes_[size_class]) {
+ AllocateOneSizeClass(size_class);
+ }
+ FakeFrame *fake_frame = size_classes_[size_class].FifoPop();
+ CHECK(fake_frame);
+ fake_frame->size_minus_one = size - 1;
+ fake_frame->real_stack = real_stack;
+ while (FakeFrame *top = call_stack_.top()) {
+ if (top->real_stack > real_stack) break;
+ call_stack_.LifoPop();
+ DeallocateFrame(top);
+ }
+ call_stack_.LifoPush(fake_frame);
+ uptr ptr = (uptr)fake_frame;
+ PoisonShadow(ptr, size, 0);
+ return ptr;
+}
+
+void FakeStack::DeallocateFrame(FakeFrame *fake_frame) {
+ CHECK(alive_);
+ uptr size = fake_frame->size_minus_one + 1;
+ uptr size_class = ComputeSizeClass(size);
+ CHECK(allocated_size_classes_[size_class]);
+ uptr ptr = (uptr)fake_frame;
+ CHECK(AddrIsInSizeClass(ptr, size_class));
+ CHECK(AddrIsInSizeClass(ptr + size - 1, size_class));
+ size_classes_[size_class].FifoPush(fake_frame);
+}
+
+void FakeStack::OnFree(uptr ptr, uptr size, uptr real_stack) {
+ FakeFrame *fake_frame = (FakeFrame*)ptr;
+ CHECK(fake_frame->magic = kRetiredStackFrameMagic);
+ CHECK(fake_frame->descr != 0);
+ CHECK(fake_frame->size_minus_one == size - 1);
+ PoisonShadow(ptr, size, kAsanStackAfterReturnMagic);
+}
+
+} // namespace __asan
+
+// ---------------------- Interface ---------------- {{{1
+using namespace __asan; // NOLINT
+
+uptr __asan_stack_malloc(uptr size, uptr real_stack) {
+ if (!flags()->use_fake_stack) return real_stack;
+ AsanThread *t = asanThreadRegistry().GetCurrent();
+ if (!t) {
+ // TSD is gone, use the real stack.
+ return real_stack;
+ }
+ uptr ptr = t->fake_stack().AllocateStack(size, real_stack);
+ // Printf("__asan_stack_malloc %p %zu %p\n", ptr, size, real_stack);
+ return ptr;
+}
+
+void __asan_stack_free(uptr ptr, uptr size, uptr real_stack) {
+ if (!flags()->use_fake_stack) return;
+ if (ptr != real_stack) {
+ FakeStack::OnFree(ptr, size, real_stack);
+ }
+}