1 //=-- lsan_allocator.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 LeakSanitizer. 11 // See lsan_allocator.h for details. 12 // 13 //===----------------------------------------------------------------------===// 14 15 #include "lsan_allocator.h" 16 17 #include "sanitizer_common/sanitizer_allocator.h" 18 #include "sanitizer_common/sanitizer_allocator_interface.h" 19 #include "sanitizer_common/sanitizer_internal_defs.h" 20 #include "sanitizer_common/sanitizer_stackdepot.h" 21 #include "sanitizer_common/sanitizer_stacktrace.h" 22 #include "lsan_common.h" 23 24 extern "C" void *memset(void *ptr, int value, uptr num); 25 26 namespace __lsan { 27 28 static const uptr kMaxAllowedMallocSize = 8UL << 30; 29 static const uptr kAllocatorSpace = 0x600000000000ULL; 30 static const uptr kAllocatorSize = 0x40000000000ULL; // 4T. 31 32 struct ChunkMetadata { 33 bool allocated : 8; // Must be first. 34 ChunkTag tag : 2; 35 uptr requested_size : 54; 36 u32 stack_trace_id; 37 }; 38 39 typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, 40 sizeof(ChunkMetadata), DefaultSizeClassMap> PrimaryAllocator; 41 typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache; 42 typedef LargeMmapAllocator<> SecondaryAllocator; 43 typedef CombinedAllocator<PrimaryAllocator, AllocatorCache, 44 SecondaryAllocator> Allocator; 45 46 static Allocator allocator; 47 static THREADLOCAL AllocatorCache cache; 48 49 void InitializeAllocator() { 50 allocator.Init(); 51 } 52 53 void AllocatorThreadFinish() { 54 allocator.SwallowCache(&cache); 55 } 56 57 static ChunkMetadata *Metadata(const void *p) { 58 return reinterpret_cast<ChunkMetadata *>(allocator.GetMetaData(p)); 59 } 60 61 static void RegisterAllocation(const StackTrace &stack, void *p, uptr size) { 62 if (!p) return; 63 ChunkMetadata *m = Metadata(p); 64 CHECK(m); 65 m->tag = DisabledInThisThread() ? kIgnored : kDirectlyLeaked; 66 m->stack_trace_id = StackDepotPut(stack); 67 m->requested_size = size; 68 atomic_store(reinterpret_cast<atomic_uint8_t *>(m), 1, memory_order_relaxed); 69 } 70 71 static void RegisterDeallocation(void *p) { 72 if (!p) return; 73 ChunkMetadata *m = Metadata(p); 74 CHECK(m); 75 atomic_store(reinterpret_cast<atomic_uint8_t *>(m), 0, memory_order_relaxed); 76 } 77 78 void *Allocate(const StackTrace &stack, uptr size, uptr alignment, 79 bool cleared) { 80 if (size == 0) 81 size = 1; 82 if (size > kMaxAllowedMallocSize) { 83 Report("WARNING: LeakSanitizer failed to allocate %zu bytes\n", size); 84 return 0; 85 } 86 void *p = allocator.Allocate(&cache, size, alignment, false); 87 // Do not rely on the allocator to clear the memory (it's slow). 88 if (cleared && allocator.FromPrimary(p)) 89 memset(p, 0, size); 90 RegisterAllocation(stack, p, size); 91 if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(p, size); 92 return p; 93 } 94 95 void Deallocate(void *p) { 96 if (&__sanitizer_free_hook) __sanitizer_free_hook(p); 97 RegisterDeallocation(p); 98 allocator.Deallocate(&cache, p); 99 } 100 101 void *Reallocate(const StackTrace &stack, void *p, uptr new_size, 102 uptr alignment) { 103 RegisterDeallocation(p); 104 if (new_size > kMaxAllowedMallocSize) { 105 Report("WARNING: LeakSanitizer failed to allocate %zu bytes\n", new_size); 106 allocator.Deallocate(&cache, p); 107 return 0; 108 } 109 p = allocator.Reallocate(&cache, p, new_size, alignment); 110 RegisterAllocation(stack, p, new_size); 111 return p; 112 } 113 114 void GetAllocatorCacheRange(uptr *begin, uptr *end) { 115 *begin = (uptr)&cache; 116 *end = *begin + sizeof(cache); 117 } 118 119 uptr GetMallocUsableSize(const void *p) { 120 ChunkMetadata *m = Metadata(p); 121 if (!m) return 0; 122 return m->requested_size; 123 } 124 125 ///// Interface to the common LSan module. ///// 126 127 void LockAllocator() { 128 allocator.ForceLock(); 129 } 130 131 void UnlockAllocator() { 132 allocator.ForceUnlock(); 133 } 134 135 void GetAllocatorGlobalRange(uptr *begin, uptr *end) { 136 *begin = (uptr)&allocator; 137 *end = *begin + sizeof(allocator); 138 } 139 140 uptr PointsIntoChunk(void* p) { 141 uptr addr = reinterpret_cast<uptr>(p); 142 uptr chunk = reinterpret_cast<uptr>(allocator.GetBlockBeginFastLocked(p)); 143 if (!chunk) return 0; 144 // LargeMmapAllocator considers pointers to the meta-region of a chunk to be 145 // valid, but we don't want that. 146 if (addr < chunk) return 0; 147 ChunkMetadata *m = Metadata(reinterpret_cast<void *>(chunk)); 148 CHECK(m); 149 if (!m->allocated) 150 return 0; 151 if (addr < chunk + m->requested_size) 152 return chunk; 153 if (IsSpecialCaseOfOperatorNew0(chunk, m->requested_size, addr)) 154 return chunk; 155 return 0; 156 } 157 158 uptr GetUserBegin(uptr chunk) { 159 return chunk; 160 } 161 162 LsanMetadata::LsanMetadata(uptr chunk) { 163 metadata_ = Metadata(reinterpret_cast<void *>(chunk)); 164 CHECK(metadata_); 165 } 166 167 bool LsanMetadata::allocated() const { 168 return reinterpret_cast<ChunkMetadata *>(metadata_)->allocated; 169 } 170 171 ChunkTag LsanMetadata::tag() const { 172 return reinterpret_cast<ChunkMetadata *>(metadata_)->tag; 173 } 174 175 void LsanMetadata::set_tag(ChunkTag value) { 176 reinterpret_cast<ChunkMetadata *>(metadata_)->tag = value; 177 } 178 179 uptr LsanMetadata::requested_size() const { 180 return reinterpret_cast<ChunkMetadata *>(metadata_)->requested_size; 181 } 182 183 u32 LsanMetadata::stack_trace_id() const { 184 return reinterpret_cast<ChunkMetadata *>(metadata_)->stack_trace_id; 185 } 186 187 void ForEachChunk(ForEachChunkCallback callback, void *arg) { 188 allocator.ForEachChunk(callback, arg); 189 } 190 191 IgnoreObjectResult IgnoreObjectLocked(const void *p) { 192 void *chunk = allocator.GetBlockBegin(p); 193 if (!chunk || p < chunk) return kIgnoreObjectInvalid; 194 ChunkMetadata *m = Metadata(chunk); 195 CHECK(m); 196 if (m->allocated && (uptr)p < (uptr)chunk + m->requested_size) { 197 if (m->tag == kIgnored) 198 return kIgnoreObjectAlreadyIgnored; 199 m->tag = kIgnored; 200 return kIgnoreObjectSuccess; 201 } else { 202 return kIgnoreObjectInvalid; 203 } 204 } 205 } // namespace __lsan 206 207 using namespace __lsan; 208 209 extern "C" { 210 SANITIZER_INTERFACE_ATTRIBUTE 211 uptr __sanitizer_get_current_allocated_bytes() { 212 uptr stats[AllocatorStatCount]; 213 allocator.GetStats(stats); 214 return stats[AllocatorStatAllocated]; 215 } 216 217 SANITIZER_INTERFACE_ATTRIBUTE 218 uptr __sanitizer_get_heap_size() { 219 uptr stats[AllocatorStatCount]; 220 allocator.GetStats(stats); 221 return stats[AllocatorStatMapped]; 222 } 223 224 SANITIZER_INTERFACE_ATTRIBUTE 225 uptr __sanitizer_get_free_bytes() { return 0; } 226 227 SANITIZER_INTERFACE_ATTRIBUTE 228 uptr __sanitizer_get_unmapped_bytes() { return 0; } 229 230 SANITIZER_INTERFACE_ATTRIBUTE 231 uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; } 232 233 SANITIZER_INTERFACE_ATTRIBUTE 234 int __sanitizer_get_ownership(const void *p) { return Metadata(p) != 0; } 235 236 SANITIZER_INTERFACE_ATTRIBUTE 237 uptr __sanitizer_get_allocated_size(const void *p) { 238 return GetMallocUsableSize(p); 239 } 240 } // extern "C" 241