Path: blob/main/contrib/llvm-project/compiler-rt/lib/dfsan/dfsan_allocator.cpp
35233 views
//===-- dfsan_allocator.cpp -------------------------- --------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file is a part of DataflowSanitizer.9//10// DataflowSanitizer allocator.11//===----------------------------------------------------------------------===//1213#include "dfsan_allocator.h"1415#include "dfsan.h"16#include "dfsan_flags.h"17#include "dfsan_thread.h"18#include "sanitizer_common/sanitizer_allocator.h"19#include "sanitizer_common/sanitizer_allocator_checks.h"20#include "sanitizer_common/sanitizer_allocator_interface.h"21#include "sanitizer_common/sanitizer_allocator_report.h"22#include "sanitizer_common/sanitizer_errno.h"2324namespace __dfsan {2526struct Metadata {27uptr requested_size;28};2930struct DFsanMapUnmapCallback {31void OnMap(uptr p, uptr size) const { dfsan_set_label(0, (void *)p, size); }32void OnMapSecondary(uptr p, uptr size, uptr user_begin,33uptr user_size) const {34OnMap(p, size);35}36void OnUnmap(uptr p, uptr size) const { dfsan_set_label(0, (void *)p, size); }37};3839// Note: to ensure that the allocator is compatible with the application memory40// layout (especially with high-entropy ASLR), kSpaceBeg and kSpaceSize must be41// duplicated as MappingDesc::ALLOCATOR in dfsan_platform.h.42#if defined(__aarch64__)43const uptr kAllocatorSpace = 0xE00000000000ULL;44#else45const uptr kAllocatorSpace = 0x700000000000ULL;46#endif47const uptr kMaxAllowedMallocSize = 1ULL << 40;4849struct AP64 { // Allocator64 parameters. Deliberately using a short name.50static const uptr kSpaceBeg = kAllocatorSpace;51static const uptr kSpaceSize = 0x40000000000; // 4T.52static const uptr kMetadataSize = sizeof(Metadata);53typedef DefaultSizeClassMap SizeClassMap;54typedef DFsanMapUnmapCallback MapUnmapCallback;55static const uptr kFlags = 0;56using AddressSpaceView = LocalAddressSpaceView;57};5859typedef SizeClassAllocator64<AP64> PrimaryAllocator;6061typedef CombinedAllocator<PrimaryAllocator> Allocator;62typedef Allocator::AllocatorCache AllocatorCache;6364static Allocator allocator;65static AllocatorCache fallback_allocator_cache;66static StaticSpinMutex fallback_mutex;6768static uptr max_malloc_size;6970void dfsan_allocator_init() {71SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null);72allocator.Init(common_flags()->allocator_release_to_os_interval_ms);73if (common_flags()->max_allocation_size_mb)74max_malloc_size = Min(common_flags()->max_allocation_size_mb << 20,75kMaxAllowedMallocSize);76else77max_malloc_size = kMaxAllowedMallocSize;78}7980AllocatorCache *GetAllocatorCache(DFsanThreadLocalMallocStorage *ms) {81CHECK(ms);82CHECK_LE(sizeof(AllocatorCache), sizeof(ms->allocator_cache));83return reinterpret_cast<AllocatorCache *>(ms->allocator_cache);84}8586void DFsanThreadLocalMallocStorage::CommitBack() {87allocator.SwallowCache(GetAllocatorCache(this));88}8990static void *DFsanAllocate(uptr size, uptr alignment, bool zeroise) {91if (size > max_malloc_size) {92if (AllocatorMayReturnNull()) {93Report("WARNING: DataflowSanitizer failed to allocate 0x%zx bytes\n",94size);95return nullptr;96}97BufferedStackTrace stack;98ReportAllocationSizeTooBig(size, max_malloc_size, &stack);99}100if (UNLIKELY(IsRssLimitExceeded())) {101if (AllocatorMayReturnNull())102return nullptr;103BufferedStackTrace stack;104ReportRssLimitExceeded(&stack);105}106DFsanThread *t = GetCurrentThread();107void *allocated;108if (t) {109AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage());110allocated = allocator.Allocate(cache, size, alignment);111} else {112SpinMutexLock l(&fallback_mutex);113AllocatorCache *cache = &fallback_allocator_cache;114allocated = allocator.Allocate(cache, size, alignment);115}116if (UNLIKELY(!allocated)) {117SetAllocatorOutOfMemory();118if (AllocatorMayReturnNull())119return nullptr;120BufferedStackTrace stack;121ReportOutOfMemory(size, &stack);122}123Metadata *meta =124reinterpret_cast<Metadata *>(allocator.GetMetaData(allocated));125meta->requested_size = size;126if (zeroise) {127internal_memset(allocated, 0, size);128dfsan_set_label(0, allocated, size);129} else if (flags().zero_in_malloc) {130dfsan_set_label(0, allocated, size);131}132return allocated;133}134135void dfsan_deallocate(void *p) {136CHECK(p);137Metadata *meta = reinterpret_cast<Metadata *>(allocator.GetMetaData(p));138uptr size = meta->requested_size;139meta->requested_size = 0;140if (flags().zero_in_free)141dfsan_set_label(0, p, size);142DFsanThread *t = GetCurrentThread();143if (t) {144AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage());145allocator.Deallocate(cache, p);146} else {147SpinMutexLock l(&fallback_mutex);148AllocatorCache *cache = &fallback_allocator_cache;149allocator.Deallocate(cache, p);150}151}152153void *DFsanReallocate(void *old_p, uptr new_size, uptr alignment) {154Metadata *meta = reinterpret_cast<Metadata *>(allocator.GetMetaData(old_p));155uptr old_size = meta->requested_size;156uptr actually_allocated_size = allocator.GetActuallyAllocatedSize(old_p);157if (new_size <= actually_allocated_size) {158// We are not reallocating here.159meta->requested_size = new_size;160if (new_size > old_size && flags().zero_in_malloc)161dfsan_set_label(0, (char *)old_p + old_size, new_size - old_size);162return old_p;163}164uptr memcpy_size = Min(new_size, old_size);165void *new_p = DFsanAllocate(new_size, alignment, false /*zeroise*/);166if (new_p) {167dfsan_copy_memory(new_p, old_p, memcpy_size);168dfsan_deallocate(old_p);169}170return new_p;171}172173void *DFsanCalloc(uptr nmemb, uptr size) {174if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {175if (AllocatorMayReturnNull())176return nullptr;177BufferedStackTrace stack;178ReportCallocOverflow(nmemb, size, &stack);179}180return DFsanAllocate(nmemb * size, sizeof(u64), true /*zeroise*/);181}182183static const void *AllocationBegin(const void *p) {184if (!p)185return nullptr;186void *beg = allocator.GetBlockBegin(p);187if (!beg)188return nullptr;189Metadata *b = (Metadata *)allocator.GetMetaData(beg);190if (!b)191return nullptr;192if (b->requested_size == 0)193return nullptr;194return (const void *)beg;195}196197static uptr AllocationSize(const void *p) {198if (!p)199return 0;200const void *beg = allocator.GetBlockBegin(p);201if (beg != p)202return 0;203Metadata *b = (Metadata *)allocator.GetMetaData(p);204return b->requested_size;205}206207static uptr AllocationSizeFast(const void *p) {208return reinterpret_cast<Metadata *>(allocator.GetMetaData(p))->requested_size;209}210211void *dfsan_malloc(uptr size) {212return SetErrnoOnNull(DFsanAllocate(size, sizeof(u64), false /*zeroise*/));213}214215void *dfsan_calloc(uptr nmemb, uptr size) {216return SetErrnoOnNull(DFsanCalloc(nmemb, size));217}218219void *dfsan_realloc(void *ptr, uptr size) {220if (!ptr)221return SetErrnoOnNull(DFsanAllocate(size, sizeof(u64), false /*zeroise*/));222if (size == 0) {223dfsan_deallocate(ptr);224return nullptr;225}226return SetErrnoOnNull(DFsanReallocate(ptr, size, sizeof(u64)));227}228229void *dfsan_reallocarray(void *ptr, uptr nmemb, uptr size) {230if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {231errno = errno_ENOMEM;232if (AllocatorMayReturnNull())233return nullptr;234BufferedStackTrace stack;235ReportReallocArrayOverflow(nmemb, size, &stack);236}237return dfsan_realloc(ptr, nmemb * size);238}239240void *dfsan_valloc(uptr size) {241return SetErrnoOnNull(242DFsanAllocate(size, GetPageSizeCached(), false /*zeroise*/));243}244245void *dfsan_pvalloc(uptr size) {246uptr PageSize = GetPageSizeCached();247if (UNLIKELY(CheckForPvallocOverflow(size, PageSize))) {248errno = errno_ENOMEM;249if (AllocatorMayReturnNull())250return nullptr;251BufferedStackTrace stack;252ReportPvallocOverflow(size, &stack);253}254// pvalloc(0) should allocate one page.255size = size ? RoundUpTo(size, PageSize) : PageSize;256return SetErrnoOnNull(DFsanAllocate(size, PageSize, false /*zeroise*/));257}258259void *dfsan_aligned_alloc(uptr alignment, uptr size) {260if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(alignment, size))) {261errno = errno_EINVAL;262if (AllocatorMayReturnNull())263return nullptr;264BufferedStackTrace stack;265ReportInvalidAlignedAllocAlignment(size, alignment, &stack);266}267return SetErrnoOnNull(DFsanAllocate(size, alignment, false /*zeroise*/));268}269270void *dfsan_memalign(uptr alignment, uptr size) {271if (UNLIKELY(!IsPowerOfTwo(alignment))) {272errno = errno_EINVAL;273if (AllocatorMayReturnNull())274return nullptr;275BufferedStackTrace stack;276ReportInvalidAllocationAlignment(alignment, &stack);277}278return SetErrnoOnNull(DFsanAllocate(size, alignment, false /*zeroise*/));279}280281int dfsan_posix_memalign(void **memptr, uptr alignment, uptr size) {282if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) {283if (AllocatorMayReturnNull())284return errno_EINVAL;285BufferedStackTrace stack;286ReportInvalidPosixMemalignAlignment(alignment, &stack);287}288void *ptr = DFsanAllocate(size, alignment, false /*zeroise*/);289if (UNLIKELY(!ptr))290// OOM error is already taken care of by DFsanAllocate.291return errno_ENOMEM;292CHECK(IsAligned((uptr)ptr, alignment));293*memptr = ptr;294return 0;295}296297} // namespace __dfsan298299using namespace __dfsan;300301uptr __sanitizer_get_current_allocated_bytes() {302uptr stats[AllocatorStatCount];303allocator.GetStats(stats);304return stats[AllocatorStatAllocated];305}306307uptr __sanitizer_get_heap_size() {308uptr stats[AllocatorStatCount];309allocator.GetStats(stats);310return stats[AllocatorStatMapped];311}312313uptr __sanitizer_get_free_bytes() { return 1; }314315uptr __sanitizer_get_unmapped_bytes() { return 1; }316317uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; }318319int __sanitizer_get_ownership(const void *p) { return AllocationSize(p) != 0; }320321const void *__sanitizer_get_allocated_begin(const void *p) {322return AllocationBegin(p);323}324325uptr __sanitizer_get_allocated_size(const void *p) { return AllocationSize(p); }326327uptr __sanitizer_get_allocated_size_fast(const void *p) {328DCHECK_EQ(p, __sanitizer_get_allocated_begin(p));329uptr ret = AllocationSizeFast(p);330DCHECK_EQ(ret, __sanitizer_get_allocated_size(p));331return ret;332}333334335