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