]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/compiler-rt/lib/hwasan/hwasan_allocator.cc
Upgrade our copies of clang, llvm, lld, lldb, compiler-rt and libc++ to
[FreeBSD/FreeBSD.git] / contrib / compiler-rt / lib / hwasan / hwasan_allocator.cc
1 //===-- hwasan_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 HWAddressSanitizer.
11 //
12 // HWAddressSanitizer allocator.
13 //===----------------------------------------------------------------------===//
14
15 #include "sanitizer_common/sanitizer_allocator.h"
16 #include "sanitizer_common/sanitizer_allocator_checks.h"
17 #include "sanitizer_common/sanitizer_allocator_interface.h"
18 #include "sanitizer_common/sanitizer_atomic.h"
19 #include "sanitizer_common/sanitizer_errno.h"
20 #include "sanitizer_common/sanitizer_stackdepot.h"
21 #include "hwasan.h"
22 #include "hwasan_allocator.h"
23 #include "hwasan_thread.h"
24 #include "hwasan_poisoning.h"
25
26 namespace __hwasan {
27
28 enum {
29   CHUNK_INVALID = 0,
30   CHUNK_FREE = 1,
31   CHUNK_ALLOCATED = 2
32 };
33
34 struct Metadata {
35   u64 state : 2;
36   u64 requested_size : 62;
37   u32 alloc_context_id;
38   u32 free_context_id;
39 };
40
41 bool HwasanChunkView::IsValid() const {
42   return metadata_ && metadata_->state != CHUNK_INVALID;
43 }
44 bool HwasanChunkView::IsAllocated() const {
45   return metadata_ && metadata_->state == CHUNK_ALLOCATED;
46 }
47 uptr HwasanChunkView::Beg() const {
48   return block_;
49 }
50 uptr HwasanChunkView::End() const {
51   return Beg() + UsedSize();
52 }
53 uptr HwasanChunkView::UsedSize() const {
54   return metadata_->requested_size;
55 }
56 u32 HwasanChunkView::GetAllocStackId() const {
57   return metadata_->alloc_context_id;
58 }
59 u32 HwasanChunkView::GetFreeStackId() const {
60   return metadata_->free_context_id;
61 }
62
63 struct HwasanMapUnmapCallback {
64   void OnMap(uptr p, uptr size) const {}
65   void OnUnmap(uptr p, uptr size) const {
66     // We are about to unmap a chunk of user memory.
67     // It can return as user-requested mmap() or another thread stack.
68     // Make it accessible with zero-tagged pointer.
69     TagMemory(p, size, 0);
70   }
71 };
72
73 #if !defined(__aarch64__)
74 #error unsupported platform
75 #endif
76
77 static const uptr kMaxAllowedMallocSize = 2UL << 30;  // 2G
78 static const uptr kRegionSizeLog = 20;
79 static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog;
80 typedef TwoLevelByteMap<(kNumRegions >> 12), 1 << 12> ByteMap;
81
82 struct AP32 {
83   static const uptr kSpaceBeg = 0;
84   static const u64 kSpaceSize = SANITIZER_MMAP_RANGE_SIZE;
85   static const uptr kMetadataSize = sizeof(Metadata);
86   typedef __sanitizer::CompactSizeClassMap SizeClassMap;
87   static const uptr kRegionSizeLog = __hwasan::kRegionSizeLog;
88   typedef __hwasan::ByteMap ByteMap;
89   typedef HwasanMapUnmapCallback MapUnmapCallback;
90   static const uptr kFlags = 0;
91 };
92 typedef SizeClassAllocator32<AP32> PrimaryAllocator;
93 typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
94 typedef LargeMmapAllocator<HwasanMapUnmapCallback> SecondaryAllocator;
95 typedef CombinedAllocator<PrimaryAllocator, AllocatorCache,
96                           SecondaryAllocator> Allocator;
97
98 static Allocator allocator;
99 static AllocatorCache fallback_allocator_cache;
100 static SpinMutex fallback_mutex;
101 static atomic_uint8_t hwasan_allocator_tagging_enabled;
102
103 void HwasanAllocatorInit() {
104   atomic_store_relaxed(&hwasan_allocator_tagging_enabled,
105                        !flags()->disable_allocator_tagging);
106   SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null);
107   allocator.Init(common_flags()->allocator_release_to_os_interval_ms);
108 }
109
110 AllocatorCache *GetAllocatorCache(HwasanThreadLocalMallocStorage *ms) {
111   CHECK(ms);
112   CHECK_LE(sizeof(AllocatorCache), sizeof(ms->allocator_cache));
113   return reinterpret_cast<AllocatorCache *>(ms->allocator_cache);
114 }
115
116 void HwasanThreadLocalMallocStorage::CommitBack() {
117   allocator.SwallowCache(GetAllocatorCache(this));
118 }
119
120 static void *HwasanAllocate(StackTrace *stack, uptr size, uptr alignment,
121                           bool zeroise) {
122   alignment = Max(alignment, kShadowAlignment);
123   size = RoundUpTo(size, kShadowAlignment);
124
125   if (size > kMaxAllowedMallocSize) {
126     Report("WARNING: HWAddressSanitizer failed to allocate %p bytes\n",
127            (void *)size);
128     return Allocator::FailureHandler::OnBadRequest();
129   }
130   HwasanThread *t = GetCurrentThread();
131   void *allocated;
132   if (t) {
133     AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage());
134     allocated = allocator.Allocate(cache, size, alignment);
135   } else {
136     SpinMutexLock l(&fallback_mutex);
137     AllocatorCache *cache = &fallback_allocator_cache;
138     allocated = allocator.Allocate(cache, size, alignment);
139   }
140   Metadata *meta =
141       reinterpret_cast<Metadata *>(allocator.GetMetaData(allocated));
142   meta->state = CHUNK_ALLOCATED;
143   meta->requested_size = size;
144   meta->alloc_context_id = StackDepotPut(*stack);
145   if (zeroise)
146     internal_memset(allocated, 0, size);
147
148   void *user_ptr = (flags()->tag_in_malloc &&
149                     atomic_load_relaxed(&hwasan_allocator_tagging_enabled))
150                        ? (void *)TagMemoryAligned((uptr)allocated, size, 0xBB)
151                        : allocated;
152
153   HWASAN_MALLOC_HOOK(user_ptr, size);
154   return user_ptr;
155 }
156
157 void HwasanDeallocate(StackTrace *stack, void *user_ptr) {
158   CHECK(user_ptr);
159   HWASAN_FREE_HOOK(user_ptr);
160
161   void *p = GetAddressFromPointer(user_ptr);
162   Metadata *meta = reinterpret_cast<Metadata *>(allocator.GetMetaData(p));
163   uptr size = meta->requested_size;
164   meta->state = CHUNK_FREE;
165   meta->requested_size = 0;
166   meta->free_context_id = StackDepotPut(*stack);
167   // This memory will not be reused by anyone else, so we are free to keep it
168   // poisoned.
169   if (flags()->tag_in_free &&
170       atomic_load_relaxed(&hwasan_allocator_tagging_enabled))
171     TagMemoryAligned((uptr)p, size, 0xBC);
172   HwasanThread *t = GetCurrentThread();
173   if (t) {
174     AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage());
175     allocator.Deallocate(cache, p);
176   } else {
177     SpinMutexLock l(&fallback_mutex);
178     AllocatorCache *cache = &fallback_allocator_cache;
179     allocator.Deallocate(cache, p);
180   }
181 }
182
183 void *HwasanReallocate(StackTrace *stack, void *user_old_p, uptr new_size,
184                      uptr alignment) {
185   alignment = Max(alignment, kShadowAlignment);
186   new_size = RoundUpTo(new_size, kShadowAlignment);
187
188   void *old_p = GetAddressFromPointer(user_old_p);
189   Metadata *meta = reinterpret_cast<Metadata*>(allocator.GetMetaData(old_p));
190   uptr old_size = meta->requested_size;
191   uptr actually_allocated_size = allocator.GetActuallyAllocatedSize(old_p);
192   if (new_size <= actually_allocated_size) {
193     // We are not reallocating here.
194     // FIXME: update stack trace for the allocation?
195     meta->requested_size = new_size;
196     if (!atomic_load_relaxed(&hwasan_allocator_tagging_enabled))
197       return user_old_p;
198     if (flags()->retag_in_realloc)
199       return (void *)TagMemoryAligned((uptr)old_p, new_size, 0xCC);
200     if (new_size > old_size) {
201       tag_t tag = GetTagFromPointer((uptr)user_old_p);
202       TagMemoryAligned((uptr)old_p + old_size, new_size - old_size, tag);
203     }
204     return user_old_p;
205   }
206   uptr memcpy_size = Min(new_size, old_size);
207   void *new_p = HwasanAllocate(stack, new_size, alignment, false /*zeroise*/);
208   if (new_p) {
209     internal_memcpy(new_p, old_p, memcpy_size);
210     HwasanDeallocate(stack, old_p);
211   }
212   return new_p;
213 }
214
215 HwasanChunkView FindHeapChunkByAddress(uptr address) {
216   void *block = allocator.GetBlockBegin(reinterpret_cast<void*>(address));
217   if (!block)
218     return HwasanChunkView();
219   Metadata *metadata =
220       reinterpret_cast<Metadata*>(allocator.GetMetaData(block));
221   return HwasanChunkView(reinterpret_cast<uptr>(block), metadata);
222 }
223
224 static uptr AllocationSize(const void *user_ptr) {
225   const void *p = GetAddressFromPointer(user_ptr);
226   if (!p) return 0;
227   const void *beg = allocator.GetBlockBegin(p);
228   if (beg != p) return 0;
229   Metadata *b = (Metadata *)allocator.GetMetaData(p);
230   return b->requested_size;
231 }
232
233 void *hwasan_malloc(uptr size, StackTrace *stack) {
234   return SetErrnoOnNull(HwasanAllocate(stack, size, sizeof(u64), false));
235 }
236
237 void *hwasan_calloc(uptr nmemb, uptr size, StackTrace *stack) {
238   if (UNLIKELY(CheckForCallocOverflow(size, nmemb)))
239     return SetErrnoOnNull(Allocator::FailureHandler::OnBadRequest());
240   return SetErrnoOnNull(HwasanAllocate(stack, nmemb * size, sizeof(u64), true));
241 }
242
243 void *hwasan_realloc(void *ptr, uptr size, StackTrace *stack) {
244   if (!ptr)
245     return SetErrnoOnNull(HwasanAllocate(stack, size, sizeof(u64), false));
246   if (size == 0) {
247     HwasanDeallocate(stack, ptr);
248     return nullptr;
249   }
250   return SetErrnoOnNull(HwasanReallocate(stack, ptr, size, sizeof(u64)));
251 }
252
253 void *hwasan_valloc(uptr size, StackTrace *stack) {
254   return SetErrnoOnNull(HwasanAllocate(stack, size, GetPageSizeCached(), false));
255 }
256
257 void *hwasan_pvalloc(uptr size, StackTrace *stack) {
258   uptr PageSize = GetPageSizeCached();
259   if (UNLIKELY(CheckForPvallocOverflow(size, PageSize))) {
260     errno = errno_ENOMEM;
261     return Allocator::FailureHandler::OnBadRequest();
262   }
263   // pvalloc(0) should allocate one page.
264   size = size ? RoundUpTo(size, PageSize) : PageSize;
265   return SetErrnoOnNull(HwasanAllocate(stack, size, PageSize, false));
266 }
267
268 void *hwasan_aligned_alloc(uptr alignment, uptr size, StackTrace *stack) {
269   if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(alignment, size))) {
270     errno = errno_EINVAL;
271     return Allocator::FailureHandler::OnBadRequest();
272   }
273   return SetErrnoOnNull(HwasanAllocate(stack, size, alignment, false));
274 }
275
276 void *hwasan_memalign(uptr alignment, uptr size, StackTrace *stack) {
277   if (UNLIKELY(!IsPowerOfTwo(alignment))) {
278     errno = errno_EINVAL;
279     return Allocator::FailureHandler::OnBadRequest();
280   }
281   return SetErrnoOnNull(HwasanAllocate(stack, size, alignment, false));
282 }
283
284 int hwasan_posix_memalign(void **memptr, uptr alignment, uptr size,
285                         StackTrace *stack) {
286   if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) {
287     Allocator::FailureHandler::OnBadRequest();
288     return errno_EINVAL;
289   }
290   void *ptr = HwasanAllocate(stack, size, alignment, false);
291   if (UNLIKELY(!ptr))
292     return errno_ENOMEM;
293   CHECK(IsAligned((uptr)ptr, alignment));
294   *memptr = ptr;
295   return 0;
296 }
297
298 } // namespace __hwasan
299
300 using namespace __hwasan;
301
302 void __hwasan_enable_allocator_tagging() {
303   atomic_store_relaxed(&hwasan_allocator_tagging_enabled, 1);
304 }
305
306 void __hwasan_disable_allocator_tagging() {
307   atomic_store_relaxed(&hwasan_allocator_tagging_enabled, 0);
308 }
309
310 uptr __sanitizer_get_current_allocated_bytes() {
311   uptr stats[AllocatorStatCount];
312   allocator.GetStats(stats);
313   return stats[AllocatorStatAllocated];
314 }
315
316 uptr __sanitizer_get_heap_size() {
317   uptr stats[AllocatorStatCount];
318   allocator.GetStats(stats);
319   return stats[AllocatorStatMapped];
320 }
321
322 uptr __sanitizer_get_free_bytes() { return 1; }
323
324 uptr __sanitizer_get_unmapped_bytes() { return 1; }
325
326 uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; }
327
328 int __sanitizer_get_ownership(const void *p) { return AllocationSize(p) != 0; }
329
330 uptr __sanitizer_get_allocated_size(const void *p) { return AllocationSize(p); }