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