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