1*7c3d14c8STreehugger Robot //===-- tsan_mman.cc ------------------------------------------------------===//
2*7c3d14c8STreehugger Robot //
3*7c3d14c8STreehugger Robot // The LLVM Compiler Infrastructure
4*7c3d14c8STreehugger Robot //
5*7c3d14c8STreehugger Robot // This file is distributed under the University of Illinois Open Source
6*7c3d14c8STreehugger Robot // License. See LICENSE.TXT for details.
7*7c3d14c8STreehugger Robot //
8*7c3d14c8STreehugger Robot //===----------------------------------------------------------------------===//
9*7c3d14c8STreehugger Robot //
10*7c3d14c8STreehugger Robot // This file is a part of ThreadSanitizer (TSan), a race detector.
11*7c3d14c8STreehugger Robot //
12*7c3d14c8STreehugger Robot //===----------------------------------------------------------------------===//
13*7c3d14c8STreehugger Robot #include "sanitizer_common/sanitizer_allocator_interface.h"
14*7c3d14c8STreehugger Robot #include "sanitizer_common/sanitizer_common.h"
15*7c3d14c8STreehugger Robot #include "sanitizer_common/sanitizer_placement_new.h"
16*7c3d14c8STreehugger Robot #include "tsan_mman.h"
17*7c3d14c8STreehugger Robot #include "tsan_rtl.h"
18*7c3d14c8STreehugger Robot #include "tsan_report.h"
19*7c3d14c8STreehugger Robot #include "tsan_flags.h"
20*7c3d14c8STreehugger Robot
21*7c3d14c8STreehugger Robot // May be overriden by front-end.
22*7c3d14c8STreehugger Robot SANITIZER_WEAK_DEFAULT_IMPL
__sanitizer_malloc_hook(void * ptr,uptr size)23*7c3d14c8STreehugger Robot void __sanitizer_malloc_hook(void *ptr, uptr size) {
24*7c3d14c8STreehugger Robot (void)ptr;
25*7c3d14c8STreehugger Robot (void)size;
26*7c3d14c8STreehugger Robot }
27*7c3d14c8STreehugger Robot
28*7c3d14c8STreehugger Robot SANITIZER_WEAK_DEFAULT_IMPL
__sanitizer_free_hook(void * ptr)29*7c3d14c8STreehugger Robot void __sanitizer_free_hook(void *ptr) {
30*7c3d14c8STreehugger Robot (void)ptr;
31*7c3d14c8STreehugger Robot }
32*7c3d14c8STreehugger Robot
33*7c3d14c8STreehugger Robot namespace __tsan {
34*7c3d14c8STreehugger Robot
35*7c3d14c8STreehugger Robot struct MapUnmapCallback {
OnMap__tsan::MapUnmapCallback36*7c3d14c8STreehugger Robot void OnMap(uptr p, uptr size) const { }
OnUnmap__tsan::MapUnmapCallback37*7c3d14c8STreehugger Robot void OnUnmap(uptr p, uptr size) const {
38*7c3d14c8STreehugger Robot // We are about to unmap a chunk of user memory.
39*7c3d14c8STreehugger Robot // Mark the corresponding shadow memory as not needed.
40*7c3d14c8STreehugger Robot DontNeedShadowFor(p, size);
41*7c3d14c8STreehugger Robot // Mark the corresponding meta shadow memory as not needed.
42*7c3d14c8STreehugger Robot // Note the block does not contain any meta info at this point
43*7c3d14c8STreehugger Robot // (this happens after free).
44*7c3d14c8STreehugger Robot const uptr kMetaRatio = kMetaShadowCell / kMetaShadowSize;
45*7c3d14c8STreehugger Robot const uptr kPageSize = GetPageSizeCached() * kMetaRatio;
46*7c3d14c8STreehugger Robot // Block came from LargeMmapAllocator, so must be large.
47*7c3d14c8STreehugger Robot // We rely on this in the calculations below.
48*7c3d14c8STreehugger Robot CHECK_GE(size, 2 * kPageSize);
49*7c3d14c8STreehugger Robot uptr diff = RoundUp(p, kPageSize) - p;
50*7c3d14c8STreehugger Robot if (diff != 0) {
51*7c3d14c8STreehugger Robot p += diff;
52*7c3d14c8STreehugger Robot size -= diff;
53*7c3d14c8STreehugger Robot }
54*7c3d14c8STreehugger Robot diff = p + size - RoundDown(p + size, kPageSize);
55*7c3d14c8STreehugger Robot if (diff != 0)
56*7c3d14c8STreehugger Robot size -= diff;
57*7c3d14c8STreehugger Robot FlushUnneededShadowMemory((uptr)MemToMeta(p), size / kMetaRatio);
58*7c3d14c8STreehugger Robot }
59*7c3d14c8STreehugger Robot };
60*7c3d14c8STreehugger Robot
61*7c3d14c8STreehugger Robot static char allocator_placeholder[sizeof(Allocator)] ALIGNED(64);
allocator()62*7c3d14c8STreehugger Robot Allocator *allocator() {
63*7c3d14c8STreehugger Robot return reinterpret_cast<Allocator*>(&allocator_placeholder);
64*7c3d14c8STreehugger Robot }
65*7c3d14c8STreehugger Robot
66*7c3d14c8STreehugger Robot struct GlobalProc {
67*7c3d14c8STreehugger Robot Mutex mtx;
68*7c3d14c8STreehugger Robot Processor *proc;
69*7c3d14c8STreehugger Robot
GlobalProc__tsan::GlobalProc70*7c3d14c8STreehugger Robot GlobalProc()
71*7c3d14c8STreehugger Robot : mtx(MutexTypeGlobalProc, StatMtxGlobalProc)
72*7c3d14c8STreehugger Robot , proc(ProcCreate()) {
73*7c3d14c8STreehugger Robot }
74*7c3d14c8STreehugger Robot };
75*7c3d14c8STreehugger Robot
76*7c3d14c8STreehugger Robot static char global_proc_placeholder[sizeof(GlobalProc)] ALIGNED(64);
global_proc()77*7c3d14c8STreehugger Robot GlobalProc *global_proc() {
78*7c3d14c8STreehugger Robot return reinterpret_cast<GlobalProc*>(&global_proc_placeholder);
79*7c3d14c8STreehugger Robot }
80*7c3d14c8STreehugger Robot
ScopedGlobalProcessor()81*7c3d14c8STreehugger Robot ScopedGlobalProcessor::ScopedGlobalProcessor() {
82*7c3d14c8STreehugger Robot GlobalProc *gp = global_proc();
83*7c3d14c8STreehugger Robot ThreadState *thr = cur_thread();
84*7c3d14c8STreehugger Robot if (thr->proc())
85*7c3d14c8STreehugger Robot return;
86*7c3d14c8STreehugger Robot // If we don't have a proc, use the global one.
87*7c3d14c8STreehugger Robot // There are currently only two known case where this path is triggered:
88*7c3d14c8STreehugger Robot // __interceptor_free
89*7c3d14c8STreehugger Robot // __nptl_deallocate_tsd
90*7c3d14c8STreehugger Robot // start_thread
91*7c3d14c8STreehugger Robot // clone
92*7c3d14c8STreehugger Robot // and:
93*7c3d14c8STreehugger Robot // ResetRange
94*7c3d14c8STreehugger Robot // __interceptor_munmap
95*7c3d14c8STreehugger Robot // __deallocate_stack
96*7c3d14c8STreehugger Robot // start_thread
97*7c3d14c8STreehugger Robot // clone
98*7c3d14c8STreehugger Robot // Ideally, we destroy thread state (and unwire proc) when a thread actually
99*7c3d14c8STreehugger Robot // exits (i.e. when we join/wait it). Then we would not need the global proc
100*7c3d14c8STreehugger Robot gp->mtx.Lock();
101*7c3d14c8STreehugger Robot ProcWire(gp->proc, thr);
102*7c3d14c8STreehugger Robot }
103*7c3d14c8STreehugger Robot
~ScopedGlobalProcessor()104*7c3d14c8STreehugger Robot ScopedGlobalProcessor::~ScopedGlobalProcessor() {
105*7c3d14c8STreehugger Robot GlobalProc *gp = global_proc();
106*7c3d14c8STreehugger Robot ThreadState *thr = cur_thread();
107*7c3d14c8STreehugger Robot if (thr->proc() != gp->proc)
108*7c3d14c8STreehugger Robot return;
109*7c3d14c8STreehugger Robot ProcUnwire(gp->proc, thr);
110*7c3d14c8STreehugger Robot gp->mtx.Unlock();
111*7c3d14c8STreehugger Robot }
112*7c3d14c8STreehugger Robot
InitializeAllocator()113*7c3d14c8STreehugger Robot void InitializeAllocator() {
114*7c3d14c8STreehugger Robot allocator()->Init(common_flags()->allocator_may_return_null);
115*7c3d14c8STreehugger Robot }
116*7c3d14c8STreehugger Robot
InitializeAllocatorLate()117*7c3d14c8STreehugger Robot void InitializeAllocatorLate() {
118*7c3d14c8STreehugger Robot new(global_proc()) GlobalProc();
119*7c3d14c8STreehugger Robot }
120*7c3d14c8STreehugger Robot
AllocatorProcStart(Processor * proc)121*7c3d14c8STreehugger Robot void AllocatorProcStart(Processor *proc) {
122*7c3d14c8STreehugger Robot allocator()->InitCache(&proc->alloc_cache);
123*7c3d14c8STreehugger Robot internal_allocator()->InitCache(&proc->internal_alloc_cache);
124*7c3d14c8STreehugger Robot }
125*7c3d14c8STreehugger Robot
AllocatorProcFinish(Processor * proc)126*7c3d14c8STreehugger Robot void AllocatorProcFinish(Processor *proc) {
127*7c3d14c8STreehugger Robot allocator()->DestroyCache(&proc->alloc_cache);
128*7c3d14c8STreehugger Robot internal_allocator()->DestroyCache(&proc->internal_alloc_cache);
129*7c3d14c8STreehugger Robot }
130*7c3d14c8STreehugger Robot
AllocatorPrintStats()131*7c3d14c8STreehugger Robot void AllocatorPrintStats() {
132*7c3d14c8STreehugger Robot allocator()->PrintStats();
133*7c3d14c8STreehugger Robot }
134*7c3d14c8STreehugger Robot
SignalUnsafeCall(ThreadState * thr,uptr pc)135*7c3d14c8STreehugger Robot static void SignalUnsafeCall(ThreadState *thr, uptr pc) {
136*7c3d14c8STreehugger Robot if (atomic_load_relaxed(&thr->in_signal_handler) == 0 ||
137*7c3d14c8STreehugger Robot !flags()->report_signal_unsafe)
138*7c3d14c8STreehugger Robot return;
139*7c3d14c8STreehugger Robot VarSizeStackTrace stack;
140*7c3d14c8STreehugger Robot ObtainCurrentStack(thr, pc, &stack);
141*7c3d14c8STreehugger Robot if (IsFiredSuppression(ctx, ReportTypeSignalUnsafe, stack))
142*7c3d14c8STreehugger Robot return;
143*7c3d14c8STreehugger Robot ThreadRegistryLock l(ctx->thread_registry);
144*7c3d14c8STreehugger Robot ScopedReport rep(ReportTypeSignalUnsafe);
145*7c3d14c8STreehugger Robot rep.AddStack(stack, true);
146*7c3d14c8STreehugger Robot OutputReport(thr, rep);
147*7c3d14c8STreehugger Robot }
148*7c3d14c8STreehugger Robot
user_alloc(ThreadState * thr,uptr pc,uptr sz,uptr align,bool signal)149*7c3d14c8STreehugger Robot void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align, bool signal) {
150*7c3d14c8STreehugger Robot if ((sz >= (1ull << 40)) || (align >= (1ull << 40)))
151*7c3d14c8STreehugger Robot return allocator()->ReturnNullOrDie();
152*7c3d14c8STreehugger Robot void *p = allocator()->Allocate(&thr->proc()->alloc_cache, sz, align);
153*7c3d14c8STreehugger Robot if (p == 0)
154*7c3d14c8STreehugger Robot return 0;
155*7c3d14c8STreehugger Robot if (ctx && ctx->initialized)
156*7c3d14c8STreehugger Robot OnUserAlloc(thr, pc, (uptr)p, sz, true);
157*7c3d14c8STreehugger Robot if (signal)
158*7c3d14c8STreehugger Robot SignalUnsafeCall(thr, pc);
159*7c3d14c8STreehugger Robot return p;
160*7c3d14c8STreehugger Robot }
161*7c3d14c8STreehugger Robot
user_calloc(ThreadState * thr,uptr pc,uptr size,uptr n)162*7c3d14c8STreehugger Robot void *user_calloc(ThreadState *thr, uptr pc, uptr size, uptr n) {
163*7c3d14c8STreehugger Robot if (CallocShouldReturnNullDueToOverflow(size, n))
164*7c3d14c8STreehugger Robot return allocator()->ReturnNullOrDie();
165*7c3d14c8STreehugger Robot void *p = user_alloc(thr, pc, n * size);
166*7c3d14c8STreehugger Robot if (p)
167*7c3d14c8STreehugger Robot internal_memset(p, 0, n * size);
168*7c3d14c8STreehugger Robot return p;
169*7c3d14c8STreehugger Robot }
170*7c3d14c8STreehugger Robot
user_free(ThreadState * thr,uptr pc,void * p,bool signal)171*7c3d14c8STreehugger Robot void user_free(ThreadState *thr, uptr pc, void *p, bool signal) {
172*7c3d14c8STreehugger Robot ScopedGlobalProcessor sgp;
173*7c3d14c8STreehugger Robot if (ctx && ctx->initialized)
174*7c3d14c8STreehugger Robot OnUserFree(thr, pc, (uptr)p, true);
175*7c3d14c8STreehugger Robot allocator()->Deallocate(&thr->proc()->alloc_cache, p);
176*7c3d14c8STreehugger Robot if (signal)
177*7c3d14c8STreehugger Robot SignalUnsafeCall(thr, pc);
178*7c3d14c8STreehugger Robot }
179*7c3d14c8STreehugger Robot
OnUserAlloc(ThreadState * thr,uptr pc,uptr p,uptr sz,bool write)180*7c3d14c8STreehugger Robot void OnUserAlloc(ThreadState *thr, uptr pc, uptr p, uptr sz, bool write) {
181*7c3d14c8STreehugger Robot DPrintf("#%d: alloc(%zu) = %p\n", thr->tid, sz, p);
182*7c3d14c8STreehugger Robot ctx->metamap.AllocBlock(thr, pc, p, sz);
183*7c3d14c8STreehugger Robot if (write && thr->ignore_reads_and_writes == 0)
184*7c3d14c8STreehugger Robot MemoryRangeImitateWrite(thr, pc, (uptr)p, sz);
185*7c3d14c8STreehugger Robot else
186*7c3d14c8STreehugger Robot MemoryResetRange(thr, pc, (uptr)p, sz);
187*7c3d14c8STreehugger Robot }
188*7c3d14c8STreehugger Robot
OnUserFree(ThreadState * thr,uptr pc,uptr p,bool write)189*7c3d14c8STreehugger Robot void OnUserFree(ThreadState *thr, uptr pc, uptr p, bool write) {
190*7c3d14c8STreehugger Robot CHECK_NE(p, (void*)0);
191*7c3d14c8STreehugger Robot uptr sz = ctx->metamap.FreeBlock(thr->proc(), p);
192*7c3d14c8STreehugger Robot DPrintf("#%d: free(%p, %zu)\n", thr->tid, p, sz);
193*7c3d14c8STreehugger Robot if (write && thr->ignore_reads_and_writes == 0)
194*7c3d14c8STreehugger Robot MemoryRangeFreed(thr, pc, (uptr)p, sz);
195*7c3d14c8STreehugger Robot }
196*7c3d14c8STreehugger Robot
user_realloc(ThreadState * thr,uptr pc,void * p,uptr sz)197*7c3d14c8STreehugger Robot void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz) {
198*7c3d14c8STreehugger Robot void *p2 = 0;
199*7c3d14c8STreehugger Robot // FIXME: Handle "shrinking" more efficiently,
200*7c3d14c8STreehugger Robot // it seems that some software actually does this.
201*7c3d14c8STreehugger Robot if (sz) {
202*7c3d14c8STreehugger Robot p2 = user_alloc(thr, pc, sz);
203*7c3d14c8STreehugger Robot if (p2 == 0)
204*7c3d14c8STreehugger Robot return 0;
205*7c3d14c8STreehugger Robot if (p) {
206*7c3d14c8STreehugger Robot uptr oldsz = user_alloc_usable_size(p);
207*7c3d14c8STreehugger Robot internal_memcpy(p2, p, min(oldsz, sz));
208*7c3d14c8STreehugger Robot }
209*7c3d14c8STreehugger Robot }
210*7c3d14c8STreehugger Robot if (p)
211*7c3d14c8STreehugger Robot user_free(thr, pc, p);
212*7c3d14c8STreehugger Robot return p2;
213*7c3d14c8STreehugger Robot }
214*7c3d14c8STreehugger Robot
user_alloc_usable_size(const void * p)215*7c3d14c8STreehugger Robot uptr user_alloc_usable_size(const void *p) {
216*7c3d14c8STreehugger Robot if (p == 0)
217*7c3d14c8STreehugger Robot return 0;
218*7c3d14c8STreehugger Robot MBlock *b = ctx->metamap.GetBlock((uptr)p);
219*7c3d14c8STreehugger Robot if (!b)
220*7c3d14c8STreehugger Robot return 0; // Not a valid pointer.
221*7c3d14c8STreehugger Robot if (b->siz == 0)
222*7c3d14c8STreehugger Robot return 1; // Zero-sized allocations are actually 1 byte.
223*7c3d14c8STreehugger Robot return b->siz;
224*7c3d14c8STreehugger Robot }
225*7c3d14c8STreehugger Robot
invoke_malloc_hook(void * ptr,uptr size)226*7c3d14c8STreehugger Robot void invoke_malloc_hook(void *ptr, uptr size) {
227*7c3d14c8STreehugger Robot ThreadState *thr = cur_thread();
228*7c3d14c8STreehugger Robot if (ctx == 0 || !ctx->initialized || thr->ignore_interceptors)
229*7c3d14c8STreehugger Robot return;
230*7c3d14c8STreehugger Robot __sanitizer_malloc_hook(ptr, size);
231*7c3d14c8STreehugger Robot RunMallocHooks(ptr, size);
232*7c3d14c8STreehugger Robot }
233*7c3d14c8STreehugger Robot
invoke_free_hook(void * ptr)234*7c3d14c8STreehugger Robot void invoke_free_hook(void *ptr) {
235*7c3d14c8STreehugger Robot ThreadState *thr = cur_thread();
236*7c3d14c8STreehugger Robot if (ctx == 0 || !ctx->initialized || thr->ignore_interceptors)
237*7c3d14c8STreehugger Robot return;
238*7c3d14c8STreehugger Robot __sanitizer_free_hook(ptr);
239*7c3d14c8STreehugger Robot RunFreeHooks(ptr);
240*7c3d14c8STreehugger Robot }
241*7c3d14c8STreehugger Robot
internal_alloc(MBlockType typ,uptr sz)242*7c3d14c8STreehugger Robot void *internal_alloc(MBlockType typ, uptr sz) {
243*7c3d14c8STreehugger Robot ThreadState *thr = cur_thread();
244*7c3d14c8STreehugger Robot if (thr->nomalloc) {
245*7c3d14c8STreehugger Robot thr->nomalloc = 0; // CHECK calls internal_malloc().
246*7c3d14c8STreehugger Robot CHECK(0);
247*7c3d14c8STreehugger Robot }
248*7c3d14c8STreehugger Robot return InternalAlloc(sz, &thr->proc()->internal_alloc_cache);
249*7c3d14c8STreehugger Robot }
250*7c3d14c8STreehugger Robot
internal_free(void * p)251*7c3d14c8STreehugger Robot void internal_free(void *p) {
252*7c3d14c8STreehugger Robot ThreadState *thr = cur_thread();
253*7c3d14c8STreehugger Robot if (thr->nomalloc) {
254*7c3d14c8STreehugger Robot thr->nomalloc = 0; // CHECK calls internal_malloc().
255*7c3d14c8STreehugger Robot CHECK(0);
256*7c3d14c8STreehugger Robot }
257*7c3d14c8STreehugger Robot InternalFree(p, &thr->proc()->internal_alloc_cache);
258*7c3d14c8STreehugger Robot }
259*7c3d14c8STreehugger Robot
260*7c3d14c8STreehugger Robot } // namespace __tsan
261*7c3d14c8STreehugger Robot
262*7c3d14c8STreehugger Robot using namespace __tsan;
263*7c3d14c8STreehugger Robot
264*7c3d14c8STreehugger Robot extern "C" {
__sanitizer_get_current_allocated_bytes()265*7c3d14c8STreehugger Robot uptr __sanitizer_get_current_allocated_bytes() {
266*7c3d14c8STreehugger Robot uptr stats[AllocatorStatCount];
267*7c3d14c8STreehugger Robot allocator()->GetStats(stats);
268*7c3d14c8STreehugger Robot return stats[AllocatorStatAllocated];
269*7c3d14c8STreehugger Robot }
270*7c3d14c8STreehugger Robot
__sanitizer_get_heap_size()271*7c3d14c8STreehugger Robot uptr __sanitizer_get_heap_size() {
272*7c3d14c8STreehugger Robot uptr stats[AllocatorStatCount];
273*7c3d14c8STreehugger Robot allocator()->GetStats(stats);
274*7c3d14c8STreehugger Robot return stats[AllocatorStatMapped];
275*7c3d14c8STreehugger Robot }
276*7c3d14c8STreehugger Robot
__sanitizer_get_free_bytes()277*7c3d14c8STreehugger Robot uptr __sanitizer_get_free_bytes() {
278*7c3d14c8STreehugger Robot return 1;
279*7c3d14c8STreehugger Robot }
280*7c3d14c8STreehugger Robot
__sanitizer_get_unmapped_bytes()281*7c3d14c8STreehugger Robot uptr __sanitizer_get_unmapped_bytes() {
282*7c3d14c8STreehugger Robot return 1;
283*7c3d14c8STreehugger Robot }
284*7c3d14c8STreehugger Robot
__sanitizer_get_estimated_allocated_size(uptr size)285*7c3d14c8STreehugger Robot uptr __sanitizer_get_estimated_allocated_size(uptr size) {
286*7c3d14c8STreehugger Robot return size;
287*7c3d14c8STreehugger Robot }
288*7c3d14c8STreehugger Robot
__sanitizer_get_ownership(const void * p)289*7c3d14c8STreehugger Robot int __sanitizer_get_ownership(const void *p) {
290*7c3d14c8STreehugger Robot return allocator()->GetBlockBegin(p) != 0;
291*7c3d14c8STreehugger Robot }
292*7c3d14c8STreehugger Robot
__sanitizer_get_allocated_size(const void * p)293*7c3d14c8STreehugger Robot uptr __sanitizer_get_allocated_size(const void *p) {
294*7c3d14c8STreehugger Robot return user_alloc_usable_size(p);
295*7c3d14c8STreehugger Robot }
296*7c3d14c8STreehugger Robot
__tsan_on_thread_idle()297*7c3d14c8STreehugger Robot void __tsan_on_thread_idle() {
298*7c3d14c8STreehugger Robot ThreadState *thr = cur_thread();
299*7c3d14c8STreehugger Robot allocator()->SwallowCache(&thr->proc()->alloc_cache);
300*7c3d14c8STreehugger Robot internal_allocator()->SwallowCache(&thr->proc()->internal_alloc_cache);
301*7c3d14c8STreehugger Robot ctx->metamap.OnProcIdle(thr->proc());
302*7c3d14c8STreehugger Robot }
303*7c3d14c8STreehugger Robot } // extern "C"
304