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