Path: blob/main/contrib/llvm-project/compiler-rt/lib/nsan/nsan_allocator.cpp
213766 views
//===- nsan_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// NumericalStabilitySanitizer allocator.9//10//===----------------------------------------------------------------------===//1112#include "nsan_allocator.h"13#include "interception/interception.h"14#include "nsan.h"15#include "nsan_flags.h"16#include "nsan_platform.h"17#include "nsan_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_common.h"23#include "sanitizer_common/sanitizer_errno.h"2425using namespace __nsan;2627DECLARE_REAL(void *, memcpy, void *dest, const void *src, SIZE_T n)28DECLARE_REAL(void *, memset, void *dest, int c, SIZE_T n)2930namespace {31struct Metadata {32uptr requested_size;33};3435struct NsanMapUnmapCallback {36void OnMap(uptr p, uptr size) const {}37void OnMapSecondary(uptr p, uptr size, uptr user_begin,38uptr user_size) const {}39void OnUnmap(uptr p, uptr size) const {}40};4142const uptr kMaxAllowedMallocSize = 1ULL << 40;4344// Allocator64 parameters. Deliberately using a short name.45struct AP64 {46static const uptr kSpaceBeg = Mapping::kHeapMemBeg;47static const uptr kSpaceSize = 0x40000000000; // 4T.48static const uptr kMetadataSize = sizeof(Metadata);49using SizeClassMap = DefaultSizeClassMap;50using MapUnmapCallback = NsanMapUnmapCallback;51static const uptr kFlags = 0;52using AddressSpaceView = LocalAddressSpaceView;53};54} // namespace5556using PrimaryAllocator = SizeClassAllocator64<AP64>;57using Allocator = CombinedAllocator<PrimaryAllocator>;58using AllocatorCache = Allocator::AllocatorCache;5960static Allocator allocator;61static AllocatorCache fallback_allocator_cache;62static StaticSpinMutex fallback_mutex;6364static uptr max_malloc_size;6566void __nsan::NsanAllocatorInit() {67SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null);68allocator.Init(common_flags()->allocator_release_to_os_interval_ms);69if (common_flags()->max_allocation_size_mb)70max_malloc_size = Min(common_flags()->max_allocation_size_mb << 20,71kMaxAllowedMallocSize);72else73max_malloc_size = kMaxAllowedMallocSize;74}7576static AllocatorCache *GetAllocatorCache(NsanThreadLocalMallocStorage *ms) {77CHECK_LE(sizeof(AllocatorCache), sizeof(ms->allocator_cache));78return reinterpret_cast<AllocatorCache *>(ms->allocator_cache);79}8081void NsanThreadLocalMallocStorage::Init() {82allocator.InitCache(GetAllocatorCache(this));83}8485void NsanThreadLocalMallocStorage::CommitBack() {86allocator.SwallowCache(GetAllocatorCache(this));87allocator.DestroyCache(GetAllocatorCache(this));88}8990static void *NsanAllocate(uptr size, uptr alignment, bool zero) {91if (UNLIKELY(size > max_malloc_size)) {92if (AllocatorMayReturnNull()) {93Report("WARNING: NumericalStabilitySanitizer failed to allocate 0x%zx "94"bytes\n",95size);96return nullptr;97}98BufferedStackTrace stack;99GET_FATAL_STACK_TRACE_IF_EMPTY(&stack);100ReportAllocationSizeTooBig(size, max_malloc_size, &stack);101}102if (UNLIKELY(IsRssLimitExceeded())) {103if (AllocatorMayReturnNull())104return nullptr;105BufferedStackTrace stack;106GET_FATAL_STACK_TRACE_IF_EMPTY(&stack);107ReportRssLimitExceeded(&stack);108}109110void *allocated;111if (NsanThread *t = GetCurrentThread()) {112AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage());113allocated = allocator.Allocate(cache, size, alignment);114} else {115SpinMutexLock l(&fallback_mutex);116AllocatorCache *cache = &fallback_allocator_cache;117allocated = allocator.Allocate(cache, size, alignment);118}119if (UNLIKELY(!allocated)) {120SetAllocatorOutOfMemory();121if (AllocatorMayReturnNull())122return nullptr;123BufferedStackTrace stack;124GET_FATAL_STACK_TRACE_IF_EMPTY(&stack);125ReportOutOfMemory(size, &stack);126}127auto *meta = reinterpret_cast<Metadata *>(allocator.GetMetaData(allocated));128meta->requested_size = size;129if (zero && allocator.FromPrimary(allocated))130REAL(memset)(allocated, 0, size);131__nsan_set_value_unknown(allocated, size);132RunMallocHooks(allocated, size);133return allocated;134}135136void __nsan::NsanDeallocate(void *p) {137DCHECK(p);138RunFreeHooks(p);139auto *meta = reinterpret_cast<Metadata *>(allocator.GetMetaData(p));140uptr size = meta->requested_size;141meta->requested_size = 0;142if (flags().poison_in_free)143__nsan_set_value_unknown(p, size);144if (NsanThread *t = GetCurrentThread()) {145AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage());146allocator.Deallocate(cache, p);147} else {148// In a just created thread, glibc's _dl_deallocate_tls might reach here149// before nsan_current_thread is set.150SpinMutexLock l(&fallback_mutex);151AllocatorCache *cache = &fallback_allocator_cache;152allocator.Deallocate(cache, p);153}154}155156static void *NsanReallocate(void *ptr, uptr new_size, uptr alignment) {157Metadata *meta = reinterpret_cast<Metadata *>(allocator.GetMetaData(ptr));158uptr old_size = meta->requested_size;159uptr actually_allocated_size = allocator.GetActuallyAllocatedSize(ptr);160if (new_size <= actually_allocated_size) {161// We are not reallocating here.162meta->requested_size = new_size;163if (new_size > old_size)164__nsan_set_value_unknown((u8 *)ptr + old_size, new_size - old_size);165return ptr;166}167void *new_p = NsanAllocate(new_size, alignment, false);168if (new_p) {169uptr memcpy_size = Min(new_size, old_size);170REAL(memcpy)(new_p, ptr, memcpy_size);171__nsan_copy_values(new_p, ptr, memcpy_size);172NsanDeallocate(ptr);173}174return new_p;175}176177static void *NsanCalloc(uptr nmemb, uptr size) {178if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {179if (AllocatorMayReturnNull())180return nullptr;181BufferedStackTrace stack;182GET_FATAL_STACK_TRACE_IF_EMPTY(&stack);183ReportCallocOverflow(nmemb, size, &stack);184}185return NsanAllocate(nmemb * size, sizeof(u64), true);186}187188static const void *AllocationBegin(const void *p) {189if (!p)190return nullptr;191void *beg = allocator.GetBlockBegin(p);192if (!beg)193return nullptr;194auto *b = reinterpret_cast<Metadata *>(allocator.GetMetaData(beg));195if (!b)196return nullptr;197if (b->requested_size == 0)198return nullptr;199200return beg;201}202203static uptr AllocationSizeFast(const void *p) {204return reinterpret_cast<Metadata *>(allocator.GetMetaData(p))->requested_size;205}206207static uptr AllocationSize(const void *p) {208if (!p)209return 0;210if (allocator.GetBlockBegin(p) != p)211return 0;212return AllocationSizeFast(p);213}214215void *__nsan::nsan_malloc(uptr size) {216return SetErrnoOnNull(NsanAllocate(size, sizeof(u64), false));217}218219void *__nsan::nsan_calloc(uptr nmemb, uptr size) {220return SetErrnoOnNull(NsanCalloc(nmemb, size));221}222223void *__nsan::nsan_realloc(void *ptr, uptr size) {224if (!ptr)225return SetErrnoOnNull(NsanAllocate(size, sizeof(u64), false));226if (size == 0) {227NsanDeallocate(ptr);228return nullptr;229}230return SetErrnoOnNull(NsanReallocate(ptr, size, sizeof(u64)));231}232233void *__nsan::nsan_reallocarray(void *ptr, uptr nmemb, uptr size) {234if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) {235errno = errno_ENOMEM;236if (AllocatorMayReturnNull())237return nullptr;238BufferedStackTrace stack;239GET_FATAL_STACK_TRACE_IF_EMPTY(&stack);240ReportReallocArrayOverflow(nmemb, size, &stack);241}242return nsan_realloc(ptr, nmemb * size);243}244245void *__nsan::nsan_valloc(uptr size) {246return SetErrnoOnNull(NsanAllocate(size, GetPageSizeCached(), false));247}248249void *__nsan::nsan_pvalloc(uptr size) {250uptr PageSize = GetPageSizeCached();251if (UNLIKELY(CheckForPvallocOverflow(size, PageSize))) {252errno = errno_ENOMEM;253if (AllocatorMayReturnNull())254return nullptr;255BufferedStackTrace stack;256GET_FATAL_STACK_TRACE_IF_EMPTY(&stack);257ReportPvallocOverflow(size, &stack);258}259// pvalloc(0) should allocate one page.260size = size ? RoundUpTo(size, PageSize) : PageSize;261return SetErrnoOnNull(NsanAllocate(size, PageSize, false));262}263264void *__nsan::nsan_aligned_alloc(uptr alignment, uptr size) {265if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(alignment, size))) {266errno = errno_EINVAL;267if (AllocatorMayReturnNull())268return nullptr;269BufferedStackTrace stack;270GET_FATAL_STACK_TRACE_IF_EMPTY(&stack);271ReportInvalidAlignedAllocAlignment(size, alignment, &stack);272}273return SetErrnoOnNull(NsanAllocate(size, alignment, false));274}275276void *__nsan::nsan_memalign(uptr alignment, uptr size) {277if (UNLIKELY(!IsPowerOfTwo(alignment))) {278errno = errno_EINVAL;279if (AllocatorMayReturnNull())280return nullptr;281BufferedStackTrace stack;282GET_FATAL_STACK_TRACE_IF_EMPTY(&stack);283ReportInvalidAllocationAlignment(alignment, &stack);284}285return SetErrnoOnNull(NsanAllocate(size, alignment, false));286}287288int __nsan::nsan_posix_memalign(void **memptr, uptr alignment, uptr size) {289if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) {290if (AllocatorMayReturnNull())291return errno_EINVAL;292BufferedStackTrace stack;293ReportInvalidPosixMemalignAlignment(alignment, &stack);294}295void *ptr = NsanAllocate(size, alignment, false);296if (UNLIKELY(!ptr))297// OOM error is already taken care of by NsanAllocate.298return errno_ENOMEM;299DCHECK(IsAligned((uptr)ptr, alignment));300*memptr = ptr;301return 0;302}303304extern "C" {305uptr __sanitizer_get_current_allocated_bytes() {306uptr stats[AllocatorStatCount];307allocator.GetStats(stats);308return stats[AllocatorStatAllocated];309}310311uptr __sanitizer_get_heap_size() {312uptr stats[AllocatorStatCount];313allocator.GetStats(stats);314return stats[AllocatorStatMapped];315}316317uptr __sanitizer_get_free_bytes() { return 1; }318319uptr __sanitizer_get_unmapped_bytes() { return 1; }320321uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; }322323int __sanitizer_get_ownership(const void *p) { return AllocationSize(p) != 0; }324325const void *__sanitizer_get_allocated_begin(const void *p) {326return AllocationBegin(p);327}328329uptr __sanitizer_get_allocated_size(const void *p) { return AllocationSize(p); }330331uptr __sanitizer_get_allocated_size_fast(const void *p) {332DCHECK_EQ(p, __sanitizer_get_allocated_begin(p));333uptr ret = AllocationSizeFast(p);334DCHECK_EQ(ret, __sanitizer_get_allocated_size(p));335return ret;336}337338void __sanitizer_purge_allocator() { allocator.ForceReleaseToOS(); }339}340341342