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