1fe6060f1SDimitry Andric //===-- dfsan_allocator.cpp -------------------------- --------------------===// 2fe6060f1SDimitry Andric // 3fe6060f1SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4fe6060f1SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5fe6060f1SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6fe6060f1SDimitry Andric // 7fe6060f1SDimitry Andric //===----------------------------------------------------------------------===// 8fe6060f1SDimitry Andric // 9fe6060f1SDimitry Andric // This file is a part of DataflowSanitizer. 10fe6060f1SDimitry Andric // 11fe6060f1SDimitry Andric // DataflowSanitizer allocator. 12fe6060f1SDimitry Andric //===----------------------------------------------------------------------===// 13fe6060f1SDimitry Andric 14fe6060f1SDimitry Andric #include "dfsan_allocator.h" 15fe6060f1SDimitry Andric 16fe6060f1SDimitry Andric #include "dfsan.h" 17fe6060f1SDimitry Andric #include "dfsan_flags.h" 18fe6060f1SDimitry Andric #include "dfsan_thread.h" 19fe6060f1SDimitry Andric #include "sanitizer_common/sanitizer_allocator.h" 20fe6060f1SDimitry Andric #include "sanitizer_common/sanitizer_allocator_checks.h" 21fe6060f1SDimitry Andric #include "sanitizer_common/sanitizer_allocator_interface.h" 22fe6060f1SDimitry Andric #include "sanitizer_common/sanitizer_allocator_report.h" 23fe6060f1SDimitry Andric #include "sanitizer_common/sanitizer_errno.h" 24fe6060f1SDimitry Andric 25fe6060f1SDimitry Andric namespace __dfsan { 26fe6060f1SDimitry Andric 27fe6060f1SDimitry Andric struct Metadata { 28fe6060f1SDimitry Andric uptr requested_size; 29fe6060f1SDimitry Andric }; 30fe6060f1SDimitry Andric 31fe6060f1SDimitry Andric struct DFsanMapUnmapCallback { 32fe6060f1SDimitry Andric void OnMap(uptr p, uptr size) const { dfsan_set_label(0, (void *)p, size); } 3306c3fb27SDimitry Andric void OnMapSecondary(uptr p, uptr size, uptr user_begin, 3406c3fb27SDimitry Andric uptr user_size) const { 3506c3fb27SDimitry Andric OnMap(p, size); 3606c3fb27SDimitry Andric } 37fe6060f1SDimitry Andric void OnUnmap(uptr p, uptr size) const { dfsan_set_label(0, (void *)p, size); } 38fe6060f1SDimitry Andric }; 39fe6060f1SDimitry Andric 40*0fca6ea1SDimitry Andric // Note: to ensure that the allocator is compatible with the application memory 41*0fca6ea1SDimitry Andric // layout (especially with high-entropy ASLR), kSpaceBeg and kSpaceSize must be 42*0fca6ea1SDimitry Andric // duplicated as MappingDesc::ALLOCATOR in dfsan_platform.h. 43bdd1243dSDimitry Andric #if defined(__aarch64__) 44bdd1243dSDimitry Andric const uptr kAllocatorSpace = 0xE00000000000ULL; 45bdd1243dSDimitry Andric #else 46bdd1243dSDimitry Andric const uptr kAllocatorSpace = 0x700000000000ULL; 47bdd1243dSDimitry Andric #endif 48*0fca6ea1SDimitry Andric const uptr kMaxAllowedMallocSize = 1ULL << 40; 49fe6060f1SDimitry Andric 50fe6060f1SDimitry Andric struct AP64 { // Allocator64 parameters. Deliberately using a short name. 51fe6060f1SDimitry Andric static const uptr kSpaceBeg = kAllocatorSpace; 52fe6060f1SDimitry Andric static const uptr kSpaceSize = 0x40000000000; // 4T. 53fe6060f1SDimitry Andric static const uptr kMetadataSize = sizeof(Metadata); 54fe6060f1SDimitry Andric typedef DefaultSizeClassMap SizeClassMap; 55fe6060f1SDimitry Andric typedef DFsanMapUnmapCallback MapUnmapCallback; 56fe6060f1SDimitry Andric static const uptr kFlags = 0; 57fe6060f1SDimitry Andric using AddressSpaceView = LocalAddressSpaceView; 58fe6060f1SDimitry Andric }; 59fe6060f1SDimitry Andric 60fe6060f1SDimitry Andric typedef SizeClassAllocator64<AP64> PrimaryAllocator; 61fe6060f1SDimitry Andric 62fe6060f1SDimitry Andric typedef CombinedAllocator<PrimaryAllocator> Allocator; 63fe6060f1SDimitry Andric typedef Allocator::AllocatorCache AllocatorCache; 64fe6060f1SDimitry Andric 65fe6060f1SDimitry Andric static Allocator allocator; 66fe6060f1SDimitry Andric static AllocatorCache fallback_allocator_cache; 67fe6060f1SDimitry Andric static StaticSpinMutex fallback_mutex; 68fe6060f1SDimitry Andric 69fe6060f1SDimitry Andric static uptr max_malloc_size; 70fe6060f1SDimitry Andric 71fe6060f1SDimitry Andric void dfsan_allocator_init() { 72fe6060f1SDimitry Andric SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null); 73fe6060f1SDimitry Andric allocator.Init(common_flags()->allocator_release_to_os_interval_ms); 74fe6060f1SDimitry Andric if (common_flags()->max_allocation_size_mb) 75fe6060f1SDimitry Andric max_malloc_size = Min(common_flags()->max_allocation_size_mb << 20, 76fe6060f1SDimitry Andric kMaxAllowedMallocSize); 77fe6060f1SDimitry Andric else 78fe6060f1SDimitry Andric max_malloc_size = kMaxAllowedMallocSize; 79fe6060f1SDimitry Andric } 80fe6060f1SDimitry Andric 81fe6060f1SDimitry Andric AllocatorCache *GetAllocatorCache(DFsanThreadLocalMallocStorage *ms) { 82fe6060f1SDimitry Andric CHECK(ms); 83fe6060f1SDimitry Andric CHECK_LE(sizeof(AllocatorCache), sizeof(ms->allocator_cache)); 84fe6060f1SDimitry Andric return reinterpret_cast<AllocatorCache *>(ms->allocator_cache); 85fe6060f1SDimitry Andric } 86fe6060f1SDimitry Andric 87fe6060f1SDimitry Andric void DFsanThreadLocalMallocStorage::CommitBack() { 88fe6060f1SDimitry Andric allocator.SwallowCache(GetAllocatorCache(this)); 89fe6060f1SDimitry Andric } 90fe6060f1SDimitry Andric 91fe6060f1SDimitry Andric static void *DFsanAllocate(uptr size, uptr alignment, bool zeroise) { 92fe6060f1SDimitry Andric if (size > max_malloc_size) { 93fe6060f1SDimitry Andric if (AllocatorMayReturnNull()) { 94fe6060f1SDimitry Andric Report("WARNING: DataflowSanitizer failed to allocate 0x%zx bytes\n", 95fe6060f1SDimitry Andric size); 96fe6060f1SDimitry Andric return nullptr; 97fe6060f1SDimitry Andric } 98fe6060f1SDimitry Andric BufferedStackTrace stack; 99fe6060f1SDimitry Andric ReportAllocationSizeTooBig(size, max_malloc_size, &stack); 100fe6060f1SDimitry Andric } 1010eae32dcSDimitry Andric if (UNLIKELY(IsRssLimitExceeded())) { 1020eae32dcSDimitry Andric if (AllocatorMayReturnNull()) 1030eae32dcSDimitry Andric return nullptr; 1040eae32dcSDimitry Andric BufferedStackTrace stack; 1050eae32dcSDimitry Andric ReportRssLimitExceeded(&stack); 1060eae32dcSDimitry Andric } 107fe6060f1SDimitry Andric DFsanThread *t = GetCurrentThread(); 108fe6060f1SDimitry Andric void *allocated; 109fe6060f1SDimitry Andric if (t) { 110fe6060f1SDimitry Andric AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage()); 111fe6060f1SDimitry Andric allocated = allocator.Allocate(cache, size, alignment); 112fe6060f1SDimitry Andric } else { 113fe6060f1SDimitry Andric SpinMutexLock l(&fallback_mutex); 114fe6060f1SDimitry Andric AllocatorCache *cache = &fallback_allocator_cache; 115fe6060f1SDimitry Andric allocated = allocator.Allocate(cache, size, alignment); 116fe6060f1SDimitry Andric } 117fe6060f1SDimitry Andric if (UNLIKELY(!allocated)) { 118fe6060f1SDimitry Andric SetAllocatorOutOfMemory(); 119fe6060f1SDimitry Andric if (AllocatorMayReturnNull()) 120fe6060f1SDimitry Andric return nullptr; 121fe6060f1SDimitry Andric BufferedStackTrace stack; 122fe6060f1SDimitry Andric ReportOutOfMemory(size, &stack); 123fe6060f1SDimitry Andric } 124fe6060f1SDimitry Andric Metadata *meta = 125fe6060f1SDimitry Andric reinterpret_cast<Metadata *>(allocator.GetMetaData(allocated)); 126fe6060f1SDimitry Andric meta->requested_size = size; 127fe6060f1SDimitry Andric if (zeroise) { 128fe6060f1SDimitry Andric internal_memset(allocated, 0, size); 129fe6060f1SDimitry Andric dfsan_set_label(0, allocated, size); 130fe6060f1SDimitry Andric } else if (flags().zero_in_malloc) { 131fe6060f1SDimitry Andric dfsan_set_label(0, allocated, size); 132fe6060f1SDimitry Andric } 133fe6060f1SDimitry Andric return allocated; 134fe6060f1SDimitry Andric } 135fe6060f1SDimitry Andric 136fe6060f1SDimitry Andric void dfsan_deallocate(void *p) { 137fe6060f1SDimitry Andric CHECK(p); 138fe6060f1SDimitry Andric Metadata *meta = reinterpret_cast<Metadata *>(allocator.GetMetaData(p)); 139fe6060f1SDimitry Andric uptr size = meta->requested_size; 140fe6060f1SDimitry Andric meta->requested_size = 0; 141fe6060f1SDimitry Andric if (flags().zero_in_free) 142fe6060f1SDimitry Andric dfsan_set_label(0, p, size); 143fe6060f1SDimitry Andric DFsanThread *t = GetCurrentThread(); 144fe6060f1SDimitry Andric if (t) { 145fe6060f1SDimitry Andric AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage()); 146fe6060f1SDimitry Andric allocator.Deallocate(cache, p); 147fe6060f1SDimitry Andric } else { 148fe6060f1SDimitry Andric SpinMutexLock l(&fallback_mutex); 149fe6060f1SDimitry Andric AllocatorCache *cache = &fallback_allocator_cache; 150fe6060f1SDimitry Andric allocator.Deallocate(cache, p); 151fe6060f1SDimitry Andric } 152fe6060f1SDimitry Andric } 153fe6060f1SDimitry Andric 154fe6060f1SDimitry Andric void *DFsanReallocate(void *old_p, uptr new_size, uptr alignment) { 155fe6060f1SDimitry Andric Metadata *meta = reinterpret_cast<Metadata *>(allocator.GetMetaData(old_p)); 156fe6060f1SDimitry Andric uptr old_size = meta->requested_size; 157fe6060f1SDimitry Andric uptr actually_allocated_size = allocator.GetActuallyAllocatedSize(old_p); 158fe6060f1SDimitry Andric if (new_size <= actually_allocated_size) { 159fe6060f1SDimitry Andric // We are not reallocating here. 160fe6060f1SDimitry Andric meta->requested_size = new_size; 161fe6060f1SDimitry Andric if (new_size > old_size && flags().zero_in_malloc) 162fe6060f1SDimitry Andric dfsan_set_label(0, (char *)old_p + old_size, new_size - old_size); 163fe6060f1SDimitry Andric return old_p; 164fe6060f1SDimitry Andric } 165fe6060f1SDimitry Andric uptr memcpy_size = Min(new_size, old_size); 166fe6060f1SDimitry Andric void *new_p = DFsanAllocate(new_size, alignment, false /*zeroise*/); 167fe6060f1SDimitry Andric if (new_p) { 168fe6060f1SDimitry Andric dfsan_copy_memory(new_p, old_p, memcpy_size); 169fe6060f1SDimitry Andric dfsan_deallocate(old_p); 170fe6060f1SDimitry Andric } 171fe6060f1SDimitry Andric return new_p; 172fe6060f1SDimitry Andric } 173fe6060f1SDimitry Andric 174fe6060f1SDimitry Andric void *DFsanCalloc(uptr nmemb, uptr size) { 175fe6060f1SDimitry Andric if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) { 176fe6060f1SDimitry Andric if (AllocatorMayReturnNull()) 177fe6060f1SDimitry Andric return nullptr; 178fe6060f1SDimitry Andric BufferedStackTrace stack; 179fe6060f1SDimitry Andric ReportCallocOverflow(nmemb, size, &stack); 180fe6060f1SDimitry Andric } 181fe6060f1SDimitry Andric return DFsanAllocate(nmemb * size, sizeof(u64), true /*zeroise*/); 182fe6060f1SDimitry Andric } 183fe6060f1SDimitry Andric 18406c3fb27SDimitry Andric static const void *AllocationBegin(const void *p) { 18506c3fb27SDimitry Andric if (!p) 18606c3fb27SDimitry Andric return nullptr; 18706c3fb27SDimitry Andric void *beg = allocator.GetBlockBegin(p); 18806c3fb27SDimitry Andric if (!beg) 18906c3fb27SDimitry Andric return nullptr; 19006c3fb27SDimitry Andric Metadata *b = (Metadata *)allocator.GetMetaData(beg); 19106c3fb27SDimitry Andric if (!b) 19206c3fb27SDimitry Andric return nullptr; 19306c3fb27SDimitry Andric if (b->requested_size == 0) 19406c3fb27SDimitry Andric return nullptr; 19506c3fb27SDimitry Andric return (const void *)beg; 19606c3fb27SDimitry Andric } 19706c3fb27SDimitry Andric 198fe6060f1SDimitry Andric static uptr AllocationSize(const void *p) { 199fe6060f1SDimitry Andric if (!p) 200fe6060f1SDimitry Andric return 0; 201fe6060f1SDimitry Andric const void *beg = allocator.GetBlockBegin(p); 202fe6060f1SDimitry Andric if (beg != p) 203fe6060f1SDimitry Andric return 0; 204fe6060f1SDimitry Andric Metadata *b = (Metadata *)allocator.GetMetaData(p); 205fe6060f1SDimitry Andric return b->requested_size; 206fe6060f1SDimitry Andric } 207fe6060f1SDimitry Andric 20806c3fb27SDimitry Andric static uptr AllocationSizeFast(const void *p) { 20906c3fb27SDimitry Andric return reinterpret_cast<Metadata *>(allocator.GetMetaData(p))->requested_size; 21006c3fb27SDimitry Andric } 21106c3fb27SDimitry Andric 212fe6060f1SDimitry Andric void *dfsan_malloc(uptr size) { 213fe6060f1SDimitry Andric return SetErrnoOnNull(DFsanAllocate(size, sizeof(u64), false /*zeroise*/)); 214fe6060f1SDimitry Andric } 215fe6060f1SDimitry Andric 216fe6060f1SDimitry Andric void *dfsan_calloc(uptr nmemb, uptr size) { 217fe6060f1SDimitry Andric return SetErrnoOnNull(DFsanCalloc(nmemb, size)); 218fe6060f1SDimitry Andric } 219fe6060f1SDimitry Andric 220fe6060f1SDimitry Andric void *dfsan_realloc(void *ptr, uptr size) { 221fe6060f1SDimitry Andric if (!ptr) 222fe6060f1SDimitry Andric return SetErrnoOnNull(DFsanAllocate(size, sizeof(u64), false /*zeroise*/)); 223fe6060f1SDimitry Andric if (size == 0) { 224fe6060f1SDimitry Andric dfsan_deallocate(ptr); 225fe6060f1SDimitry Andric return nullptr; 226fe6060f1SDimitry Andric } 227fe6060f1SDimitry Andric return SetErrnoOnNull(DFsanReallocate(ptr, size, sizeof(u64))); 228fe6060f1SDimitry Andric } 229fe6060f1SDimitry Andric 230fe6060f1SDimitry Andric void *dfsan_reallocarray(void *ptr, uptr nmemb, uptr size) { 231fe6060f1SDimitry Andric if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) { 232fe6060f1SDimitry Andric errno = errno_ENOMEM; 233fe6060f1SDimitry Andric if (AllocatorMayReturnNull()) 234fe6060f1SDimitry Andric return nullptr; 235fe6060f1SDimitry Andric BufferedStackTrace stack; 236fe6060f1SDimitry Andric ReportReallocArrayOverflow(nmemb, size, &stack); 237fe6060f1SDimitry Andric } 238fe6060f1SDimitry Andric return dfsan_realloc(ptr, nmemb * size); 239fe6060f1SDimitry Andric } 240fe6060f1SDimitry Andric 241fe6060f1SDimitry Andric void *dfsan_valloc(uptr size) { 242fe6060f1SDimitry Andric return SetErrnoOnNull( 243fe6060f1SDimitry Andric DFsanAllocate(size, GetPageSizeCached(), false /*zeroise*/)); 244fe6060f1SDimitry Andric } 245fe6060f1SDimitry Andric 246fe6060f1SDimitry Andric void *dfsan_pvalloc(uptr size) { 247fe6060f1SDimitry Andric uptr PageSize = GetPageSizeCached(); 248fe6060f1SDimitry Andric if (UNLIKELY(CheckForPvallocOverflow(size, PageSize))) { 249fe6060f1SDimitry Andric errno = errno_ENOMEM; 250fe6060f1SDimitry Andric if (AllocatorMayReturnNull()) 251fe6060f1SDimitry Andric return nullptr; 252fe6060f1SDimitry Andric BufferedStackTrace stack; 253fe6060f1SDimitry Andric ReportPvallocOverflow(size, &stack); 254fe6060f1SDimitry Andric } 255fe6060f1SDimitry Andric // pvalloc(0) should allocate one page. 256fe6060f1SDimitry Andric size = size ? RoundUpTo(size, PageSize) : PageSize; 257fe6060f1SDimitry Andric return SetErrnoOnNull(DFsanAllocate(size, PageSize, false /*zeroise*/)); 258fe6060f1SDimitry Andric } 259fe6060f1SDimitry Andric 260fe6060f1SDimitry Andric void *dfsan_aligned_alloc(uptr alignment, uptr size) { 261fe6060f1SDimitry Andric if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(alignment, size))) { 262fe6060f1SDimitry Andric errno = errno_EINVAL; 263fe6060f1SDimitry Andric if (AllocatorMayReturnNull()) 264fe6060f1SDimitry Andric return nullptr; 265fe6060f1SDimitry Andric BufferedStackTrace stack; 266fe6060f1SDimitry Andric ReportInvalidAlignedAllocAlignment(size, alignment, &stack); 267fe6060f1SDimitry Andric } 268fe6060f1SDimitry Andric return SetErrnoOnNull(DFsanAllocate(size, alignment, false /*zeroise*/)); 269fe6060f1SDimitry Andric } 270fe6060f1SDimitry Andric 271fe6060f1SDimitry Andric void *dfsan_memalign(uptr alignment, uptr size) { 272fe6060f1SDimitry Andric if (UNLIKELY(!IsPowerOfTwo(alignment))) { 273fe6060f1SDimitry Andric errno = errno_EINVAL; 274fe6060f1SDimitry Andric if (AllocatorMayReturnNull()) 275fe6060f1SDimitry Andric return nullptr; 276fe6060f1SDimitry Andric BufferedStackTrace stack; 277fe6060f1SDimitry Andric ReportInvalidAllocationAlignment(alignment, &stack); 278fe6060f1SDimitry Andric } 279fe6060f1SDimitry Andric return SetErrnoOnNull(DFsanAllocate(size, alignment, false /*zeroise*/)); 280fe6060f1SDimitry Andric } 281fe6060f1SDimitry Andric 282fe6060f1SDimitry Andric int dfsan_posix_memalign(void **memptr, uptr alignment, uptr size) { 283fe6060f1SDimitry Andric if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) { 284fe6060f1SDimitry Andric if (AllocatorMayReturnNull()) 285fe6060f1SDimitry Andric return errno_EINVAL; 286fe6060f1SDimitry Andric BufferedStackTrace stack; 287fe6060f1SDimitry Andric ReportInvalidPosixMemalignAlignment(alignment, &stack); 288fe6060f1SDimitry Andric } 289fe6060f1SDimitry Andric void *ptr = DFsanAllocate(size, alignment, false /*zeroise*/); 290fe6060f1SDimitry Andric if (UNLIKELY(!ptr)) 291fe6060f1SDimitry Andric // OOM error is already taken care of by DFsanAllocate. 292fe6060f1SDimitry Andric return errno_ENOMEM; 293fe6060f1SDimitry Andric CHECK(IsAligned((uptr)ptr, alignment)); 294fe6060f1SDimitry Andric *memptr = ptr; 295fe6060f1SDimitry Andric return 0; 296fe6060f1SDimitry Andric } 297fe6060f1SDimitry Andric 298fe6060f1SDimitry Andric } // namespace __dfsan 299fe6060f1SDimitry Andric 300fe6060f1SDimitry Andric using namespace __dfsan; 301fe6060f1SDimitry Andric 302fe6060f1SDimitry Andric uptr __sanitizer_get_current_allocated_bytes() { 303fe6060f1SDimitry Andric uptr stats[AllocatorStatCount]; 304fe6060f1SDimitry Andric allocator.GetStats(stats); 305fe6060f1SDimitry Andric return stats[AllocatorStatAllocated]; 306fe6060f1SDimitry Andric } 307fe6060f1SDimitry Andric 308fe6060f1SDimitry Andric uptr __sanitizer_get_heap_size() { 309fe6060f1SDimitry Andric uptr stats[AllocatorStatCount]; 310fe6060f1SDimitry Andric allocator.GetStats(stats); 311fe6060f1SDimitry Andric return stats[AllocatorStatMapped]; 312fe6060f1SDimitry Andric } 313fe6060f1SDimitry Andric 314fe6060f1SDimitry Andric uptr __sanitizer_get_free_bytes() { return 1; } 315fe6060f1SDimitry Andric 316fe6060f1SDimitry Andric uptr __sanitizer_get_unmapped_bytes() { return 1; } 317fe6060f1SDimitry Andric 318fe6060f1SDimitry Andric uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; } 319fe6060f1SDimitry Andric 320fe6060f1SDimitry Andric int __sanitizer_get_ownership(const void *p) { return AllocationSize(p) != 0; } 321fe6060f1SDimitry Andric 32206c3fb27SDimitry Andric const void *__sanitizer_get_allocated_begin(const void *p) { 32306c3fb27SDimitry Andric return AllocationBegin(p); 32406c3fb27SDimitry Andric } 32506c3fb27SDimitry Andric 326fe6060f1SDimitry Andric uptr __sanitizer_get_allocated_size(const void *p) { return AllocationSize(p); } 32706c3fb27SDimitry Andric 32806c3fb27SDimitry Andric uptr __sanitizer_get_allocated_size_fast(const void *p) { 32906c3fb27SDimitry Andric DCHECK_EQ(p, __sanitizer_get_allocated_begin(p)); 33006c3fb27SDimitry Andric uptr ret = AllocationSizeFast(p); 33106c3fb27SDimitry Andric DCHECK_EQ(ret, __sanitizer_get_allocated_size(p)); 33206c3fb27SDimitry Andric return ret; 33306c3fb27SDimitry Andric } 334