• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //===-- tsan_mman.cc ------------------------------------------------------===//
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 ThreadSanitizer (TSan), a race detector.
11 //
12 //===----------------------------------------------------------------------===//
13 #include "sanitizer_common/sanitizer_common.h"
14 #include "sanitizer_common/sanitizer_placement_new.h"
15 #include "tsan_mman.h"
16 #include "tsan_rtl.h"
17 #include "tsan_report.h"
18 #include "tsan_flags.h"
19 
20 // May be overriden by front-end.
__tsan_malloc_hook(void * ptr,uptr size)21 extern "C" void WEAK __tsan_malloc_hook(void *ptr, uptr size) {
22   (void)ptr;
23   (void)size;
24 }
25 
__tsan_free_hook(void * ptr)26 extern "C" void WEAK __tsan_free_hook(void *ptr) {
27   (void)ptr;
28 }
29 
30 namespace __tsan {
31 
32 COMPILER_CHECK(sizeof(MBlock) == 16);
33 
Lock()34 void MBlock::Lock() {
35   atomic_uintptr_t *a = reinterpret_cast<atomic_uintptr_t*>(this);
36   uptr v = atomic_load(a, memory_order_relaxed);
37   for (int iter = 0;; iter++) {
38     if (v & 1) {
39       if (iter < 10)
40         proc_yield(20);
41       else
42         internal_sched_yield();
43       v = atomic_load(a, memory_order_relaxed);
44       continue;
45     }
46     if (atomic_compare_exchange_weak(a, &v, v | 1, memory_order_acquire))
47       break;
48   }
49 }
50 
Unlock()51 void MBlock::Unlock() {
52   atomic_uintptr_t *a = reinterpret_cast<atomic_uintptr_t*>(this);
53   uptr v = atomic_load(a, memory_order_relaxed);
54   DCHECK(v & 1);
55   atomic_store(a, v & ~1, memory_order_relaxed);
56 }
57 
58 struct MapUnmapCallback {
OnMap__tsan::MapUnmapCallback59   void OnMap(uptr p, uptr size) const { }
OnUnmap__tsan::MapUnmapCallback60   void OnUnmap(uptr p, uptr size) const {
61     // We are about to unmap a chunk of user memory.
62     // Mark the corresponding shadow memory as not needed.
63     DontNeedShadowFor(p, size);
64   }
65 };
66 
67 static char allocator_placeholder[sizeof(Allocator)] ALIGNED(64);
allocator()68 Allocator *allocator() {
69   return reinterpret_cast<Allocator*>(&allocator_placeholder);
70 }
71 
InitializeAllocator()72 void InitializeAllocator() {
73   allocator()->Init();
74 }
75 
AllocatorThreadStart(ThreadState * thr)76 void AllocatorThreadStart(ThreadState *thr) {
77   allocator()->InitCache(&thr->alloc_cache);
78 }
79 
AllocatorThreadFinish(ThreadState * thr)80 void AllocatorThreadFinish(ThreadState *thr) {
81   allocator()->DestroyCache(&thr->alloc_cache);
82 }
83 
AllocatorPrintStats()84 void AllocatorPrintStats() {
85   allocator()->PrintStats();
86 }
87 
SignalUnsafeCall(ThreadState * thr,uptr pc)88 static void SignalUnsafeCall(ThreadState *thr, uptr pc) {
89   if (!thr->in_signal_handler || !flags()->report_signal_unsafe)
90     return;
91   Context *ctx = CTX();
92   StackTrace stack;
93   stack.ObtainCurrent(thr, pc);
94   ThreadRegistryLock l(ctx->thread_registry);
95   ScopedReport rep(ReportTypeSignalUnsafe);
96   if (!IsFiredSuppression(ctx, rep, stack)) {
97     rep.AddStack(&stack);
98     OutputReport(ctx, rep, rep.GetReport()->stacks[0]);
99   }
100 }
101 
user_alloc(ThreadState * thr,uptr pc,uptr sz,uptr align)102 void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align) {
103   CHECK_GT(thr->in_rtl, 0);
104   void *p = allocator()->Allocate(&thr->alloc_cache, sz, align);
105   if (p == 0)
106     return 0;
107   MBlock *b = new(allocator()->GetMetaData(p)) MBlock;
108   b->Init(sz, thr->tid, CurrentStackId(thr, pc));
109   if (CTX() && CTX()->initialized)
110     MemoryRangeImitateWrite(thr, pc, (uptr)p, sz);
111   DPrintf("#%d: alloc(%zu) = %p\n", thr->tid, sz, p);
112   SignalUnsafeCall(thr, pc);
113   return p;
114 }
115 
user_free(ThreadState * thr,uptr pc,void * p)116 void user_free(ThreadState *thr, uptr pc, void *p) {
117   CHECK_GT(thr->in_rtl, 0);
118   CHECK_NE(p, (void*)0);
119   DPrintf("#%d: free(%p)\n", thr->tid, p);
120   MBlock *b = (MBlock*)allocator()->GetMetaData(p);
121   if (b->ListHead()) {
122     MBlock::ScopedLock l(b);
123     for (SyncVar *s = b->ListHead(); s;) {
124       SyncVar *res = s;
125       s = s->next;
126       StatInc(thr, StatSyncDestroyed);
127       res->mtx.Lock();
128       res->mtx.Unlock();
129       DestroyAndFree(res);
130     }
131     b->ListReset();
132   }
133   if (CTX() && CTX()->initialized && thr->in_rtl == 1)
134     MemoryRangeFreed(thr, pc, (uptr)p, b->Size());
135   allocator()->Deallocate(&thr->alloc_cache, p);
136   SignalUnsafeCall(thr, pc);
137 }
138 
user_realloc(ThreadState * thr,uptr pc,void * p,uptr sz)139 void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz) {
140   CHECK_GT(thr->in_rtl, 0);
141   void *p2 = 0;
142   // FIXME: Handle "shrinking" more efficiently,
143   // it seems that some software actually does this.
144   if (sz) {
145     p2 = user_alloc(thr, pc, sz);
146     if (p2 == 0)
147       return 0;
148     if (p) {
149       MBlock *b = user_mblock(thr, p);
150       internal_memcpy(p2, p, min(b->Size(), sz));
151     }
152   }
153   if (p)
154     user_free(thr, pc, p);
155   return p2;
156 }
157 
user_alloc_usable_size(ThreadState * thr,uptr pc,void * p)158 uptr user_alloc_usable_size(ThreadState *thr, uptr pc, void *p) {
159   CHECK_GT(thr->in_rtl, 0);
160   if (p == 0)
161     return 0;
162   MBlock *b = (MBlock*)allocator()->GetMetaData(p);
163   return b ? b->Size() : 0;
164 }
165 
user_mblock(ThreadState * thr,void * p)166 MBlock *user_mblock(ThreadState *thr, void *p) {
167   CHECK_NE(p, (void*)0);
168   Allocator *a = allocator();
169   void *b = a->GetBlockBegin(p);
170   CHECK_NE(b, 0);
171   return (MBlock*)a->GetMetaData(b);
172 }
173 
invoke_malloc_hook(void * ptr,uptr size)174 void invoke_malloc_hook(void *ptr, uptr size) {
175   Context *ctx = CTX();
176   ThreadState *thr = cur_thread();
177   if (ctx == 0 || !ctx->initialized || thr->in_rtl)
178     return;
179   __tsan_malloc_hook(ptr, size);
180 }
181 
invoke_free_hook(void * ptr)182 void invoke_free_hook(void *ptr) {
183   Context *ctx = CTX();
184   ThreadState *thr = cur_thread();
185   if (ctx == 0 || !ctx->initialized || thr->in_rtl)
186     return;
187   __tsan_free_hook(ptr);
188 }
189 
internal_alloc(MBlockType typ,uptr sz)190 void *internal_alloc(MBlockType typ, uptr sz) {
191   ThreadState *thr = cur_thread();
192   CHECK_GT(thr->in_rtl, 0);
193   if (thr->nomalloc) {
194     thr->nomalloc = 0;  // CHECK calls internal_malloc().
195     CHECK(0);
196   }
197   return InternalAlloc(sz);
198 }
199 
internal_free(void * p)200 void internal_free(void *p) {
201   ThreadState *thr = cur_thread();
202   CHECK_GT(thr->in_rtl, 0);
203   if (thr->nomalloc) {
204     thr->nomalloc = 0;  // CHECK calls internal_malloc().
205     CHECK(0);
206   }
207   InternalFree(p);
208 }
209 
210 }  // namespace __tsan
211 
212 using namespace __tsan;
213 
214 extern "C" {
__tsan_get_current_allocated_bytes()215 uptr __tsan_get_current_allocated_bytes() {
216   u64 stats[AllocatorStatCount];
217   allocator()->GetStats(stats);
218   u64 m = stats[AllocatorStatMalloced];
219   u64 f = stats[AllocatorStatFreed];
220   return m >= f ? m - f : 1;
221 }
222 
__tsan_get_heap_size()223 uptr __tsan_get_heap_size() {
224   u64 stats[AllocatorStatCount];
225   allocator()->GetStats(stats);
226   u64 m = stats[AllocatorStatMmapped];
227   u64 f = stats[AllocatorStatUnmapped];
228   return m >= f ? m - f : 1;
229 }
230 
__tsan_get_free_bytes()231 uptr __tsan_get_free_bytes() {
232   return 1;
233 }
234 
__tsan_get_unmapped_bytes()235 uptr __tsan_get_unmapped_bytes() {
236   return 1;
237 }
238 
__tsan_get_estimated_allocated_size(uptr size)239 uptr __tsan_get_estimated_allocated_size(uptr size) {
240   return size;
241 }
242 
__tsan_get_ownership(void * p)243 bool __tsan_get_ownership(void *p) {
244   return allocator()->GetBlockBegin(p) != 0;
245 }
246 
__tsan_get_allocated_size(void * p)247 uptr __tsan_get_allocated_size(void *p) {
248   if (p == 0)
249     return 0;
250   p = allocator()->GetBlockBegin(p);
251   if (p == 0)
252     return 0;
253   MBlock *b = (MBlock*)allocator()->GetMetaData(p);
254   return b->Size();
255 }
256 
__tsan_on_thread_idle()257 void __tsan_on_thread_idle() {
258   ThreadState *thr = cur_thread();
259   allocator()->SwallowCache(&thr->alloc_cache);
260 }
261 }  // extern "C"
262