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