1 //===-- hwasan_thread_list.h ------------------------------------*- C++ -*-===//
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 HWAddressSanitizer.
11 //
12 //===----------------------------------------------------------------------===//
13
14 // HwasanThreadList is a registry for live threads, as well as an allocator for
15 // HwasanThread objects and their stack history ring buffers. There are
16 // constraints on memory layout of the shadow region and CompactRingBuffer that
17 // are part of the ABI contract between compiler-rt and llvm.
18 //
19 // * Start of the shadow memory region is aligned to 2**kShadowBaseAlignment.
20 // * All stack ring buffers are located within (2**kShadowBaseAlignment)
21 // sized region below and adjacent to the shadow region.
22 // * Each ring buffer has a size of (2**N)*4096 where N is in [0, 8), and is
23 // aligned to twice its size. The value of N can be different for each buffer.
24 //
25 // These constrains guarantee that, given an address A of any element of the
26 // ring buffer,
27 // A_next = (A + sizeof(uptr)) & ~((1 << (N + 13)) - 1)
28 // is the address of the next element of that ring buffer (with wrap-around).
29 // And, with K = kShadowBaseAlignment,
30 // S = (A | ((1 << K) - 1)) + 1
31 // (align up to kShadowBaseAlignment) is the start of the shadow region.
32 //
33 // These calculations are used in compiler instrumentation to update the ring
34 // buffer and obtain the base address of shadow using only two inputs: address
35 // of the current element of the ring buffer, and N (i.e. size of the ring
36 // buffer). Since the value of N is very limited, we pack both inputs into a
37 // single thread-local word as
38 // (1 << (N + 56)) | A
39 // See the implementation of class CompactRingBuffer, which is what is stored in
40 // said thread-local word.
41 //
42 // Note the unusual way of aligning up the address of the shadow:
43 // (A | ((1 << K) - 1)) + 1
44 // It is only correct if A is not already equal to the shadow base address, but
45 // it saves 2 instructions on AArch64.
46
47 #include "hwasan.h"
48 #include "hwasan_allocator.h"
49 #include "hwasan_flags.h"
50 #include "hwasan_thread.h"
51
52 #include "sanitizer_common/sanitizer_placement_new.h"
53
54 namespace __hwasan {
55
RingBufferSize()56 static uptr RingBufferSize() {
57 uptr desired_bytes = flags()->stack_history_size * sizeof(uptr);
58 // FIXME: increase the limit to 8 once this bug is fixed:
59 // https://bugs.llvm.org/show_bug.cgi?id=39030
60 for (int shift = 1; shift < 7; ++shift) {
61 uptr size = 4096 * (1ULL << shift);
62 if (size >= desired_bytes)
63 return size;
64 }
65 Printf("stack history size too large: %d\n", flags()->stack_history_size);
66 CHECK(0);
67 return 0;
68 }
69
70 struct ThreadListHead {
71 Thread *list_;
72
ThreadListHeadThreadListHead73 ThreadListHead() : list_(nullptr) {}
74
PushThreadListHead75 void Push(Thread *t) {
76 t->next_ = list_;
77 list_ = t;
78 }
79
PopThreadListHead80 Thread *Pop() {
81 Thread *t = list_;
82 if (t)
83 list_ = t->next_;
84 return t;
85 }
86
RemoveThreadListHead87 void Remove(Thread *t) {
88 Thread **cur = &list_;
89 while (*cur != t) cur = &(*cur)->next_;
90 CHECK(*cur && "thread not found");
91 *cur = (*cur)->next_;
92 }
93
94 template <class CB>
ForEachThreadListHead95 void ForEach(CB cb) {
96 Thread *t = list_;
97 while (t) {
98 cb(t);
99 t = t->next_;
100 }
101 }
102 };
103
104 struct ThreadStats {
105 uptr n_live_threads;
106 uptr total_stack_size;
107 };
108
109 class HwasanThreadList {
110 public:
HwasanThreadList(uptr storage,uptr size)111 HwasanThreadList(uptr storage, uptr size)
112 : free_space_(storage),
113 free_space_end_(storage + size),
114 ring_buffer_size_(RingBufferSize()) {}
115
CreateCurrentThread()116 Thread *CreateCurrentThread() {
117 Thread *t;
118 {
119 SpinMutexLock l(&list_mutex_);
120 t = free_list_.Pop();
121 if (t)
122 internal_memset((void *)t, 0, sizeof(Thread) + ring_buffer_size_);
123 else
124 t = AllocThread();
125 live_list_.Push(t);
126 }
127 t->Init((uptr)(t + 1), ring_buffer_size_);
128 AddThreadStats(t);
129 return t;
130 }
131
ReleaseThread(Thread * t)132 void ReleaseThread(Thread *t) {
133 // FIXME: madvise away the ring buffer?
134 RemoveThreadStats(t);
135 t->Destroy();
136 SpinMutexLock l(&list_mutex_);
137 live_list_.Remove(t);
138 free_list_.Push(t);
139 }
140
GetThreadByBufferAddress(uptr p)141 Thread *GetThreadByBufferAddress(uptr p) {
142 uptr align = ring_buffer_size_ * 2;
143 return (Thread *)(RoundDownTo(p, align) - sizeof(Thread));
144 }
145
MemoryUsedPerThread()146 uptr MemoryUsedPerThread() {
147 uptr res = sizeof(Thread) + ring_buffer_size_;
148 if (auto sz = flags()->heap_history_size)
149 res += HeapAllocationsRingBuffer::SizeInBytes(sz);
150 return res;
151 }
152
153 template <class CB>
VisitAllLiveThreads(CB cb)154 void VisitAllLiveThreads(CB cb) {
155 SpinMutexLock l(&list_mutex_);
156 live_list_.ForEach(cb);
157 }
158
AddThreadStats(Thread * t)159 void AddThreadStats(Thread *t) {
160 SpinMutexLock l(&stats_mutex_);
161 stats_.n_live_threads++;
162 stats_.total_stack_size += t->stack_size();
163 }
164
RemoveThreadStats(Thread * t)165 void RemoveThreadStats(Thread *t) {
166 SpinMutexLock l(&stats_mutex_);
167 stats_.n_live_threads--;
168 stats_.total_stack_size -= t->stack_size();
169 }
170
GetThreadStats()171 ThreadStats GetThreadStats() {
172 SpinMutexLock l(&stats_mutex_);
173 return stats_;
174 }
175
176 private:
AllocThread()177 Thread *AllocThread() {
178 uptr align = ring_buffer_size_ * 2;
179 uptr ring_buffer_start = RoundUpTo(free_space_ + sizeof(Thread), align);
180 free_space_ = ring_buffer_start + ring_buffer_size_;
181 CHECK(free_space_ <= free_space_end_ && "out of thread memory");
182 return (Thread *)(ring_buffer_start - sizeof(Thread));
183 }
184
185 uptr free_space_;
186 uptr free_space_end_;
187 uptr ring_buffer_size_;
188
189 ThreadListHead free_list_;
190 ThreadListHead live_list_;
191 SpinMutex list_mutex_;
192
193 ThreadStats stats_;
194 SpinMutex stats_mutex_;
195 };
196
197 void InitThreadList(uptr storage, uptr size);
198 HwasanThreadList &hwasanThreadList();
199
200 } // namespace
201