//===-- wrappers_c.inc ------------------------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef SCUDO_PREFIX #error "Define SCUDO_PREFIX prior to including this file!" #endif // malloc-type functions have to be aligned to std::max_align_t. This is // distinct from (1U << SCUDO_MIN_ALIGNMENT_LOG), since C++ new-type functions // do not have to abide by the same requirement. #ifndef SCUDO_MALLOC_ALIGNMENT #define SCUDO_MALLOC_ALIGNMENT FIRST_32_SECOND_64(8U, 16U) #endif static void reportAllocation(void *ptr, size_t size) { if (SCUDO_ENABLE_HOOKS) if (__scudo_allocate_hook && ptr) __scudo_allocate_hook(ptr, size); } static void reportDeallocation(void *ptr) { if (SCUDO_ENABLE_HOOKS) if (__scudo_deallocate_hook) __scudo_deallocate_hook(ptr); } static void reportReallocAllocation(void *old_ptr, void *new_ptr, size_t size) { DCHECK_NE(new_ptr, nullptr); if (SCUDO_ENABLE_HOOKS) { if (__scudo_realloc_allocate_hook) __scudo_realloc_allocate_hook(old_ptr, new_ptr, size); else if (__scudo_allocate_hook) __scudo_allocate_hook(new_ptr, size); } } static void reportReallocDeallocation(void *old_ptr) { if (SCUDO_ENABLE_HOOKS) { if (__scudo_realloc_deallocate_hook) __scudo_realloc_deallocate_hook(old_ptr); else if (__scudo_deallocate_hook) __scudo_deallocate_hook(old_ptr); } } extern "C" { INTERFACE WEAK void *SCUDO_PREFIX(calloc)(size_t nmemb, size_t size) { scudo::uptr Product; if (UNLIKELY(scudo::checkForCallocOverflow(size, nmemb, &Product))) { if (SCUDO_ALLOCATOR.canReturnNull()) { errno = ENOMEM; return nullptr; } scudo::reportCallocOverflow(nmemb, size); } void *Ptr = SCUDO_ALLOCATOR.allocate(Product, scudo::Chunk::Origin::Malloc, SCUDO_MALLOC_ALIGNMENT, true); reportAllocation(Ptr, Product); return scudo::setErrnoOnNull(Ptr); } INTERFACE WEAK void SCUDO_PREFIX(free)(void *ptr) { reportDeallocation(ptr); SCUDO_ALLOCATOR.deallocate(ptr, scudo::Chunk::Origin::Malloc); } INTERFACE WEAK struct SCUDO_MALLINFO SCUDO_PREFIX(mallinfo)(void) { struct SCUDO_MALLINFO Info = {}; scudo::StatCounters Stats; SCUDO_ALLOCATOR.getStats(Stats); // Space allocated in mmapped regions (bytes) Info.hblkhd = static_cast<__scudo_mallinfo_data_t>(Stats[scudo::StatMapped]); // Maximum total allocated space (bytes) Info.usmblks = Info.hblkhd; // Space in freed fastbin blocks (bytes) Info.fsmblks = static_cast<__scudo_mallinfo_data_t>(Stats[scudo::StatFree]); // Total allocated space (bytes) Info.uordblks = static_cast<__scudo_mallinfo_data_t>(Stats[scudo::StatAllocated]); // Total free space (bytes) Info.fordblks = Info.fsmblks; return Info; } // On Android, mallinfo2 is an alias of mallinfo, so don't define both. #if !SCUDO_ANDROID INTERFACE WEAK struct __scudo_mallinfo2 SCUDO_PREFIX(mallinfo2)(void) { struct __scudo_mallinfo2 Info = {}; scudo::StatCounters Stats; SCUDO_ALLOCATOR.getStats(Stats); // Space allocated in mmapped regions (bytes) Info.hblkhd = Stats[scudo::StatMapped]; // Maximum total allocated space (bytes) Info.usmblks = Info.hblkhd; // Space in freed fastbin blocks (bytes) Info.fsmblks = Stats[scudo::StatFree]; // Total allocated space (bytes) Info.uordblks = Stats[scudo::StatAllocated]; // Total free space (bytes) Info.fordblks = Info.fsmblks; return Info; } #endif INTERFACE WEAK void *SCUDO_PREFIX(malloc)(size_t size) { void *Ptr = SCUDO_ALLOCATOR.allocate(size, scudo::Chunk::Origin::Malloc, SCUDO_MALLOC_ALIGNMENT); reportAllocation(Ptr, size); return scudo::setErrnoOnNull(Ptr); } #if SCUDO_ANDROID INTERFACE WEAK size_t SCUDO_PREFIX(malloc_usable_size)(const void *ptr) { #else INTERFACE WEAK size_t SCUDO_PREFIX(malloc_usable_size)(void *ptr) { #endif return SCUDO_ALLOCATOR.getUsableSize(ptr); } INTERFACE WEAK void *SCUDO_PREFIX(memalign)(size_t alignment, size_t size) { // Android rounds up the alignment to a power of two if it isn't one. if (SCUDO_ANDROID) { if (UNLIKELY(!alignment)) { alignment = 1U; } else { if (UNLIKELY(!scudo::isPowerOfTwo(alignment))) alignment = scudo::roundUpPowerOfTwo(alignment); } } else { if (UNLIKELY(!scudo::isPowerOfTwo(alignment))) { if (SCUDO_ALLOCATOR.canReturnNull()) { errno = EINVAL; return nullptr; } scudo::reportAlignmentNotPowerOfTwo(alignment); } } void *Ptr = SCUDO_ALLOCATOR.allocate(size, scudo::Chunk::Origin::Memalign, alignment); reportAllocation(Ptr, size); return Ptr; } INTERFACE WEAK int SCUDO_PREFIX(posix_memalign)(void **memptr, size_t alignment, size_t size) { if (UNLIKELY(scudo::checkPosixMemalignAlignment(alignment))) { if (!SCUDO_ALLOCATOR.canReturnNull()) scudo::reportInvalidPosixMemalignAlignment(alignment); return EINVAL; } void *Ptr = SCUDO_ALLOCATOR.allocate(size, scudo::Chunk::Origin::Memalign, alignment); if (UNLIKELY(!Ptr)) return ENOMEM; reportAllocation(Ptr, size); *memptr = Ptr; return 0; } INTERFACE WEAK void *SCUDO_PREFIX(pvalloc)(size_t size) { const scudo::uptr PageSize = scudo::getPageSizeCached(); if (UNLIKELY(scudo::checkForPvallocOverflow(size, PageSize))) { if (SCUDO_ALLOCATOR.canReturnNull()) { errno = ENOMEM; return nullptr; } scudo::reportPvallocOverflow(size); } // pvalloc(0) should allocate one page. void *Ptr = SCUDO_ALLOCATOR.allocate(size ? scudo::roundUp(size, PageSize) : PageSize, scudo::Chunk::Origin::Memalign, PageSize); reportAllocation(Ptr, scudo::roundUp(size, PageSize)); return scudo::setErrnoOnNull(Ptr); } INTERFACE WEAK void *SCUDO_PREFIX(realloc)(void *ptr, size_t size) { if (!ptr) { void *Ptr = SCUDO_ALLOCATOR.allocate(size, scudo::Chunk::Origin::Malloc, SCUDO_MALLOC_ALIGNMENT); reportAllocation(Ptr, size); return scudo::setErrnoOnNull(Ptr); } if (size == 0) { reportDeallocation(ptr); SCUDO_ALLOCATOR.deallocate(ptr, scudo::Chunk::Origin::Malloc); return nullptr; } // Given that the reporting of deallocation and allocation are not atomic, we // always pretend the old pointer will be released so that the user doesn't // need to worry about the false double-use case from the view of hooks. // // For example, assume that `realloc` releases the old pointer and allocates a // new pointer. Before the reporting of both operations has been done, another // thread may get the old pointer from `malloc`. It may be misinterpreted as // double-use if it's not handled properly on the hook side. reportReallocDeallocation(ptr); void *NewPtr = SCUDO_ALLOCATOR.reallocate(ptr, size, SCUDO_MALLOC_ALIGNMENT); if (NewPtr != nullptr) { // Note that even if NewPtr == ptr, the size has changed. We still need to // report the new size. reportReallocAllocation(/*OldPtr=*/ptr, NewPtr, size); } else { // If `realloc` fails, the old pointer is not released. Report the old // pointer as allocated again. reportReallocAllocation(/*OldPtr=*/ptr, /*NewPtr=*/ptr, SCUDO_ALLOCATOR.getAllocSize(ptr)); } return scudo::setErrnoOnNull(NewPtr); } INTERFACE WEAK void *SCUDO_PREFIX(valloc)(size_t size) { void *Ptr = SCUDO_ALLOCATOR.allocate(size, scudo::Chunk::Origin::Memalign, scudo::getPageSizeCached()); reportAllocation(Ptr, size); return scudo::setErrnoOnNull(Ptr); } INTERFACE WEAK int SCUDO_PREFIX(malloc_iterate)( uintptr_t base, size_t size, void (*callback)(uintptr_t base, size_t size, void *arg), void *arg) { SCUDO_ALLOCATOR.iterateOverChunks(base, size, callback, arg); return 0; } INTERFACE WEAK void SCUDO_PREFIX(malloc_enable)() { SCUDO_ALLOCATOR.enable(); } INTERFACE WEAK void SCUDO_PREFIX(malloc_disable)() { SCUDO_ALLOCATOR.disable(); } void SCUDO_PREFIX(malloc_postinit)() { SCUDO_ALLOCATOR.initGwpAsan(); pthread_atfork(SCUDO_PREFIX(malloc_disable), SCUDO_PREFIX(malloc_enable), SCUDO_PREFIX(malloc_enable)); } INTERFACE WEAK int SCUDO_PREFIX(mallopt)(int param, int value) { if (param == M_DECAY_TIME) { if (SCUDO_ANDROID) { if (value == 0) { // Will set the release values to their minimum values. value = INT32_MIN; } else { // Will set the release values to their maximum values. value = INT32_MAX; } } SCUDO_ALLOCATOR.setOption(scudo::Option::ReleaseInterval, static_cast(value)); return 1; } else if (param == M_PURGE) { SCUDO_ALLOCATOR.releaseToOS(scudo::ReleaseToOS::Force); return 1; } else if (param == M_PURGE_ALL) { SCUDO_ALLOCATOR.releaseToOS(scudo::ReleaseToOS::ForceAll); return 1; } else if (param == M_LOG_STATS) { SCUDO_ALLOCATOR.printStats(); SCUDO_ALLOCATOR.printFragmentationInfo(); return 1; } else { scudo::Option option; switch (param) { case M_MEMTAG_TUNING: option = scudo::Option::MemtagTuning; break; case M_THREAD_DISABLE_MEM_INIT: option = scudo::Option::ThreadDisableMemInit; break; case M_CACHE_COUNT_MAX: option = scudo::Option::MaxCacheEntriesCount; break; case M_CACHE_SIZE_MAX: option = scudo::Option::MaxCacheEntrySize; break; case M_TSDS_COUNT_MAX: option = scudo::Option::MaxTSDsCount; break; default: return 0; } return SCUDO_ALLOCATOR.setOption(option, static_cast(value)); } } INTERFACE WEAK void *SCUDO_PREFIX(aligned_alloc)(size_t alignment, size_t size) { if (UNLIKELY(scudo::checkAlignedAllocAlignmentAndSize(alignment, size))) { if (SCUDO_ALLOCATOR.canReturnNull()) { errno = EINVAL; return nullptr; } scudo::reportInvalidAlignedAllocAlignment(alignment, size); } void *Ptr = SCUDO_ALLOCATOR.allocate(size, scudo::Chunk::Origin::Malloc, alignment); reportAllocation(Ptr, size); return scudo::setErrnoOnNull(Ptr); } INTERFACE WEAK int SCUDO_PREFIX(malloc_info)(UNUSED int options, FILE *stream) { const scudo::uptr max_size = decltype(SCUDO_ALLOCATOR)::PrimaryT::SizeClassMap::MaxSize; auto *sizes = static_cast( SCUDO_PREFIX(calloc)(max_size, sizeof(scudo::uptr))); auto callback = [](uintptr_t, size_t size, void *arg) { auto *sizes = reinterpret_cast(arg); if (size < max_size) sizes[size]++; }; SCUDO_ALLOCATOR.disable(); SCUDO_ALLOCATOR.iterateOverChunks(0, -1ul, callback, sizes); SCUDO_ALLOCATOR.enable(); fputs("\n", stream); for (scudo::uptr i = 0; i != max_size; ++i) if (sizes[i]) fprintf(stream, "\n", i, sizes[i]); fputs("\n", stream); SCUDO_PREFIX(free)(sizes); return 0; } // Disable memory tagging for the heap. The caller must disable memory tag // checks globally (e.g. by clearing TCF0 on aarch64) before calling this // function, and may not re-enable them after calling the function. INTERFACE WEAK void SCUDO_PREFIX(malloc_disable_memory_tagging)() { SCUDO_ALLOCATOR.disableMemoryTagging(); } // Sets whether scudo records stack traces and other metadata for allocations // and deallocations. This function only has an effect if the allocator and // hardware support memory tagging. INTERFACE WEAK void SCUDO_PREFIX(malloc_set_track_allocation_stacks)(int track) { SCUDO_ALLOCATOR.setTrackAllocationStacks(track); } // Sets whether scudo zero-initializes all allocated memory. INTERFACE WEAK void SCUDO_PREFIX(malloc_set_zero_contents)(int zero_contents) { SCUDO_ALLOCATOR.setFillContents(zero_contents ? scudo::ZeroFill : scudo::NoFill); } // Sets whether scudo pattern-initializes all allocated memory. INTERFACE WEAK void SCUDO_PREFIX(malloc_set_pattern_fill_contents)(int pattern_fill_contents) { SCUDO_ALLOCATOR.setFillContents( pattern_fill_contents ? scudo::PatternOrZeroFill : scudo::NoFill); } // Sets whether scudo adds a small amount of slack at the end of large // allocations, before the guard page. This can be enabled to work around buggy // applications that read a few bytes past the end of their allocation. INTERFACE WEAK void SCUDO_PREFIX(malloc_set_add_large_allocation_slack)(int add_slack) { SCUDO_ALLOCATOR.setAddLargeAllocationSlack(add_slack); } } // extern "C"