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_allocator_interface.h" 14 #include "sanitizer_common/sanitizer_common.h" 15 #include "sanitizer_common/sanitizer_placement_new.h" 16 #include "tsan_mman.h" 17 #include "tsan_rtl.h" 18 #include "tsan_report.h" 19 #include "tsan_flags.h" 20 21 // May be overriden by front-end. 22 extern "C" void WEAK __sanitizer_malloc_hook(void *ptr, uptr size) { 23 (void)ptr; 24 (void)size; 25 } 26 27 extern "C" void WEAK __sanitizer_free_hook(void *ptr) { 28 (void)ptr; 29 } 30 31 namespace __tsan { 32 33 struct MapUnmapCallback { 34 void OnMap(uptr p, uptr size) const { } 35 void OnUnmap(uptr p, uptr size) const { 36 // We are about to unmap a chunk of user memory. 37 // Mark the corresponding shadow memory as not needed. 38 DontNeedShadowFor(p, size); 39 } 40 }; 41 42 static char allocator_placeholder[sizeof(Allocator)] ALIGNED(64); 43 Allocator *allocator() { 44 return reinterpret_cast<Allocator*>(&allocator_placeholder); 45 } 46 47 void InitializeAllocator() { 48 allocator()->Init(common_flags()->allocator_may_return_null); 49 } 50 51 void AllocatorThreadStart(ThreadState *thr) { 52 allocator()->InitCache(&thr->alloc_cache); 53 internal_allocator()->InitCache(&thr->internal_alloc_cache); 54 } 55 56 void AllocatorThreadFinish(ThreadState *thr) { 57 allocator()->DestroyCache(&thr->alloc_cache); 58 internal_allocator()->DestroyCache(&thr->internal_alloc_cache); 59 } 60 61 void AllocatorPrintStats() { 62 allocator()->PrintStats(); 63 } 64 65 static void SignalUnsafeCall(ThreadState *thr, uptr pc) { 66 if (atomic_load(&thr->in_signal_handler, memory_order_relaxed) == 0 || 67 !flags()->report_signal_unsafe) 68 return; 69 VarSizeStackTrace stack; 70 ObtainCurrentStack(thr, pc, &stack); 71 ThreadRegistryLock l(ctx->thread_registry); 72 ScopedReport rep(ReportTypeSignalUnsafe); 73 if (!IsFiredSuppression(ctx, rep, stack)) { 74 rep.AddStack(stack, true); 75 OutputReport(thr, rep); 76 } 77 } 78 79 void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align, bool signal) { 80 if ((sz >= (1ull << 40)) || (align >= (1ull << 40))) 81 return allocator()->ReturnNullOrDie(); 82 void *p = allocator()->Allocate(&thr->alloc_cache, sz, align); 83 if (p == 0) 84 return 0; 85 if (ctx && ctx->initialized) 86 OnUserAlloc(thr, pc, (uptr)p, sz, true); 87 if (signal) 88 SignalUnsafeCall(thr, pc); 89 return p; 90 } 91 92 void *user_calloc(ThreadState *thr, uptr pc, uptr size, uptr n) { 93 if (CallocShouldReturnNullDueToOverflow(size, n)) 94 return allocator()->ReturnNullOrDie(); 95 void *p = user_alloc(thr, pc, n * size); 96 if (p) 97 internal_memset(p, 0, n * size); 98 return p; 99 } 100 101 void user_free(ThreadState *thr, uptr pc, void *p, bool signal) { 102 if (ctx && ctx->initialized) 103 OnUserFree(thr, pc, (uptr)p, true); 104 allocator()->Deallocate(&thr->alloc_cache, p); 105 if (signal) 106 SignalUnsafeCall(thr, pc); 107 } 108 109 void OnUserAlloc(ThreadState *thr, uptr pc, uptr p, uptr sz, bool write) { 110 DPrintf("#%d: alloc(%zu) = %p\n", thr->tid, sz, p); 111 ctx->metamap.AllocBlock(thr, pc, p, sz); 112 if (write && thr->ignore_reads_and_writes == 0) 113 MemoryRangeImitateWrite(thr, pc, (uptr)p, sz); 114 else 115 MemoryResetRange(thr, pc, (uptr)p, sz); 116 } 117 118 void OnUserFree(ThreadState *thr, uptr pc, uptr p, bool write) { 119 CHECK_NE(p, (void*)0); 120 uptr sz = ctx->metamap.FreeBlock(thr, pc, p); 121 DPrintf("#%d: free(%p, %zu)\n", thr->tid, p, sz); 122 if (write && thr->ignore_reads_and_writes == 0) 123 MemoryRangeFreed(thr, pc, (uptr)p, sz); 124 } 125 126 void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz) { 127 void *p2 = 0; 128 // FIXME: Handle "shrinking" more efficiently, 129 // it seems that some software actually does this. 130 if (sz) { 131 p2 = user_alloc(thr, pc, sz); 132 if (p2 == 0) 133 return 0; 134 if (p) { 135 uptr oldsz = user_alloc_usable_size(p); 136 internal_memcpy(p2, p, min(oldsz, sz)); 137 } 138 } 139 if (p) 140 user_free(thr, pc, p); 141 return p2; 142 } 143 144 uptr user_alloc_usable_size(const void *p) { 145 if (p == 0) 146 return 0; 147 MBlock *b = ctx->metamap.GetBlock((uptr)p); 148 return b ? b->siz : 0; 149 } 150 151 void invoke_malloc_hook(void *ptr, uptr size) { 152 ThreadState *thr = cur_thread(); 153 if (ctx == 0 || !ctx->initialized || thr->ignore_interceptors) 154 return; 155 __sanitizer_malloc_hook(ptr, size); 156 } 157 158 void invoke_free_hook(void *ptr) { 159 ThreadState *thr = cur_thread(); 160 if (ctx == 0 || !ctx->initialized || thr->ignore_interceptors) 161 return; 162 __sanitizer_free_hook(ptr); 163 } 164 165 void *internal_alloc(MBlockType typ, uptr sz) { 166 ThreadState *thr = cur_thread(); 167 if (thr->nomalloc) { 168 thr->nomalloc = 0; // CHECK calls internal_malloc(). 169 CHECK(0); 170 } 171 return InternalAlloc(sz, &thr->internal_alloc_cache); 172 } 173 174 void internal_free(void *p) { 175 ThreadState *thr = cur_thread(); 176 if (thr->nomalloc) { 177 thr->nomalloc = 0; // CHECK calls internal_malloc(). 178 CHECK(0); 179 } 180 InternalFree(p, &thr->internal_alloc_cache); 181 } 182 183 } // namespace __tsan 184 185 using namespace __tsan; 186 187 extern "C" { 188 uptr __sanitizer_get_current_allocated_bytes() { 189 uptr stats[AllocatorStatCount]; 190 allocator()->GetStats(stats); 191 return stats[AllocatorStatAllocated]; 192 } 193 194 uptr __sanitizer_get_heap_size() { 195 uptr stats[AllocatorStatCount]; 196 allocator()->GetStats(stats); 197 return stats[AllocatorStatMapped]; 198 } 199 200 uptr __sanitizer_get_free_bytes() { 201 return 1; 202 } 203 204 uptr __sanitizer_get_unmapped_bytes() { 205 return 1; 206 } 207 208 uptr __sanitizer_get_estimated_allocated_size(uptr size) { 209 return size; 210 } 211 212 int __sanitizer_get_ownership(const void *p) { 213 return allocator()->GetBlockBegin(p) != 0; 214 } 215 216 uptr __sanitizer_get_allocated_size(const void *p) { 217 return user_alloc_usable_size(p); 218 } 219 220 void __tsan_on_thread_idle() { 221 ThreadState *thr = cur_thread(); 222 allocator()->SwallowCache(&thr->alloc_cache); 223 internal_allocator()->SwallowCache(&thr->internal_alloc_cache); 224 ctx->metamap.OnThreadIdle(thr); 225 } 226 } // extern "C" 227