1 //===-- dfsan_allocator.cpp -------------------------- --------------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This file is a part of DataflowSanitizer.
11 // DataflowSanitizer allocator.
12 //===----------------------------------------------------------------------===//
14 #include "dfsan_allocator.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"
31 struct DFsanMapUnmapCallback
{
32 void OnMap(uptr p
, uptr size
) const { dfsan_set_label(0, (void *)p
, size
); }
33 void OnMapSecondary(uptr p
, uptr size
, uptr user_begin
,
34 uptr user_size
) const {
37 void OnUnmap(uptr p
, uptr size
) const { dfsan_set_label(0, (void *)p
, size
); }
40 #if defined(__aarch64__)
41 const uptr kAllocatorSpace
= 0xE00000000000ULL
;
43 const uptr kAllocatorSpace
= 0x700000000000ULL
;
45 const uptr kMaxAllowedMallocSize
= 8UL << 30;
47 struct AP64
{ // Allocator64 parameters. Deliberately using a short name.
48 static const uptr kSpaceBeg
= kAllocatorSpace
;
49 static const uptr kSpaceSize
= 0x40000000000; // 4T.
50 static const uptr kMetadataSize
= sizeof(Metadata
);
51 typedef DefaultSizeClassMap SizeClassMap
;
52 typedef DFsanMapUnmapCallback MapUnmapCallback
;
53 static const uptr kFlags
= 0;
54 using AddressSpaceView
= LocalAddressSpaceView
;
57 typedef SizeClassAllocator64
<AP64
> PrimaryAllocator
;
59 typedef CombinedAllocator
<PrimaryAllocator
> Allocator
;
60 typedef Allocator::AllocatorCache AllocatorCache
;
62 static Allocator allocator
;
63 static AllocatorCache fallback_allocator_cache
;
64 static StaticSpinMutex fallback_mutex
;
66 static uptr max_malloc_size
;
68 void dfsan_allocator_init() {
69 SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null
);
70 allocator
.Init(common_flags()->allocator_release_to_os_interval_ms
);
71 if (common_flags()->max_allocation_size_mb
)
72 max_malloc_size
= Min(common_flags()->max_allocation_size_mb
<< 20,
73 kMaxAllowedMallocSize
);
75 max_malloc_size
= kMaxAllowedMallocSize
;
78 AllocatorCache
*GetAllocatorCache(DFsanThreadLocalMallocStorage
*ms
) {
80 CHECK_LE(sizeof(AllocatorCache
), sizeof(ms
->allocator_cache
));
81 return reinterpret_cast<AllocatorCache
*>(ms
->allocator_cache
);
84 void DFsanThreadLocalMallocStorage::CommitBack() {
85 allocator
.SwallowCache(GetAllocatorCache(this));
88 static void *DFsanAllocate(uptr size
, uptr alignment
, bool zeroise
) {
89 if (size
> max_malloc_size
) {
90 if (AllocatorMayReturnNull()) {
91 Report("WARNING: DataflowSanitizer failed to allocate 0x%zx bytes\n",
95 BufferedStackTrace stack
;
96 ReportAllocationSizeTooBig(size
, max_malloc_size
, &stack
);
98 if (UNLIKELY(IsRssLimitExceeded())) {
99 if (AllocatorMayReturnNull())
101 BufferedStackTrace stack
;
102 ReportRssLimitExceeded(&stack
);
104 DFsanThread
*t
= GetCurrentThread();
107 AllocatorCache
*cache
= GetAllocatorCache(&t
->malloc_storage());
108 allocated
= allocator
.Allocate(cache
, size
, alignment
);
110 SpinMutexLock
l(&fallback_mutex
);
111 AllocatorCache
*cache
= &fallback_allocator_cache
;
112 allocated
= allocator
.Allocate(cache
, size
, alignment
);
114 if (UNLIKELY(!allocated
)) {
115 SetAllocatorOutOfMemory();
116 if (AllocatorMayReturnNull())
118 BufferedStackTrace stack
;
119 ReportOutOfMemory(size
, &stack
);
122 reinterpret_cast<Metadata
*>(allocator
.GetMetaData(allocated
));
123 meta
->requested_size
= size
;
125 internal_memset(allocated
, 0, size
);
126 dfsan_set_label(0, allocated
, size
);
127 } else if (flags().zero_in_malloc
) {
128 dfsan_set_label(0, allocated
, size
);
133 void dfsan_deallocate(void *p
) {
135 Metadata
*meta
= reinterpret_cast<Metadata
*>(allocator
.GetMetaData(p
));
136 uptr size
= meta
->requested_size
;
137 meta
->requested_size
= 0;
138 if (flags().zero_in_free
)
139 dfsan_set_label(0, p
, size
);
140 DFsanThread
*t
= GetCurrentThread();
142 AllocatorCache
*cache
= GetAllocatorCache(&t
->malloc_storage());
143 allocator
.Deallocate(cache
, p
);
145 SpinMutexLock
l(&fallback_mutex
);
146 AllocatorCache
*cache
= &fallback_allocator_cache
;
147 allocator
.Deallocate(cache
, p
);
151 void *DFsanReallocate(void *old_p
, uptr new_size
, uptr alignment
) {
152 Metadata
*meta
= reinterpret_cast<Metadata
*>(allocator
.GetMetaData(old_p
));
153 uptr old_size
= meta
->requested_size
;
154 uptr actually_allocated_size
= allocator
.GetActuallyAllocatedSize(old_p
);
155 if (new_size
<= actually_allocated_size
) {
156 // We are not reallocating here.
157 meta
->requested_size
= new_size
;
158 if (new_size
> old_size
&& flags().zero_in_malloc
)
159 dfsan_set_label(0, (char *)old_p
+ old_size
, new_size
- old_size
);
162 uptr memcpy_size
= Min(new_size
, old_size
);
163 void *new_p
= DFsanAllocate(new_size
, alignment
, false /*zeroise*/);
165 dfsan_copy_memory(new_p
, old_p
, memcpy_size
);
166 dfsan_deallocate(old_p
);
171 void *DFsanCalloc(uptr nmemb
, uptr size
) {
172 if (UNLIKELY(CheckForCallocOverflow(size
, nmemb
))) {
173 if (AllocatorMayReturnNull())
175 BufferedStackTrace stack
;
176 ReportCallocOverflow(nmemb
, size
, &stack
);
178 return DFsanAllocate(nmemb
* size
, sizeof(u64
), true /*zeroise*/);
181 static const void *AllocationBegin(const void *p
) {
184 void *beg
= allocator
.GetBlockBegin(p
);
187 Metadata
*b
= (Metadata
*)allocator
.GetMetaData(beg
);
190 if (b
->requested_size
== 0)
192 return (const void *)beg
;
195 static uptr
AllocationSize(const void *p
) {
198 const void *beg
= allocator
.GetBlockBegin(p
);
201 Metadata
*b
= (Metadata
*)allocator
.GetMetaData(p
);
202 return b
->requested_size
;
205 static uptr
AllocationSizeFast(const void *p
) {
206 return reinterpret_cast<Metadata
*>(allocator
.GetMetaData(p
))->requested_size
;
209 void *dfsan_malloc(uptr size
) {
210 return SetErrnoOnNull(DFsanAllocate(size
, sizeof(u64
), false /*zeroise*/));
213 void *dfsan_calloc(uptr nmemb
, uptr size
) {
214 return SetErrnoOnNull(DFsanCalloc(nmemb
, size
));
217 void *dfsan_realloc(void *ptr
, uptr size
) {
219 return SetErrnoOnNull(DFsanAllocate(size
, sizeof(u64
), false /*zeroise*/));
221 dfsan_deallocate(ptr
);
224 return SetErrnoOnNull(DFsanReallocate(ptr
, size
, sizeof(u64
)));
227 void *dfsan_reallocarray(void *ptr
, uptr nmemb
, uptr size
) {
228 if (UNLIKELY(CheckForCallocOverflow(size
, nmemb
))) {
229 errno
= errno_ENOMEM
;
230 if (AllocatorMayReturnNull())
232 BufferedStackTrace stack
;
233 ReportReallocArrayOverflow(nmemb
, size
, &stack
);
235 return dfsan_realloc(ptr
, nmemb
* size
);
238 void *dfsan_valloc(uptr size
) {
239 return SetErrnoOnNull(
240 DFsanAllocate(size
, GetPageSizeCached(), false /*zeroise*/));
243 void *dfsan_pvalloc(uptr size
) {
244 uptr PageSize
= GetPageSizeCached();
245 if (UNLIKELY(CheckForPvallocOverflow(size
, PageSize
))) {
246 errno
= errno_ENOMEM
;
247 if (AllocatorMayReturnNull())
249 BufferedStackTrace stack
;
250 ReportPvallocOverflow(size
, &stack
);
252 // pvalloc(0) should allocate one page.
253 size
= size
? RoundUpTo(size
, PageSize
) : PageSize
;
254 return SetErrnoOnNull(DFsanAllocate(size
, PageSize
, false /*zeroise*/));
257 void *dfsan_aligned_alloc(uptr alignment
, uptr size
) {
258 if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(alignment
, size
))) {
259 errno
= errno_EINVAL
;
260 if (AllocatorMayReturnNull())
262 BufferedStackTrace stack
;
263 ReportInvalidAlignedAllocAlignment(size
, alignment
, &stack
);
265 return SetErrnoOnNull(DFsanAllocate(size
, alignment
, false /*zeroise*/));
268 void *dfsan_memalign(uptr alignment
, uptr size
) {
269 if (UNLIKELY(!IsPowerOfTwo(alignment
))) {
270 errno
= errno_EINVAL
;
271 if (AllocatorMayReturnNull())
273 BufferedStackTrace stack
;
274 ReportInvalidAllocationAlignment(alignment
, &stack
);
276 return SetErrnoOnNull(DFsanAllocate(size
, alignment
, false /*zeroise*/));
279 int dfsan_posix_memalign(void **memptr
, uptr alignment
, uptr size
) {
280 if (UNLIKELY(!CheckPosixMemalignAlignment(alignment
))) {
281 if (AllocatorMayReturnNull())
283 BufferedStackTrace stack
;
284 ReportInvalidPosixMemalignAlignment(alignment
, &stack
);
286 void *ptr
= DFsanAllocate(size
, alignment
, false /*zeroise*/);
288 // OOM error is already taken care of by DFsanAllocate.
290 CHECK(IsAligned((uptr
)ptr
, alignment
));
295 } // namespace __dfsan
297 using namespace __dfsan
;
299 uptr
__sanitizer_get_current_allocated_bytes() {
300 uptr stats
[AllocatorStatCount
];
301 allocator
.GetStats(stats
);
302 return stats
[AllocatorStatAllocated
];
305 uptr
__sanitizer_get_heap_size() {
306 uptr stats
[AllocatorStatCount
];
307 allocator
.GetStats(stats
);
308 return stats
[AllocatorStatMapped
];
311 uptr
__sanitizer_get_free_bytes() { return 1; }
313 uptr
__sanitizer_get_unmapped_bytes() { return 1; }
315 uptr
__sanitizer_get_estimated_allocated_size(uptr size
) { return size
; }
317 int __sanitizer_get_ownership(const void *p
) { return AllocationSize(p
) != 0; }
319 const void *__sanitizer_get_allocated_begin(const void *p
) {
320 return AllocationBegin(p
);
323 uptr
__sanitizer_get_allocated_size(const void *p
) { return AllocationSize(p
); }
325 uptr
__sanitizer_get_allocated_size_fast(const void *p
) {
326 DCHECK_EQ(p
, __sanitizer_get_allocated_begin(p
));
327 uptr ret
= AllocationSizeFast(p
);
328 DCHECK_EQ(ret
, __sanitizer_get_allocated_size(p
));