1 //===-- asan_fake_stack.cc ------------------------------------------------===//
3 // This file is distributed under the University of Illinois Open Source
4 // License. See LICENSE.TXT for details.
6 //===----------------------------------------------------------------------===//
8 // This file is a part of AddressSanitizer, an address sanity checker.
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"
18 FakeStack::FakeStack() {
19 CHECK(REAL(memset
) != 0);
20 REAL(memset
)(this, 0, sizeof(*this));
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
;
30 uptr
FakeStack::AddrIsInFakeStack(uptr addr
) {
31 for (uptr i
= 0; i
< kNumberOfSizeClasses
; i
++) {
32 if (AddrIsInSizeClass(addr
, i
)) return allocated_size_classes_
[i
];
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
);
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
);
52 void FakeFrameFifo::FifoPush(FakeFrame
*node
) {
55 if (first_
== 0 && last_
== 0) {
56 first_
= last_
= node
;
65 FakeFrame
*FakeFrameFifo::FifoPop() {
66 CHECK(first_
&& last_
&& "Exhausted fake stack");
68 if (first_
== last_
) {
73 first_
= first_
->next
;
78 void FakeStack::Init(uptr stack_size
) {
79 stack_size_
= stack_size
;
83 void FakeStack::Cleanup() {
85 for (uptr i
= 0; i
< kNumberOfSizeClasses
; i
++) {
86 uptr mem
= allocated_size_classes_
[i
];
88 PoisonShadow(mem
, ClassMmapSize(i
), 0);
89 allocated_size_classes_
[i
] = 0;
90 UnmapOrDie((void*)mem
, ClassMmapSize(i
));
95 uptr
FakeStack::ClassMmapSize(uptr size_class
) {
96 return RoundUpToPowerOfTwo(stack_size_
);
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));
108 for (i
= 0; i
< ClassMmapSize(size_class
);
109 i
+= ClassSize(size_class
)) {
110 size_classes_
[size_class
].FifoPush((FakeFrame
*)(new_mem
+ i
));
112 CHECK(i
== ClassMmapSize(size_class
));
113 allocated_size_classes_
[size_class
] = new_mem
;
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
);
123 FakeFrame
*fake_frame
= size_classes_
[size_class
].FifoPop();
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
);
132 call_stack_
.LifoPush(fake_frame
);
133 uptr ptr
= (uptr
)fake_frame
;
134 PoisonShadow(ptr
, size
, 0);
138 void FakeStack::DeallocateFrame(FakeFrame
*fake_frame
) {
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
);
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
);
157 } // namespace __asan
159 // ---------------------- Interface ---------------- {{{1
160 using namespace __asan
; // NOLINT
162 uptr
__asan_stack_malloc(uptr size
, uptr real_stack
) {
163 if (!flags()->use_fake_stack
) return real_stack
;
164 AsanThread
*t
= asanThreadRegistry().GetCurrent();
166 // TSD is gone, use the real stack.
169 uptr ptr
= t
->fake_stack().AllocateStack(size
, real_stack
);
170 // Printf("__asan_stack_malloc %p %zu %p\n", ptr, size, real_stack);
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
);