]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/hwasan/hwasan_thread_list.h
Vendor import of compiler-rt trunk r351319 (just before the release_80
[FreeBSD/FreeBSD.git] / lib / hwasan / hwasan_thread_list.h
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
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
73   ThreadListHead() : list_(nullptr) {}
74
75   void Push(Thread *t) {
76     t->next_ = list_;
77     list_ = t;
78   }
79
80   Thread *Pop() {
81     Thread *t = list_;
82     if (t)
83       list_ = t->next_;
84     return t;
85   }
86
87   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>
95   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:
111   HwasanThreadList(uptr storage, uptr size)
112       : free_space_(storage),
113         free_space_end_(storage + size),
114         ring_buffer_size_(RingBufferSize()) {}
115
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
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
141   Thread *GetThreadByBufferAddress(uptr p) {
142     uptr align = ring_buffer_size_ * 2;
143     return (Thread *)(RoundDownTo(p, align) - sizeof(Thread));
144   }
145
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>
154   void VisitAllLiveThreads(CB cb) {
155     SpinMutexLock l(&list_mutex_);
156     live_list_.ForEach(cb);
157   }
158
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
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
171   ThreadStats GetThreadStats() {
172     SpinMutexLock l(&stats_mutex_);
173     return stats_;
174   }
175
176  private:
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