xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/dfsan/dfsan_allocator.cpp (revision 0eae32dcef82f6f06de6419a0d623d7def0cc8f6)
1fe6060f1SDimitry Andric //===-- dfsan_allocator.cpp -------------------------- --------------------===//
2fe6060f1SDimitry Andric //
3fe6060f1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4fe6060f1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5fe6060f1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6fe6060f1SDimitry Andric //
7fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
8fe6060f1SDimitry Andric //
9fe6060f1SDimitry Andric // This file is a part of DataflowSanitizer.
10fe6060f1SDimitry Andric //
11fe6060f1SDimitry Andric // DataflowSanitizer allocator.
12fe6060f1SDimitry Andric //===----------------------------------------------------------------------===//
13fe6060f1SDimitry Andric 
14fe6060f1SDimitry Andric #include "dfsan_allocator.h"
15fe6060f1SDimitry Andric 
16fe6060f1SDimitry Andric #include "dfsan.h"
17fe6060f1SDimitry Andric #include "dfsan_flags.h"
18fe6060f1SDimitry Andric #include "dfsan_thread.h"
19fe6060f1SDimitry Andric #include "sanitizer_common/sanitizer_allocator.h"
20fe6060f1SDimitry Andric #include "sanitizer_common/sanitizer_allocator_checks.h"
21fe6060f1SDimitry Andric #include "sanitizer_common/sanitizer_allocator_interface.h"
22fe6060f1SDimitry Andric #include "sanitizer_common/sanitizer_allocator_report.h"
23fe6060f1SDimitry Andric #include "sanitizer_common/sanitizer_errno.h"
24fe6060f1SDimitry Andric 
25fe6060f1SDimitry Andric namespace __dfsan {
26fe6060f1SDimitry Andric 
27fe6060f1SDimitry Andric struct Metadata {
28fe6060f1SDimitry Andric   uptr requested_size;
29fe6060f1SDimitry Andric };
30fe6060f1SDimitry Andric 
31fe6060f1SDimitry Andric struct DFsanMapUnmapCallback {
32fe6060f1SDimitry Andric   void OnMap(uptr p, uptr size) const { dfsan_set_label(0, (void *)p, size); }
33fe6060f1SDimitry Andric   void OnUnmap(uptr p, uptr size) const { dfsan_set_label(0, (void *)p, size); }
34fe6060f1SDimitry Andric };
35fe6060f1SDimitry Andric 
36fe6060f1SDimitry Andric static const uptr kAllocatorSpace = 0x700000000000ULL;
37fe6060f1SDimitry Andric static const uptr kMaxAllowedMallocSize = 8UL << 30;
38fe6060f1SDimitry Andric 
39fe6060f1SDimitry Andric struct AP64 {  // Allocator64 parameters. Deliberately using a short name.
40fe6060f1SDimitry Andric   static const uptr kSpaceBeg = kAllocatorSpace;
41fe6060f1SDimitry Andric   static const uptr kSpaceSize = 0x40000000000;  // 4T.
42fe6060f1SDimitry Andric   static const uptr kMetadataSize = sizeof(Metadata);
43fe6060f1SDimitry Andric   typedef DefaultSizeClassMap SizeClassMap;
44fe6060f1SDimitry Andric   typedef DFsanMapUnmapCallback MapUnmapCallback;
45fe6060f1SDimitry Andric   static const uptr kFlags = 0;
46fe6060f1SDimitry Andric   using AddressSpaceView = LocalAddressSpaceView;
47fe6060f1SDimitry Andric };
48fe6060f1SDimitry Andric 
49fe6060f1SDimitry Andric typedef SizeClassAllocator64<AP64> PrimaryAllocator;
50fe6060f1SDimitry Andric 
51fe6060f1SDimitry Andric typedef CombinedAllocator<PrimaryAllocator> Allocator;
52fe6060f1SDimitry Andric typedef Allocator::AllocatorCache AllocatorCache;
53fe6060f1SDimitry Andric 
54fe6060f1SDimitry Andric static Allocator allocator;
55fe6060f1SDimitry Andric static AllocatorCache fallback_allocator_cache;
56fe6060f1SDimitry Andric static StaticSpinMutex fallback_mutex;
57fe6060f1SDimitry Andric 
58fe6060f1SDimitry Andric static uptr max_malloc_size;
59fe6060f1SDimitry Andric 
60fe6060f1SDimitry Andric void dfsan_allocator_init() {
61fe6060f1SDimitry Andric   SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null);
62fe6060f1SDimitry Andric   allocator.Init(common_flags()->allocator_release_to_os_interval_ms);
63fe6060f1SDimitry Andric   if (common_flags()->max_allocation_size_mb)
64fe6060f1SDimitry Andric     max_malloc_size = Min(common_flags()->max_allocation_size_mb << 20,
65fe6060f1SDimitry Andric                           kMaxAllowedMallocSize);
66fe6060f1SDimitry Andric   else
67fe6060f1SDimitry Andric     max_malloc_size = kMaxAllowedMallocSize;
68fe6060f1SDimitry Andric }
69fe6060f1SDimitry Andric 
70fe6060f1SDimitry Andric AllocatorCache *GetAllocatorCache(DFsanThreadLocalMallocStorage *ms) {
71fe6060f1SDimitry Andric   CHECK(ms);
72fe6060f1SDimitry Andric   CHECK_LE(sizeof(AllocatorCache), sizeof(ms->allocator_cache));
73fe6060f1SDimitry Andric   return reinterpret_cast<AllocatorCache *>(ms->allocator_cache);
74fe6060f1SDimitry Andric }
75fe6060f1SDimitry Andric 
76fe6060f1SDimitry Andric void DFsanThreadLocalMallocStorage::CommitBack() {
77fe6060f1SDimitry Andric   allocator.SwallowCache(GetAllocatorCache(this));
78fe6060f1SDimitry Andric }
79fe6060f1SDimitry Andric 
80fe6060f1SDimitry Andric static void *DFsanAllocate(uptr size, uptr alignment, bool zeroise) {
81fe6060f1SDimitry Andric   if (size > max_malloc_size) {
82fe6060f1SDimitry Andric     if (AllocatorMayReturnNull()) {
83fe6060f1SDimitry Andric       Report("WARNING: DataflowSanitizer failed to allocate 0x%zx bytes\n",
84fe6060f1SDimitry Andric              size);
85fe6060f1SDimitry Andric       return nullptr;
86fe6060f1SDimitry Andric     }
87fe6060f1SDimitry Andric     BufferedStackTrace stack;
88fe6060f1SDimitry Andric     ReportAllocationSizeTooBig(size, max_malloc_size, &stack);
89fe6060f1SDimitry Andric   }
90*0eae32dcSDimitry Andric   if (UNLIKELY(IsRssLimitExceeded())) {
91*0eae32dcSDimitry Andric     if (AllocatorMayReturnNull())
92*0eae32dcSDimitry Andric       return nullptr;
93*0eae32dcSDimitry Andric     BufferedStackTrace stack;
94*0eae32dcSDimitry Andric     ReportRssLimitExceeded(&stack);
95*0eae32dcSDimitry Andric   }
96fe6060f1SDimitry Andric   DFsanThread *t = GetCurrentThread();
97fe6060f1SDimitry Andric   void *allocated;
98fe6060f1SDimitry Andric   if (t) {
99fe6060f1SDimitry Andric     AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage());
100fe6060f1SDimitry Andric     allocated = allocator.Allocate(cache, size, alignment);
101fe6060f1SDimitry Andric   } else {
102fe6060f1SDimitry Andric     SpinMutexLock l(&fallback_mutex);
103fe6060f1SDimitry Andric     AllocatorCache *cache = &fallback_allocator_cache;
104fe6060f1SDimitry Andric     allocated = allocator.Allocate(cache, size, alignment);
105fe6060f1SDimitry Andric   }
106fe6060f1SDimitry Andric   if (UNLIKELY(!allocated)) {
107fe6060f1SDimitry Andric     SetAllocatorOutOfMemory();
108fe6060f1SDimitry Andric     if (AllocatorMayReturnNull())
109fe6060f1SDimitry Andric       return nullptr;
110fe6060f1SDimitry Andric     BufferedStackTrace stack;
111fe6060f1SDimitry Andric     ReportOutOfMemory(size, &stack);
112fe6060f1SDimitry Andric   }
113fe6060f1SDimitry Andric   Metadata *meta =
114fe6060f1SDimitry Andric       reinterpret_cast<Metadata *>(allocator.GetMetaData(allocated));
115fe6060f1SDimitry Andric   meta->requested_size = size;
116fe6060f1SDimitry Andric   if (zeroise) {
117fe6060f1SDimitry Andric     internal_memset(allocated, 0, size);
118fe6060f1SDimitry Andric     dfsan_set_label(0, allocated, size);
119fe6060f1SDimitry Andric   } else if (flags().zero_in_malloc) {
120fe6060f1SDimitry Andric     dfsan_set_label(0, allocated, size);
121fe6060f1SDimitry Andric   }
122fe6060f1SDimitry Andric   return allocated;
123fe6060f1SDimitry Andric }
124fe6060f1SDimitry Andric 
125fe6060f1SDimitry Andric void dfsan_deallocate(void *p) {
126fe6060f1SDimitry Andric   CHECK(p);
127fe6060f1SDimitry Andric   Metadata *meta = reinterpret_cast<Metadata *>(allocator.GetMetaData(p));
128fe6060f1SDimitry Andric   uptr size = meta->requested_size;
129fe6060f1SDimitry Andric   meta->requested_size = 0;
130fe6060f1SDimitry Andric   if (flags().zero_in_free)
131fe6060f1SDimitry Andric     dfsan_set_label(0, p, size);
132fe6060f1SDimitry Andric   DFsanThread *t = GetCurrentThread();
133fe6060f1SDimitry Andric   if (t) {
134fe6060f1SDimitry Andric     AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage());
135fe6060f1SDimitry Andric     allocator.Deallocate(cache, p);
136fe6060f1SDimitry Andric   } else {
137fe6060f1SDimitry Andric     SpinMutexLock l(&fallback_mutex);
138fe6060f1SDimitry Andric     AllocatorCache *cache = &fallback_allocator_cache;
139fe6060f1SDimitry Andric     allocator.Deallocate(cache, p);
140fe6060f1SDimitry Andric   }
141fe6060f1SDimitry Andric }
142fe6060f1SDimitry Andric 
143fe6060f1SDimitry Andric void *DFsanReallocate(void *old_p, uptr new_size, uptr alignment) {
144fe6060f1SDimitry Andric   Metadata *meta = reinterpret_cast<Metadata *>(allocator.GetMetaData(old_p));
145fe6060f1SDimitry Andric   uptr old_size = meta->requested_size;
146fe6060f1SDimitry Andric   uptr actually_allocated_size = allocator.GetActuallyAllocatedSize(old_p);
147fe6060f1SDimitry Andric   if (new_size <= actually_allocated_size) {
148fe6060f1SDimitry Andric     // We are not reallocating here.
149fe6060f1SDimitry Andric     meta->requested_size = new_size;
150fe6060f1SDimitry Andric     if (new_size > old_size && flags().zero_in_malloc)
151fe6060f1SDimitry Andric       dfsan_set_label(0, (char *)old_p + old_size, new_size - old_size);
152fe6060f1SDimitry Andric     return old_p;
153fe6060f1SDimitry Andric   }
154fe6060f1SDimitry Andric   uptr memcpy_size = Min(new_size, old_size);
155fe6060f1SDimitry Andric   void *new_p = DFsanAllocate(new_size, alignment, false /*zeroise*/);
156fe6060f1SDimitry Andric   if (new_p) {
157fe6060f1SDimitry Andric     dfsan_copy_memory(new_p, old_p, memcpy_size);
158fe6060f1SDimitry Andric     dfsan_deallocate(old_p);
159fe6060f1SDimitry Andric   }
160fe6060f1SDimitry Andric   return new_p;
161fe6060f1SDimitry Andric }
162fe6060f1SDimitry Andric 
163fe6060f1SDimitry Andric void *DFsanCalloc(uptr nmemb, uptr size) {
164fe6060f1SDimitry Andric   if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
165fe6060f1SDimitry Andric     if (AllocatorMayReturnNull())
166fe6060f1SDimitry Andric       return nullptr;
167fe6060f1SDimitry Andric     BufferedStackTrace stack;
168fe6060f1SDimitry Andric     ReportCallocOverflow(nmemb, size, &stack);
169fe6060f1SDimitry Andric   }
170fe6060f1SDimitry Andric   return DFsanAllocate(nmemb * size, sizeof(u64), true /*zeroise*/);
171fe6060f1SDimitry Andric }
172fe6060f1SDimitry Andric 
173fe6060f1SDimitry Andric static uptr AllocationSize(const void *p) {
174fe6060f1SDimitry Andric   if (!p)
175fe6060f1SDimitry Andric     return 0;
176fe6060f1SDimitry Andric   const void *beg = allocator.GetBlockBegin(p);
177fe6060f1SDimitry Andric   if (beg != p)
178fe6060f1SDimitry Andric     return 0;
179fe6060f1SDimitry Andric   Metadata *b = (Metadata *)allocator.GetMetaData(p);
180fe6060f1SDimitry Andric   return b->requested_size;
181fe6060f1SDimitry Andric }
182fe6060f1SDimitry Andric 
183fe6060f1SDimitry Andric void *dfsan_malloc(uptr size) {
184fe6060f1SDimitry Andric   return SetErrnoOnNull(DFsanAllocate(size, sizeof(u64), false /*zeroise*/));
185fe6060f1SDimitry Andric }
186fe6060f1SDimitry Andric 
187fe6060f1SDimitry Andric void *dfsan_calloc(uptr nmemb, uptr size) {
188fe6060f1SDimitry Andric   return SetErrnoOnNull(DFsanCalloc(nmemb, size));
189fe6060f1SDimitry Andric }
190fe6060f1SDimitry Andric 
191fe6060f1SDimitry Andric void *dfsan_realloc(void *ptr, uptr size) {
192fe6060f1SDimitry Andric   if (!ptr)
193fe6060f1SDimitry Andric     return SetErrnoOnNull(DFsanAllocate(size, sizeof(u64), false /*zeroise*/));
194fe6060f1SDimitry Andric   if (size == 0) {
195fe6060f1SDimitry Andric     dfsan_deallocate(ptr);
196fe6060f1SDimitry Andric     return nullptr;
197fe6060f1SDimitry Andric   }
198fe6060f1SDimitry Andric   return SetErrnoOnNull(DFsanReallocate(ptr, size, sizeof(u64)));
199fe6060f1SDimitry Andric }
200fe6060f1SDimitry Andric 
201fe6060f1SDimitry Andric void *dfsan_reallocarray(void *ptr, uptr nmemb, uptr size) {
202fe6060f1SDimitry Andric   if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {
203fe6060f1SDimitry Andric     errno = errno_ENOMEM;
204fe6060f1SDimitry Andric     if (AllocatorMayReturnNull())
205fe6060f1SDimitry Andric       return nullptr;
206fe6060f1SDimitry Andric     BufferedStackTrace stack;
207fe6060f1SDimitry Andric     ReportReallocArrayOverflow(nmemb, size, &stack);
208fe6060f1SDimitry Andric   }
209fe6060f1SDimitry Andric   return dfsan_realloc(ptr, nmemb * size);
210fe6060f1SDimitry Andric }
211fe6060f1SDimitry Andric 
212fe6060f1SDimitry Andric void *dfsan_valloc(uptr size) {
213fe6060f1SDimitry Andric   return SetErrnoOnNull(
214fe6060f1SDimitry Andric       DFsanAllocate(size, GetPageSizeCached(), false /*zeroise*/));
215fe6060f1SDimitry Andric }
216fe6060f1SDimitry Andric 
217fe6060f1SDimitry Andric void *dfsan_pvalloc(uptr size) {
218fe6060f1SDimitry Andric   uptr PageSize = GetPageSizeCached();
219fe6060f1SDimitry Andric   if (UNLIKELY(CheckForPvallocOverflow(size, PageSize))) {
220fe6060f1SDimitry Andric     errno = errno_ENOMEM;
221fe6060f1SDimitry Andric     if (AllocatorMayReturnNull())
222fe6060f1SDimitry Andric       return nullptr;
223fe6060f1SDimitry Andric     BufferedStackTrace stack;
224fe6060f1SDimitry Andric     ReportPvallocOverflow(size, &stack);
225fe6060f1SDimitry Andric   }
226fe6060f1SDimitry Andric   // pvalloc(0) should allocate one page.
227fe6060f1SDimitry Andric   size = size ? RoundUpTo(size, PageSize) : PageSize;
228fe6060f1SDimitry Andric   return SetErrnoOnNull(DFsanAllocate(size, PageSize, false /*zeroise*/));
229fe6060f1SDimitry Andric }
230fe6060f1SDimitry Andric 
231fe6060f1SDimitry Andric void *dfsan_aligned_alloc(uptr alignment, uptr size) {
232fe6060f1SDimitry Andric   if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(alignment, size))) {
233fe6060f1SDimitry Andric     errno = errno_EINVAL;
234fe6060f1SDimitry Andric     if (AllocatorMayReturnNull())
235fe6060f1SDimitry Andric       return nullptr;
236fe6060f1SDimitry Andric     BufferedStackTrace stack;
237fe6060f1SDimitry Andric     ReportInvalidAlignedAllocAlignment(size, alignment, &stack);
238fe6060f1SDimitry Andric   }
239fe6060f1SDimitry Andric   return SetErrnoOnNull(DFsanAllocate(size, alignment, false /*zeroise*/));
240fe6060f1SDimitry Andric }
241fe6060f1SDimitry Andric 
242fe6060f1SDimitry Andric void *dfsan_memalign(uptr alignment, uptr size) {
243fe6060f1SDimitry Andric   if (UNLIKELY(!IsPowerOfTwo(alignment))) {
244fe6060f1SDimitry Andric     errno = errno_EINVAL;
245fe6060f1SDimitry Andric     if (AllocatorMayReturnNull())
246fe6060f1SDimitry Andric       return nullptr;
247fe6060f1SDimitry Andric     BufferedStackTrace stack;
248fe6060f1SDimitry Andric     ReportInvalidAllocationAlignment(alignment, &stack);
249fe6060f1SDimitry Andric   }
250fe6060f1SDimitry Andric   return SetErrnoOnNull(DFsanAllocate(size, alignment, false /*zeroise*/));
251fe6060f1SDimitry Andric }
252fe6060f1SDimitry Andric 
253fe6060f1SDimitry Andric int dfsan_posix_memalign(void **memptr, uptr alignment, uptr size) {
254fe6060f1SDimitry Andric   if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) {
255fe6060f1SDimitry Andric     if (AllocatorMayReturnNull())
256fe6060f1SDimitry Andric       return errno_EINVAL;
257fe6060f1SDimitry Andric     BufferedStackTrace stack;
258fe6060f1SDimitry Andric     ReportInvalidPosixMemalignAlignment(alignment, &stack);
259fe6060f1SDimitry Andric   }
260fe6060f1SDimitry Andric   void *ptr = DFsanAllocate(size, alignment, false /*zeroise*/);
261fe6060f1SDimitry Andric   if (UNLIKELY(!ptr))
262fe6060f1SDimitry Andric     // OOM error is already taken care of by DFsanAllocate.
263fe6060f1SDimitry Andric     return errno_ENOMEM;
264fe6060f1SDimitry Andric   CHECK(IsAligned((uptr)ptr, alignment));
265fe6060f1SDimitry Andric   *memptr = ptr;
266fe6060f1SDimitry Andric   return 0;
267fe6060f1SDimitry Andric }
268fe6060f1SDimitry Andric 
269fe6060f1SDimitry Andric }  // namespace __dfsan
270fe6060f1SDimitry Andric 
271fe6060f1SDimitry Andric using namespace __dfsan;
272fe6060f1SDimitry Andric 
273fe6060f1SDimitry Andric uptr __sanitizer_get_current_allocated_bytes() {
274fe6060f1SDimitry Andric   uptr stats[AllocatorStatCount];
275fe6060f1SDimitry Andric   allocator.GetStats(stats);
276fe6060f1SDimitry Andric   return stats[AllocatorStatAllocated];
277fe6060f1SDimitry Andric }
278fe6060f1SDimitry Andric 
279fe6060f1SDimitry Andric uptr __sanitizer_get_heap_size() {
280fe6060f1SDimitry Andric   uptr stats[AllocatorStatCount];
281fe6060f1SDimitry Andric   allocator.GetStats(stats);
282fe6060f1SDimitry Andric   return stats[AllocatorStatMapped];
283fe6060f1SDimitry Andric }
284fe6060f1SDimitry Andric 
285fe6060f1SDimitry Andric uptr __sanitizer_get_free_bytes() { return 1; }
286fe6060f1SDimitry Andric 
287fe6060f1SDimitry Andric uptr __sanitizer_get_unmapped_bytes() { return 1; }
288fe6060f1SDimitry Andric 
289fe6060f1SDimitry Andric uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; }
290fe6060f1SDimitry Andric 
291fe6060f1SDimitry Andric int __sanitizer_get_ownership(const void *p) { return AllocationSize(p) != 0; }
292fe6060f1SDimitry Andric 
293fe6060f1SDimitry Andric uptr __sanitizer_get_allocated_size(const void *p) { return AllocationSize(p); }
294