168d75effSDimitry Andric //=-- lsan_allocator.cpp --------------------------------------------------===// 268d75effSDimitry Andric // 368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 668d75effSDimitry Andric // 768d75effSDimitry Andric //===----------------------------------------------------------------------===// 868d75effSDimitry Andric // 968d75effSDimitry Andric // This file is a part of LeakSanitizer. 1068d75effSDimitry Andric // See lsan_allocator.h for details. 1168d75effSDimitry Andric // 1268d75effSDimitry Andric //===----------------------------------------------------------------------===// 1368d75effSDimitry Andric 1468d75effSDimitry Andric #include "lsan_allocator.h" 1568d75effSDimitry Andric 1668d75effSDimitry Andric #include "sanitizer_common/sanitizer_allocator.h" 1768d75effSDimitry Andric #include "sanitizer_common/sanitizer_allocator_checks.h" 1868d75effSDimitry Andric #include "sanitizer_common/sanitizer_allocator_interface.h" 1968d75effSDimitry Andric #include "sanitizer_common/sanitizer_allocator_report.h" 2068d75effSDimitry Andric #include "sanitizer_common/sanitizer_errno.h" 2168d75effSDimitry Andric #include "sanitizer_common/sanitizer_internal_defs.h" 2268d75effSDimitry Andric #include "sanitizer_common/sanitizer_stackdepot.h" 2368d75effSDimitry Andric #include "sanitizer_common/sanitizer_stacktrace.h" 2468d75effSDimitry Andric #include "lsan_common.h" 2568d75effSDimitry Andric 2668d75effSDimitry Andric extern "C" void *memset(void *ptr, int value, uptr num); 2768d75effSDimitry Andric 2868d75effSDimitry Andric namespace __lsan { 2968d75effSDimitry Andric #if defined(__i386__) || defined(__arm__) 3068d75effSDimitry Andric static const uptr kMaxAllowedMallocSize = 1UL << 30; 3168d75effSDimitry Andric #elif defined(__mips64) || defined(__aarch64__) 3268d75effSDimitry Andric static const uptr kMaxAllowedMallocSize = 4UL << 30; 3368d75effSDimitry Andric #else 3468d75effSDimitry Andric static const uptr kMaxAllowedMallocSize = 8UL << 30; 3568d75effSDimitry Andric #endif 3668d75effSDimitry Andric 3768d75effSDimitry Andric static Allocator allocator; 3868d75effSDimitry Andric 39*480093f4SDimitry Andric static uptr max_malloc_size; 40*480093f4SDimitry Andric 4168d75effSDimitry Andric void InitializeAllocator() { 4268d75effSDimitry Andric SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null); 4368d75effSDimitry Andric allocator.InitLinkerInitialized( 4468d75effSDimitry Andric common_flags()->allocator_release_to_os_interval_ms); 45*480093f4SDimitry Andric if (common_flags()->max_allocation_size_mb) 46*480093f4SDimitry Andric max_malloc_size = Min(common_flags()->max_allocation_size_mb << 20, 47*480093f4SDimitry Andric kMaxAllowedMallocSize); 48*480093f4SDimitry Andric else 49*480093f4SDimitry Andric max_malloc_size = kMaxAllowedMallocSize; 5068d75effSDimitry Andric } 5168d75effSDimitry Andric 5268d75effSDimitry Andric void AllocatorThreadFinish() { 5368d75effSDimitry Andric allocator.SwallowCache(GetAllocatorCache()); 5468d75effSDimitry Andric } 5568d75effSDimitry Andric 5668d75effSDimitry Andric static ChunkMetadata *Metadata(const void *p) { 5768d75effSDimitry Andric return reinterpret_cast<ChunkMetadata *>(allocator.GetMetaData(p)); 5868d75effSDimitry Andric } 5968d75effSDimitry Andric 6068d75effSDimitry Andric static void RegisterAllocation(const StackTrace &stack, void *p, uptr size) { 6168d75effSDimitry Andric if (!p) return; 6268d75effSDimitry Andric ChunkMetadata *m = Metadata(p); 6368d75effSDimitry Andric CHECK(m); 6468d75effSDimitry Andric m->tag = DisabledInThisThread() ? kIgnored : kDirectlyLeaked; 6568d75effSDimitry Andric m->stack_trace_id = StackDepotPut(stack); 6668d75effSDimitry Andric m->requested_size = size; 6768d75effSDimitry Andric atomic_store(reinterpret_cast<atomic_uint8_t *>(m), 1, memory_order_relaxed); 6868d75effSDimitry Andric } 6968d75effSDimitry Andric 7068d75effSDimitry Andric static void RegisterDeallocation(void *p) { 7168d75effSDimitry Andric if (!p) return; 7268d75effSDimitry Andric ChunkMetadata *m = Metadata(p); 7368d75effSDimitry Andric CHECK(m); 7468d75effSDimitry Andric atomic_store(reinterpret_cast<atomic_uint8_t *>(m), 0, memory_order_relaxed); 7568d75effSDimitry Andric } 7668d75effSDimitry Andric 7768d75effSDimitry Andric static void *ReportAllocationSizeTooBig(uptr size, const StackTrace &stack) { 7868d75effSDimitry Andric if (AllocatorMayReturnNull()) { 7968d75effSDimitry Andric Report("WARNING: LeakSanitizer failed to allocate 0x%zx bytes\n", size); 8068d75effSDimitry Andric return nullptr; 8168d75effSDimitry Andric } 82*480093f4SDimitry Andric ReportAllocationSizeTooBig(size, max_malloc_size, &stack); 8368d75effSDimitry Andric } 8468d75effSDimitry Andric 8568d75effSDimitry Andric void *Allocate(const StackTrace &stack, uptr size, uptr alignment, 8668d75effSDimitry Andric bool cleared) { 8768d75effSDimitry Andric if (size == 0) 8868d75effSDimitry Andric size = 1; 89*480093f4SDimitry Andric if (size > max_malloc_size) 9068d75effSDimitry Andric return ReportAllocationSizeTooBig(size, stack); 9168d75effSDimitry Andric void *p = allocator.Allocate(GetAllocatorCache(), size, alignment); 9268d75effSDimitry Andric if (UNLIKELY(!p)) { 9368d75effSDimitry Andric SetAllocatorOutOfMemory(); 9468d75effSDimitry Andric if (AllocatorMayReturnNull()) 9568d75effSDimitry Andric return nullptr; 9668d75effSDimitry Andric ReportOutOfMemory(size, &stack); 9768d75effSDimitry Andric } 9868d75effSDimitry Andric // Do not rely on the allocator to clear the memory (it's slow). 9968d75effSDimitry Andric if (cleared && allocator.FromPrimary(p)) 10068d75effSDimitry Andric memset(p, 0, size); 10168d75effSDimitry Andric RegisterAllocation(stack, p, size); 10268d75effSDimitry Andric if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(p, size); 10368d75effSDimitry Andric RunMallocHooks(p, size); 10468d75effSDimitry Andric return p; 10568d75effSDimitry Andric } 10668d75effSDimitry Andric 10768d75effSDimitry Andric static void *Calloc(uptr nmemb, uptr size, const StackTrace &stack) { 10868d75effSDimitry Andric if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) { 10968d75effSDimitry Andric if (AllocatorMayReturnNull()) 11068d75effSDimitry Andric return nullptr; 11168d75effSDimitry Andric ReportCallocOverflow(nmemb, size, &stack); 11268d75effSDimitry Andric } 11368d75effSDimitry Andric size *= nmemb; 11468d75effSDimitry Andric return Allocate(stack, size, 1, true); 11568d75effSDimitry Andric } 11668d75effSDimitry Andric 11768d75effSDimitry Andric void Deallocate(void *p) { 11868d75effSDimitry Andric if (&__sanitizer_free_hook) __sanitizer_free_hook(p); 11968d75effSDimitry Andric RunFreeHooks(p); 12068d75effSDimitry Andric RegisterDeallocation(p); 12168d75effSDimitry Andric allocator.Deallocate(GetAllocatorCache(), p); 12268d75effSDimitry Andric } 12368d75effSDimitry Andric 12468d75effSDimitry Andric void *Reallocate(const StackTrace &stack, void *p, uptr new_size, 12568d75effSDimitry Andric uptr alignment) { 12668d75effSDimitry Andric RegisterDeallocation(p); 127*480093f4SDimitry Andric if (new_size > max_malloc_size) { 12868d75effSDimitry Andric allocator.Deallocate(GetAllocatorCache(), p); 12968d75effSDimitry Andric return ReportAllocationSizeTooBig(new_size, stack); 13068d75effSDimitry Andric } 13168d75effSDimitry Andric p = allocator.Reallocate(GetAllocatorCache(), p, new_size, alignment); 13268d75effSDimitry Andric RegisterAllocation(stack, p, new_size); 13368d75effSDimitry Andric return p; 13468d75effSDimitry Andric } 13568d75effSDimitry Andric 13668d75effSDimitry Andric void GetAllocatorCacheRange(uptr *begin, uptr *end) { 13768d75effSDimitry Andric *begin = (uptr)GetAllocatorCache(); 13868d75effSDimitry Andric *end = *begin + sizeof(AllocatorCache); 13968d75effSDimitry Andric } 14068d75effSDimitry Andric 14168d75effSDimitry Andric uptr GetMallocUsableSize(const void *p) { 14268d75effSDimitry Andric ChunkMetadata *m = Metadata(p); 14368d75effSDimitry Andric if (!m) return 0; 14468d75effSDimitry Andric return m->requested_size; 14568d75effSDimitry Andric } 14668d75effSDimitry Andric 14768d75effSDimitry Andric int lsan_posix_memalign(void **memptr, uptr alignment, uptr size, 14868d75effSDimitry Andric const StackTrace &stack) { 14968d75effSDimitry Andric if (UNLIKELY(!CheckPosixMemalignAlignment(alignment))) { 15068d75effSDimitry Andric if (AllocatorMayReturnNull()) 15168d75effSDimitry Andric return errno_EINVAL; 15268d75effSDimitry Andric ReportInvalidPosixMemalignAlignment(alignment, &stack); 15368d75effSDimitry Andric } 15468d75effSDimitry Andric void *ptr = Allocate(stack, size, alignment, kAlwaysClearMemory); 15568d75effSDimitry Andric if (UNLIKELY(!ptr)) 15668d75effSDimitry Andric // OOM error is already taken care of by Allocate. 15768d75effSDimitry Andric return errno_ENOMEM; 15868d75effSDimitry Andric CHECK(IsAligned((uptr)ptr, alignment)); 15968d75effSDimitry Andric *memptr = ptr; 16068d75effSDimitry Andric return 0; 16168d75effSDimitry Andric } 16268d75effSDimitry Andric 16368d75effSDimitry Andric void *lsan_aligned_alloc(uptr alignment, uptr size, const StackTrace &stack) { 16468d75effSDimitry Andric if (UNLIKELY(!CheckAlignedAllocAlignmentAndSize(alignment, size))) { 16568d75effSDimitry Andric errno = errno_EINVAL; 16668d75effSDimitry Andric if (AllocatorMayReturnNull()) 16768d75effSDimitry Andric return nullptr; 16868d75effSDimitry Andric ReportInvalidAlignedAllocAlignment(size, alignment, &stack); 16968d75effSDimitry Andric } 17068d75effSDimitry Andric return SetErrnoOnNull(Allocate(stack, size, alignment, kAlwaysClearMemory)); 17168d75effSDimitry Andric } 17268d75effSDimitry Andric 17368d75effSDimitry Andric void *lsan_memalign(uptr alignment, uptr size, const StackTrace &stack) { 17468d75effSDimitry Andric if (UNLIKELY(!IsPowerOfTwo(alignment))) { 17568d75effSDimitry Andric errno = errno_EINVAL; 17668d75effSDimitry Andric if (AllocatorMayReturnNull()) 17768d75effSDimitry Andric return nullptr; 17868d75effSDimitry Andric ReportInvalidAllocationAlignment(alignment, &stack); 17968d75effSDimitry Andric } 18068d75effSDimitry Andric return SetErrnoOnNull(Allocate(stack, size, alignment, kAlwaysClearMemory)); 18168d75effSDimitry Andric } 18268d75effSDimitry Andric 18368d75effSDimitry Andric void *lsan_malloc(uptr size, const StackTrace &stack) { 18468d75effSDimitry Andric return SetErrnoOnNull(Allocate(stack, size, 1, kAlwaysClearMemory)); 18568d75effSDimitry Andric } 18668d75effSDimitry Andric 18768d75effSDimitry Andric void lsan_free(void *p) { 18868d75effSDimitry Andric Deallocate(p); 18968d75effSDimitry Andric } 19068d75effSDimitry Andric 19168d75effSDimitry Andric void *lsan_realloc(void *p, uptr size, const StackTrace &stack) { 19268d75effSDimitry Andric return SetErrnoOnNull(Reallocate(stack, p, size, 1)); 19368d75effSDimitry Andric } 19468d75effSDimitry Andric 19568d75effSDimitry Andric void *lsan_reallocarray(void *ptr, uptr nmemb, uptr size, 19668d75effSDimitry Andric const StackTrace &stack) { 19768d75effSDimitry Andric if (UNLIKELY(CheckForCallocOverflow(size, nmemb))) { 19868d75effSDimitry Andric errno = errno_ENOMEM; 19968d75effSDimitry Andric if (AllocatorMayReturnNull()) 20068d75effSDimitry Andric return nullptr; 20168d75effSDimitry Andric ReportReallocArrayOverflow(nmemb, size, &stack); 20268d75effSDimitry Andric } 20368d75effSDimitry Andric return lsan_realloc(ptr, nmemb * size, stack); 20468d75effSDimitry Andric } 20568d75effSDimitry Andric 20668d75effSDimitry Andric void *lsan_calloc(uptr nmemb, uptr size, const StackTrace &stack) { 20768d75effSDimitry Andric return SetErrnoOnNull(Calloc(nmemb, size, stack)); 20868d75effSDimitry Andric } 20968d75effSDimitry Andric 21068d75effSDimitry Andric void *lsan_valloc(uptr size, const StackTrace &stack) { 21168d75effSDimitry Andric return SetErrnoOnNull( 21268d75effSDimitry Andric Allocate(stack, size, GetPageSizeCached(), kAlwaysClearMemory)); 21368d75effSDimitry Andric } 21468d75effSDimitry Andric 21568d75effSDimitry Andric void *lsan_pvalloc(uptr size, const StackTrace &stack) { 21668d75effSDimitry Andric uptr PageSize = GetPageSizeCached(); 21768d75effSDimitry Andric if (UNLIKELY(CheckForPvallocOverflow(size, PageSize))) { 21868d75effSDimitry Andric errno = errno_ENOMEM; 21968d75effSDimitry Andric if (AllocatorMayReturnNull()) 22068d75effSDimitry Andric return nullptr; 22168d75effSDimitry Andric ReportPvallocOverflow(size, &stack); 22268d75effSDimitry Andric } 22368d75effSDimitry Andric // pvalloc(0) should allocate one page. 22468d75effSDimitry Andric size = size ? RoundUpTo(size, PageSize) : PageSize; 22568d75effSDimitry Andric return SetErrnoOnNull(Allocate(stack, size, PageSize, kAlwaysClearMemory)); 22668d75effSDimitry Andric } 22768d75effSDimitry Andric 22868d75effSDimitry Andric uptr lsan_mz_size(const void *p) { 22968d75effSDimitry Andric return GetMallocUsableSize(p); 23068d75effSDimitry Andric } 23168d75effSDimitry Andric 23268d75effSDimitry Andric ///// Interface to the common LSan module. ///// 23368d75effSDimitry Andric 23468d75effSDimitry Andric void LockAllocator() { 23568d75effSDimitry Andric allocator.ForceLock(); 23668d75effSDimitry Andric } 23768d75effSDimitry Andric 23868d75effSDimitry Andric void UnlockAllocator() { 23968d75effSDimitry Andric allocator.ForceUnlock(); 24068d75effSDimitry Andric } 24168d75effSDimitry Andric 24268d75effSDimitry Andric void GetAllocatorGlobalRange(uptr *begin, uptr *end) { 24368d75effSDimitry Andric *begin = (uptr)&allocator; 24468d75effSDimitry Andric *end = *begin + sizeof(allocator); 24568d75effSDimitry Andric } 24668d75effSDimitry Andric 24768d75effSDimitry Andric uptr PointsIntoChunk(void* p) { 24868d75effSDimitry Andric uptr addr = reinterpret_cast<uptr>(p); 24968d75effSDimitry Andric uptr chunk = reinterpret_cast<uptr>(allocator.GetBlockBeginFastLocked(p)); 25068d75effSDimitry Andric if (!chunk) return 0; 25168d75effSDimitry Andric // LargeMmapAllocator considers pointers to the meta-region of a chunk to be 25268d75effSDimitry Andric // valid, but we don't want that. 25368d75effSDimitry Andric if (addr < chunk) return 0; 25468d75effSDimitry Andric ChunkMetadata *m = Metadata(reinterpret_cast<void *>(chunk)); 25568d75effSDimitry Andric CHECK(m); 25668d75effSDimitry Andric if (!m->allocated) 25768d75effSDimitry Andric return 0; 25868d75effSDimitry Andric if (addr < chunk + m->requested_size) 25968d75effSDimitry Andric return chunk; 26068d75effSDimitry Andric if (IsSpecialCaseOfOperatorNew0(chunk, m->requested_size, addr)) 26168d75effSDimitry Andric return chunk; 26268d75effSDimitry Andric return 0; 26368d75effSDimitry Andric } 26468d75effSDimitry Andric 26568d75effSDimitry Andric uptr GetUserBegin(uptr chunk) { 26668d75effSDimitry Andric return chunk; 26768d75effSDimitry Andric } 26868d75effSDimitry Andric 26968d75effSDimitry Andric LsanMetadata::LsanMetadata(uptr chunk) { 27068d75effSDimitry Andric metadata_ = Metadata(reinterpret_cast<void *>(chunk)); 27168d75effSDimitry Andric CHECK(metadata_); 27268d75effSDimitry Andric } 27368d75effSDimitry Andric 27468d75effSDimitry Andric bool LsanMetadata::allocated() const { 27568d75effSDimitry Andric return reinterpret_cast<ChunkMetadata *>(metadata_)->allocated; 27668d75effSDimitry Andric } 27768d75effSDimitry Andric 27868d75effSDimitry Andric ChunkTag LsanMetadata::tag() const { 27968d75effSDimitry Andric return reinterpret_cast<ChunkMetadata *>(metadata_)->tag; 28068d75effSDimitry Andric } 28168d75effSDimitry Andric 28268d75effSDimitry Andric void LsanMetadata::set_tag(ChunkTag value) { 28368d75effSDimitry Andric reinterpret_cast<ChunkMetadata *>(metadata_)->tag = value; 28468d75effSDimitry Andric } 28568d75effSDimitry Andric 28668d75effSDimitry Andric uptr LsanMetadata::requested_size() const { 28768d75effSDimitry Andric return reinterpret_cast<ChunkMetadata *>(metadata_)->requested_size; 28868d75effSDimitry Andric } 28968d75effSDimitry Andric 29068d75effSDimitry Andric u32 LsanMetadata::stack_trace_id() const { 29168d75effSDimitry Andric return reinterpret_cast<ChunkMetadata *>(metadata_)->stack_trace_id; 29268d75effSDimitry Andric } 29368d75effSDimitry Andric 29468d75effSDimitry Andric void ForEachChunk(ForEachChunkCallback callback, void *arg) { 29568d75effSDimitry Andric allocator.ForEachChunk(callback, arg); 29668d75effSDimitry Andric } 29768d75effSDimitry Andric 29868d75effSDimitry Andric IgnoreObjectResult IgnoreObjectLocked(const void *p) { 29968d75effSDimitry Andric void *chunk = allocator.GetBlockBegin(p); 30068d75effSDimitry Andric if (!chunk || p < chunk) return kIgnoreObjectInvalid; 30168d75effSDimitry Andric ChunkMetadata *m = Metadata(chunk); 30268d75effSDimitry Andric CHECK(m); 30368d75effSDimitry Andric if (m->allocated && (uptr)p < (uptr)chunk + m->requested_size) { 30468d75effSDimitry Andric if (m->tag == kIgnored) 30568d75effSDimitry Andric return kIgnoreObjectAlreadyIgnored; 30668d75effSDimitry Andric m->tag = kIgnored; 30768d75effSDimitry Andric return kIgnoreObjectSuccess; 30868d75effSDimitry Andric } else { 30968d75effSDimitry Andric return kIgnoreObjectInvalid; 31068d75effSDimitry Andric } 31168d75effSDimitry Andric } 31268d75effSDimitry Andric } // namespace __lsan 31368d75effSDimitry Andric 31468d75effSDimitry Andric using namespace __lsan; 31568d75effSDimitry Andric 31668d75effSDimitry Andric extern "C" { 31768d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE 31868d75effSDimitry Andric uptr __sanitizer_get_current_allocated_bytes() { 31968d75effSDimitry Andric uptr stats[AllocatorStatCount]; 32068d75effSDimitry Andric allocator.GetStats(stats); 32168d75effSDimitry Andric return stats[AllocatorStatAllocated]; 32268d75effSDimitry Andric } 32368d75effSDimitry Andric 32468d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE 32568d75effSDimitry Andric uptr __sanitizer_get_heap_size() { 32668d75effSDimitry Andric uptr stats[AllocatorStatCount]; 32768d75effSDimitry Andric allocator.GetStats(stats); 32868d75effSDimitry Andric return stats[AllocatorStatMapped]; 32968d75effSDimitry Andric } 33068d75effSDimitry Andric 33168d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE 33268d75effSDimitry Andric uptr __sanitizer_get_free_bytes() { return 0; } 33368d75effSDimitry Andric 33468d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE 33568d75effSDimitry Andric uptr __sanitizer_get_unmapped_bytes() { return 0; } 33668d75effSDimitry Andric 33768d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE 33868d75effSDimitry Andric uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; } 33968d75effSDimitry Andric 34068d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE 34168d75effSDimitry Andric int __sanitizer_get_ownership(const void *p) { return Metadata(p) != nullptr; } 34268d75effSDimitry Andric 34368d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE 34468d75effSDimitry Andric uptr __sanitizer_get_allocated_size(const void *p) { 34568d75effSDimitry Andric return GetMallocUsableSize(p); 34668d75effSDimitry Andric } 34768d75effSDimitry Andric 34868d75effSDimitry Andric #if !SANITIZER_SUPPORTS_WEAK_HOOKS 34968d75effSDimitry Andric // Provide default (no-op) implementation of malloc hooks. 35068d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE 35168d75effSDimitry Andric void __sanitizer_malloc_hook(void *ptr, uptr size) { 35268d75effSDimitry Andric (void)ptr; 35368d75effSDimitry Andric (void)size; 35468d75effSDimitry Andric } 35568d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE 35668d75effSDimitry Andric void __sanitizer_free_hook(void *ptr) { 35768d75effSDimitry Andric (void)ptr; 35868d75effSDimitry Andric } 35968d75effSDimitry Andric #endif 36068d75effSDimitry Andric } // extern "C" 361