Index: vendor/compiler-rt/dist/lib/CMakeLists.txt =================================================================== --- vendor/compiler-rt/dist/lib/CMakeLists.txt (revision 320536) +++ vendor/compiler-rt/dist/lib/CMakeLists.txt (revision 320537) @@ -1,62 +1,48 @@ # First, add the subdirectories which contain feature-based runtime libraries # and several convenience helper libraries. include(AddCompilerRT) include(SanitizerUtils) # Hoist the building of sanitizer_common on whether we're building either the # sanitizers or xray (or both). # #TODO: Refactor sanitizer_common into smaller pieces (e.g. flag parsing, utils). if (COMPILER_RT_HAS_SANITIZER_COMMON AND (COMPILER_RT_BUILD_SANITIZERS OR COMPILER_RT_BUILD_XRAY)) add_subdirectory(sanitizer_common) endif() if(COMPILER_RT_BUILD_BUILTINS) add_subdirectory(builtins) endif() function(compiler_rt_build_runtime runtime) string(TOUPPER ${runtime} runtime_uppercase) if(COMPILER_RT_HAS_${runtime_uppercase}) add_subdirectory(${runtime}) - foreach(directory ${ARGN}) - add_subdirectory(${directory}) - endforeach() + if(${runtime} STREQUAL tsan) + add_subdirectory(tsan/dd) + endif() endif() endfunction() -function(compiler_rt_build_sanitizer sanitizer) - string(TOUPPER ${sanitizer} sanitizer_uppercase) - string(TOLOWER ${sanitizer} sanitizer_lowercase) - list(FIND COMPILER_RT_SANITIZERS_TO_BUILD ${sanitizer_lowercase} result) - if(NOT ${result} EQUAL -1) - compiler_rt_build_runtime(${sanitizer} ${ARGN}) - endif() -endfunction() - if(COMPILER_RT_BUILD_SANITIZERS) compiler_rt_build_runtime(interception) if(COMPILER_RT_HAS_SANITIZER_COMMON) add_subdirectory(stats) add_subdirectory(lsan) add_subdirectory(ubsan) endif() - compiler_rt_build_sanitizer(asan) - compiler_rt_build_sanitizer(dfsan) - compiler_rt_build_sanitizer(msan) - compiler_rt_build_sanitizer(tsan tsan/dd) - compiler_rt_build_sanitizer(safestack) - compiler_rt_build_sanitizer(cfi) - compiler_rt_build_sanitizer(esan) - compiler_rt_build_sanitizer(scudo) + foreach(sanitizer ${COMPILER_RT_SANITIZERS_TO_BUILD}) + compiler_rt_build_runtime(${sanitizer}) + endforeach() compiler_rt_build_runtime(profile) endif() if(COMPILER_RT_BUILD_XRAY) compiler_rt_build_runtime(xray) endif() Index: vendor/compiler-rt/dist/lib/asan/asan_allocator.cc =================================================================== --- vendor/compiler-rt/dist/lib/asan/asan_allocator.cc (revision 320536) +++ vendor/compiler-rt/dist/lib/asan/asan_allocator.cc (revision 320537) @@ -1,1006 +1,1009 @@ //===-- asan_allocator.cc -------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // // Implementation of ASan's memory allocator, 2-nd version. // This variant uses the allocator from sanitizer_common, i.e. the one shared // with ThreadSanitizer and MemorySanitizer. // //===----------------------------------------------------------------------===// #include "asan_allocator.h" #include "asan_mapping.h" #include "asan_poisoning.h" #include "asan_report.h" #include "asan_stack.h" #include "asan_thread.h" #include "sanitizer_common/sanitizer_allocator_interface.h" #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_list.h" #include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_quarantine.h" #include "lsan/lsan_common.h" namespace __asan { // Valid redzone sizes are 16, 32, 64, ... 2048, so we encode them in 3 bits. // We use adaptive redzones: for larger allocation larger redzones are used. static u32 RZLog2Size(u32 rz_log) { CHECK_LT(rz_log, 8); return 16 << rz_log; } static u32 RZSize2Log(u32 rz_size) { CHECK_GE(rz_size, 16); CHECK_LE(rz_size, 2048); CHECK(IsPowerOfTwo(rz_size)); u32 res = Log2(rz_size) - 4; CHECK_EQ(rz_size, RZLog2Size(res)); return res; } static AsanAllocator &get_allocator(); // The memory chunk allocated from the underlying allocator looks like this: // L L L L L L H H U U U U U U R R // L -- left redzone words (0 or more bytes) // H -- ChunkHeader (16 bytes), which is also a part of the left redzone. // U -- user memory. // R -- right redzone (0 or more bytes) // ChunkBase consists of ChunkHeader and other bytes that overlap with user // memory. // If the left redzone is greater than the ChunkHeader size we store a magic // value in the first uptr word of the memory block and store the address of // ChunkBase in the next uptr. // M B L L L L L L L L L H H U U U U U U // | ^ // ---------------------| // M -- magic value kAllocBegMagic // B -- address of ChunkHeader pointing to the first 'H' static const uptr kAllocBegMagic = 0xCC6E96B9; struct ChunkHeader { // 1-st 8 bytes. u32 chunk_state : 8; // Must be first. u32 alloc_tid : 24; u32 free_tid : 24; u32 from_memalign : 1; u32 alloc_type : 2; u32 rz_log : 3; u32 lsan_tag : 2; // 2-nd 8 bytes // This field is used for small sizes. For large sizes it is equal to // SizeClassMap::kMaxSize and the actual size is stored in the // SecondaryAllocator's metadata. u32 user_requested_size; u32 alloc_context_id; }; struct ChunkBase : ChunkHeader { // Header2, intersects with user memory. u32 free_context_id; }; static const uptr kChunkHeaderSize = sizeof(ChunkHeader); static const uptr kChunkHeader2Size = sizeof(ChunkBase) - kChunkHeaderSize; COMPILER_CHECK(kChunkHeaderSize == 16); COMPILER_CHECK(kChunkHeader2Size <= 16); // Every chunk of memory allocated by this allocator can be in one of 3 states: // CHUNK_AVAILABLE: the chunk is in the free list and ready to be allocated. // CHUNK_ALLOCATED: the chunk is allocated and not yet freed. // CHUNK_QUARANTINE: the chunk was freed and put into quarantine zone. enum { CHUNK_AVAILABLE = 0, // 0 is the default value even if we didn't set it. CHUNK_ALLOCATED = 2, CHUNK_QUARANTINE = 3 }; struct AsanChunk: ChunkBase { uptr Beg() { return reinterpret_cast(this) + kChunkHeaderSize; } uptr UsedSize(bool locked_version = false) { if (user_requested_size != SizeClassMap::kMaxSize) return user_requested_size; return *reinterpret_cast( get_allocator().GetMetaData(AllocBeg(locked_version))); } void *AllocBeg(bool locked_version = false) { if (from_memalign) { if (locked_version) return get_allocator().GetBlockBeginFastLocked( reinterpret_cast(this)); return get_allocator().GetBlockBegin(reinterpret_cast(this)); } return reinterpret_cast(Beg() - RZLog2Size(rz_log)); } bool AddrIsInside(uptr addr, bool locked_version = false) { return (addr >= Beg()) && (addr < Beg() + UsedSize(locked_version)); } }; struct QuarantineCallback { explicit QuarantineCallback(AllocatorCache *cache) : cache_(cache) { } void Recycle(AsanChunk *m) { CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE); atomic_store((atomic_uint8_t*)m, CHUNK_AVAILABLE, memory_order_relaxed); CHECK_NE(m->alloc_tid, kInvalidTid); CHECK_NE(m->free_tid, kInvalidTid); PoisonShadow(m->Beg(), RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY), kAsanHeapLeftRedzoneMagic); void *p = reinterpret_cast(m->AllocBeg()); if (p != m) { uptr *alloc_magic = reinterpret_cast(p); CHECK_EQ(alloc_magic[0], kAllocBegMagic); // Clear the magic value, as allocator internals may overwrite the // contents of deallocated chunk, confusing GetAsanChunk lookup. alloc_magic[0] = 0; CHECK_EQ(alloc_magic[1], reinterpret_cast(m)); } // Statistics. AsanStats &thread_stats = GetCurrentThreadStats(); thread_stats.real_frees++; thread_stats.really_freed += m->UsedSize(); get_allocator().Deallocate(cache_, p); } void *Allocate(uptr size) { - return get_allocator().Allocate(cache_, size, 1); + void *res = get_allocator().Allocate(cache_, size, 1); + // TODO(alekseys): Consider making quarantine OOM-friendly. + if (UNLIKELY(!res)) + return DieOnFailure::OnOOM(); + return res; } void Deallocate(void *p) { get_allocator().Deallocate(cache_, p); } AllocatorCache *cache_; }; typedef Quarantine AsanQuarantine; typedef AsanQuarantine::Cache QuarantineCache; void AsanMapUnmapCallback::OnMap(uptr p, uptr size) const { PoisonShadow(p, size, kAsanHeapLeftRedzoneMagic); // Statistics. AsanStats &thread_stats = GetCurrentThreadStats(); thread_stats.mmaps++; thread_stats.mmaped += size; } void AsanMapUnmapCallback::OnUnmap(uptr p, uptr size) const { PoisonShadow(p, size, 0); // We are about to unmap a chunk of user memory. // Mark the corresponding shadow memory as not needed. FlushUnneededASanShadowMemory(p, size); // Statistics. AsanStats &thread_stats = GetCurrentThreadStats(); thread_stats.munmaps++; thread_stats.munmaped += size; } // We can not use THREADLOCAL because it is not supported on some of the // platforms we care about (OSX 10.6, Android). // static THREADLOCAL AllocatorCache cache; AllocatorCache *GetAllocatorCache(AsanThreadLocalMallocStorage *ms) { CHECK(ms); return &ms->allocator_cache; } QuarantineCache *GetQuarantineCache(AsanThreadLocalMallocStorage *ms) { CHECK(ms); CHECK_LE(sizeof(QuarantineCache), sizeof(ms->quarantine_cache)); return reinterpret_cast(ms->quarantine_cache); } void AllocatorOptions::SetFrom(const Flags *f, const CommonFlags *cf) { quarantine_size_mb = f->quarantine_size_mb; thread_local_quarantine_size_kb = f->thread_local_quarantine_size_kb; min_redzone = f->redzone; max_redzone = f->max_redzone; may_return_null = cf->allocator_may_return_null; alloc_dealloc_mismatch = f->alloc_dealloc_mismatch; release_to_os_interval_ms = cf->allocator_release_to_os_interval_ms; } void AllocatorOptions::CopyTo(Flags *f, CommonFlags *cf) { f->quarantine_size_mb = quarantine_size_mb; f->thread_local_quarantine_size_kb = thread_local_quarantine_size_kb; f->redzone = min_redzone; f->max_redzone = max_redzone; cf->allocator_may_return_null = may_return_null; f->alloc_dealloc_mismatch = alloc_dealloc_mismatch; cf->allocator_release_to_os_interval_ms = release_to_os_interval_ms; } struct Allocator { static const uptr kMaxAllowedMallocSize = FIRST_32_SECOND_64(3UL << 30, 1ULL << 40); AsanAllocator allocator; AsanQuarantine quarantine; StaticSpinMutex fallback_mutex; AllocatorCache fallback_allocator_cache; QuarantineCache fallback_quarantine_cache; atomic_uint8_t rss_limit_exceeded; // ------------------- Options -------------------------- atomic_uint16_t min_redzone; atomic_uint16_t max_redzone; atomic_uint8_t alloc_dealloc_mismatch; // ------------------- Initialization ------------------------ explicit Allocator(LinkerInitialized) : quarantine(LINKER_INITIALIZED), fallback_quarantine_cache(LINKER_INITIALIZED) {} void CheckOptions(const AllocatorOptions &options) const { CHECK_GE(options.min_redzone, 16); CHECK_GE(options.max_redzone, options.min_redzone); CHECK_LE(options.max_redzone, 2048); CHECK(IsPowerOfTwo(options.min_redzone)); CHECK(IsPowerOfTwo(options.max_redzone)); } void SharedInitCode(const AllocatorOptions &options) { CheckOptions(options); quarantine.Init((uptr)options.quarantine_size_mb << 20, (uptr)options.thread_local_quarantine_size_kb << 10); atomic_store(&alloc_dealloc_mismatch, options.alloc_dealloc_mismatch, memory_order_release); atomic_store(&min_redzone, options.min_redzone, memory_order_release); atomic_store(&max_redzone, options.max_redzone, memory_order_release); } void Initialize(const AllocatorOptions &options) { SetAllocatorMayReturnNull(options.may_return_null); allocator.Init(options.release_to_os_interval_ms); SharedInitCode(options); } bool RssLimitExceeded() { return atomic_load(&rss_limit_exceeded, memory_order_relaxed); } void SetRssLimitExceeded(bool limit_exceeded) { atomic_store(&rss_limit_exceeded, limit_exceeded, memory_order_relaxed); } void RePoisonChunk(uptr chunk) { // This could be a user-facing chunk (with redzones), or some internal // housekeeping chunk, like TransferBatch. Start by assuming the former. AsanChunk *ac = GetAsanChunk((void *)chunk); uptr allocated_size = allocator.GetActuallyAllocatedSize((void *)ac); uptr beg = ac->Beg(); uptr end = ac->Beg() + ac->UsedSize(true); uptr chunk_end = chunk + allocated_size; if (chunk < beg && beg < end && end <= chunk_end && ac->chunk_state == CHUNK_ALLOCATED) { // Looks like a valid AsanChunk in use, poison redzones only. PoisonShadow(chunk, beg - chunk, kAsanHeapLeftRedzoneMagic); uptr end_aligned_down = RoundDownTo(end, SHADOW_GRANULARITY); FastPoisonShadowPartialRightRedzone( end_aligned_down, end - end_aligned_down, chunk_end - end_aligned_down, kAsanHeapLeftRedzoneMagic); } else { // This is either not an AsanChunk or freed or quarantined AsanChunk. // In either case, poison everything. PoisonShadow(chunk, allocated_size, kAsanHeapLeftRedzoneMagic); } } void ReInitialize(const AllocatorOptions &options) { SetAllocatorMayReturnNull(options.may_return_null); allocator.SetReleaseToOSIntervalMs(options.release_to_os_interval_ms); SharedInitCode(options); // Poison all existing allocation's redzones. if (CanPoisonMemory()) { allocator.ForceLock(); allocator.ForEachChunk( [](uptr chunk, void *alloc) { ((Allocator *)alloc)->RePoisonChunk(chunk); }, this); allocator.ForceUnlock(); } } void GetOptions(AllocatorOptions *options) const { options->quarantine_size_mb = quarantine.GetSize() >> 20; options->thread_local_quarantine_size_kb = quarantine.GetCacheSize() >> 10; options->min_redzone = atomic_load(&min_redzone, memory_order_acquire); options->max_redzone = atomic_load(&max_redzone, memory_order_acquire); options->may_return_null = AllocatorMayReturnNull(); options->alloc_dealloc_mismatch = atomic_load(&alloc_dealloc_mismatch, memory_order_acquire); options->release_to_os_interval_ms = allocator.ReleaseToOSIntervalMs(); } // -------------------- Helper methods. ------------------------- uptr ComputeRZLog(uptr user_requested_size) { u32 rz_log = user_requested_size <= 64 - 16 ? 0 : user_requested_size <= 128 - 32 ? 1 : user_requested_size <= 512 - 64 ? 2 : user_requested_size <= 4096 - 128 ? 3 : user_requested_size <= (1 << 14) - 256 ? 4 : user_requested_size <= (1 << 15) - 512 ? 5 : user_requested_size <= (1 << 16) - 1024 ? 6 : 7; u32 min_rz = atomic_load(&min_redzone, memory_order_acquire); u32 max_rz = atomic_load(&max_redzone, memory_order_acquire); return Min(Max(rz_log, RZSize2Log(min_rz)), RZSize2Log(max_rz)); } // We have an address between two chunks, and we want to report just one. AsanChunk *ChooseChunk(uptr addr, AsanChunk *left_chunk, AsanChunk *right_chunk) { // Prefer an allocated chunk over freed chunk and freed chunk // over available chunk. if (left_chunk->chunk_state != right_chunk->chunk_state) { if (left_chunk->chunk_state == CHUNK_ALLOCATED) return left_chunk; if (right_chunk->chunk_state == CHUNK_ALLOCATED) return right_chunk; if (left_chunk->chunk_state == CHUNK_QUARANTINE) return left_chunk; if (right_chunk->chunk_state == CHUNK_QUARANTINE) return right_chunk; } // Same chunk_state: choose based on offset. sptr l_offset = 0, r_offset = 0; CHECK(AsanChunkView(left_chunk).AddrIsAtRight(addr, 1, &l_offset)); CHECK(AsanChunkView(right_chunk).AddrIsAtLeft(addr, 1, &r_offset)); if (l_offset < r_offset) return left_chunk; return right_chunk; } // -------------------- Allocation/Deallocation routines --------------- void *Allocate(uptr size, uptr alignment, BufferedStackTrace *stack, AllocType alloc_type, bool can_fill) { if (UNLIKELY(!asan_inited)) AsanInitFromRtl(); if (RssLimitExceeded()) return AsanAllocator::FailureHandler::OnOOM(); Flags &fl = *flags(); CHECK(stack); const uptr min_alignment = SHADOW_GRANULARITY; if (alignment < min_alignment) alignment = min_alignment; if (size == 0) { // We'd be happy to avoid allocating memory for zero-size requests, but // some programs/tests depend on this behavior and assume that malloc // would not return NULL even for zero-size allocations. Moreover, it // looks like operator new should never return NULL, and results of // consecutive "new" calls must be different even if the allocated size // is zero. size = 1; } CHECK(IsPowerOfTwo(alignment)); uptr rz_log = ComputeRZLog(size); uptr rz_size = RZLog2Size(rz_log); uptr rounded_size = RoundUpTo(Max(size, kChunkHeader2Size), alignment); uptr needed_size = rounded_size + rz_size; if (alignment > min_alignment) needed_size += alignment; bool using_primary_allocator = true; // If we are allocating from the secondary allocator, there will be no // automatic right redzone, so add the right redzone manually. if (!PrimaryAllocator::CanAllocate(needed_size, alignment)) { needed_size += rz_size; using_primary_allocator = false; } CHECK(IsAligned(needed_size, min_alignment)); if (size > kMaxAllowedMallocSize || needed_size > kMaxAllowedMallocSize) { Report("WARNING: AddressSanitizer failed to allocate 0x%zx bytes\n", (void*)size); return AsanAllocator::FailureHandler::OnBadRequest(); } AsanThread *t = GetCurrentThread(); void *allocated; if (t) { AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage()); allocated = allocator.Allocate(cache, needed_size, 8); } else { SpinMutexLock l(&fallback_mutex); AllocatorCache *cache = &fallback_allocator_cache; allocated = allocator.Allocate(cache, needed_size, 8); } if (!allocated) return nullptr; if (*(u8 *)MEM_TO_SHADOW((uptr)allocated) == 0 && CanPoisonMemory()) { // Heap poisoning is enabled, but the allocator provides an unpoisoned // chunk. This is possible if CanPoisonMemory() was false for some // time, for example, due to flags()->start_disabled. // Anyway, poison the block before using it for anything else. uptr allocated_size = allocator.GetActuallyAllocatedSize(allocated); PoisonShadow((uptr)allocated, allocated_size, kAsanHeapLeftRedzoneMagic); } uptr alloc_beg = reinterpret_cast(allocated); uptr alloc_end = alloc_beg + needed_size; uptr beg_plus_redzone = alloc_beg + rz_size; uptr user_beg = beg_plus_redzone; if (!IsAligned(user_beg, alignment)) user_beg = RoundUpTo(user_beg, alignment); uptr user_end = user_beg + size; CHECK_LE(user_end, alloc_end); uptr chunk_beg = user_beg - kChunkHeaderSize; AsanChunk *m = reinterpret_cast(chunk_beg); m->alloc_type = alloc_type; m->rz_log = rz_log; u32 alloc_tid = t ? t->tid() : 0; m->alloc_tid = alloc_tid; CHECK_EQ(alloc_tid, m->alloc_tid); // Does alloc_tid fit into the bitfield? m->free_tid = kInvalidTid; m->from_memalign = user_beg != beg_plus_redzone; if (alloc_beg != chunk_beg) { CHECK_LE(alloc_beg+ 2 * sizeof(uptr), chunk_beg); reinterpret_cast(alloc_beg)[0] = kAllocBegMagic; reinterpret_cast(alloc_beg)[1] = chunk_beg; } if (using_primary_allocator) { CHECK(size); m->user_requested_size = size; CHECK(allocator.FromPrimary(allocated)); } else { CHECK(!allocator.FromPrimary(allocated)); m->user_requested_size = SizeClassMap::kMaxSize; uptr *meta = reinterpret_cast(allocator.GetMetaData(allocated)); meta[0] = size; meta[1] = chunk_beg; } m->alloc_context_id = StackDepotPut(*stack); uptr size_rounded_down_to_granularity = RoundDownTo(size, SHADOW_GRANULARITY); // Unpoison the bulk of the memory region. if (size_rounded_down_to_granularity) PoisonShadow(user_beg, size_rounded_down_to_granularity, 0); // Deal with the end of the region if size is not aligned to granularity. if (size != size_rounded_down_to_granularity && CanPoisonMemory()) { u8 *shadow = (u8 *)MemToShadow(user_beg + size_rounded_down_to_granularity); *shadow = fl.poison_partial ? (size & (SHADOW_GRANULARITY - 1)) : 0; } AsanStats &thread_stats = GetCurrentThreadStats(); thread_stats.mallocs++; thread_stats.malloced += size; thread_stats.malloced_redzones += needed_size - size; if (needed_size > SizeClassMap::kMaxSize) thread_stats.malloc_large++; else thread_stats.malloced_by_size[SizeClassMap::ClassID(needed_size)]++; void *res = reinterpret_cast(user_beg); if (can_fill && fl.max_malloc_fill_size) { uptr fill_size = Min(size, (uptr)fl.max_malloc_fill_size); REAL(memset)(res, fl.malloc_fill_byte, fill_size); } #if CAN_SANITIZE_LEAKS m->lsan_tag = __lsan::DisabledInThisThread() ? __lsan::kIgnored : __lsan::kDirectlyLeaked; #endif // Must be the last mutation of metadata in this function. atomic_store((atomic_uint8_t *)m, CHUNK_ALLOCATED, memory_order_release); ASAN_MALLOC_HOOK(res, size); return res; } // Set quarantine flag if chunk is allocated, issue ASan error report on // available and quarantined chunks. Return true on success, false otherwise. bool AtomicallySetQuarantineFlagIfAllocated(AsanChunk *m, void *ptr, BufferedStackTrace *stack) { u8 old_chunk_state = CHUNK_ALLOCATED; // Flip the chunk_state atomically to avoid race on double-free. if (!atomic_compare_exchange_strong((atomic_uint8_t *)m, &old_chunk_state, CHUNK_QUARANTINE, memory_order_acquire)) { ReportInvalidFree(ptr, old_chunk_state, stack); // It's not safe to push a chunk in quarantine on invalid free. return false; } CHECK_EQ(CHUNK_ALLOCATED, old_chunk_state); return true; } // Expects the chunk to already be marked as quarantined by using // AtomicallySetQuarantineFlagIfAllocated. - void QuarantineChunk(AsanChunk *m, void *ptr, BufferedStackTrace *stack, - AllocType alloc_type) { + void QuarantineChunk(AsanChunk *m, void *ptr, BufferedStackTrace *stack) { CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE); CHECK_GE(m->alloc_tid, 0); if (SANITIZER_WORDSIZE == 64) // On 32-bits this resides in user area. CHECK_EQ(m->free_tid, kInvalidTid); AsanThread *t = GetCurrentThread(); m->free_tid = t ? t->tid() : 0; m->free_context_id = StackDepotPut(*stack); Flags &fl = *flags(); if (fl.max_free_fill_size > 0) { // We have to skip the chunk header, it contains free_context_id. uptr scribble_start = (uptr)m + kChunkHeaderSize + kChunkHeader2Size; if (m->UsedSize() >= kChunkHeader2Size) { // Skip Header2 in user area. uptr size_to_fill = m->UsedSize() - kChunkHeader2Size; size_to_fill = Min(size_to_fill, (uptr)fl.max_free_fill_size); REAL(memset)((void *)scribble_start, fl.free_fill_byte, size_to_fill); } } // Poison the region. PoisonShadow(m->Beg(), RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY), kAsanHeapFreeMagic); AsanStats &thread_stats = GetCurrentThreadStats(); thread_stats.frees++; thread_stats.freed += m->UsedSize(); // Push into quarantine. if (t) { AsanThreadLocalMallocStorage *ms = &t->malloc_storage(); AllocatorCache *ac = GetAllocatorCache(ms); quarantine.Put(GetQuarantineCache(ms), QuarantineCallback(ac), m, m->UsedSize()); } else { SpinMutexLock l(&fallback_mutex); AllocatorCache *ac = &fallback_allocator_cache; quarantine.Put(&fallback_quarantine_cache, QuarantineCallback(ac), m, m->UsedSize()); } } void Deallocate(void *ptr, uptr delete_size, BufferedStackTrace *stack, AllocType alloc_type) { uptr p = reinterpret_cast(ptr); if (p == 0) return; uptr chunk_beg = p - kChunkHeaderSize; AsanChunk *m = reinterpret_cast(chunk_beg); // On Windows, uninstrumented DLLs may allocate memory before ASan hooks // malloc. Don't report an invalid free in this case. if (SANITIZER_WINDOWS && !get_allocator().PointerIsMine(ptr)) { if (!IsSystemHeapAddress(p)) ReportFreeNotMalloced(p, stack); return; } ASAN_FREE_HOOK(ptr); // Must mark the chunk as quarantined before any changes to its metadata. // Do not quarantine given chunk if we failed to set CHUNK_QUARANTINE flag. if (!AtomicallySetQuarantineFlagIfAllocated(m, ptr, stack)) return; if (m->alloc_type != alloc_type) { if (atomic_load(&alloc_dealloc_mismatch, memory_order_acquire)) { ReportAllocTypeMismatch((uptr)ptr, stack, (AllocType)m->alloc_type, (AllocType)alloc_type); } } if (delete_size && flags()->new_delete_type_mismatch && delete_size != m->UsedSize()) { ReportNewDeleteSizeMismatch(p, delete_size, stack); } - QuarantineChunk(m, ptr, stack, alloc_type); + QuarantineChunk(m, ptr, stack); } void *Reallocate(void *old_ptr, uptr new_size, BufferedStackTrace *stack) { CHECK(old_ptr && new_size); uptr p = reinterpret_cast(old_ptr); uptr chunk_beg = p - kChunkHeaderSize; AsanChunk *m = reinterpret_cast(chunk_beg); AsanStats &thread_stats = GetCurrentThreadStats(); thread_stats.reallocs++; thread_stats.realloced += new_size; void *new_ptr = Allocate(new_size, 8, stack, FROM_MALLOC, true); if (new_ptr) { u8 chunk_state = m->chunk_state; if (chunk_state != CHUNK_ALLOCATED) ReportInvalidFree(old_ptr, chunk_state, stack); CHECK_NE(REAL(memcpy), nullptr); uptr memcpy_size = Min(new_size, m->UsedSize()); // If realloc() races with free(), we may start copying freed memory. // However, we will report racy double-free later anyway. REAL(memcpy)(new_ptr, old_ptr, memcpy_size); Deallocate(old_ptr, 0, stack, FROM_MALLOC); } return new_ptr; } void *Calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) { - if (CallocShouldReturnNullDueToOverflow(size, nmemb)) + if (CheckForCallocOverflow(size, nmemb)) return AsanAllocator::FailureHandler::OnBadRequest(); void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC, false); // If the memory comes from the secondary allocator no need to clear it // as it comes directly from mmap. if (ptr && allocator.FromPrimary(ptr)) REAL(memset)(ptr, 0, nmemb * size); return ptr; } void ReportInvalidFree(void *ptr, u8 chunk_state, BufferedStackTrace *stack) { if (chunk_state == CHUNK_QUARANTINE) ReportDoubleFree((uptr)ptr, stack); else ReportFreeNotMalloced((uptr)ptr, stack); } void CommitBack(AsanThreadLocalMallocStorage *ms) { AllocatorCache *ac = GetAllocatorCache(ms); quarantine.Drain(GetQuarantineCache(ms), QuarantineCallback(ac)); allocator.SwallowCache(ac); } // -------------------------- Chunk lookup ---------------------- // Assumes alloc_beg == allocator.GetBlockBegin(alloc_beg). AsanChunk *GetAsanChunk(void *alloc_beg) { if (!alloc_beg) return nullptr; if (!allocator.FromPrimary(alloc_beg)) { uptr *meta = reinterpret_cast(allocator.GetMetaData(alloc_beg)); AsanChunk *m = reinterpret_cast(meta[1]); return m; } uptr *alloc_magic = reinterpret_cast(alloc_beg); if (alloc_magic[0] == kAllocBegMagic) return reinterpret_cast(alloc_magic[1]); return reinterpret_cast(alloc_beg); } AsanChunk *GetAsanChunkByAddr(uptr p) { void *alloc_beg = allocator.GetBlockBegin(reinterpret_cast(p)); return GetAsanChunk(alloc_beg); } // Allocator must be locked when this function is called. AsanChunk *GetAsanChunkByAddrFastLocked(uptr p) { void *alloc_beg = allocator.GetBlockBeginFastLocked(reinterpret_cast(p)); return GetAsanChunk(alloc_beg); } uptr AllocationSize(uptr p) { AsanChunk *m = GetAsanChunkByAddr(p); if (!m) return 0; if (m->chunk_state != CHUNK_ALLOCATED) return 0; if (m->Beg() != p) return 0; return m->UsedSize(); } AsanChunkView FindHeapChunkByAddress(uptr addr) { AsanChunk *m1 = GetAsanChunkByAddr(addr); if (!m1) return AsanChunkView(m1); sptr offset = 0; if (AsanChunkView(m1).AddrIsAtLeft(addr, 1, &offset)) { // The address is in the chunk's left redzone, so maybe it is actually // a right buffer overflow from the other chunk to the left. // Search a bit to the left to see if there is another chunk. AsanChunk *m2 = nullptr; for (uptr l = 1; l < GetPageSizeCached(); l++) { m2 = GetAsanChunkByAddr(addr - l); if (m2 == m1) continue; // Still the same chunk. break; } if (m2 && AsanChunkView(m2).AddrIsAtRight(addr, 1, &offset)) m1 = ChooseChunk(addr, m2, m1); } return AsanChunkView(m1); } void PrintStats() { allocator.PrintStats(); quarantine.PrintStats(); } void ForceLock() { allocator.ForceLock(); fallback_mutex.Lock(); } void ForceUnlock() { fallback_mutex.Unlock(); allocator.ForceUnlock(); } }; static Allocator instance(LINKER_INITIALIZED); static AsanAllocator &get_allocator() { return instance.allocator; } bool AsanChunkView::IsValid() const { return chunk_ && chunk_->chunk_state != CHUNK_AVAILABLE; } bool AsanChunkView::IsAllocated() const { return chunk_ && chunk_->chunk_state == CHUNK_ALLOCATED; } bool AsanChunkView::IsQuarantined() const { return chunk_ && chunk_->chunk_state == CHUNK_QUARANTINE; } uptr AsanChunkView::Beg() const { return chunk_->Beg(); } uptr AsanChunkView::End() const { return Beg() + UsedSize(); } uptr AsanChunkView::UsedSize() const { return chunk_->UsedSize(); } uptr AsanChunkView::AllocTid() const { return chunk_->alloc_tid; } uptr AsanChunkView::FreeTid() const { return chunk_->free_tid; } AllocType AsanChunkView::GetAllocType() const { return (AllocType)chunk_->alloc_type; } static StackTrace GetStackTraceFromId(u32 id) { CHECK(id); StackTrace res = StackDepotGet(id); CHECK(res.trace); return res; } u32 AsanChunkView::GetAllocStackId() const { return chunk_->alloc_context_id; } u32 AsanChunkView::GetFreeStackId() const { return chunk_->free_context_id; } StackTrace AsanChunkView::GetAllocStack() const { return GetStackTraceFromId(GetAllocStackId()); } StackTrace AsanChunkView::GetFreeStack() const { return GetStackTraceFromId(GetFreeStackId()); } void InitializeAllocator(const AllocatorOptions &options) { instance.Initialize(options); } void ReInitializeAllocator(const AllocatorOptions &options) { instance.ReInitialize(options); } void GetAllocatorOptions(AllocatorOptions *options) { instance.GetOptions(options); } AsanChunkView FindHeapChunkByAddress(uptr addr) { return instance.FindHeapChunkByAddress(addr); } AsanChunkView FindHeapChunkByAllocBeg(uptr addr) { return AsanChunkView(instance.GetAsanChunk(reinterpret_cast(addr))); } void AsanThreadLocalMallocStorage::CommitBack() { instance.CommitBack(this); } void PrintInternalAllocatorStats() { instance.PrintStats(); } void *asan_memalign(uptr alignment, uptr size, BufferedStackTrace *stack, AllocType alloc_type) { return instance.Allocate(size, alignment, stack, alloc_type, true); } void asan_free(void *ptr, BufferedStackTrace *stack, AllocType alloc_type) { instance.Deallocate(ptr, 0, stack, alloc_type); } void asan_sized_free(void *ptr, uptr size, BufferedStackTrace *stack, AllocType alloc_type) { instance.Deallocate(ptr, size, stack, alloc_type); } void *asan_malloc(uptr size, BufferedStackTrace *stack) { return instance.Allocate(size, 8, stack, FROM_MALLOC, true); } void *asan_calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) { return instance.Calloc(nmemb, size, stack); } void *asan_realloc(void *p, uptr size, BufferedStackTrace *stack) { if (!p) return instance.Allocate(size, 8, stack, FROM_MALLOC, true); if (size == 0) { if (flags()->allocator_frees_and_returns_null_on_realloc_zero) { instance.Deallocate(p, 0, stack, FROM_MALLOC); return nullptr; } // Allocate a size of 1 if we shouldn't free() on Realloc to 0 size = 1; } return instance.Reallocate(p, size, stack); } void *asan_valloc(uptr size, BufferedStackTrace *stack) { return instance.Allocate(size, GetPageSizeCached(), stack, FROM_MALLOC, true); } void *asan_pvalloc(uptr size, BufferedStackTrace *stack) { uptr PageSize = GetPageSizeCached(); size = RoundUpTo(size, PageSize); if (size == 0) { // pvalloc(0) should allocate one page. size = PageSize; } return instance.Allocate(size, PageSize, stack, FROM_MALLOC, true); } int asan_posix_memalign(void **memptr, uptr alignment, uptr size, BufferedStackTrace *stack) { void *ptr = instance.Allocate(size, alignment, stack, FROM_MALLOC, true); CHECK(IsAligned((uptr)ptr, alignment)); *memptr = ptr; return 0; } uptr asan_malloc_usable_size(const void *ptr, uptr pc, uptr bp) { if (!ptr) return 0; uptr usable_size = instance.AllocationSize(reinterpret_cast(ptr)); if (flags()->check_malloc_usable_size && (usable_size == 0)) { GET_STACK_TRACE_FATAL(pc, bp); ReportMallocUsableSizeNotOwned((uptr)ptr, &stack); } return usable_size; } uptr asan_mz_size(const void *ptr) { return instance.AllocationSize(reinterpret_cast(ptr)); } void asan_mz_force_lock() { instance.ForceLock(); } void asan_mz_force_unlock() { instance.ForceUnlock(); } void AsanSoftRssLimitExceededCallback(bool limit_exceeded) { instance.SetRssLimitExceeded(limit_exceeded); } } // namespace __asan // --- Implementation of LSan-specific functions --- {{{1 namespace __lsan { void LockAllocator() { __asan::get_allocator().ForceLock(); } void UnlockAllocator() { __asan::get_allocator().ForceUnlock(); } void GetAllocatorGlobalRange(uptr *begin, uptr *end) { *begin = (uptr)&__asan::get_allocator(); *end = *begin + sizeof(__asan::get_allocator()); } uptr PointsIntoChunk(void* p) { uptr addr = reinterpret_cast(p); __asan::AsanChunk *m = __asan::instance.GetAsanChunkByAddrFastLocked(addr); if (!m) return 0; uptr chunk = m->Beg(); if (m->chunk_state != __asan::CHUNK_ALLOCATED) return 0; if (m->AddrIsInside(addr, /*locked_version=*/true)) return chunk; if (IsSpecialCaseOfOperatorNew0(chunk, m->UsedSize(/*locked_version*/ true), addr)) return chunk; return 0; } uptr GetUserBegin(uptr chunk) { __asan::AsanChunk *m = __asan::instance.GetAsanChunkByAddrFastLocked(chunk); CHECK(m); return m->Beg(); } LsanMetadata::LsanMetadata(uptr chunk) { metadata_ = reinterpret_cast(chunk - __asan::kChunkHeaderSize); } bool LsanMetadata::allocated() const { __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); return m->chunk_state == __asan::CHUNK_ALLOCATED; } ChunkTag LsanMetadata::tag() const { __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); return static_cast(m->lsan_tag); } void LsanMetadata::set_tag(ChunkTag value) { __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); m->lsan_tag = value; } uptr LsanMetadata::requested_size() const { __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); return m->UsedSize(/*locked_version=*/true); } u32 LsanMetadata::stack_trace_id() const { __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); return m->alloc_context_id; } void ForEachChunk(ForEachChunkCallback callback, void *arg) { __asan::get_allocator().ForEachChunk(callback, arg); } IgnoreObjectResult IgnoreObjectLocked(const void *p) { uptr addr = reinterpret_cast(p); __asan::AsanChunk *m = __asan::instance.GetAsanChunkByAddr(addr); if (!m) return kIgnoreObjectInvalid; if ((m->chunk_state == __asan::CHUNK_ALLOCATED) && m->AddrIsInside(addr)) { if (m->lsan_tag == kIgnored) return kIgnoreObjectAlreadyIgnored; m->lsan_tag = __lsan::kIgnored; return kIgnoreObjectSuccess; } else { return kIgnoreObjectInvalid; } } } // namespace __lsan // ---------------------- Interface ---------------- {{{1 using namespace __asan; // NOLINT // ASan allocator doesn't reserve extra bytes, so normally we would // just return "size". We don't want to expose our redzone sizes, etc here. uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; } int __sanitizer_get_ownership(const void *p) { uptr ptr = reinterpret_cast(p); return instance.AllocationSize(ptr) > 0; } uptr __sanitizer_get_allocated_size(const void *p) { if (!p) return 0; uptr ptr = reinterpret_cast(p); uptr allocated_size = instance.AllocationSize(ptr); // Die if p is not malloced or if it is already freed. if (allocated_size == 0) { GET_STACK_TRACE_FATAL_HERE; ReportSanitizerGetAllocatedSizeNotOwned(ptr, &stack); } return allocated_size; } #if !SANITIZER_SUPPORTS_WEAK_HOOKS // Provide default (no-op) implementation of malloc hooks. SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_malloc_hook, void *ptr, uptr size) { (void)ptr; (void)size; } SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_free_hook, void *ptr) { (void)ptr; } #endif Index: vendor/compiler-rt/dist/lib/asan/asan_interceptors.cc =================================================================== --- vendor/compiler-rt/dist/lib/asan/asan_interceptors.cc (revision 320536) +++ vendor/compiler-rt/dist/lib/asan/asan_interceptors.cc (revision 320537) @@ -1,795 +1,783 @@ //===-- asan_interceptors.cc ----------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // // Intercept various libc functions. //===----------------------------------------------------------------------===// #include "asan_interceptors.h" #include "asan_allocator.h" #include "asan_internal.h" #include "asan_mapping.h" #include "asan_poisoning.h" #include "asan_report.h" #include "asan_stack.h" #include "asan_stats.h" #include "asan_suppressions.h" #include "lsan/lsan_common.h" #include "sanitizer_common/sanitizer_libc.h" #if SANITIZER_POSIX #include "sanitizer_common/sanitizer_posix.h" #endif #if defined(__i386) && SANITIZER_LINUX #define ASAN_PTHREAD_CREATE_VERSION "GLIBC_2.1" #elif defined(__mips__) && SANITIZER_LINUX #define ASAN_PTHREAD_CREATE_VERSION "GLIBC_2.2" #endif namespace __asan { // Return true if we can quickly decide that the region is unpoisoned. // We assume that a redzone is at least 16 bytes. static inline bool QuickCheckForUnpoisonedRegion(uptr beg, uptr size) { if (size == 0) return true; if (size <= 32) return !AddressIsPoisoned(beg) && !AddressIsPoisoned(beg + size - 1) && !AddressIsPoisoned(beg + size / 2); if (size <= 64) return !AddressIsPoisoned(beg) && !AddressIsPoisoned(beg + size / 4) && !AddressIsPoisoned(beg + size - 1) && !AddressIsPoisoned(beg + 3 * size / 4) && !AddressIsPoisoned(beg + size / 2); return false; } struct AsanInterceptorContext { const char *interceptor_name; }; // We implement ACCESS_MEMORY_RANGE, ASAN_READ_RANGE, // and ASAN_WRITE_RANGE as macro instead of function so // that no extra frames are created, and stack trace contains // relevant information only. // We check all shadow bytes. #define ACCESS_MEMORY_RANGE(ctx, offset, size, isWrite) do { \ uptr __offset = (uptr)(offset); \ uptr __size = (uptr)(size); \ uptr __bad = 0; \ if (__offset > __offset + __size) { \ GET_STACK_TRACE_FATAL_HERE; \ ReportStringFunctionSizeOverflow(__offset, __size, &stack); \ } \ if (!QuickCheckForUnpoisonedRegion(__offset, __size) && \ (__bad = __asan_region_is_poisoned(__offset, __size))) { \ AsanInterceptorContext *_ctx = (AsanInterceptorContext *)ctx; \ bool suppressed = false; \ if (_ctx) { \ suppressed = IsInterceptorSuppressed(_ctx->interceptor_name); \ if (!suppressed && HaveStackTraceBasedSuppressions()) { \ GET_STACK_TRACE_FATAL_HERE; \ suppressed = IsStackTraceSuppressed(&stack); \ } \ } \ if (!suppressed) { \ GET_CURRENT_PC_BP_SP; \ ReportGenericError(pc, bp, sp, __bad, isWrite, __size, 0, false);\ } \ } \ } while (0) // memcpy is called during __asan_init() from the internals of printf(...). // We do not treat memcpy with to==from as a bug. // See http://llvm.org/bugs/show_bug.cgi?id=11763. #define ASAN_MEMCPY_IMPL(ctx, to, from, size) \ do { \ if (UNLIKELY(!asan_inited)) return internal_memcpy(to, from, size); \ if (asan_init_is_running) { \ return REAL(memcpy)(to, from, size); \ } \ ENSURE_ASAN_INITED(); \ if (flags()->replace_intrin) { \ if (to != from) { \ CHECK_RANGES_OVERLAP("memcpy", to, size, from, size); \ } \ ASAN_READ_RANGE(ctx, from, size); \ ASAN_WRITE_RANGE(ctx, to, size); \ } \ return REAL(memcpy)(to, from, size); \ } while (0) // memset is called inside Printf. #define ASAN_MEMSET_IMPL(ctx, block, c, size) \ do { \ if (UNLIKELY(!asan_inited)) return internal_memset(block, c, size); \ if (asan_init_is_running) { \ return REAL(memset)(block, c, size); \ } \ ENSURE_ASAN_INITED(); \ if (flags()->replace_intrin) { \ ASAN_WRITE_RANGE(ctx, block, size); \ } \ return REAL(memset)(block, c, size); \ } while (0) #define ASAN_MEMMOVE_IMPL(ctx, to, from, size) \ do { \ if (UNLIKELY(!asan_inited)) return internal_memmove(to, from, size); \ ENSURE_ASAN_INITED(); \ if (flags()->replace_intrin) { \ ASAN_READ_RANGE(ctx, from, size); \ ASAN_WRITE_RANGE(ctx, to, size); \ } \ return internal_memmove(to, from, size); \ } while (0) #define ASAN_READ_RANGE(ctx, offset, size) \ ACCESS_MEMORY_RANGE(ctx, offset, size, false) #define ASAN_WRITE_RANGE(ctx, offset, size) \ ACCESS_MEMORY_RANGE(ctx, offset, size, true) #define ASAN_READ_STRING_OF_LEN(ctx, s, len, n) \ ASAN_READ_RANGE((ctx), (s), \ common_flags()->strict_string_checks ? (len) + 1 : (n)) #define ASAN_READ_STRING(ctx, s, n) \ ASAN_READ_STRING_OF_LEN((ctx), (s), REAL(strlen)(s), (n)) // Behavior of functions like "memcpy" or "strcpy" is undefined // if memory intervals overlap. We report error in this case. // Macro is used to avoid creation of new frames. static inline bool RangesOverlap(const char *offset1, uptr length1, const char *offset2, uptr length2) { return !((offset1 + length1 <= offset2) || (offset2 + length2 <= offset1)); } #define CHECK_RANGES_OVERLAP(name, _offset1, length1, _offset2, length2) do { \ const char *offset1 = (const char*)_offset1; \ const char *offset2 = (const char*)_offset2; \ if (RangesOverlap(offset1, length1, offset2, length2)) { \ GET_STACK_TRACE_FATAL_HERE; \ ReportStringFunctionMemoryRangesOverlap(name, offset1, length1, \ offset2, length2, &stack); \ } \ } while (0) static inline uptr MaybeRealStrnlen(const char *s, uptr maxlen) { #if SANITIZER_INTERCEPT_STRNLEN if (REAL(strnlen)) { return REAL(strnlen)(s, maxlen); } #endif return internal_strnlen(s, maxlen); } void SetThreadName(const char *name) { AsanThread *t = GetCurrentThread(); if (t) asanThreadRegistry().SetThreadName(t->tid(), name); } int OnExit() { // FIXME: ask frontend whether we need to return failure. return 0; } } // namespace __asan // ---------------------- Wrappers ---------------- {{{1 using namespace __asan; // NOLINT DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, uptr) DECLARE_REAL_AND_INTERCEPTOR(void, free, void *) #define ASAN_INTERCEPTOR_ENTER(ctx, func) \ AsanInterceptorContext _ctx = {#func}; \ ctx = (void *)&_ctx; \ (void) ctx; \ #define COMMON_INTERCEPT_FUNCTION(name) ASAN_INTERCEPT_FUNC(name) #define COMMON_INTERCEPT_FUNCTION_VER(name, ver) \ ASAN_INTERCEPT_FUNC_VER(name, ver) #define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ ASAN_WRITE_RANGE(ctx, ptr, size) #define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \ ASAN_READ_RANGE(ctx, ptr, size) #define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ ASAN_INTERCEPTOR_ENTER(ctx, func); \ do { \ if (asan_init_is_running) \ return REAL(func)(__VA_ARGS__); \ if (SANITIZER_MAC && UNLIKELY(!asan_inited)) \ return REAL(func)(__VA_ARGS__); \ ENSURE_ASAN_INITED(); \ } while (false) #define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path) \ do { \ } while (false) #define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \ do { \ } while (false) #define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \ do { \ } while (false) #define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \ do { \ } while (false) #define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) SetThreadName(name) // Should be asanThreadRegistry().SetThreadNameByUserId(thread, name) // But asan does not remember UserId's for threads (pthread_t); // and remembers all ever existed threads, so the linear search by UserId // can be slow. #define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \ do { \ } while (false) #define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name) // Strict init-order checking is dlopen-hostile: // https://github.com/google/sanitizers/issues/178 #define COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag) \ do { \ if (flags()->strict_init_order) \ StopInitOrderChecking(); \ CheckNoDeepBind(filename, flag); \ } while (false) #define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit() #define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle) #define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() #define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (!asan_inited) #define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) \ if (AsanThread *t = GetCurrentThread()) { \ *begin = t->tls_begin(); \ *end = t->tls_end(); \ } else { \ *begin = *end = 0; \ } #define COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, to, from, size) \ do { \ ASAN_INTERCEPTOR_ENTER(ctx, memmove); \ ASAN_MEMMOVE_IMPL(ctx, to, from, size); \ } while (false) #define COMMON_INTERCEPTOR_MEMCPY_IMPL(ctx, to, from, size) \ do { \ ASAN_INTERCEPTOR_ENTER(ctx, memcpy); \ ASAN_MEMCPY_IMPL(ctx, to, from, size); \ } while (false) #define COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, c, size) \ do { \ ASAN_INTERCEPTOR_ENTER(ctx, memset); \ ASAN_MEMSET_IMPL(ctx, block, c, size); \ } while (false) #include "sanitizer_common/sanitizer_common_interceptors.inc" // Syscall interceptors don't have contexts, we don't support suppressions // for them. #define COMMON_SYSCALL_PRE_READ_RANGE(p, s) ASAN_READ_RANGE(nullptr, p, s) #define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) ASAN_WRITE_RANGE(nullptr, p, s) #define COMMON_SYSCALL_POST_READ_RANGE(p, s) \ do { \ (void)(p); \ (void)(s); \ } while (false) #define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) \ do { \ (void)(p); \ (void)(s); \ } while (false) #include "sanitizer_common/sanitizer_common_syscalls.inc" struct ThreadStartParam { atomic_uintptr_t t; atomic_uintptr_t is_registered; }; #if ASAN_INTERCEPT_PTHREAD_CREATE static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) { ThreadStartParam *param = reinterpret_cast(arg); AsanThread *t = nullptr; while ((t = reinterpret_cast( atomic_load(¶m->t, memory_order_acquire))) == nullptr) internal_sched_yield(); SetCurrentThread(t); return t->ThreadStart(GetTid(), ¶m->is_registered); } INTERCEPTOR(int, pthread_create, void *thread, void *attr, void *(*start_routine)(void*), void *arg) { EnsureMainThreadIDIsCorrect(); // Strict init-order checking is thread-hostile. if (flags()->strict_init_order) StopInitOrderChecking(); GET_STACK_TRACE_THREAD; int detached = 0; if (attr) REAL(pthread_attr_getdetachstate)(attr, &detached); ThreadStartParam param; atomic_store(¶m.t, 0, memory_order_relaxed); atomic_store(¶m.is_registered, 0, memory_order_relaxed); int result; { // Ignore all allocations made by pthread_create: thread stack/TLS may be // stored by pthread for future reuse even after thread destruction, and // the linked list it's stored in doesn't even hold valid pointers to the // objects, the latter are calculated by obscure pointer arithmetic. #if CAN_SANITIZE_LEAKS __lsan::ScopedInterceptorDisabler disabler; #endif result = REAL(pthread_create)(thread, attr, asan_thread_start, ¶m); } if (result == 0) { u32 current_tid = GetCurrentTidOrInvalid(); AsanThread *t = AsanThread::Create(start_routine, arg, current_tid, &stack, detached); atomic_store(¶m.t, reinterpret_cast(t), memory_order_release); // Wait until the AsanThread object is initialized and the ThreadRegistry // entry is in "started" state. One reason for this is that after this // interceptor exits, the child thread's stack may be the only thing holding // the |arg| pointer. This may cause LSan to report a leak if leak checking // happens at a point when the interceptor has already exited, but the stack // range for the child thread is not yet known. while (atomic_load(¶m.is_registered, memory_order_acquire) == 0) internal_sched_yield(); } return result; } INTERCEPTOR(int, pthread_join, void *t, void **arg) { return real_pthread_join(t, arg); } DEFINE_REAL_PTHREAD_FUNCTIONS #endif // ASAN_INTERCEPT_PTHREAD_CREATE #if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION #if SANITIZER_ANDROID INTERCEPTOR(void*, bsd_signal, int signum, void *handler) { if (GetHandleSignalMode(signum) != kHandleSignalExclusive) return REAL(bsd_signal)(signum, handler); return 0; } #endif INTERCEPTOR(void*, signal, int signum, void *handler) { if (GetHandleSignalMode(signum) != kHandleSignalExclusive) return REAL(signal)(signum, handler); return nullptr; } INTERCEPTOR(int, sigaction, int signum, const struct sigaction *act, struct sigaction *oldact) { if (GetHandleSignalMode(signum) != kHandleSignalExclusive) return REAL(sigaction)(signum, act, oldact); return 0; } namespace __sanitizer { int real_sigaction(int signum, const void *act, void *oldact) { return REAL(sigaction)(signum, (const struct sigaction *)act, (struct sigaction *)oldact); } } // namespace __sanitizer #elif SANITIZER_POSIX // We need to have defined REAL(sigaction) on posix systems. DEFINE_REAL(int, sigaction, int signum, const struct sigaction *act, struct sigaction *oldact) #endif // ASAN_INTERCEPT_SIGNAL_AND_SIGACTION #if ASAN_INTERCEPT_SWAPCONTEXT static void ClearShadowMemoryForContextStack(uptr stack, uptr ssize) { // Align to page size. uptr PageSize = GetPageSizeCached(); uptr bottom = stack & ~(PageSize - 1); ssize += stack - bottom; ssize = RoundUpTo(ssize, PageSize); static const uptr kMaxSaneContextStackSize = 1 << 22; // 4 Mb if (AddrIsInMem(bottom) && ssize && ssize <= kMaxSaneContextStackSize) { PoisonShadow(bottom, ssize, 0); } } INTERCEPTOR(int, swapcontext, struct ucontext_t *oucp, struct ucontext_t *ucp) { static bool reported_warning = false; if (!reported_warning) { Report("WARNING: ASan doesn't fully support makecontext/swapcontext " "functions and may produce false positives in some cases!\n"); reported_warning = true; } // Clear shadow memory for new context (it may share stack // with current context). uptr stack, ssize; ReadContextStack(ucp, &stack, &ssize); ClearShadowMemoryForContextStack(stack, ssize); int res = REAL(swapcontext)(oucp, ucp); // swapcontext technically does not return, but program may swap context to // "oucp" later, that would look as if swapcontext() returned 0. // We need to clear shadow for ucp once again, as it may be in arbitrary // state. ClearShadowMemoryForContextStack(stack, ssize); return res; } #endif // ASAN_INTERCEPT_SWAPCONTEXT INTERCEPTOR(void, longjmp, void *env, int val) { __asan_handle_no_return(); REAL(longjmp)(env, val); } #if ASAN_INTERCEPT__LONGJMP INTERCEPTOR(void, _longjmp, void *env, int val) { __asan_handle_no_return(); REAL(_longjmp)(env, val); } #endif #if ASAN_INTERCEPT___LONGJMP_CHK INTERCEPTOR(void, __longjmp_chk, void *env, int val) { __asan_handle_no_return(); REAL(__longjmp_chk)(env, val); } #endif #if ASAN_INTERCEPT_SIGLONGJMP INTERCEPTOR(void, siglongjmp, void *env, int val) { __asan_handle_no_return(); REAL(siglongjmp)(env, val); } #endif #if ASAN_INTERCEPT___CXA_THROW INTERCEPTOR(void, __cxa_throw, void *a, void *b, void *c) { CHECK(REAL(__cxa_throw)); __asan_handle_no_return(); REAL(__cxa_throw)(a, b, c); } #endif void *__asan_memcpy(void *to, const void *from, uptr size) { ASAN_MEMCPY_IMPL(nullptr, to, from, size); } void *__asan_memset(void *block, int c, uptr size) { ASAN_MEMSET_IMPL(nullptr, block, c, size); } void *__asan_memmove(void *to, const void *from, uptr size) { ASAN_MEMMOVE_IMPL(nullptr, to, from, size); } #if ASAN_INTERCEPT_INDEX # if ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX INTERCEPTOR(char*, index, const char *string, int c) ALIAS(WRAPPER_NAME(strchr)); # else # if SANITIZER_MAC DECLARE_REAL(char*, index, const char *string, int c) OVERRIDE_FUNCTION(index, strchr); # else DEFINE_REAL(char*, index, const char *string, int c) # endif # endif #endif // ASAN_INTERCEPT_INDEX // For both strcat() and strncat() we need to check the validity of |to| // argument irrespective of the |from| length. INTERCEPTOR(char*, strcat, char *to, const char *from) { // NOLINT void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, strcat); // NOLINT ENSURE_ASAN_INITED(); if (flags()->replace_str) { uptr from_length = REAL(strlen)(from); ASAN_READ_RANGE(ctx, from, from_length + 1); uptr to_length = REAL(strlen)(to); ASAN_READ_STRING_OF_LEN(ctx, to, to_length, to_length); ASAN_WRITE_RANGE(ctx, to + to_length, from_length + 1); // If the copying actually happens, the |from| string should not overlap // with the resulting string starting at |to|, which has a length of // to_length + from_length + 1. if (from_length > 0) { CHECK_RANGES_OVERLAP("strcat", to, from_length + to_length + 1, from, from_length + 1); } } return REAL(strcat)(to, from); // NOLINT } INTERCEPTOR(char*, strncat, char *to, const char *from, uptr size) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, strncat); ENSURE_ASAN_INITED(); if (flags()->replace_str) { uptr from_length = MaybeRealStrnlen(from, size); uptr copy_length = Min(size, from_length + 1); ASAN_READ_RANGE(ctx, from, copy_length); uptr to_length = REAL(strlen)(to); ASAN_READ_STRING_OF_LEN(ctx, to, to_length, to_length); ASAN_WRITE_RANGE(ctx, to + to_length, from_length + 1); if (from_length > 0) { CHECK_RANGES_OVERLAP("strncat", to, to_length + copy_length + 1, from, copy_length); } } return REAL(strncat)(to, from, size); } INTERCEPTOR(char*, strcpy, char *to, const char *from) { // NOLINT void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, strcpy); // NOLINT #if SANITIZER_MAC if (UNLIKELY(!asan_inited)) return REAL(strcpy)(to, from); // NOLINT #endif // strcpy is called from malloc_default_purgeable_zone() // in __asan::ReplaceSystemAlloc() on Mac. if (asan_init_is_running) { return REAL(strcpy)(to, from); // NOLINT } ENSURE_ASAN_INITED(); if (flags()->replace_str) { uptr from_size = REAL(strlen)(from) + 1; CHECK_RANGES_OVERLAP("strcpy", to, from_size, from, from_size); ASAN_READ_RANGE(ctx, from, from_size); ASAN_WRITE_RANGE(ctx, to, from_size); } return REAL(strcpy)(to, from); // NOLINT } INTERCEPTOR(char*, strdup, const char *s) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, strdup); if (UNLIKELY(!asan_inited)) return internal_strdup(s); ENSURE_ASAN_INITED(); uptr length = REAL(strlen)(s); if (flags()->replace_str) { ASAN_READ_RANGE(ctx, s, length + 1); } GET_STACK_TRACE_MALLOC; void *new_mem = asan_malloc(length + 1, &stack); REAL(memcpy)(new_mem, s, length + 1); return reinterpret_cast(new_mem); } #if ASAN_INTERCEPT___STRDUP INTERCEPTOR(char*, __strdup, const char *s) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, strdup); if (UNLIKELY(!asan_inited)) return internal_strdup(s); ENSURE_ASAN_INITED(); uptr length = REAL(strlen)(s); if (flags()->replace_str) { ASAN_READ_RANGE(ctx, s, length + 1); } GET_STACK_TRACE_MALLOC; void *new_mem = asan_malloc(length + 1, &stack); REAL(memcpy)(new_mem, s, length + 1); return reinterpret_cast(new_mem); } #endif // ASAN_INTERCEPT___STRDUP -INTERCEPTOR(SIZE_T, wcslen, const wchar_t *s) { - void *ctx; - ASAN_INTERCEPTOR_ENTER(ctx, wcslen); - SIZE_T length = internal_wcslen(s); - if (!asan_init_is_running) { - ENSURE_ASAN_INITED(); - ASAN_READ_RANGE(ctx, s, (length + 1) * sizeof(wchar_t)); - } - return length; -} - INTERCEPTOR(char*, strncpy, char *to, const char *from, uptr size) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, strncpy); ENSURE_ASAN_INITED(); if (flags()->replace_str) { uptr from_size = Min(size, MaybeRealStrnlen(from, size) + 1); CHECK_RANGES_OVERLAP("strncpy", to, from_size, from, from_size); ASAN_READ_RANGE(ctx, from, from_size); ASAN_WRITE_RANGE(ctx, to, size); } return REAL(strncpy)(to, from, size); } INTERCEPTOR(long, strtol, const char *nptr, // NOLINT char **endptr, int base) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, strtol); ENSURE_ASAN_INITED(); if (!flags()->replace_str) { return REAL(strtol)(nptr, endptr, base); } char *real_endptr; long result = REAL(strtol)(nptr, &real_endptr, base); // NOLINT StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base); return result; } INTERCEPTOR(int, atoi, const char *nptr) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, atoi); #if SANITIZER_MAC if (UNLIKELY(!asan_inited)) return REAL(atoi)(nptr); #endif ENSURE_ASAN_INITED(); if (!flags()->replace_str) { return REAL(atoi)(nptr); } char *real_endptr; // "man atoi" tells that behavior of atoi(nptr) is the same as // strtol(nptr, 0, 10), i.e. it sets errno to ERANGE if the // parsed integer can't be stored in *long* type (even if it's // different from int). So, we just imitate this behavior. int result = REAL(strtol)(nptr, &real_endptr, 10); FixRealStrtolEndptr(nptr, &real_endptr); ASAN_READ_STRING(ctx, nptr, (real_endptr - nptr) + 1); return result; } INTERCEPTOR(long, atol, const char *nptr) { // NOLINT void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, atol); #if SANITIZER_MAC if (UNLIKELY(!asan_inited)) return REAL(atol)(nptr); #endif ENSURE_ASAN_INITED(); if (!flags()->replace_str) { return REAL(atol)(nptr); } char *real_endptr; long result = REAL(strtol)(nptr, &real_endptr, 10); // NOLINT FixRealStrtolEndptr(nptr, &real_endptr); ASAN_READ_STRING(ctx, nptr, (real_endptr - nptr) + 1); return result; } #if ASAN_INTERCEPT_ATOLL_AND_STRTOLL INTERCEPTOR(long long, strtoll, const char *nptr, // NOLINT char **endptr, int base) { void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, strtoll); ENSURE_ASAN_INITED(); if (!flags()->replace_str) { return REAL(strtoll)(nptr, endptr, base); } char *real_endptr; long long result = REAL(strtoll)(nptr, &real_endptr, base); // NOLINT StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base); return result; } INTERCEPTOR(long long, atoll, const char *nptr) { // NOLINT void *ctx; ASAN_INTERCEPTOR_ENTER(ctx, atoll); ENSURE_ASAN_INITED(); if (!flags()->replace_str) { return REAL(atoll)(nptr); } char *real_endptr; long long result = REAL(strtoll)(nptr, &real_endptr, 10); // NOLINT FixRealStrtolEndptr(nptr, &real_endptr); ASAN_READ_STRING(ctx, nptr, (real_endptr - nptr) + 1); return result; } #endif // ASAN_INTERCEPT_ATOLL_AND_STRTOLL #if ASAN_INTERCEPT___CXA_ATEXIT static void AtCxaAtexit(void *unused) { (void)unused; StopInitOrderChecking(); } INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg, void *dso_handle) { #if SANITIZER_MAC if (UNLIKELY(!asan_inited)) return REAL(__cxa_atexit)(func, arg, dso_handle); #endif ENSURE_ASAN_INITED(); int res = REAL(__cxa_atexit)(func, arg, dso_handle); REAL(__cxa_atexit)(AtCxaAtexit, nullptr, nullptr); return res; } #endif // ASAN_INTERCEPT___CXA_ATEXIT #if ASAN_INTERCEPT_FORK INTERCEPTOR(int, fork, void) { ENSURE_ASAN_INITED(); int pid = REAL(fork)(); return pid; } #endif // ASAN_INTERCEPT_FORK // ---------------------- InitializeAsanInterceptors ---------------- {{{1 namespace __asan { void InitializeAsanInterceptors() { static bool was_called_once; CHECK(!was_called_once); was_called_once = true; InitializeCommonInterceptors(); // Intercept str* functions. ASAN_INTERCEPT_FUNC(strcat); // NOLINT ASAN_INTERCEPT_FUNC(strcpy); // NOLINT - ASAN_INTERCEPT_FUNC(wcslen); ASAN_INTERCEPT_FUNC(strncat); ASAN_INTERCEPT_FUNC(strncpy); ASAN_INTERCEPT_FUNC(strdup); #if ASAN_INTERCEPT___STRDUP ASAN_INTERCEPT_FUNC(__strdup); #endif #if ASAN_INTERCEPT_INDEX && ASAN_USE_ALIAS_ATTRIBUTE_FOR_INDEX ASAN_INTERCEPT_FUNC(index); #endif ASAN_INTERCEPT_FUNC(atoi); ASAN_INTERCEPT_FUNC(atol); ASAN_INTERCEPT_FUNC(strtol); #if ASAN_INTERCEPT_ATOLL_AND_STRTOLL ASAN_INTERCEPT_FUNC(atoll); ASAN_INTERCEPT_FUNC(strtoll); #endif // Intecept signal- and jump-related functions. ASAN_INTERCEPT_FUNC(longjmp); #if ASAN_INTERCEPT_SIGNAL_AND_SIGACTION ASAN_INTERCEPT_FUNC(sigaction); #if SANITIZER_ANDROID ASAN_INTERCEPT_FUNC(bsd_signal); #endif ASAN_INTERCEPT_FUNC(signal); #endif #if ASAN_INTERCEPT_SWAPCONTEXT ASAN_INTERCEPT_FUNC(swapcontext); #endif #if ASAN_INTERCEPT__LONGJMP ASAN_INTERCEPT_FUNC(_longjmp); #endif #if ASAN_INTERCEPT___LONGJMP_CHK ASAN_INTERCEPT_FUNC(__longjmp_chk); #endif #if ASAN_INTERCEPT_SIGLONGJMP ASAN_INTERCEPT_FUNC(siglongjmp); #endif // Intercept exception handling functions. #if ASAN_INTERCEPT___CXA_THROW ASAN_INTERCEPT_FUNC(__cxa_throw); #endif // Intercept threading-related functions #if ASAN_INTERCEPT_PTHREAD_CREATE #if defined(ASAN_PTHREAD_CREATE_VERSION) ASAN_INTERCEPT_FUNC_VER(pthread_create, ASAN_PTHREAD_CREATE_VERSION); #else ASAN_INTERCEPT_FUNC(pthread_create); #endif ASAN_INTERCEPT_FUNC(pthread_join); #endif // Intercept atexit function. #if ASAN_INTERCEPT___CXA_ATEXIT ASAN_INTERCEPT_FUNC(__cxa_atexit); #endif #if ASAN_INTERCEPT_FORK ASAN_INTERCEPT_FUNC(fork); #endif InitializePlatformInterceptors(); VReport(1, "AddressSanitizer: libc interceptors initialized\n"); } } // namespace __asan Index: vendor/compiler-rt/dist/lib/asan/asan_new_delete.cc =================================================================== --- vendor/compiler-rt/dist/lib/asan/asan_new_delete.cc (revision 320536) +++ vendor/compiler-rt/dist/lib/asan/asan_new_delete.cc (revision 320537) @@ -1,190 +1,201 @@ //===-- asan_interceptors.cc ----------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // // Interceptors for operators new and delete. //===----------------------------------------------------------------------===// #include "asan_allocator.h" #include "asan_internal.h" #include "asan_stack.h" #include "interception/interception.h" #include // C++ operators can't have dllexport attributes on Windows. We export them // anyway by passing extra -export flags to the linker, which is exactly that // dllexport would normally do. We need to export them in order to make the // VS2015 dynamic CRT (MD) work. #if SANITIZER_WINDOWS -# define CXX_OPERATOR_ATTRIBUTE -# ifdef _WIN64 -# pragma comment(linker, "/export:??2@YAPEAX_K@Z") // operator new -# pragma comment(linker, "/export:??3@YAXPEAX@Z") // operator delete -# pragma comment(linker, "/export:??3@YAXPEAX_K@Z") // sized operator delete -# pragma comment(linker, "/export:??_U@YAPEAX_K@Z") // operator new[] -# pragma comment(linker, "/export:??_V@YAXPEAX@Z") // operator delete[] -# else -# pragma comment(linker, "/export:??2@YAPAXI@Z") // operator new -# pragma comment(linker, "/export:??3@YAXPAX@Z") // operator delete -# pragma comment(linker, "/export:??3@YAXPAXI@Z") // sized operator delete -# pragma comment(linker, "/export:??_U@YAPAXI@Z") // operator new[] -# pragma comment(linker, "/export:??_V@YAXPAX@Z") // operator delete[] -# endif +#define CXX_OPERATOR_ATTRIBUTE +#define COMMENT_EXPORT(sym) __pragma(comment(linker, "/export:"##sym)) +#ifdef _WIN64 +COMMENT_EXPORT("??2@YAPEAX_K@Z") // operator new +COMMENT_EXPORT("??2@YAPEAX_KAEBUnothrow_t@std@@@Z") // operator new nothrow +COMMENT_EXPORT("??3@YAXPEAX@Z") // operator delete +COMMENT_EXPORT("??3@YAXPEAX_K@Z") // sized operator delete +COMMENT_EXPORT("??_U@YAPEAX_K@Z") // operator new[] +COMMENT_EXPORT("??_V@YAXPEAX@Z") // operator delete[] #else -# define CXX_OPERATOR_ATTRIBUTE INTERCEPTOR_ATTRIBUTE +COMMENT_EXPORT("??2@YAPAXI@Z") // operator new +COMMENT_EXPORT("??2@YAPAXIABUnothrow_t@std@@@Z") // operator new nothrow +COMMENT_EXPORT("??3@YAXPAX@Z") // operator delete +COMMENT_EXPORT("??3@YAXPAXI@Z") // sized operator delete +COMMENT_EXPORT("??_U@YAPAXI@Z") // operator new[] +COMMENT_EXPORT("??_V@YAXPAX@Z") // operator delete[] #endif +#undef COMMENT_EXPORT +#else +#define CXX_OPERATOR_ATTRIBUTE INTERCEPTOR_ATTRIBUTE +#endif using namespace __asan; // NOLINT // FreeBSD prior v9.2 have wrong definition of 'size_t'. // http://svnweb.freebsd.org/base?view=revision&revision=232261 #if SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 32 #include #if __FreeBSD_version <= 902001 // v9.2 #define size_t unsigned #endif // __FreeBSD_version #endif // SANITIZER_FREEBSD && SANITIZER_WORDSIZE == 32 // This code has issues on OSX. // See https://github.com/google/sanitizers/issues/131. // Fake std::nothrow_t and std::align_val_t to avoid including . namespace std { struct nothrow_t {}; enum class align_val_t: size_t {}; } // namespace std -#define OPERATOR_NEW_BODY(type) \ +// TODO(alekseys): throw std::bad_alloc instead of dying on OOM. +#define OPERATOR_NEW_BODY(type, nothrow) \ GET_STACK_TRACE_MALLOC;\ - return asan_memalign(0, size, &stack, type); -#define OPERATOR_NEW_BODY_ALIGN(type) \ + void *res = asan_memalign(0, size, &stack, type);\ + if (!nothrow && UNLIKELY(!res)) DieOnFailure::OnOOM();\ + return res; +#define OPERATOR_NEW_BODY_ALIGN(type, nothrow) \ GET_STACK_TRACE_MALLOC;\ - return asan_memalign((uptr)align, size, &stack, type); + void *res = asan_memalign((uptr)align, size, &stack, type);\ + if (!nothrow && UNLIKELY(!res)) DieOnFailure::OnOOM();\ + return res; // On OS X it's not enough to just provide our own 'operator new' and // 'operator delete' implementations, because they're going to be in the // runtime dylib, and the main executable will depend on both the runtime // dylib and libstdc++, each of those'll have its implementation of new and // delete. // To make sure that C++ allocation/deallocation operators are overridden on // OS X we need to intercept them using their mangled names. #if !SANITIZER_MAC CXX_OPERATOR_ATTRIBUTE -void *operator new(size_t size) { OPERATOR_NEW_BODY(FROM_NEW); } +void *operator new(size_t size) +{ OPERATOR_NEW_BODY(FROM_NEW, false /*nothrow*/); } CXX_OPERATOR_ATTRIBUTE -void *operator new[](size_t size) { OPERATOR_NEW_BODY(FROM_NEW_BR); } +void *operator new[](size_t size) +{ OPERATOR_NEW_BODY(FROM_NEW_BR, false /*nothrow*/); } CXX_OPERATOR_ATTRIBUTE void *operator new(size_t size, std::nothrow_t const&) -{ OPERATOR_NEW_BODY(FROM_NEW); } +{ OPERATOR_NEW_BODY(FROM_NEW, true /*nothrow*/); } CXX_OPERATOR_ATTRIBUTE void *operator new[](size_t size, std::nothrow_t const&) -{ OPERATOR_NEW_BODY(FROM_NEW_BR); } +{ OPERATOR_NEW_BODY(FROM_NEW_BR, true /*nothrow*/); } CXX_OPERATOR_ATTRIBUTE void *operator new(size_t size, std::align_val_t align) -{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW); } +{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW, false /*nothrow*/); } CXX_OPERATOR_ATTRIBUTE void *operator new[](size_t size, std::align_val_t align) -{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW_BR); } +{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW_BR, false /*nothrow*/); } CXX_OPERATOR_ATTRIBUTE void *operator new(size_t size, std::align_val_t align, std::nothrow_t const&) -{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW); } +{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW, true /*nothrow*/); } CXX_OPERATOR_ATTRIBUTE void *operator new[](size_t size, std::align_val_t align, std::nothrow_t const&) -{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW_BR); } +{ OPERATOR_NEW_BODY_ALIGN(FROM_NEW_BR, true /*nothrow*/); } #else // SANITIZER_MAC INTERCEPTOR(void *, _Znwm, size_t size) { - OPERATOR_NEW_BODY(FROM_NEW); + OPERATOR_NEW_BODY(FROM_NEW, false /*nothrow*/); } INTERCEPTOR(void *, _Znam, size_t size) { - OPERATOR_NEW_BODY(FROM_NEW_BR); + OPERATOR_NEW_BODY(FROM_NEW_BR, false /*nothrow*/); } INTERCEPTOR(void *, _ZnwmRKSt9nothrow_t, size_t size, std::nothrow_t const&) { - OPERATOR_NEW_BODY(FROM_NEW); + OPERATOR_NEW_BODY(FROM_NEW, true /*nothrow*/); } INTERCEPTOR(void *, _ZnamRKSt9nothrow_t, size_t size, std::nothrow_t const&) { - OPERATOR_NEW_BODY(FROM_NEW_BR); + OPERATOR_NEW_BODY(FROM_NEW_BR, true /*nothrow*/); } #endif #define OPERATOR_DELETE_BODY(type) \ GET_STACK_TRACE_FREE;\ asan_free(ptr, &stack, type); #if !SANITIZER_MAC CXX_OPERATOR_ATTRIBUTE void operator delete(void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY(FROM_NEW); } CXX_OPERATOR_ATTRIBUTE void operator delete[](void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY(FROM_NEW_BR); } CXX_OPERATOR_ATTRIBUTE void operator delete(void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY(FROM_NEW); } CXX_OPERATOR_ATTRIBUTE void operator delete[](void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY(FROM_NEW_BR); } CXX_OPERATOR_ATTRIBUTE void operator delete(void *ptr, size_t size) NOEXCEPT { GET_STACK_TRACE_FREE; asan_sized_free(ptr, size, &stack, FROM_NEW); } CXX_OPERATOR_ATTRIBUTE void operator delete[](void *ptr, size_t size) NOEXCEPT { GET_STACK_TRACE_FREE; asan_sized_free(ptr, size, &stack, FROM_NEW_BR); } CXX_OPERATOR_ATTRIBUTE void operator delete(void *ptr, std::align_val_t) NOEXCEPT { OPERATOR_DELETE_BODY(FROM_NEW); } CXX_OPERATOR_ATTRIBUTE void operator delete[](void *ptr, std::align_val_t) NOEXCEPT { OPERATOR_DELETE_BODY(FROM_NEW_BR); } CXX_OPERATOR_ATTRIBUTE void operator delete(void *ptr, std::align_val_t, std::nothrow_t const&) { OPERATOR_DELETE_BODY(FROM_NEW); } CXX_OPERATOR_ATTRIBUTE void operator delete[](void *ptr, std::align_val_t, std::nothrow_t const&) { OPERATOR_DELETE_BODY(FROM_NEW_BR); } CXX_OPERATOR_ATTRIBUTE void operator delete(void *ptr, size_t size, std::align_val_t) NOEXCEPT { GET_STACK_TRACE_FREE; asan_sized_free(ptr, size, &stack, FROM_NEW); } CXX_OPERATOR_ATTRIBUTE void operator delete[](void *ptr, size_t size, std::align_val_t) NOEXCEPT { GET_STACK_TRACE_FREE; asan_sized_free(ptr, size, &stack, FROM_NEW_BR); } #else // SANITIZER_MAC INTERCEPTOR(void, _ZdlPv, void *ptr) { OPERATOR_DELETE_BODY(FROM_NEW); } INTERCEPTOR(void, _ZdaPv, void *ptr) { OPERATOR_DELETE_BODY(FROM_NEW_BR); } INTERCEPTOR(void, _ZdlPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY(FROM_NEW); } INTERCEPTOR(void, _ZdaPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY(FROM_NEW_BR); } #endif Index: vendor/compiler-rt/dist/lib/asan/asan_win_dll_thunk.cc =================================================================== --- vendor/compiler-rt/dist/lib/asan/asan_win_dll_thunk.cc (revision 320536) +++ vendor/compiler-rt/dist/lib/asan/asan_win_dll_thunk.cc (revision 320537) @@ -1,151 +1,152 @@ //===-- asan_win_dll_thunk.cc ---------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of AddressSanitizer, an address sanity checker. // // This file defines a family of thunks that should be statically linked into // the DLLs that have ASan instrumentation in order to delegate the calls to the // shared runtime that lives in the main binary. // See https://github.com/google/sanitizers/issues/209 for the details. //===----------------------------------------------------------------------===// #ifdef SANITIZER_DLL_THUNK #include "asan_init_version.h" #include "interception/interception.h" #include "sanitizer_common/sanitizer_win_defs.h" #include "sanitizer_common/sanitizer_win_dll_thunk.h" #include "sanitizer_common/sanitizer_platform_interceptors.h" // ASan own interface functions. #define INTERFACE_FUNCTION(Name) INTERCEPT_SANITIZER_FUNCTION(Name) #define INTERFACE_WEAK_FUNCTION(Name) INTERCEPT_SANITIZER_WEAK_FUNCTION(Name) #include "asan_interface.inc" // Memory allocation functions. INTERCEPT_WRAP_V_W(free) INTERCEPT_WRAP_V_W(_free_base) INTERCEPT_WRAP_V_WW(_free_dbg) INTERCEPT_WRAP_W_W(malloc) INTERCEPT_WRAP_W_W(_malloc_base) INTERCEPT_WRAP_W_WWWW(_malloc_dbg) INTERCEPT_WRAP_W_WW(calloc) INTERCEPT_WRAP_W_WW(_calloc_base) INTERCEPT_WRAP_W_WWWWW(_calloc_dbg) INTERCEPT_WRAP_W_WWW(_calloc_impl) INTERCEPT_WRAP_W_WW(realloc) INTERCEPT_WRAP_W_WW(_realloc_base) INTERCEPT_WRAP_W_WWW(_realloc_dbg) INTERCEPT_WRAP_W_WWW(_recalloc) INTERCEPT_WRAP_W_WWW(_recalloc_base) INTERCEPT_WRAP_W_W(_msize) INTERCEPT_WRAP_W_W(_expand) INTERCEPT_WRAP_W_W(_expand_dbg) // TODO(timurrrr): Might want to add support for _aligned_* allocation // functions to detect a bit more bugs. Those functions seem to wrap malloc(). // TODO(timurrrr): Do we need to add _Crt* stuff here? (see asan_malloc_win.cc). INTERCEPT_LIBRARY_FUNCTION(atoi); INTERCEPT_LIBRARY_FUNCTION(atol); INTERCEPT_LIBRARY_FUNCTION(frexp); INTERCEPT_LIBRARY_FUNCTION(longjmp); #if SANITIZER_INTERCEPT_MEMCHR INTERCEPT_LIBRARY_FUNCTION(memchr); #endif INTERCEPT_LIBRARY_FUNCTION(memcmp); INTERCEPT_LIBRARY_FUNCTION(memcpy); INTERCEPT_LIBRARY_FUNCTION(memmove); INTERCEPT_LIBRARY_FUNCTION(memset); INTERCEPT_LIBRARY_FUNCTION(strcat); // NOLINT INTERCEPT_LIBRARY_FUNCTION(strchr); INTERCEPT_LIBRARY_FUNCTION(strcmp); INTERCEPT_LIBRARY_FUNCTION(strcpy); // NOLINT INTERCEPT_LIBRARY_FUNCTION(strcspn); INTERCEPT_LIBRARY_FUNCTION(strdup); INTERCEPT_LIBRARY_FUNCTION(strlen); INTERCEPT_LIBRARY_FUNCTION(strncat); INTERCEPT_LIBRARY_FUNCTION(strncmp); INTERCEPT_LIBRARY_FUNCTION(strncpy); INTERCEPT_LIBRARY_FUNCTION(strnlen); INTERCEPT_LIBRARY_FUNCTION(strpbrk); INTERCEPT_LIBRARY_FUNCTION(strrchr); INTERCEPT_LIBRARY_FUNCTION(strspn); INTERCEPT_LIBRARY_FUNCTION(strstr); INTERCEPT_LIBRARY_FUNCTION(strtok); INTERCEPT_LIBRARY_FUNCTION(strtol); INTERCEPT_LIBRARY_FUNCTION(wcslen); +INTERCEPT_LIBRARY_FUNCTION(wcsnlen); #ifdef _WIN64 INTERCEPT_LIBRARY_FUNCTION(__C_specific_handler); #else INTERCEPT_LIBRARY_FUNCTION(_except_handler3); // _except_handler4 checks -GS cookie which is different for each module, so we // can't use INTERCEPT_LIBRARY_FUNCTION(_except_handler4). INTERCEPTOR(int, _except_handler4, void *a, void *b, void *c, void *d) { __asan_handle_no_return(); return REAL(_except_handler4)(a, b, c, d); } #endif // Window specific functions not included in asan_interface.inc. INTERCEPT_WRAP_W_V(__asan_should_detect_stack_use_after_return) INTERCEPT_WRAP_W_V(__asan_get_shadow_memory_dynamic_address) INTERCEPT_WRAP_W_W(__asan_unhandled_exception_filter) using namespace __sanitizer; extern "C" { int __asan_option_detect_stack_use_after_return; uptr __asan_shadow_memory_dynamic_address; } // extern "C" static int asan_dll_thunk_init() { typedef void (*fntype)(); static fntype fn = 0; // asan_dll_thunk_init is expected to be called by only one thread. if (fn) return 0; // Ensure all interception was executed. __dll_thunk_init(); fn = (fntype) dllThunkGetRealAddrOrDie("__asan_init"); fn(); __asan_option_detect_stack_use_after_return = (__asan_should_detect_stack_use_after_return() != 0); __asan_shadow_memory_dynamic_address = (uptr)__asan_get_shadow_memory_dynamic_address(); #ifndef _WIN64 INTERCEPT_FUNCTION(_except_handler4); #endif // In DLLs, the callbacks are expected to return 0, // otherwise CRT initialization fails. return 0; } #pragma section(".CRT$XIB", long, read) // NOLINT __declspec(allocate(".CRT$XIB")) int (*__asan_preinit)() = asan_dll_thunk_init; static void WINAPI asan_thread_init(void *mod, unsigned long reason, void *reserved) { if (reason == /*DLL_PROCESS_ATTACH=*/1) asan_dll_thunk_init(); } #pragma section(".CRT$XLAB", long, read) // NOLINT __declspec(allocate(".CRT$XLAB")) void (WINAPI *__asan_tls_init)(void *, unsigned long, void *) = asan_thread_init; WIN_FORCE_LINK(__asan_dso_reg_hook) #endif // SANITIZER_DLL_THUNK Index: vendor/compiler-rt/dist/lib/lsan/lsan_allocator.cc =================================================================== --- vendor/compiler-rt/dist/lib/lsan/lsan_allocator.cc (revision 320536) +++ vendor/compiler-rt/dist/lib/lsan/lsan_allocator.cc (revision 320537) @@ -1,279 +1,281 @@ //=-- lsan_allocator.cc ---------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of LeakSanitizer. // See lsan_allocator.h for details. // //===----------------------------------------------------------------------===// #include "lsan_allocator.h" #include "sanitizer_common/sanitizer_allocator.h" #include "sanitizer_common/sanitizer_allocator_interface.h" #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_stacktrace.h" #include "lsan_common.h" extern "C" void *memset(void *ptr, int value, uptr num); namespace __lsan { #if defined(__i386__) || defined(__arm__) static const uptr kMaxAllowedMallocSize = 1UL << 30; #elif defined(__mips64) || defined(__aarch64__) static const uptr kMaxAllowedMallocSize = 4UL << 30; #else static const uptr kMaxAllowedMallocSize = 8UL << 30; #endif typedef LargeMmapAllocator<> SecondaryAllocator; typedef CombinedAllocator Allocator; static Allocator allocator; void InitializeAllocator() { SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null); allocator.InitLinkerInitialized( common_flags()->allocator_release_to_os_interval_ms); } void AllocatorThreadFinish() { allocator.SwallowCache(GetAllocatorCache()); } static ChunkMetadata *Metadata(const void *p) { return reinterpret_cast(allocator.GetMetaData(p)); } static void RegisterAllocation(const StackTrace &stack, void *p, uptr size) { if (!p) return; ChunkMetadata *m = Metadata(p); CHECK(m); m->tag = DisabledInThisThread() ? kIgnored : kDirectlyLeaked; m->stack_trace_id = StackDepotPut(stack); m->requested_size = size; atomic_store(reinterpret_cast(m), 1, memory_order_relaxed); } static void RegisterDeallocation(void *p) { if (!p) return; ChunkMetadata *m = Metadata(p); CHECK(m); atomic_store(reinterpret_cast(m), 0, memory_order_relaxed); } void *Allocate(const StackTrace &stack, uptr size, uptr alignment, bool cleared) { if (size == 0) size = 1; if (size > kMaxAllowedMallocSize) { Report("WARNING: LeakSanitizer failed to allocate %zu bytes\n", size); - return nullptr; + return Allocator::FailureHandler::OnBadRequest(); } void *p = allocator.Allocate(GetAllocatorCache(), size, alignment); // Do not rely on the allocator to clear the memory (it's slow). if (cleared && allocator.FromPrimary(p)) memset(p, 0, size); RegisterAllocation(stack, p, size); if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(p, size); RunMallocHooks(p, size); return p; } void Deallocate(void *p) { if (&__sanitizer_free_hook) __sanitizer_free_hook(p); RunFreeHooks(p); RegisterDeallocation(p); allocator.Deallocate(GetAllocatorCache(), p); } void *Reallocate(const StackTrace &stack, void *p, uptr new_size, uptr alignment) { RegisterDeallocation(p); if (new_size > kMaxAllowedMallocSize) { Report("WARNING: LeakSanitizer failed to allocate %zu bytes\n", new_size); allocator.Deallocate(GetAllocatorCache(), p); - return nullptr; + return Allocator::FailureHandler::OnBadRequest(); } p = allocator.Reallocate(GetAllocatorCache(), p, new_size, alignment); RegisterAllocation(stack, p, new_size); return p; } void GetAllocatorCacheRange(uptr *begin, uptr *end) { *begin = (uptr)GetAllocatorCache(); *end = *begin + sizeof(AllocatorCache); } uptr GetMallocUsableSize(const void *p) { ChunkMetadata *m = Metadata(p); if (!m) return 0; return m->requested_size; } void *lsan_memalign(uptr alignment, uptr size, const StackTrace &stack) { return Allocate(stack, size, alignment, kAlwaysClearMemory); } void *lsan_malloc(uptr size, const StackTrace &stack) { return Allocate(stack, size, 1, kAlwaysClearMemory); } void lsan_free(void *p) { Deallocate(p); } void *lsan_realloc(void *p, uptr size, const StackTrace &stack) { return Reallocate(stack, p, size, 1); } void *lsan_calloc(uptr nmemb, uptr size, const StackTrace &stack) { + if (CheckForCallocOverflow(size, nmemb)) + return Allocator::FailureHandler::OnBadRequest(); size *= nmemb; return Allocate(stack, size, 1, true); } void *lsan_valloc(uptr size, const StackTrace &stack) { if (size == 0) size = GetPageSizeCached(); return Allocate(stack, size, GetPageSizeCached(), kAlwaysClearMemory); } uptr lsan_mz_size(const void *p) { return GetMallocUsableSize(p); } ///// Interface to the common LSan module. ///// void LockAllocator() { allocator.ForceLock(); } void UnlockAllocator() { allocator.ForceUnlock(); } void GetAllocatorGlobalRange(uptr *begin, uptr *end) { *begin = (uptr)&allocator; *end = *begin + sizeof(allocator); } uptr PointsIntoChunk(void* p) { uptr addr = reinterpret_cast(p); uptr chunk = reinterpret_cast(allocator.GetBlockBeginFastLocked(p)); if (!chunk) return 0; // LargeMmapAllocator considers pointers to the meta-region of a chunk to be // valid, but we don't want that. if (addr < chunk) return 0; ChunkMetadata *m = Metadata(reinterpret_cast(chunk)); CHECK(m); if (!m->allocated) return 0; if (addr < chunk + m->requested_size) return chunk; if (IsSpecialCaseOfOperatorNew0(chunk, m->requested_size, addr)) return chunk; return 0; } uptr GetUserBegin(uptr chunk) { return chunk; } LsanMetadata::LsanMetadata(uptr chunk) { metadata_ = Metadata(reinterpret_cast(chunk)); CHECK(metadata_); } bool LsanMetadata::allocated() const { return reinterpret_cast(metadata_)->allocated; } ChunkTag LsanMetadata::tag() const { return reinterpret_cast(metadata_)->tag; } void LsanMetadata::set_tag(ChunkTag value) { reinterpret_cast(metadata_)->tag = value; } uptr LsanMetadata::requested_size() const { return reinterpret_cast(metadata_)->requested_size; } u32 LsanMetadata::stack_trace_id() const { return reinterpret_cast(metadata_)->stack_trace_id; } void ForEachChunk(ForEachChunkCallback callback, void *arg) { allocator.ForEachChunk(callback, arg); } IgnoreObjectResult IgnoreObjectLocked(const void *p) { void *chunk = allocator.GetBlockBegin(p); if (!chunk || p < chunk) return kIgnoreObjectInvalid; ChunkMetadata *m = Metadata(chunk); CHECK(m); if (m->allocated && (uptr)p < (uptr)chunk + m->requested_size) { if (m->tag == kIgnored) return kIgnoreObjectAlreadyIgnored; m->tag = kIgnored; return kIgnoreObjectSuccess; } else { return kIgnoreObjectInvalid; } } } // namespace __lsan using namespace __lsan; extern "C" { SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_current_allocated_bytes() { uptr stats[AllocatorStatCount]; allocator.GetStats(stats); return stats[AllocatorStatAllocated]; } SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_heap_size() { uptr stats[AllocatorStatCount]; allocator.GetStats(stats); return stats[AllocatorStatMapped]; } SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_free_bytes() { return 0; } SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_unmapped_bytes() { return 0; } SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; } SANITIZER_INTERFACE_ATTRIBUTE int __sanitizer_get_ownership(const void *p) { return Metadata(p) != nullptr; } SANITIZER_INTERFACE_ATTRIBUTE uptr __sanitizer_get_allocated_size(const void *p) { return GetMallocUsableSize(p); } #if !SANITIZER_SUPPORTS_WEAK_HOOKS // Provide default (no-op) implementation of malloc hooks. SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void __sanitizer_malloc_hook(void *ptr, uptr size) { (void)ptr; (void)size; } SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void __sanitizer_free_hook(void *ptr) { (void)ptr; } #endif } // extern "C" Index: vendor/compiler-rt/dist/lib/lsan/lsan_interceptors.cc =================================================================== --- vendor/compiler-rt/dist/lib/lsan/lsan_interceptors.cc (revision 320536) +++ vendor/compiler-rt/dist/lib/lsan/lsan_interceptors.cc (revision 320537) @@ -1,346 +1,381 @@ //=-- lsan_interceptors.cc ------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of LeakSanitizer. // Interceptors for standalone LSan. // //===----------------------------------------------------------------------===// #include "interception/interception.h" #include "sanitizer_common/sanitizer_allocator.h" #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_internal_defs.h" #include "sanitizer_common/sanitizer_linux.h" #include "sanitizer_common/sanitizer_platform_interceptors.h" #include "sanitizer_common/sanitizer_platform_limits_posix.h" #include "sanitizer_common/sanitizer_posix.h" #include "sanitizer_common/sanitizer_tls_get_addr.h" #include "lsan.h" #include "lsan_allocator.h" #include "lsan_common.h" #include "lsan_thread.h" #include using namespace __lsan; extern "C" { int pthread_attr_init(void *attr); int pthread_attr_destroy(void *attr); int pthread_attr_getdetachstate(void *attr, int *v); int pthread_key_create(unsigned *key, void (*destructor)(void* v)); int pthread_setspecific(unsigned key, const void *v); } ///// Malloc/free interceptors. ///// namespace std { struct nothrow_t; } #if !SANITIZER_MAC INTERCEPTOR(void*, malloc, uptr size) { ENSURE_LSAN_INITED; GET_STACK_TRACE_MALLOC; return lsan_malloc(size, stack); } INTERCEPTOR(void, free, void *p) { ENSURE_LSAN_INITED; lsan_free(p); } INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) { if (lsan_init_is_running) { // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. const uptr kCallocPoolSize = 1024; static uptr calloc_memory_for_dlsym[kCallocPoolSize]; static uptr allocated; uptr size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize; void *mem = (void*)&calloc_memory_for_dlsym[allocated]; allocated += size_in_words; CHECK(allocated < kCallocPoolSize); return mem; } - if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return nullptr; ENSURE_LSAN_INITED; GET_STACK_TRACE_MALLOC; return lsan_calloc(nmemb, size, stack); } INTERCEPTOR(void*, realloc, void *q, uptr size) { ENSURE_LSAN_INITED; GET_STACK_TRACE_MALLOC; return lsan_realloc(q, size, stack); } INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) { ENSURE_LSAN_INITED; GET_STACK_TRACE_MALLOC; *memptr = lsan_memalign(alignment, size, stack); // FIXME: Return ENOMEM if user requested more than max alloc size. return 0; } INTERCEPTOR(void*, valloc, uptr size) { ENSURE_LSAN_INITED; GET_STACK_TRACE_MALLOC; return lsan_valloc(size, stack); } #endif #if SANITIZER_INTERCEPT_MEMALIGN INTERCEPTOR(void*, memalign, uptr alignment, uptr size) { ENSURE_LSAN_INITED; GET_STACK_TRACE_MALLOC; return lsan_memalign(alignment, size, stack); } #define LSAN_MAYBE_INTERCEPT_MEMALIGN INTERCEPT_FUNCTION(memalign) INTERCEPTOR(void *, __libc_memalign, uptr alignment, uptr size) { ENSURE_LSAN_INITED; GET_STACK_TRACE_MALLOC; void *res = lsan_memalign(alignment, size, stack); DTLS_on_libc_memalign(res, size); return res; } #define LSAN_MAYBE_INTERCEPT___LIBC_MEMALIGN INTERCEPT_FUNCTION(__libc_memalign) #else #define LSAN_MAYBE_INTERCEPT_MEMALIGN #define LSAN_MAYBE_INTERCEPT___LIBC_MEMALIGN #endif // SANITIZER_INTERCEPT_MEMALIGN #if SANITIZER_INTERCEPT_ALIGNED_ALLOC INTERCEPTOR(void*, aligned_alloc, uptr alignment, uptr size) { ENSURE_LSAN_INITED; GET_STACK_TRACE_MALLOC; return lsan_memalign(alignment, size, stack); } #define LSAN_MAYBE_INTERCEPT_ALIGNED_ALLOC INTERCEPT_FUNCTION(aligned_alloc) #else #define LSAN_MAYBE_INTERCEPT_ALIGNED_ALLOC #endif #if SANITIZER_INTERCEPT_MALLOC_USABLE_SIZE INTERCEPTOR(uptr, malloc_usable_size, void *ptr) { ENSURE_LSAN_INITED; return GetMallocUsableSize(ptr); } #define LSAN_MAYBE_INTERCEPT_MALLOC_USABLE_SIZE \ INTERCEPT_FUNCTION(malloc_usable_size) #else #define LSAN_MAYBE_INTERCEPT_MALLOC_USABLE_SIZE #endif #if SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO struct fake_mallinfo { int x[10]; }; INTERCEPTOR(struct fake_mallinfo, mallinfo, void) { struct fake_mallinfo res; internal_memset(&res, 0, sizeof(res)); return res; } #define LSAN_MAYBE_INTERCEPT_MALLINFO INTERCEPT_FUNCTION(mallinfo) INTERCEPTOR(int, mallopt, int cmd, int value) { return -1; } #define LSAN_MAYBE_INTERCEPT_MALLOPT INTERCEPT_FUNCTION(mallopt) #else #define LSAN_MAYBE_INTERCEPT_MALLINFO #define LSAN_MAYBE_INTERCEPT_MALLOPT #endif // SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO #if SANITIZER_INTERCEPT_PVALLOC INTERCEPTOR(void*, pvalloc, uptr size) { ENSURE_LSAN_INITED; GET_STACK_TRACE_MALLOC; uptr PageSize = GetPageSizeCached(); size = RoundUpTo(size, PageSize); if (size == 0) { // pvalloc(0) should allocate one page. size = PageSize; } return Allocate(stack, size, GetPageSizeCached(), kAlwaysClearMemory); } #define LSAN_MAYBE_INTERCEPT_PVALLOC INTERCEPT_FUNCTION(pvalloc) #else #define LSAN_MAYBE_INTERCEPT_PVALLOC #endif // SANITIZER_INTERCEPT_PVALLOC #if SANITIZER_INTERCEPT_CFREE INTERCEPTOR(void, cfree, void *p) ALIAS(WRAPPER_NAME(free)); #define LSAN_MAYBE_INTERCEPT_CFREE INTERCEPT_FUNCTION(cfree) #else #define LSAN_MAYBE_INTERCEPT_CFREE #endif // SANITIZER_INTERCEPT_CFREE #if SANITIZER_INTERCEPT_MCHECK_MPROBE INTERCEPTOR(int, mcheck, void (*abortfunc)(int mstatus)) { return 0; } INTERCEPTOR(int, mcheck_pedantic, void (*abortfunc)(int mstatus)) { return 0; } INTERCEPTOR(int, mprobe, void *ptr) { return 0; } #endif // SANITIZER_INTERCEPT_MCHECK_MPROBE -#define OPERATOR_NEW_BODY \ - ENSURE_LSAN_INITED; \ - GET_STACK_TRACE_MALLOC; \ - return Allocate(stack, size, 1, kAlwaysClearMemory); -INTERCEPTOR_ATTRIBUTE -void *operator new(size_t size) { OPERATOR_NEW_BODY; } -INTERCEPTOR_ATTRIBUTE -void *operator new[](size_t size) { OPERATOR_NEW_BODY; } -INTERCEPTOR_ATTRIBUTE -void *operator new(size_t size, std::nothrow_t const&) { OPERATOR_NEW_BODY; } -INTERCEPTOR_ATTRIBUTE -void *operator new[](size_t size, std::nothrow_t const&) { OPERATOR_NEW_BODY; } +// TODO(alekseys): throw std::bad_alloc instead of dying on OOM. +#define OPERATOR_NEW_BODY(nothrow) \ + ENSURE_LSAN_INITED; \ + GET_STACK_TRACE_MALLOC; \ + void *res = Allocate(stack, size, 1, kAlwaysClearMemory);\ + if (!nothrow && UNLIKELY(!res)) DieOnFailure::OnOOM();\ + return res; #define OPERATOR_DELETE_BODY \ ENSURE_LSAN_INITED; \ Deallocate(ptr); +// On OS X it's not enough to just provide our own 'operator new' and +// 'operator delete' implementations, because they're going to be in the runtime +// dylib, and the main executable will depend on both the runtime dylib and +// libstdc++, each of has its implementation of new and delete. +// To make sure that C++ allocation/deallocation operators are overridden on +// OS X we need to intercept them using their mangled names. +#if !SANITIZER_MAC + INTERCEPTOR_ATTRIBUTE +void *operator new(size_t size) { OPERATOR_NEW_BODY(false /*nothrow*/); } +INTERCEPTOR_ATTRIBUTE +void *operator new[](size_t size) { OPERATOR_NEW_BODY(false /*nothrow*/); } +INTERCEPTOR_ATTRIBUTE +void *operator new(size_t size, std::nothrow_t const&) +{ OPERATOR_NEW_BODY(true /*nothrow*/); } +INTERCEPTOR_ATTRIBUTE +void *operator new[](size_t size, std::nothrow_t const&) +{ OPERATOR_NEW_BODY(true /*nothrow*/); } + +INTERCEPTOR_ATTRIBUTE void operator delete(void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY; } INTERCEPTOR_ATTRIBUTE void operator delete[](void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY; } INTERCEPTOR_ATTRIBUTE void operator delete(void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY; } INTERCEPTOR_ATTRIBUTE -void operator delete[](void *ptr, std::nothrow_t const &) { - OPERATOR_DELETE_BODY; -} +void operator delete[](void *ptr, std::nothrow_t const &) +{ OPERATOR_DELETE_BODY; } + +#else // SANITIZER_MAC + +INTERCEPTOR(void *, _Znwm, size_t size) +{ OPERATOR_NEW_BODY(false /*nothrow*/); } +INTERCEPTOR(void *, _Znam, size_t size) +{ OPERATOR_NEW_BODY(false /*nothrow*/); } +INTERCEPTOR(void *, _ZnwmRKSt9nothrow_t, size_t size, std::nothrow_t const&) +{ OPERATOR_NEW_BODY(true /*nothrow*/); } +INTERCEPTOR(void *, _ZnamRKSt9nothrow_t, size_t size, std::nothrow_t const&) +{ OPERATOR_NEW_BODY(true /*nothrow*/); } + +INTERCEPTOR(void, _ZdlPv, void *ptr) +{ OPERATOR_DELETE_BODY; } +INTERCEPTOR(void, _ZdaPv, void *ptr) +{ OPERATOR_DELETE_BODY; } +INTERCEPTOR(void, _ZdlPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&) +{ OPERATOR_DELETE_BODY; } +INTERCEPTOR(void, _ZdaPvRKSt9nothrow_t, void *ptr, std::nothrow_t const&) +{ OPERATOR_DELETE_BODY; } + +#endif // !SANITIZER_MAC + ///// Thread initialization and finalization. ///// static unsigned g_thread_finalize_key; static void thread_finalize(void *v) { uptr iter = (uptr)v; if (iter > 1) { if (pthread_setspecific(g_thread_finalize_key, (void*)(iter - 1))) { Report("LeakSanitizer: failed to set thread key.\n"); Die(); } return; } ThreadFinish(); } struct ThreadParam { void *(*callback)(void *arg); void *param; atomic_uintptr_t tid; }; extern "C" void *__lsan_thread_start_func(void *arg) { ThreadParam *p = (ThreadParam*)arg; void* (*callback)(void *arg) = p->callback; void *param = p->param; // Wait until the last iteration to maximize the chance that we are the last // destructor to run. if (pthread_setspecific(g_thread_finalize_key, (void*)GetPthreadDestructorIterations())) { Report("LeakSanitizer: failed to set thread key.\n"); Die(); } int tid = 0; while ((tid = atomic_load(&p->tid, memory_order_acquire)) == 0) internal_sched_yield(); SetCurrentThread(tid); ThreadStart(tid, GetTid()); atomic_store(&p->tid, 0, memory_order_release); return callback(param); } INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void *), void *param) { ENSURE_LSAN_INITED; EnsureMainThreadIDIsCorrect(); __sanitizer_pthread_attr_t myattr; if (!attr) { pthread_attr_init(&myattr); attr = &myattr; } AdjustStackSize(attr); int detached = 0; pthread_attr_getdetachstate(attr, &detached); ThreadParam p; p.callback = callback; p.param = param; atomic_store(&p.tid, 0, memory_order_relaxed); int res; { // Ignore all allocations made by pthread_create: thread stack/TLS may be // stored by pthread for future reuse even after thread destruction, and // the linked list it's stored in doesn't even hold valid pointers to the // objects, the latter are calculated by obscure pointer arithmetic. ScopedInterceptorDisabler disabler; res = REAL(pthread_create)(th, attr, __lsan_thread_start_func, &p); } if (res == 0) { int tid = ThreadCreate(GetCurrentThread(), *(uptr *)th, IsStateDetached(detached)); CHECK_NE(tid, 0); atomic_store(&p.tid, tid, memory_order_release); while (atomic_load(&p.tid, memory_order_acquire) != 0) internal_sched_yield(); } if (attr == &myattr) pthread_attr_destroy(&myattr); return res; } INTERCEPTOR(int, pthread_join, void *th, void **ret) { ENSURE_LSAN_INITED; int tid = ThreadTid((uptr)th); int res = REAL(pthread_join)(th, ret); if (res == 0) ThreadJoin(tid); return res; } namespace __lsan { void InitializeInterceptors() { INTERCEPT_FUNCTION(malloc); INTERCEPT_FUNCTION(free); LSAN_MAYBE_INTERCEPT_CFREE; INTERCEPT_FUNCTION(calloc); INTERCEPT_FUNCTION(realloc); LSAN_MAYBE_INTERCEPT_MEMALIGN; LSAN_MAYBE_INTERCEPT___LIBC_MEMALIGN; LSAN_MAYBE_INTERCEPT_ALIGNED_ALLOC; INTERCEPT_FUNCTION(posix_memalign); INTERCEPT_FUNCTION(valloc); LSAN_MAYBE_INTERCEPT_PVALLOC; LSAN_MAYBE_INTERCEPT_MALLOC_USABLE_SIZE; LSAN_MAYBE_INTERCEPT_MALLINFO; LSAN_MAYBE_INTERCEPT_MALLOPT; INTERCEPT_FUNCTION(pthread_create); INTERCEPT_FUNCTION(pthread_join); if (pthread_key_create(&g_thread_finalize_key, &thread_finalize)) { Report("LeakSanitizer: failed to create thread key.\n"); Die(); } } } // namespace __lsan Index: vendor/compiler-rt/dist/lib/msan/msan_allocator.cc =================================================================== --- vendor/compiler-rt/dist/lib/msan/msan_allocator.cc (revision 320536) +++ vendor/compiler-rt/dist/lib/msan/msan_allocator.cc (revision 320537) @@ -1,270 +1,270 @@ //===-- msan_allocator.cc --------------------------- ---------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of MemorySanitizer. // // MemorySanitizer allocator. //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_allocator.h" #include "sanitizer_common/sanitizer_allocator_interface.h" #include "msan.h" #include "msan_allocator.h" #include "msan_origin.h" #include "msan_thread.h" #include "msan_poisoning.h" namespace __msan { struct Metadata { uptr requested_size; }; struct MsanMapUnmapCallback { void OnMap(uptr p, uptr size) const {} void OnUnmap(uptr p, uptr size) const { __msan_unpoison((void *)p, size); // We are about to unmap a chunk of user memory. // Mark the corresponding shadow memory as not needed. uptr shadow_p = MEM_TO_SHADOW(p); ReleaseMemoryPagesToOS(shadow_p, shadow_p + size); if (__msan_get_track_origins()) { uptr origin_p = MEM_TO_ORIGIN(p); ReleaseMemoryPagesToOS(origin_p, origin_p + size); } } }; #if defined(__mips64) static const uptr kMaxAllowedMallocSize = 2UL << 30; static const uptr kRegionSizeLog = 20; static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog; typedef TwoLevelByteMap<(kNumRegions >> 12), 1 << 12> ByteMap; struct AP32 { static const uptr kSpaceBeg = 0; static const u64 kSpaceSize = SANITIZER_MMAP_RANGE_SIZE; static const uptr kMetadataSize = sizeof(Metadata); typedef __sanitizer::CompactSizeClassMap SizeClassMap; static const uptr kRegionSizeLog = __msan::kRegionSizeLog; typedef __msan::ByteMap ByteMap; typedef MsanMapUnmapCallback MapUnmapCallback; static const uptr kFlags = 0; }; typedef SizeClassAllocator32 PrimaryAllocator; #elif defined(__x86_64__) #if SANITIZER_LINUX && !defined(MSAN_LINUX_X86_64_OLD_MAPPING) static const uptr kAllocatorSpace = 0x700000000000ULL; #else static const uptr kAllocatorSpace = 0x600000000000ULL; #endif static const uptr kMaxAllowedMallocSize = 8UL << 30; struct AP64 { // Allocator64 parameters. Deliberately using a short name. static const uptr kSpaceBeg = kAllocatorSpace; static const uptr kSpaceSize = 0x40000000000; // 4T. static const uptr kMetadataSize = sizeof(Metadata); typedef DefaultSizeClassMap SizeClassMap; typedef MsanMapUnmapCallback MapUnmapCallback; static const uptr kFlags = 0; }; typedef SizeClassAllocator64 PrimaryAllocator; #elif defined(__powerpc64__) static const uptr kMaxAllowedMallocSize = 2UL << 30; // 2G struct AP64 { // Allocator64 parameters. Deliberately using a short name. static const uptr kSpaceBeg = 0x300000000000; static const uptr kSpaceSize = 0x020000000000; // 2T. static const uptr kMetadataSize = sizeof(Metadata); typedef DefaultSizeClassMap SizeClassMap; typedef MsanMapUnmapCallback MapUnmapCallback; static const uptr kFlags = 0; }; typedef SizeClassAllocator64 PrimaryAllocator; #elif defined(__aarch64__) static const uptr kMaxAllowedMallocSize = 2UL << 30; // 2G static const uptr kRegionSizeLog = 20; static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog; typedef TwoLevelByteMap<(kNumRegions >> 12), 1 << 12> ByteMap; struct AP32 { static const uptr kSpaceBeg = 0; static const u64 kSpaceSize = SANITIZER_MMAP_RANGE_SIZE; static const uptr kMetadataSize = sizeof(Metadata); typedef __sanitizer::CompactSizeClassMap SizeClassMap; static const uptr kRegionSizeLog = __msan::kRegionSizeLog; typedef __msan::ByteMap ByteMap; typedef MsanMapUnmapCallback MapUnmapCallback; static const uptr kFlags = 0; }; typedef SizeClassAllocator32 PrimaryAllocator; #endif typedef SizeClassAllocatorLocalCache AllocatorCache; typedef LargeMmapAllocator SecondaryAllocator; typedef CombinedAllocator Allocator; static Allocator allocator; static AllocatorCache fallback_allocator_cache; static SpinMutex fallback_mutex; void MsanAllocatorInit() { SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null); allocator.Init(common_flags()->allocator_release_to_os_interval_ms); } AllocatorCache *GetAllocatorCache(MsanThreadLocalMallocStorage *ms) { CHECK(ms); CHECK_LE(sizeof(AllocatorCache), sizeof(ms->allocator_cache)); return reinterpret_cast(ms->allocator_cache); } void MsanThreadLocalMallocStorage::CommitBack() { allocator.SwallowCache(GetAllocatorCache(this)); } static void *MsanAllocate(StackTrace *stack, uptr size, uptr alignment, bool zeroise) { if (size > kMaxAllowedMallocSize) { Report("WARNING: MemorySanitizer failed to allocate %p bytes\n", (void *)size); return Allocator::FailureHandler::OnBadRequest(); } MsanThread *t = GetCurrentThread(); void *allocated; if (t) { AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage()); allocated = allocator.Allocate(cache, size, alignment); } else { SpinMutexLock l(&fallback_mutex); AllocatorCache *cache = &fallback_allocator_cache; allocated = allocator.Allocate(cache, size, alignment); } Metadata *meta = reinterpret_cast(allocator.GetMetaData(allocated)); meta->requested_size = size; if (zeroise) { __msan_clear_and_unpoison(allocated, size); } else if (flags()->poison_in_malloc) { __msan_poison(allocated, size); if (__msan_get_track_origins()) { stack->tag = StackTrace::TAG_ALLOC; Origin o = Origin::CreateHeapOrigin(stack); __msan_set_origin(allocated, size, o.raw_id()); } } MSAN_MALLOC_HOOK(allocated, size); return allocated; } void MsanDeallocate(StackTrace *stack, void *p) { CHECK(p); MSAN_FREE_HOOK(p); Metadata *meta = reinterpret_cast(allocator.GetMetaData(p)); uptr size = meta->requested_size; meta->requested_size = 0; // This memory will not be reused by anyone else, so we are free to keep it // poisoned. if (flags()->poison_in_free) { __msan_poison(p, size); if (__msan_get_track_origins()) { stack->tag = StackTrace::TAG_DEALLOC; Origin o = Origin::CreateHeapOrigin(stack); __msan_set_origin(p, size, o.raw_id()); } } MsanThread *t = GetCurrentThread(); if (t) { AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage()); allocator.Deallocate(cache, p); } else { SpinMutexLock l(&fallback_mutex); AllocatorCache *cache = &fallback_allocator_cache; allocator.Deallocate(cache, p); } } void *MsanCalloc(StackTrace *stack, uptr nmemb, uptr size) { - if (CallocShouldReturnNullDueToOverflow(size, nmemb)) + if (CheckForCallocOverflow(size, nmemb)) return Allocator::FailureHandler::OnBadRequest(); return MsanReallocate(stack, nullptr, nmemb * size, sizeof(u64), true); } void *MsanReallocate(StackTrace *stack, void *old_p, uptr new_size, uptr alignment, bool zeroise) { if (!old_p) return MsanAllocate(stack, new_size, alignment, zeroise); if (!new_size) { MsanDeallocate(stack, old_p); return nullptr; } Metadata *meta = reinterpret_cast(allocator.GetMetaData(old_p)); uptr old_size = meta->requested_size; uptr actually_allocated_size = allocator.GetActuallyAllocatedSize(old_p); if (new_size <= actually_allocated_size) { // We are not reallocating here. meta->requested_size = new_size; if (new_size > old_size) { if (zeroise) { __msan_clear_and_unpoison((char *)old_p + old_size, new_size - old_size); } else if (flags()->poison_in_malloc) { stack->tag = StackTrace::TAG_ALLOC; PoisonMemory((char *)old_p + old_size, new_size - old_size, stack); } } return old_p; } uptr memcpy_size = Min(new_size, old_size); void *new_p = MsanAllocate(stack, new_size, alignment, zeroise); // Printf("realloc: old_size %zd new_size %zd\n", old_size, new_size); if (new_p) { CopyMemory(new_p, old_p, memcpy_size, stack); MsanDeallocate(stack, old_p); } return new_p; } static uptr AllocationSize(const void *p) { if (!p) return 0; const void *beg = allocator.GetBlockBegin(p); if (beg != p) return 0; Metadata *b = (Metadata *)allocator.GetMetaData(p); return b->requested_size; } } // namespace __msan using namespace __msan; uptr __sanitizer_get_current_allocated_bytes() { uptr stats[AllocatorStatCount]; allocator.GetStats(stats); return stats[AllocatorStatAllocated]; } uptr __sanitizer_get_heap_size() { uptr stats[AllocatorStatCount]; allocator.GetStats(stats); return stats[AllocatorStatMapped]; } uptr __sanitizer_get_free_bytes() { return 1; } uptr __sanitizer_get_unmapped_bytes() { return 1; } uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; } int __sanitizer_get_ownership(const void *p) { return AllocationSize(p) != 0; } uptr __sanitizer_get_allocated_size(const void *p) { return AllocationSize(p); } Index: vendor/compiler-rt/dist/lib/msan/msan_interceptors.cc =================================================================== --- vendor/compiler-rt/dist/lib/msan/msan_interceptors.cc (revision 320536) +++ vendor/compiler-rt/dist/lib/msan/msan_interceptors.cc (revision 320537) @@ -1,1606 +1,1592 @@ //===-- msan_interceptors.cc ----------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of MemorySanitizer. // // Interceptors for standard library functions. // // FIXME: move as many interceptors as possible into // sanitizer_common/sanitizer_common_interceptors.h //===----------------------------------------------------------------------===// #include "interception/interception.h" #include "msan.h" #include "msan_chained_origin_depot.h" #include "msan_origin.h" #include "msan_thread.h" #include "msan_poisoning.h" #include "sanitizer_common/sanitizer_platform_limits_posix.h" #include "sanitizer_common/sanitizer_allocator.h" #include "sanitizer_common/sanitizer_allocator_interface.h" #include "sanitizer_common/sanitizer_allocator_internal.h" #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_linux.h" #include "sanitizer_common/sanitizer_tls_get_addr.h" #include // ACHTUNG! No other system header includes in this file. // Ideally, we should get rid of stdarg.h as well. using namespace __msan; using __sanitizer::memory_order; using __sanitizer::atomic_load; using __sanitizer::atomic_store; using __sanitizer::atomic_uintptr_t; DECLARE_REAL(SIZE_T, strlen, const char *s) DECLARE_REAL(SIZE_T, strnlen, const char *s, SIZE_T maxlen) DECLARE_REAL(void *, memcpy, void *dest, const void *src, uptr n) DECLARE_REAL(void *, memset, void *dest, int c, uptr n) #if SANITIZER_FREEBSD #define __errno_location __error #endif // True if this is a nested interceptor. static THREADLOCAL int in_interceptor_scope; extern "C" int *__errno_location(void); struct InterceptorScope { InterceptorScope() { ++in_interceptor_scope; } ~InterceptorScope() { --in_interceptor_scope; } }; bool IsInInterceptorScope() { return in_interceptor_scope; } static uptr allocated_for_dlsym; static const uptr kDlsymAllocPoolSize = 1024; static uptr alloc_memory_for_dlsym[kDlsymAllocPoolSize]; static bool IsInDlsymAllocPool(const void *ptr) { uptr off = (uptr)ptr - (uptr)alloc_memory_for_dlsym; return off < sizeof(alloc_memory_for_dlsym); } static void *AllocateFromLocalPool(uptr size_in_bytes) { uptr size_in_words = RoundUpTo(size_in_bytes, kWordSize) / kWordSize; void *mem = (void *)&alloc_memory_for_dlsym[allocated_for_dlsym]; allocated_for_dlsym += size_in_words; CHECK_LT(allocated_for_dlsym, kDlsymAllocPoolSize); return mem; } #define ENSURE_MSAN_INITED() do { \ CHECK(!msan_init_is_running); \ if (!msan_inited) { \ __msan_init(); \ } \ } while (0) // Check that [x, x+n) range is unpoisoned. #define CHECK_UNPOISONED_0(x, n) \ do { \ sptr offset = __msan_test_shadow(x, n); \ if (__msan::IsInSymbolizer()) \ break; \ if (offset >= 0 && __msan::flags()->report_umrs) { \ GET_CALLER_PC_BP_SP; \ (void) sp; \ ReportUMRInsideAddressRange(__func__, x, n, offset); \ __msan::PrintWarningWithOrigin( \ pc, bp, __msan_get_origin((const char *)x + offset)); \ if (__msan::flags()->halt_on_error) { \ Printf("Exiting\n"); \ Die(); \ } \ } \ } while (0) // Check that [x, x+n) range is unpoisoned unless we are in a nested // interceptor. #define CHECK_UNPOISONED(x, n) \ do { \ if (!IsInInterceptorScope()) CHECK_UNPOISONED_0(x, n); \ } while (0); #define CHECK_UNPOISONED_STRING_OF_LEN(x, len, n) \ CHECK_UNPOISONED((x), \ common_flags()->strict_string_checks ? (len) + 1 : (n) ) #define CHECK_UNPOISONED_STRING(x, n) \ CHECK_UNPOISONED_STRING_OF_LEN((x), internal_strlen(x), (n)) #if !SANITIZER_FREEBSD INTERCEPTOR(SIZE_T, fread_unlocked, void *ptr, SIZE_T size, SIZE_T nmemb, void *file) { ENSURE_MSAN_INITED(); SIZE_T res = REAL(fread_unlocked)(ptr, size, nmemb, file); if (res > 0) __msan_unpoison(ptr, res *size); return res; } #define MSAN_MAYBE_INTERCEPT_FREAD_UNLOCKED INTERCEPT_FUNCTION(fread_unlocked) #else #define MSAN_MAYBE_INTERCEPT_FREAD_UNLOCKED #endif INTERCEPTOR(SSIZE_T, readlink, const char *path, char *buf, SIZE_T bufsiz) { ENSURE_MSAN_INITED(); CHECK_UNPOISONED_STRING(path, 0) SSIZE_T res = REAL(readlink)(path, buf, bufsiz); if (res > 0) __msan_unpoison(buf, res); return res; } INTERCEPTOR(void *, mempcpy, void *dest, const void *src, SIZE_T n) { return (char *)__msan_memcpy(dest, src, n) + n; } INTERCEPTOR(void *, memccpy, void *dest, const void *src, int c, SIZE_T n) { ENSURE_MSAN_INITED(); void *res = REAL(memccpy)(dest, src, c, n); CHECK(!res || (res >= dest && res <= (char *)dest + n)); SIZE_T sz = res ? (char *)res - (char *)dest : n; CHECK_UNPOISONED(src, sz); __msan_unpoison(dest, sz); return res; } INTERCEPTOR(void *, bcopy, const void *src, void *dest, SIZE_T n) { return __msan_memmove(dest, src, n); } INTERCEPTOR(int, posix_memalign, void **memptr, SIZE_T alignment, SIZE_T size) { GET_MALLOC_STACK_TRACE; CHECK_EQ(alignment & (alignment - 1), 0); CHECK_NE(memptr, 0); *memptr = MsanReallocate(&stack, nullptr, size, alignment, false); CHECK_NE(*memptr, 0); __msan_unpoison(memptr, sizeof(*memptr)); return 0; } #if !SANITIZER_FREEBSD INTERCEPTOR(void *, memalign, SIZE_T boundary, SIZE_T size) { GET_MALLOC_STACK_TRACE; CHECK_EQ(boundary & (boundary - 1), 0); void *ptr = MsanReallocate(&stack, nullptr, size, boundary, false); return ptr; } #define MSAN_MAYBE_INTERCEPT_MEMALIGN INTERCEPT_FUNCTION(memalign) #else #define MSAN_MAYBE_INTERCEPT_MEMALIGN #endif INTERCEPTOR(void *, aligned_alloc, SIZE_T boundary, SIZE_T size) { GET_MALLOC_STACK_TRACE; CHECK_EQ(boundary & (boundary - 1), 0); void *ptr = MsanReallocate(&stack, nullptr, size, boundary, false); return ptr; } INTERCEPTOR(void *, __libc_memalign, SIZE_T boundary, SIZE_T size) { GET_MALLOC_STACK_TRACE; CHECK_EQ(boundary & (boundary - 1), 0); void *ptr = MsanReallocate(&stack, nullptr, size, boundary, false); DTLS_on_libc_memalign(ptr, size); return ptr; } INTERCEPTOR(void *, valloc, SIZE_T size) { GET_MALLOC_STACK_TRACE; void *ptr = MsanReallocate(&stack, nullptr, size, GetPageSizeCached(), false); return ptr; } #if !SANITIZER_FREEBSD INTERCEPTOR(void *, pvalloc, SIZE_T size) { GET_MALLOC_STACK_TRACE; uptr PageSize = GetPageSizeCached(); size = RoundUpTo(size, PageSize); if (size == 0) { // pvalloc(0) should allocate one page. size = PageSize; } void *ptr = MsanReallocate(&stack, nullptr, size, PageSize, false); return ptr; } #define MSAN_MAYBE_INTERCEPT_PVALLOC INTERCEPT_FUNCTION(pvalloc) #else #define MSAN_MAYBE_INTERCEPT_PVALLOC #endif INTERCEPTOR(void, free, void *ptr) { GET_MALLOC_STACK_TRACE; if (!ptr || UNLIKELY(IsInDlsymAllocPool(ptr))) return; MsanDeallocate(&stack, ptr); } #if !SANITIZER_FREEBSD INTERCEPTOR(void, cfree, void *ptr) { GET_MALLOC_STACK_TRACE; if (!ptr || UNLIKELY(IsInDlsymAllocPool(ptr))) return; MsanDeallocate(&stack, ptr); } #define MSAN_MAYBE_INTERCEPT_CFREE INTERCEPT_FUNCTION(cfree) #else #define MSAN_MAYBE_INTERCEPT_CFREE #endif INTERCEPTOR(uptr, malloc_usable_size, void *ptr) { return __sanitizer_get_allocated_size(ptr); } #if !SANITIZER_FREEBSD // This function actually returns a struct by value, but we can't unpoison a // temporary! The following is equivalent on all supported platforms but // aarch64 (which uses a different register for sret value). We have a test // to confirm that. INTERCEPTOR(void, mallinfo, __sanitizer_mallinfo *sret) { #ifdef __aarch64__ uptr r8; asm volatile("mov %0,x8" : "=r" (r8)); sret = reinterpret_cast<__sanitizer_mallinfo*>(r8); #endif REAL(memset)(sret, 0, sizeof(*sret)); __msan_unpoison(sret, sizeof(*sret)); } #define MSAN_MAYBE_INTERCEPT_MALLINFO INTERCEPT_FUNCTION(mallinfo) #else #define MSAN_MAYBE_INTERCEPT_MALLINFO #endif #if !SANITIZER_FREEBSD INTERCEPTOR(int, mallopt, int cmd, int value) { return -1; } #define MSAN_MAYBE_INTERCEPT_MALLOPT INTERCEPT_FUNCTION(mallopt) #else #define MSAN_MAYBE_INTERCEPT_MALLOPT #endif #if !SANITIZER_FREEBSD INTERCEPTOR(void, malloc_stats, void) { // FIXME: implement, but don't call REAL(malloc_stats)! } #define MSAN_MAYBE_INTERCEPT_MALLOC_STATS INTERCEPT_FUNCTION(malloc_stats) #else #define MSAN_MAYBE_INTERCEPT_MALLOC_STATS #endif INTERCEPTOR(char *, strcpy, char *dest, const char *src) { // NOLINT ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; SIZE_T n = REAL(strlen)(src); CHECK_UNPOISONED_STRING(src + n, 0); char *res = REAL(strcpy)(dest, src); // NOLINT CopyShadowAndOrigin(dest, src, n + 1, &stack); return res; } INTERCEPTOR(char *, strncpy, char *dest, const char *src, SIZE_T n) { // NOLINT ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; SIZE_T copy_size = REAL(strnlen)(src, n); if (copy_size < n) copy_size++; // trailing \0 char *res = REAL(strncpy)(dest, src, n); // NOLINT CopyShadowAndOrigin(dest, src, copy_size, &stack); __msan_unpoison(dest + copy_size, n - copy_size); return res; } INTERCEPTOR(char *, stpcpy, char *dest, const char *src) { // NOLINT ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; SIZE_T n = REAL(strlen)(src); CHECK_UNPOISONED_STRING(src + n, 0); char *res = REAL(stpcpy)(dest, src); // NOLINT CopyShadowAndOrigin(dest, src, n + 1, &stack); return res; } INTERCEPTOR(char *, strdup, char *src) { ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; // On FreeBSD strdup() leverages strlen(). InterceptorScope interceptor_scope; SIZE_T n = REAL(strlen)(src); CHECK_UNPOISONED_STRING(src + n, 0); char *res = REAL(strdup)(src); CopyShadowAndOrigin(res, src, n + 1, &stack); return res; } #if !SANITIZER_FREEBSD INTERCEPTOR(char *, __strdup, char *src) { ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; SIZE_T n = REAL(strlen)(src); CHECK_UNPOISONED_STRING(src + n, 0); char *res = REAL(__strdup)(src); CopyShadowAndOrigin(res, src, n + 1, &stack); return res; } #define MSAN_MAYBE_INTERCEPT___STRDUP INTERCEPT_FUNCTION(__strdup) #else #define MSAN_MAYBE_INTERCEPT___STRDUP #endif INTERCEPTOR(char *, gcvt, double number, SIZE_T ndigit, char *buf) { ENSURE_MSAN_INITED(); char *res = REAL(gcvt)(number, ndigit, buf); SIZE_T n = REAL(strlen)(buf); __msan_unpoison(buf, n + 1); return res; } INTERCEPTOR(char *, strcat, char *dest, const char *src) { // NOLINT ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; SIZE_T src_size = REAL(strlen)(src); SIZE_T dest_size = REAL(strlen)(dest); CHECK_UNPOISONED_STRING(src + src_size, 0); CHECK_UNPOISONED_STRING(dest + dest_size, 0); char *res = REAL(strcat)(dest, src); // NOLINT CopyShadowAndOrigin(dest + dest_size, src, src_size + 1, &stack); return res; } INTERCEPTOR(char *, strncat, char *dest, const char *src, SIZE_T n) { // NOLINT ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; SIZE_T dest_size = REAL(strlen)(dest); SIZE_T copy_size = REAL(strnlen)(src, n); CHECK_UNPOISONED_STRING(dest + dest_size, 0); char *res = REAL(strncat)(dest, src, n); // NOLINT CopyShadowAndOrigin(dest + dest_size, src, copy_size, &stack); __msan_unpoison(dest + dest_size + copy_size, 1); // \0 return res; } // Hack: always pass nptr and endptr as part of __VA_ARGS_ to avoid having to // deal with empty __VA_ARGS__ in the case of INTERCEPTOR_STRTO. #define INTERCEPTOR_STRTO_BODY(ret_type, func, ...) \ ENSURE_MSAN_INITED(); \ ret_type res = REAL(func)(__VA_ARGS__); \ __msan_unpoison(endptr, sizeof(*endptr)); \ return res; #define INTERCEPTOR_STRTO(ret_type, func, char_type) \ INTERCEPTOR(ret_type, func, const char_type *nptr, char_type **endptr) { \ INTERCEPTOR_STRTO_BODY(ret_type, func, nptr, endptr); \ } #define INTERCEPTOR_STRTO_BASE(ret_type, func, char_type) \ INTERCEPTOR(ret_type, func, const char_type *nptr, char_type **endptr, \ int base) { \ INTERCEPTOR_STRTO_BODY(ret_type, func, nptr, endptr, base); \ } #define INTERCEPTOR_STRTO_LOC(ret_type, func, char_type) \ INTERCEPTOR(ret_type, func, const char_type *nptr, char_type **endptr, \ void *loc) { \ INTERCEPTOR_STRTO_BODY(ret_type, func, nptr, endptr, loc); \ } #define INTERCEPTOR_STRTO_BASE_LOC(ret_type, func, char_type) \ INTERCEPTOR(ret_type, func, const char_type *nptr, char_type **endptr, \ int base, void *loc) { \ INTERCEPTOR_STRTO_BODY(ret_type, func, nptr, endptr, base, loc); \ } #define INTERCEPTORS_STRTO(ret_type, func, char_type) \ INTERCEPTOR_STRTO(ret_type, func, char_type) \ INTERCEPTOR_STRTO_LOC(ret_type, func##_l, char_type) \ INTERCEPTOR_STRTO_LOC(ret_type, __##func##_l, char_type) \ INTERCEPTOR_STRTO_LOC(ret_type, __##func##_internal, char_type) #define INTERCEPTORS_STRTO_BASE(ret_type, func, char_type) \ INTERCEPTOR_STRTO_BASE(ret_type, func, char_type) \ INTERCEPTOR_STRTO_BASE_LOC(ret_type, func##_l, char_type) \ INTERCEPTOR_STRTO_BASE_LOC(ret_type, __##func##_l, char_type) \ INTERCEPTOR_STRTO_BASE_LOC(ret_type, __##func##_internal, char_type) INTERCEPTORS_STRTO(double, strtod, char) // NOLINT INTERCEPTORS_STRTO(float, strtof, char) // NOLINT INTERCEPTORS_STRTO(long double, strtold, char) // NOLINT INTERCEPTORS_STRTO_BASE(long, strtol, char) // NOLINT INTERCEPTORS_STRTO_BASE(long long, strtoll, char) // NOLINT INTERCEPTORS_STRTO_BASE(unsigned long, strtoul, char) // NOLINT INTERCEPTORS_STRTO_BASE(unsigned long long, strtoull, char) // NOLINT INTERCEPTORS_STRTO(double, wcstod, wchar_t) // NOLINT INTERCEPTORS_STRTO(float, wcstof, wchar_t) // NOLINT INTERCEPTORS_STRTO(long double, wcstold, wchar_t) // NOLINT INTERCEPTORS_STRTO_BASE(long, wcstol, wchar_t) // NOLINT INTERCEPTORS_STRTO_BASE(long long, wcstoll, wchar_t) // NOLINT INTERCEPTORS_STRTO_BASE(unsigned long, wcstoul, wchar_t) // NOLINT INTERCEPTORS_STRTO_BASE(unsigned long long, wcstoull, wchar_t) // NOLINT #define INTERCEPT_STRTO(func) \ INTERCEPT_FUNCTION(func); \ INTERCEPT_FUNCTION(func##_l); \ INTERCEPT_FUNCTION(__##func##_l); \ INTERCEPT_FUNCTION(__##func##_internal); // FIXME: support *wprintf in common format interceptors. INTERCEPTOR(int, vswprintf, void *str, uptr size, void *format, va_list ap) { ENSURE_MSAN_INITED(); int res = REAL(vswprintf)(str, size, format, ap); if (res >= 0) { __msan_unpoison(str, 4 * (res + 1)); } return res; } INTERCEPTOR(int, swprintf, void *str, uptr size, void *format, ...) { ENSURE_MSAN_INITED(); va_list ap; va_start(ap, format); int res = vswprintf(str, size, format, ap); va_end(ap); return res; } INTERCEPTOR(SIZE_T, strxfrm, char *dest, const char *src, SIZE_T n) { ENSURE_MSAN_INITED(); CHECK_UNPOISONED(src, REAL(strlen)(src) + 1); SIZE_T res = REAL(strxfrm)(dest, src, n); if (res < n) __msan_unpoison(dest, res + 1); return res; } INTERCEPTOR(SIZE_T, strxfrm_l, char *dest, const char *src, SIZE_T n, void *loc) { ENSURE_MSAN_INITED(); CHECK_UNPOISONED(src, REAL(strlen)(src) + 1); SIZE_T res = REAL(strxfrm_l)(dest, src, n, loc); if (res < n) __msan_unpoison(dest, res + 1); return res; } #define INTERCEPTOR_STRFTIME_BODY(char_type, ret_type, func, s, ...) \ ENSURE_MSAN_INITED(); \ ret_type res = REAL(func)(s, __VA_ARGS__); \ if (s) __msan_unpoison(s, sizeof(char_type) * (res + 1)); \ return res; INTERCEPTOR(SIZE_T, strftime, char *s, SIZE_T max, const char *format, __sanitizer_tm *tm) { INTERCEPTOR_STRFTIME_BODY(char, SIZE_T, strftime, s, max, format, tm); } INTERCEPTOR(SIZE_T, strftime_l, char *s, SIZE_T max, const char *format, __sanitizer_tm *tm, void *loc) { INTERCEPTOR_STRFTIME_BODY(char, SIZE_T, strftime_l, s, max, format, tm, loc); } #if !SANITIZER_FREEBSD INTERCEPTOR(SIZE_T, __strftime_l, char *s, SIZE_T max, const char *format, __sanitizer_tm *tm, void *loc) { INTERCEPTOR_STRFTIME_BODY(char, SIZE_T, __strftime_l, s, max, format, tm, loc); } #define MSAN_MAYBE_INTERCEPT___STRFTIME_L INTERCEPT_FUNCTION(__strftime_l) #else #define MSAN_MAYBE_INTERCEPT___STRFTIME_L #endif INTERCEPTOR(SIZE_T, wcsftime, wchar_t *s, SIZE_T max, const wchar_t *format, __sanitizer_tm *tm) { INTERCEPTOR_STRFTIME_BODY(wchar_t, SIZE_T, wcsftime, s, max, format, tm); } INTERCEPTOR(SIZE_T, wcsftime_l, wchar_t *s, SIZE_T max, const wchar_t *format, __sanitizer_tm *tm, void *loc) { INTERCEPTOR_STRFTIME_BODY(wchar_t, SIZE_T, wcsftime_l, s, max, format, tm, loc); } #if !SANITIZER_FREEBSD INTERCEPTOR(SIZE_T, __wcsftime_l, wchar_t *s, SIZE_T max, const wchar_t *format, __sanitizer_tm *tm, void *loc) { INTERCEPTOR_STRFTIME_BODY(wchar_t, SIZE_T, __wcsftime_l, s, max, format, tm, loc); } #define MSAN_MAYBE_INTERCEPT___WCSFTIME_L INTERCEPT_FUNCTION(__wcsftime_l) #else #define MSAN_MAYBE_INTERCEPT___WCSFTIME_L #endif INTERCEPTOR(int, mbtowc, wchar_t *dest, const char *src, SIZE_T n) { ENSURE_MSAN_INITED(); int res = REAL(mbtowc)(dest, src, n); if (res != -1 && dest) __msan_unpoison(dest, sizeof(wchar_t)); return res; } INTERCEPTOR(int, mbrtowc, wchar_t *dest, const char *src, SIZE_T n, void *ps) { ENSURE_MSAN_INITED(); SIZE_T res = REAL(mbrtowc)(dest, src, n, ps); if (res != (SIZE_T)-1 && dest) __msan_unpoison(dest, sizeof(wchar_t)); return res; } -INTERCEPTOR(SIZE_T, wcslen, const wchar_t *s) { - ENSURE_MSAN_INITED(); - SIZE_T res = REAL(wcslen)(s); - CHECK_UNPOISONED(s, sizeof(wchar_t) * (res + 1)); - return res; -} - -INTERCEPTOR(SIZE_T, wcsnlen, const wchar_t *s, SIZE_T n) { - ENSURE_MSAN_INITED(); - SIZE_T res = REAL(wcsnlen)(s, n); - CHECK_UNPOISONED(s, sizeof(wchar_t) * Min(res + 1, n)); - return res; -} - -// wchar_t *wcschr(const wchar_t *wcs, wchar_t wc); -INTERCEPTOR(wchar_t *, wcschr, void *s, wchar_t wc, void *ps) { - ENSURE_MSAN_INITED(); - wchar_t *res = REAL(wcschr)(s, wc, ps); - return res; -} - -// wchar_t *wcscpy(wchar_t *dest, const wchar_t *src); -INTERCEPTOR(wchar_t *, wcscpy, wchar_t *dest, const wchar_t *src) { - ENSURE_MSAN_INITED(); - GET_STORE_STACK_TRACE; - wchar_t *res = REAL(wcscpy)(dest, src); - CopyShadowAndOrigin(dest, src, sizeof(wchar_t) * (REAL(wcslen)(src) + 1), - &stack); - return res; -} - -INTERCEPTOR(wchar_t *, wcsncpy, wchar_t *dest, const wchar_t *src, - SIZE_T n) { // NOLINT - ENSURE_MSAN_INITED(); - GET_STORE_STACK_TRACE; - SIZE_T copy_size = REAL(wcsnlen)(src, n); - if (copy_size < n) copy_size++; // trailing \0 - wchar_t *res = REAL(wcsncpy)(dest, src, n); // NOLINT - CopyShadowAndOrigin(dest, src, copy_size * sizeof(wchar_t), &stack); - __msan_unpoison(dest + copy_size, (n - copy_size) * sizeof(wchar_t)); - return res; -} - // wchar_t *wmemcpy(wchar_t *dest, const wchar_t *src, SIZE_T n); INTERCEPTOR(wchar_t *, wmemcpy, wchar_t *dest, const wchar_t *src, SIZE_T n) { ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; wchar_t *res = REAL(wmemcpy)(dest, src, n); CopyShadowAndOrigin(dest, src, n * sizeof(wchar_t), &stack); return res; } INTERCEPTOR(wchar_t *, wmempcpy, wchar_t *dest, const wchar_t *src, SIZE_T n) { ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; wchar_t *res = REAL(wmempcpy)(dest, src, n); CopyShadowAndOrigin(dest, src, n * sizeof(wchar_t), &stack); return res; } INTERCEPTOR(wchar_t *, wmemset, wchar_t *s, wchar_t c, SIZE_T n) { CHECK(MEM_IS_APP(s)); ENSURE_MSAN_INITED(); wchar_t *res = REAL(wmemset)(s, c, n); __msan_unpoison(s, n * sizeof(wchar_t)); return res; } INTERCEPTOR(wchar_t *, wmemmove, wchar_t *dest, const wchar_t *src, SIZE_T n) { ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; wchar_t *res = REAL(wmemmove)(dest, src, n); MoveShadowAndOrigin(dest, src, n * sizeof(wchar_t), &stack); return res; } INTERCEPTOR(int, wcscmp, const wchar_t *s1, const wchar_t *s2) { ENSURE_MSAN_INITED(); int res = REAL(wcscmp)(s1, s2); return res; } INTERCEPTOR(int, gettimeofday, void *tv, void *tz) { ENSURE_MSAN_INITED(); int res = REAL(gettimeofday)(tv, tz); if (tv) __msan_unpoison(tv, 16); if (tz) __msan_unpoison(tz, 8); return res; } INTERCEPTOR(char *, fcvt, double x, int a, int *b, int *c) { ENSURE_MSAN_INITED(); char *res = REAL(fcvt)(x, a, b, c); __msan_unpoison(b, sizeof(*b)); __msan_unpoison(c, sizeof(*c)); if (res) __msan_unpoison(res, REAL(strlen)(res) + 1); return res; } INTERCEPTOR(char *, getenv, char *name) { if (msan_init_is_running) return REAL(getenv)(name); ENSURE_MSAN_INITED(); char *res = REAL(getenv)(name); if (res) __msan_unpoison(res, REAL(strlen)(res) + 1); return res; } extern char **environ; static void UnpoisonEnviron() { char **envp = environ; for (; *envp; ++envp) { __msan_unpoison(envp, sizeof(*envp)); __msan_unpoison(*envp, REAL(strlen)(*envp) + 1); } // Trailing NULL pointer. __msan_unpoison(envp, sizeof(*envp)); } INTERCEPTOR(int, setenv, const char *name, const char *value, int overwrite) { ENSURE_MSAN_INITED(); CHECK_UNPOISONED_STRING(name, 0) int res = REAL(setenv)(name, value, overwrite); if (!res) UnpoisonEnviron(); return res; } INTERCEPTOR(int, putenv, char *string) { ENSURE_MSAN_INITED(); int res = REAL(putenv)(string); if (!res) UnpoisonEnviron(); return res; } #if !SANITIZER_FREEBSD INTERCEPTOR(int, __fxstat, int magic, int fd, void *buf) { ENSURE_MSAN_INITED(); int res = REAL(__fxstat)(magic, fd, buf); if (!res) __msan_unpoison(buf, __sanitizer::struct_stat_sz); return res; } #define MSAN_MAYBE_INTERCEPT___FXSTAT INTERCEPT_FUNCTION(__fxstat) #else #define MSAN_MAYBE_INTERCEPT___FXSTAT #endif #if !SANITIZER_FREEBSD INTERCEPTOR(int, __fxstat64, int magic, int fd, void *buf) { ENSURE_MSAN_INITED(); int res = REAL(__fxstat64)(magic, fd, buf); if (!res) __msan_unpoison(buf, __sanitizer::struct_stat64_sz); return res; } #define MSAN_MAYBE_INTERCEPT___FXSTAT64 INTERCEPT_FUNCTION(__fxstat64) #else #define MSAN_MAYBE_INTERCEPT___FXSTAT64 #endif #if SANITIZER_FREEBSD INTERCEPTOR(int, fstatat, int fd, char *pathname, void *buf, int flags) { ENSURE_MSAN_INITED(); int res = REAL(fstatat)(fd, pathname, buf, flags); if (!res) __msan_unpoison(buf, __sanitizer::struct_stat_sz); return res; } # define MSAN_INTERCEPT_FSTATAT INTERCEPT_FUNCTION(fstatat) #else INTERCEPTOR(int, __fxstatat, int magic, int fd, char *pathname, void *buf, int flags) { ENSURE_MSAN_INITED(); int res = REAL(__fxstatat)(magic, fd, pathname, buf, flags); if (!res) __msan_unpoison(buf, __sanitizer::struct_stat_sz); return res; } # define MSAN_INTERCEPT_FSTATAT INTERCEPT_FUNCTION(__fxstatat) #endif #if !SANITIZER_FREEBSD INTERCEPTOR(int, __fxstatat64, int magic, int fd, char *pathname, void *buf, int flags) { ENSURE_MSAN_INITED(); int res = REAL(__fxstatat64)(magic, fd, pathname, buf, flags); if (!res) __msan_unpoison(buf, __sanitizer::struct_stat64_sz); return res; } #define MSAN_MAYBE_INTERCEPT___FXSTATAT64 INTERCEPT_FUNCTION(__fxstatat64) #else #define MSAN_MAYBE_INTERCEPT___FXSTATAT64 #endif INTERCEPTOR(int, pipe, int pipefd[2]) { if (msan_init_is_running) return REAL(pipe)(pipefd); ENSURE_MSAN_INITED(); int res = REAL(pipe)(pipefd); if (!res) __msan_unpoison(pipefd, sizeof(int[2])); return res; } INTERCEPTOR(int, pipe2, int pipefd[2], int flags) { ENSURE_MSAN_INITED(); int res = REAL(pipe2)(pipefd, flags); if (!res) __msan_unpoison(pipefd, sizeof(int[2])); return res; } INTERCEPTOR(int, socketpair, int domain, int type, int protocol, int sv[2]) { ENSURE_MSAN_INITED(); int res = REAL(socketpair)(domain, type, protocol, sv); if (!res) __msan_unpoison(sv, sizeof(int[2])); return res; } INTERCEPTOR(char *, fgets, char *s, int size, void *stream) { ENSURE_MSAN_INITED(); char *res = REAL(fgets)(s, size, stream); if (res) __msan_unpoison(s, REAL(strlen)(s) + 1); return res; } #if !SANITIZER_FREEBSD INTERCEPTOR(char *, fgets_unlocked, char *s, int size, void *stream) { ENSURE_MSAN_INITED(); char *res = REAL(fgets_unlocked)(s, size, stream); if (res) __msan_unpoison(s, REAL(strlen)(s) + 1); return res; } #define MSAN_MAYBE_INTERCEPT_FGETS_UNLOCKED INTERCEPT_FUNCTION(fgets_unlocked) #else #define MSAN_MAYBE_INTERCEPT_FGETS_UNLOCKED #endif INTERCEPTOR(int, getrlimit, int resource, void *rlim) { if (msan_init_is_running) return REAL(getrlimit)(resource, rlim); ENSURE_MSAN_INITED(); int res = REAL(getrlimit)(resource, rlim); if (!res) __msan_unpoison(rlim, __sanitizer::struct_rlimit_sz); return res; } #if !SANITIZER_FREEBSD INTERCEPTOR(int, getrlimit64, int resource, void *rlim) { if (msan_init_is_running) return REAL(getrlimit64)(resource, rlim); ENSURE_MSAN_INITED(); int res = REAL(getrlimit64)(resource, rlim); if (!res) __msan_unpoison(rlim, __sanitizer::struct_rlimit64_sz); return res; } INTERCEPTOR(int, prlimit, int pid, int resource, void *new_rlimit, void *old_rlimit) { if (msan_init_is_running) return REAL(prlimit)(pid, resource, new_rlimit, old_rlimit); ENSURE_MSAN_INITED(); CHECK_UNPOISONED(new_rlimit, __sanitizer::struct_rlimit_sz); int res = REAL(prlimit)(pid, resource, new_rlimit, old_rlimit); if (!res) __msan_unpoison(old_rlimit, __sanitizer::struct_rlimit_sz); return res; } INTERCEPTOR(int, prlimit64, int pid, int resource, void *new_rlimit, void *old_rlimit) { if (msan_init_is_running) return REAL(prlimit64)(pid, resource, new_rlimit, old_rlimit); ENSURE_MSAN_INITED(); CHECK_UNPOISONED(new_rlimit, __sanitizer::struct_rlimit64_sz); int res = REAL(prlimit64)(pid, resource, new_rlimit, old_rlimit); if (!res) __msan_unpoison(old_rlimit, __sanitizer::struct_rlimit64_sz); return res; } #define MSAN_MAYBE_INTERCEPT_GETRLIMIT64 INTERCEPT_FUNCTION(getrlimit64) #define MSAN_MAYBE_INTERCEPT_PRLIMIT INTERCEPT_FUNCTION(prlimit) #define MSAN_MAYBE_INTERCEPT_PRLIMIT64 INTERCEPT_FUNCTION(prlimit64) #else #define MSAN_MAYBE_INTERCEPT_GETRLIMIT64 #define MSAN_MAYBE_INTERCEPT_PRLIMIT #define MSAN_MAYBE_INTERCEPT_PRLIMIT64 #endif #if SANITIZER_FREEBSD // FreeBSD's define uname() as // static __inline int uname(struct utsname *name) { // return __xuname(SYS_NMLN, (void*)name); // } INTERCEPTOR(int, __xuname, int size, void *utsname) { ENSURE_MSAN_INITED(); int res = REAL(__xuname)(size, utsname); if (!res) __msan_unpoison(utsname, __sanitizer::struct_utsname_sz); return res; } #define MSAN_INTERCEPT_UNAME INTERCEPT_FUNCTION(__xuname) #else INTERCEPTOR(int, uname, struct utsname *utsname) { ENSURE_MSAN_INITED(); int res = REAL(uname)(utsname); if (!res) __msan_unpoison(utsname, __sanitizer::struct_utsname_sz); return res; } #define MSAN_INTERCEPT_UNAME INTERCEPT_FUNCTION(uname) #endif INTERCEPTOR(int, gethostname, char *name, SIZE_T len) { ENSURE_MSAN_INITED(); int res = REAL(gethostname)(name, len); if (!res) { SIZE_T real_len = REAL(strnlen)(name, len); if (real_len < len) ++real_len; __msan_unpoison(name, real_len); } return res; } #if !SANITIZER_FREEBSD INTERCEPTOR(int, epoll_wait, int epfd, void *events, int maxevents, int timeout) { ENSURE_MSAN_INITED(); int res = REAL(epoll_wait)(epfd, events, maxevents, timeout); if (res > 0) { __msan_unpoison(events, __sanitizer::struct_epoll_event_sz * res); } return res; } #define MSAN_MAYBE_INTERCEPT_EPOLL_WAIT INTERCEPT_FUNCTION(epoll_wait) #else #define MSAN_MAYBE_INTERCEPT_EPOLL_WAIT #endif #if !SANITIZER_FREEBSD INTERCEPTOR(int, epoll_pwait, int epfd, void *events, int maxevents, int timeout, void *sigmask) { ENSURE_MSAN_INITED(); int res = REAL(epoll_pwait)(epfd, events, maxevents, timeout, sigmask); if (res > 0) { __msan_unpoison(events, __sanitizer::struct_epoll_event_sz * res); } return res; } #define MSAN_MAYBE_INTERCEPT_EPOLL_PWAIT INTERCEPT_FUNCTION(epoll_pwait) #else #define MSAN_MAYBE_INTERCEPT_EPOLL_PWAIT #endif INTERCEPTOR(void *, calloc, SIZE_T nmemb, SIZE_T size) { GET_MALLOC_STACK_TRACE; if (UNLIKELY(!msan_inited)) // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. return AllocateFromLocalPool(nmemb * size); return MsanCalloc(&stack, nmemb, size); } INTERCEPTOR(void *, realloc, void *ptr, SIZE_T size) { GET_MALLOC_STACK_TRACE; if (UNLIKELY(IsInDlsymAllocPool(ptr))) { uptr offset = (uptr)ptr - (uptr)alloc_memory_for_dlsym; uptr copy_size = Min(size, kDlsymAllocPoolSize - offset); void *new_ptr; if (UNLIKELY(!msan_inited)) { new_ptr = AllocateFromLocalPool(copy_size); } else { copy_size = size; new_ptr = MsanReallocate(&stack, nullptr, copy_size, sizeof(u64), false); } internal_memcpy(new_ptr, ptr, copy_size); return new_ptr; } return MsanReallocate(&stack, ptr, size, sizeof(u64), false); } INTERCEPTOR(void *, malloc, SIZE_T size) { GET_MALLOC_STACK_TRACE; if (UNLIKELY(!msan_inited)) // Hack: dlsym calls malloc before REAL(malloc) is retrieved from dlsym. return AllocateFromLocalPool(size); return MsanReallocate(&stack, nullptr, size, sizeof(u64), false); } void __msan_allocated_memory(const void *data, uptr size) { GET_MALLOC_STACK_TRACE; if (flags()->poison_in_malloc) { stack.tag = STACK_TRACE_TAG_POISON; PoisonMemory(data, size, &stack); } } void __msan_copy_shadow(void *dest, const void *src, uptr n) { GET_STORE_STACK_TRACE; MoveShadowAndOrigin(dest, src, n, &stack); } void __sanitizer_dtor_callback(const void *data, uptr size) { GET_MALLOC_STACK_TRACE; if (flags()->poison_in_dtor) { stack.tag = STACK_TRACE_TAG_POISON; PoisonMemory(data, size, &stack); } } INTERCEPTOR(void *, mmap, void *addr, SIZE_T length, int prot, int flags, int fd, OFF_T offset) { if (msan_init_is_running) return REAL(mmap)(addr, length, prot, flags, fd, offset); ENSURE_MSAN_INITED(); if (addr && !MEM_IS_APP(addr)) { if (flags & map_fixed) { *__errno_location() = errno_EINVAL; return (void *)-1; } else { addr = nullptr; } } void *res = REAL(mmap)(addr, length, prot, flags, fd, offset); if (res != (void*)-1) __msan_unpoison(res, RoundUpTo(length, GetPageSize())); return res; } #if !SANITIZER_FREEBSD INTERCEPTOR(void *, mmap64, void *addr, SIZE_T length, int prot, int flags, int fd, OFF64_T offset) { ENSURE_MSAN_INITED(); if (addr && !MEM_IS_APP(addr)) { if (flags & map_fixed) { *__errno_location() = errno_EINVAL; return (void *)-1; } else { addr = nullptr; } } void *res = REAL(mmap64)(addr, length, prot, flags, fd, offset); if (res != (void*)-1) __msan_unpoison(res, RoundUpTo(length, GetPageSize())); return res; } #define MSAN_MAYBE_INTERCEPT_MMAP64 INTERCEPT_FUNCTION(mmap64) #else #define MSAN_MAYBE_INTERCEPT_MMAP64 #endif INTERCEPTOR(int, getrusage, int who, void *usage) { ENSURE_MSAN_INITED(); int res = REAL(getrusage)(who, usage); if (res == 0) { __msan_unpoison(usage, __sanitizer::struct_rusage_sz); } return res; } class SignalHandlerScope { public: SignalHandlerScope() { if (MsanThread *t = GetCurrentThread()) t->EnterSignalHandler(); } ~SignalHandlerScope() { if (MsanThread *t = GetCurrentThread()) t->LeaveSignalHandler(); } }; // sigactions_mu guarantees atomicity of sigaction() and signal() calls. // Access to sigactions[] is gone with relaxed atomics to avoid data race with // the signal handler. const int kMaxSignals = 1024; static atomic_uintptr_t sigactions[kMaxSignals]; static StaticSpinMutex sigactions_mu; static void SignalHandler(int signo) { SignalHandlerScope signal_handler_scope; ScopedThreadLocalStateBackup stlsb; UnpoisonParam(1); typedef void (*signal_cb)(int x); signal_cb cb = (signal_cb)atomic_load(&sigactions[signo], memory_order_relaxed); cb(signo); } static void SignalAction(int signo, void *si, void *uc) { SignalHandlerScope signal_handler_scope; ScopedThreadLocalStateBackup stlsb; UnpoisonParam(3); __msan_unpoison(si, sizeof(__sanitizer_sigaction)); __msan_unpoison(uc, __sanitizer::ucontext_t_sz); typedef void (*sigaction_cb)(int, void *, void *); sigaction_cb cb = (sigaction_cb)atomic_load(&sigactions[signo], memory_order_relaxed); cb(signo, si, uc); } INTERCEPTOR(int, sigaction, int signo, const __sanitizer_sigaction *act, __sanitizer_sigaction *oldact) { ENSURE_MSAN_INITED(); // FIXME: check that *act is unpoisoned. // That requires intercepting all of sigemptyset, sigfillset, etc. int res; if (flags()->wrap_signals) { SpinMutexLock lock(&sigactions_mu); CHECK_LT(signo, kMaxSignals); uptr old_cb = atomic_load(&sigactions[signo], memory_order_relaxed); __sanitizer_sigaction new_act; __sanitizer_sigaction *pnew_act = act ? &new_act : nullptr; if (act) { REAL(memcpy)(pnew_act, act, sizeof(__sanitizer_sigaction)); uptr cb = (uptr)pnew_act->sigaction; uptr new_cb = (pnew_act->sa_flags & __sanitizer::sa_siginfo) ? (uptr)SignalAction : (uptr)SignalHandler; if (cb != __sanitizer::sig_ign && cb != __sanitizer::sig_dfl) { atomic_store(&sigactions[signo], cb, memory_order_relaxed); pnew_act->sigaction = (void (*)(int, void *, void *))new_cb; } } res = REAL(sigaction)(signo, pnew_act, oldact); if (res == 0 && oldact) { uptr cb = (uptr)oldact->sigaction; if (cb != __sanitizer::sig_ign && cb != __sanitizer::sig_dfl) { oldact->sigaction = (void (*)(int, void *, void *))old_cb; } } } else { res = REAL(sigaction)(signo, act, oldact); } if (res == 0 && oldact) { __msan_unpoison(oldact, sizeof(__sanitizer_sigaction)); } return res; } INTERCEPTOR(int, signal, int signo, uptr cb) { ENSURE_MSAN_INITED(); if (flags()->wrap_signals) { CHECK_LT(signo, kMaxSignals); SpinMutexLock lock(&sigactions_mu); if (cb != __sanitizer::sig_ign && cb != __sanitizer::sig_dfl) { atomic_store(&sigactions[signo], cb, memory_order_relaxed); cb = (uptr) SignalHandler; } return REAL(signal)(signo, cb); } else { return REAL(signal)(signo, cb); } } extern "C" int pthread_attr_init(void *attr); extern "C" int pthread_attr_destroy(void *attr); static void *MsanThreadStartFunc(void *arg) { MsanThread *t = (MsanThread *)arg; SetCurrentThread(t); return t->ThreadStart(); } INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*), void * param) { ENSURE_MSAN_INITED(); // for GetTlsSize() __sanitizer_pthread_attr_t myattr; if (!attr) { pthread_attr_init(&myattr); attr = &myattr; } AdjustStackSize(attr); MsanThread *t = MsanThread::Create(callback, param); int res = REAL(pthread_create)(th, attr, MsanThreadStartFunc, t); if (attr == &myattr) pthread_attr_destroy(&myattr); if (!res) { __msan_unpoison(th, __sanitizer::pthread_t_sz); } return res; } INTERCEPTOR(int, pthread_key_create, __sanitizer_pthread_key_t *key, void (*dtor)(void *value)) { if (msan_init_is_running) return REAL(pthread_key_create)(key, dtor); ENSURE_MSAN_INITED(); int res = REAL(pthread_key_create)(key, dtor); if (!res && key) __msan_unpoison(key, sizeof(*key)); return res; } INTERCEPTOR(int, pthread_join, void *th, void **retval) { ENSURE_MSAN_INITED(); int res = REAL(pthread_join)(th, retval); if (!res && retval) __msan_unpoison(retval, sizeof(*retval)); return res; } extern char *tzname[2]; INTERCEPTOR(void, tzset, int fake) { ENSURE_MSAN_INITED(); REAL(tzset)(fake); if (tzname[0]) __msan_unpoison(tzname[0], REAL(strlen)(tzname[0]) + 1); if (tzname[1]) __msan_unpoison(tzname[1], REAL(strlen)(tzname[1]) + 1); return; } struct MSanAtExitRecord { void (*func)(void *arg); void *arg; }; void MSanAtExitWrapper(void *arg) { UnpoisonParam(1); MSanAtExitRecord *r = (MSanAtExitRecord *)arg; r->func(r->arg); InternalFree(r); } // Unpoison argument shadow for C++ module destructors. INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg, void *dso_handle) { if (msan_init_is_running) return REAL(__cxa_atexit)(func, arg, dso_handle); ENSURE_MSAN_INITED(); MSanAtExitRecord *r = (MSanAtExitRecord *)InternalAlloc(sizeof(MSanAtExitRecord)); r->func = func; r->arg = arg; return REAL(__cxa_atexit)(MSanAtExitWrapper, r, dso_handle); } DECLARE_REAL(int, shmctl, int shmid, int cmd, void *buf) INTERCEPTOR(void *, shmat, int shmid, const void *shmaddr, int shmflg) { ENSURE_MSAN_INITED(); void *p = REAL(shmat)(shmid, shmaddr, shmflg); if (p != (void *)-1) { __sanitizer_shmid_ds ds; int res = REAL(shmctl)(shmid, shmctl_ipc_stat, &ds); if (!res) { __msan_unpoison(p, ds.shm_segsz); } } return p; } static void BeforeFork() { StackDepotLockAll(); ChainedOriginDepotLockAll(); } static void AfterFork() { ChainedOriginDepotUnlockAll(); StackDepotUnlockAll(); } INTERCEPTOR(int, fork, void) { ENSURE_MSAN_INITED(); BeforeFork(); int pid = REAL(fork)(); AfterFork(); return pid; } INTERCEPTOR(int, openpty, int *amaster, int *aslave, char *name, const void *termp, const void *winp) { ENSURE_MSAN_INITED(); InterceptorScope interceptor_scope; int res = REAL(openpty)(amaster, aslave, name, termp, winp); if (!res) { __msan_unpoison(amaster, sizeof(*amaster)); __msan_unpoison(aslave, sizeof(*aslave)); } return res; } INTERCEPTOR(int, forkpty, int *amaster, char *name, const void *termp, const void *winp) { ENSURE_MSAN_INITED(); InterceptorScope interceptor_scope; int res = REAL(forkpty)(amaster, name, termp, winp); if (res != -1) __msan_unpoison(amaster, sizeof(*amaster)); return res; } struct MSanInterceptorContext { bool in_interceptor_scope; }; namespace __msan { int OnExit() { // FIXME: ask frontend whether we need to return failure. return 0; } } // namespace __msan // A version of CHECK_UNPOISONED using a saved scope value. Used in common // interceptors. #define CHECK_UNPOISONED_CTX(ctx, x, n) \ do { \ if (!((MSanInterceptorContext *)ctx)->in_interceptor_scope) \ CHECK_UNPOISONED_0(x, n); \ } while (0) #define MSAN_INTERCEPT_FUNC(name) \ do { \ if ((!INTERCEPT_FUNCTION(name) || !REAL(name))) \ VReport(1, "MemorySanitizer: failed to intercept '" #name "'\n"); \ } while (0) #define MSAN_INTERCEPT_FUNC_VER(name, ver) \ do { \ if ((!INTERCEPT_FUNCTION_VER(name, ver) || !REAL(name))) \ VReport( \ 1, "MemorySanitizer: failed to intercept '" #name "@@" #ver "'\n"); \ } while (0) #define COMMON_INTERCEPT_FUNCTION(name) MSAN_INTERCEPT_FUNC(name) #define COMMON_INTERCEPT_FUNCTION_VER(name, ver) \ MSAN_INTERCEPT_FUNC_VER(name, ver) #define COMMON_INTERCEPTOR_UNPOISON_PARAM(count) \ UnpoisonParam(count) #define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ __msan_unpoison(ptr, size) #define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \ CHECK_UNPOISONED_CTX(ctx, ptr, size) #define COMMON_INTERCEPTOR_INITIALIZE_RANGE(ptr, size) \ __msan_unpoison(ptr, size) #define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ if (msan_init_is_running) return REAL(func)(__VA_ARGS__); \ ENSURE_MSAN_INITED(); \ MSanInterceptorContext msan_ctx = {IsInInterceptorScope()}; \ ctx = (void *)&msan_ctx; \ (void)ctx; \ InterceptorScope interceptor_scope; \ __msan_unpoison(__errno_location(), sizeof(int)); /* NOLINT */ #define COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path) \ do { \ } while (false) #define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \ do { \ } while (false) #define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \ do { \ } while (false) #define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \ do { \ } while (false) #define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) \ do { \ } while (false) // FIXME #define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \ do { \ } while (false) // FIXME #define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name) #define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit() #define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle) \ do { \ link_map *map = GET_LINK_MAP_BY_DLOPEN_HANDLE((handle)); \ if (filename && map) \ ForEachMappedRegion(map, __msan_unpoison); \ } while (false) #define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) \ if (MsanThread *t = GetCurrentThread()) { \ *begin = t->tls_begin(); \ *end = t->tls_end(); \ } else { \ *begin = *end = 0; \ } #define COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, c, size) \ { \ (void)ctx; \ return __msan_memset(block, c, size); \ } #define COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, to, from, size) \ { \ (void)ctx; \ return __msan_memmove(to, from, size); \ } #define COMMON_INTERCEPTOR_MEMCPY_IMPL(ctx, to, from, size) \ { \ (void)ctx; \ return __msan_memcpy(to, from, size); \ } -#define COMMON_INTERCEPTOR_COPY_STRING(ctx, to, from, size) \ - do { \ - GET_STORE_STACK_TRACE; \ - CopyShadowAndOrigin(to, from, size, &stack); \ - __msan_unpoison(to + size, 1); \ +#define COMMON_INTERCEPTOR_COPY_STRING(ctx, to, from, size) \ + do { \ + GET_STORE_STACK_TRACE; \ + CopyShadowAndOrigin(to, from, size, &stack); \ + __msan_unpoison(to + size, 1); \ } while (false) #include "sanitizer_common/sanitizer_platform_interceptors.h" #include "sanitizer_common/sanitizer_common_interceptors.inc" #define COMMON_SYSCALL_PRE_READ_RANGE(p, s) CHECK_UNPOISONED(p, s) #define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) \ do { \ } while (false) #define COMMON_SYSCALL_POST_READ_RANGE(p, s) \ do { \ } while (false) #define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) __msan_unpoison(p, s) #include "sanitizer_common/sanitizer_common_syscalls.inc" struct dlinfo { char *dli_fname; void *dli_fbase; char *dli_sname; void *dli_saddr; }; INTERCEPTOR(int, dladdr, void *addr, dlinfo *info) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, dladdr, addr, info); int res = REAL(dladdr)(addr, info); if (res != 0) { __msan_unpoison(info, sizeof(*info)); if (info->dli_fname) __msan_unpoison(info->dli_fname, REAL(strlen)(info->dli_fname) + 1); if (info->dli_sname) __msan_unpoison(info->dli_sname, REAL(strlen)(info->dli_sname) + 1); } return res; } INTERCEPTOR(char *, dlerror, int fake) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, dlerror, fake); char *res = REAL(dlerror)(fake); if (res) __msan_unpoison(res, REAL(strlen)(res) + 1); return res; } typedef int (*dl_iterate_phdr_cb)(__sanitizer_dl_phdr_info *info, SIZE_T size, void *data); struct dl_iterate_phdr_data { dl_iterate_phdr_cb callback; void *data; }; static int msan_dl_iterate_phdr_cb(__sanitizer_dl_phdr_info *info, SIZE_T size, void *data) { if (info) { __msan_unpoison(info, size); if (info->dlpi_phdr && info->dlpi_phnum) __msan_unpoison(info->dlpi_phdr, struct_ElfW_Phdr_sz * info->dlpi_phnum); if (info->dlpi_name) __msan_unpoison(info->dlpi_name, REAL(strlen)(info->dlpi_name) + 1); } dl_iterate_phdr_data *cbdata = (dl_iterate_phdr_data *)data; UnpoisonParam(3); return cbdata->callback(info, size, cbdata->data); } INTERCEPTOR(int, dl_iterate_phdr, dl_iterate_phdr_cb callback, void *data) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, dl_iterate_phdr, callback, data); dl_iterate_phdr_data cbdata; cbdata.callback = callback; cbdata.data = data; int res = REAL(dl_iterate_phdr)(msan_dl_iterate_phdr_cb, (void *)&cbdata); + return res; +} + +// wchar_t *wcschr(const wchar_t *wcs, wchar_t wc); +INTERCEPTOR(wchar_t *, wcschr, void *s, wchar_t wc, void *ps) { + ENSURE_MSAN_INITED(); + wchar_t *res = REAL(wcschr)(s, wc, ps); + return res; +} + +// wchar_t *wcscpy(wchar_t *dest, const wchar_t *src); +INTERCEPTOR(wchar_t *, wcscpy, wchar_t *dest, const wchar_t *src) { + ENSURE_MSAN_INITED(); + GET_STORE_STACK_TRACE; + wchar_t *res = REAL(wcscpy)(dest, src); + CopyShadowAndOrigin(dest, src, sizeof(wchar_t) * (REAL(wcslen)(src) + 1), + &stack); + return res; +} + +INTERCEPTOR(wchar_t *, wcsncpy, wchar_t *dest, const wchar_t *src, + SIZE_T n) { // NOLINT + ENSURE_MSAN_INITED(); + GET_STORE_STACK_TRACE; + SIZE_T copy_size = REAL(wcsnlen)(src, n); + if (copy_size < n) copy_size++; // trailing \0 + wchar_t *res = REAL(wcsncpy)(dest, src, n); // NOLINT + CopyShadowAndOrigin(dest, src, copy_size * sizeof(wchar_t), &stack); + __msan_unpoison(dest + copy_size, (n - copy_size) * sizeof(wchar_t)); return res; } // These interface functions reside here so that they can use // REAL(memset), etc. void __msan_unpoison(const void *a, uptr size) { if (!MEM_IS_APP(a)) return; SetShadow(a, size, 0); } void __msan_poison(const void *a, uptr size) { if (!MEM_IS_APP(a)) return; SetShadow(a, size, __msan::flags()->poison_heap_with_zeroes ? 0 : -1); } void __msan_poison_stack(void *a, uptr size) { if (!MEM_IS_APP(a)) return; SetShadow(a, size, __msan::flags()->poison_stack_with_zeroes ? 0 : -1); } void __msan_clear_and_unpoison(void *a, uptr size) { REAL(memset)(a, 0, size); SetShadow(a, size, 0); } void *__msan_memcpy(void *dest, const void *src, SIZE_T n) { if (!msan_inited) return internal_memcpy(dest, src, n); if (msan_init_is_running || __msan::IsInSymbolizer()) return REAL(memcpy)(dest, src, n); ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; void *res = REAL(memcpy)(dest, src, n); CopyShadowAndOrigin(dest, src, n, &stack); return res; } void *__msan_memset(void *s, int c, SIZE_T n) { if (!msan_inited) return internal_memset(s, c, n); if (msan_init_is_running) return REAL(memset)(s, c, n); ENSURE_MSAN_INITED(); void *res = REAL(memset)(s, c, n); __msan_unpoison(s, n); return res; } void *__msan_memmove(void *dest, const void *src, SIZE_T n) { if (!msan_inited) return internal_memmove(dest, src, n); if (msan_init_is_running) return REAL(memmove)(dest, src, n); ENSURE_MSAN_INITED(); GET_STORE_STACK_TRACE; void *res = REAL(memmove)(dest, src, n); MoveShadowAndOrigin(dest, src, n, &stack); return res; } void __msan_unpoison_string(const char* s) { if (!MEM_IS_APP(s)) return; __msan_unpoison(s, REAL(strlen)(s) + 1); } namespace __msan { void InitializeInterceptors() { static int inited = 0; CHECK_EQ(inited, 0); InitializeCommonInterceptors(); INTERCEPT_FUNCTION(mmap); MSAN_MAYBE_INTERCEPT_MMAP64; INTERCEPT_FUNCTION(posix_memalign); MSAN_MAYBE_INTERCEPT_MEMALIGN; INTERCEPT_FUNCTION(__libc_memalign); INTERCEPT_FUNCTION(valloc); MSAN_MAYBE_INTERCEPT_PVALLOC; INTERCEPT_FUNCTION(malloc); INTERCEPT_FUNCTION(calloc); INTERCEPT_FUNCTION(realloc); INTERCEPT_FUNCTION(free); MSAN_MAYBE_INTERCEPT_CFREE; INTERCEPT_FUNCTION(malloc_usable_size); MSAN_MAYBE_INTERCEPT_MALLINFO; MSAN_MAYBE_INTERCEPT_MALLOPT; MSAN_MAYBE_INTERCEPT_MALLOC_STATS; INTERCEPT_FUNCTION(fread); MSAN_MAYBE_INTERCEPT_FREAD_UNLOCKED; INTERCEPT_FUNCTION(readlink); INTERCEPT_FUNCTION(memccpy); INTERCEPT_FUNCTION(mempcpy); INTERCEPT_FUNCTION(bcopy); INTERCEPT_FUNCTION(wmemset); INTERCEPT_FUNCTION(wmemcpy); INTERCEPT_FUNCTION(wmempcpy); INTERCEPT_FUNCTION(wmemmove); INTERCEPT_FUNCTION(strcpy); // NOLINT INTERCEPT_FUNCTION(stpcpy); // NOLINT INTERCEPT_FUNCTION(strdup); MSAN_MAYBE_INTERCEPT___STRDUP; INTERCEPT_FUNCTION(strncpy); // NOLINT INTERCEPT_FUNCTION(gcvt); INTERCEPT_FUNCTION(strcat); // NOLINT INTERCEPT_FUNCTION(strncat); // NOLINT INTERCEPT_STRTO(strtod); INTERCEPT_STRTO(strtof); INTERCEPT_STRTO(strtold); INTERCEPT_STRTO(strtol); INTERCEPT_STRTO(strtoul); INTERCEPT_STRTO(strtoll); INTERCEPT_STRTO(strtoull); INTERCEPT_STRTO(wcstod); INTERCEPT_STRTO(wcstof); INTERCEPT_STRTO(wcstold); INTERCEPT_STRTO(wcstol); INTERCEPT_STRTO(wcstoul); INTERCEPT_STRTO(wcstoll); INTERCEPT_STRTO(wcstoull); #ifdef SANITIZER_NLDBL_VERSION INTERCEPT_FUNCTION_VER(vswprintf, SANITIZER_NLDBL_VERSION); INTERCEPT_FUNCTION_VER(swprintf, SANITIZER_NLDBL_VERSION); #else INTERCEPT_FUNCTION(vswprintf); INTERCEPT_FUNCTION(swprintf); #endif INTERCEPT_FUNCTION(strxfrm); INTERCEPT_FUNCTION(strxfrm_l); INTERCEPT_FUNCTION(strftime); INTERCEPT_FUNCTION(strftime_l); MSAN_MAYBE_INTERCEPT___STRFTIME_L; INTERCEPT_FUNCTION(wcsftime); INTERCEPT_FUNCTION(wcsftime_l); MSAN_MAYBE_INTERCEPT___WCSFTIME_L; INTERCEPT_FUNCTION(mbtowc); INTERCEPT_FUNCTION(mbrtowc); INTERCEPT_FUNCTION(wcslen); INTERCEPT_FUNCTION(wcsnlen); INTERCEPT_FUNCTION(wcschr); INTERCEPT_FUNCTION(wcscpy); INTERCEPT_FUNCTION(wcsncpy); INTERCEPT_FUNCTION(wcscmp); INTERCEPT_FUNCTION(getenv); INTERCEPT_FUNCTION(setenv); INTERCEPT_FUNCTION(putenv); INTERCEPT_FUNCTION(gettimeofday); INTERCEPT_FUNCTION(fcvt); MSAN_MAYBE_INTERCEPT___FXSTAT; MSAN_INTERCEPT_FSTATAT; MSAN_MAYBE_INTERCEPT___FXSTAT64; MSAN_MAYBE_INTERCEPT___FXSTATAT64; INTERCEPT_FUNCTION(pipe); INTERCEPT_FUNCTION(pipe2); INTERCEPT_FUNCTION(socketpair); INTERCEPT_FUNCTION(fgets); MSAN_MAYBE_INTERCEPT_FGETS_UNLOCKED; INTERCEPT_FUNCTION(getrlimit); MSAN_MAYBE_INTERCEPT_GETRLIMIT64; MSAN_MAYBE_INTERCEPT_PRLIMIT; MSAN_MAYBE_INTERCEPT_PRLIMIT64; MSAN_INTERCEPT_UNAME; INTERCEPT_FUNCTION(gethostname); MSAN_MAYBE_INTERCEPT_EPOLL_WAIT; MSAN_MAYBE_INTERCEPT_EPOLL_PWAIT; INTERCEPT_FUNCTION(dladdr); INTERCEPT_FUNCTION(dlerror); INTERCEPT_FUNCTION(dl_iterate_phdr); INTERCEPT_FUNCTION(getrusage); INTERCEPT_FUNCTION(sigaction); INTERCEPT_FUNCTION(signal); #if defined(__mips__) INTERCEPT_FUNCTION_VER(pthread_create, "GLIBC_2.2"); #else INTERCEPT_FUNCTION(pthread_create); #endif INTERCEPT_FUNCTION(pthread_key_create); INTERCEPT_FUNCTION(pthread_join); INTERCEPT_FUNCTION(tzset); INTERCEPT_FUNCTION(__cxa_atexit); INTERCEPT_FUNCTION(shmat); INTERCEPT_FUNCTION(fork); INTERCEPT_FUNCTION(openpty); INTERCEPT_FUNCTION(forkpty); inited = 1; } } // namespace __msan Index: vendor/compiler-rt/dist/lib/msan/msan_new_delete.cc =================================================================== --- vendor/compiler-rt/dist/lib/msan/msan_new_delete.cc (revision 320536) +++ vendor/compiler-rt/dist/lib/msan/msan_new_delete.cc (revision 320537) @@ -1,58 +1,66 @@ //===-- msan_new_delete.cc ------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of MemorySanitizer. // // Interceptors for operators new and delete. //===----------------------------------------------------------------------===// #include "msan.h" #include "interception/interception.h" +#include "sanitizer_common/sanitizer_allocator.h" #if MSAN_REPLACE_OPERATORS_NEW_AND_DELETE #include using namespace __msan; // NOLINT // Fake std::nothrow_t to avoid including . namespace std { struct nothrow_t {}; } // namespace std -#define OPERATOR_NEW_BODY \ +// TODO(alekseys): throw std::bad_alloc instead of dying on OOM. +#define OPERATOR_NEW_BODY(nothrow) \ GET_MALLOC_STACK_TRACE; \ - return MsanReallocate(&stack, 0, size, sizeof(u64), false) + void *res = MsanReallocate(&stack, 0, size, sizeof(u64), false);\ + if (!nothrow && UNLIKELY(!res)) DieOnFailure::OnOOM();\ + return res INTERCEPTOR_ATTRIBUTE -void *operator new(size_t size) { OPERATOR_NEW_BODY; } +void *operator new(size_t size) { OPERATOR_NEW_BODY(false /*nothrow*/); } INTERCEPTOR_ATTRIBUTE -void *operator new[](size_t size) { OPERATOR_NEW_BODY; } +void *operator new[](size_t size) { OPERATOR_NEW_BODY(false /*nothrow*/); } INTERCEPTOR_ATTRIBUTE -void *operator new(size_t size, std::nothrow_t const&) { OPERATOR_NEW_BODY; } +void *operator new(size_t size, std::nothrow_t const&) { + OPERATOR_NEW_BODY(true /*nothrow*/); +} INTERCEPTOR_ATTRIBUTE -void *operator new[](size_t size, std::nothrow_t const&) { OPERATOR_NEW_BODY; } +void *operator new[](size_t size, std::nothrow_t const&) { + OPERATOR_NEW_BODY(true /*nothrow*/); +} #define OPERATOR_DELETE_BODY \ GET_MALLOC_STACK_TRACE; \ if (ptr) MsanDeallocate(&stack, ptr) INTERCEPTOR_ATTRIBUTE void operator delete(void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY; } INTERCEPTOR_ATTRIBUTE void operator delete[](void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY; } INTERCEPTOR_ATTRIBUTE void operator delete(void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY; } INTERCEPTOR_ATTRIBUTE void operator delete[](void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY; } #endif // MSAN_REPLACE_OPERATORS_NEW_AND_DELETE Index: vendor/compiler-rt/dist/lib/msan/tests/msan_test.cc =================================================================== --- vendor/compiler-rt/dist/lib/msan/tests/msan_test.cc (revision 320536) +++ vendor/compiler-rt/dist/lib/msan/tests/msan_test.cc (revision 320537) @@ -1,4566 +1,4608 @@ //===-- msan_test.cc ------------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of MemorySanitizer. // // MemorySanitizer unit tests. //===----------------------------------------------------------------------===// #ifndef MSAN_EXTERNAL_TEST_CONFIG #include "msan_test_config.h" #endif // MSAN_EXTERNAL_TEST_CONFIG #include "sanitizer_common/tests/sanitizer_test_utils.h" #include "sanitizer/allocator_interface.h" #include "sanitizer/msan_interface.h" #if defined(__FreeBSD__) # define _KERNEL // To declare 'shminfo' structure. # include # undef _KERNEL extern "C" { // doesn't declare these functions in _KERNEL mode. void *shmat(int, const void *, int); int shmget(key_t, size_t, int); int shmctl(int, int, struct shmid_ds *); int shmdt(const void *); } #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if !defined(__FreeBSD__) # include # include # include # include # include #else # include # include # include # include # include # include # include # define f_namelen f_namemax // FreeBSD names this statfs field so. # define cpu_set_t cpuset_t extern "C" { // FreeBSD's defines mempcpy() to be a macro expanding into // a __builtin___mempcpy_chk() call, but since Msan RTL defines it as an // ordinary function, we can declare it here to complete the tests. void *mempcpy(void *dest, const void *src, size_t n); } #endif #if defined(__i386__) || defined(__x86_64__) # include # define MSAN_HAS_M128 1 #else # define MSAN_HAS_M128 0 #endif #ifdef __AVX2__ # include #endif // On FreeBSD procfs is not enabled by default. #if defined(__FreeBSD__) # define FILE_TO_READ "/bin/cat" # define DIR_TO_READ "/bin" # define SUBFILE_TO_READ "cat" # define SYMLINK_TO_READ "/usr/bin/tar" # define SUPERUSER_GROUP "wheel" #else # define FILE_TO_READ "/proc/self/stat" # define DIR_TO_READ "/proc/self" # define SUBFILE_TO_READ "stat" # define SYMLINK_TO_READ "/proc/self/exe" # define SUPERUSER_GROUP "root" #endif static uintptr_t GetPageSize() { return sysconf(_SC_PAGESIZE); } const size_t kMaxPathLength = 4096; typedef unsigned char U1; typedef unsigned short U2; // NOLINT typedef unsigned int U4; typedef unsigned long long U8; // NOLINT typedef signed char S1; typedef signed short S2; // NOLINT typedef signed int S4; typedef signed long long S8; // NOLINT #define NOINLINE __attribute__((noinline)) #define INLINE __attribute__((always_inline)) static bool TrackingOrigins() { S8 x; __msan_set_origin(&x, sizeof(x), 0x1234); U4 origin = __msan_get_origin(&x); __msan_set_origin(&x, sizeof(x), 0); return __msan_origin_is_descendant_or_same(origin, 0x1234); } #define EXPECT_ORIGIN(expected, origin) \ EXPECT_TRUE(__msan_origin_is_descendant_or_same((origin), (expected))) #define EXPECT_UMR(action) \ do { \ __msan_set_expect_umr(1); \ action; \ __msan_set_expect_umr(0); \ } while (0) #define EXPECT_UMR_O(action, origin) \ do { \ __msan_set_expect_umr(1); \ action; \ __msan_set_expect_umr(0); \ if (TrackingOrigins()) EXPECT_ORIGIN(origin, __msan_get_umr_origin()); \ } while (0) #define EXPECT_POISONED(x) ExpectPoisoned(x) template void ExpectPoisoned(const T& t) { EXPECT_NE(-1, __msan_test_shadow((void*)&t, sizeof(t))); } #define EXPECT_POISONED_O(x, origin) \ ExpectPoisonedWithOrigin(x, origin) template void ExpectPoisonedWithOrigin(const T& t, unsigned origin) { EXPECT_NE(-1, __msan_test_shadow((void*)&t, sizeof(t))); if (TrackingOrigins()) EXPECT_ORIGIN(origin, __msan_get_origin((void *)&t)); } #define EXPECT_NOT_POISONED(x) EXPECT_EQ(true, TestForNotPoisoned((x))) #define EXPECT_NOT_POISONED2(data, size) \ EXPECT_EQ(true, TestForNotPoisoned((data), (size))) bool TestForNotPoisoned(const void *data, size_t size) { return __msan_test_shadow(data, size) == -1; } template bool TestForNotPoisoned(const T& t) { return TestForNotPoisoned((void *)&t, sizeof(t)); } static U8 poisoned_array[100]; template T *GetPoisoned(int i = 0, T val = 0) { T *res = (T*)&poisoned_array[i]; *res = val; __msan_poison(&poisoned_array[i], sizeof(T)); return res; } template T *GetPoisonedO(int i, U4 origin, T val = 0) { T *res = (T*)&poisoned_array[i]; *res = val; __msan_poison(&poisoned_array[i], sizeof(T)); __msan_set_origin(&poisoned_array[i], sizeof(T), origin); return res; } template T Poisoned(T v = 0, T s = (T)(-1)) { __msan_partial_poison(&v, &s, sizeof(T)); return v; } template NOINLINE T ReturnPoisoned() { return *GetPoisoned(); } static volatile int g_one = 1; static volatile int g_zero = 0; static volatile int g_0 = 0; static volatile int g_1 = 1; S4 a_s4[100]; S8 a_s8[100]; // Check that malloc poisons memory. // A lot of tests below depend on this. TEST(MemorySanitizerSanity, PoisonInMalloc) { int *x = (int*)malloc(sizeof(int)); EXPECT_POISONED(*x); free(x); } TEST(MemorySanitizer, NegativeTest1) { S4 *x = GetPoisoned(); if (g_one) *x = 0; EXPECT_NOT_POISONED(*x); } TEST(MemorySanitizer, PositiveTest1) { // Load to store. EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); // S->S conversions. EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); // ZExt EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(*GetPoisoned()); // Unary ops. EXPECT_POISONED(- *GetPoisoned()); EXPECT_UMR(a_s4[g_zero] = 100 / *GetPoisoned(0, 1)); a_s4[g_zero] = 1 - *GetPoisoned(); a_s4[g_zero] = 1 + *GetPoisoned(); } TEST(MemorySanitizer, Phi1) { S4 c; if (g_one) { c = *GetPoisoned(); } else { break_optimization(0); c = 0; } EXPECT_POISONED(c); } TEST(MemorySanitizer, Phi2) { S4 i = *GetPoisoned(); S4 n = g_one; EXPECT_UMR(for (; i < g_one; i++);); EXPECT_POISONED(i); } NOINLINE void Arg1ExpectUMR(S4 a1) { EXPECT_POISONED(a1); } NOINLINE void Arg2ExpectUMR(S4 a1, S4 a2) { EXPECT_POISONED(a2); } NOINLINE void Arg3ExpectUMR(S1 a1, S4 a2, S8 a3) { EXPECT_POISONED(a3); } TEST(MemorySanitizer, ArgTest) { Arg1ExpectUMR(*GetPoisoned()); Arg2ExpectUMR(0, *GetPoisoned()); Arg3ExpectUMR(0, 1, *GetPoisoned()); } TEST(MemorySanitizer, CallAndRet) { ReturnPoisoned(); ReturnPoisoned(); ReturnPoisoned(); ReturnPoisoned(); EXPECT_POISONED(ReturnPoisoned()); EXPECT_POISONED(ReturnPoisoned()); EXPECT_POISONED(ReturnPoisoned()); EXPECT_POISONED(ReturnPoisoned()); } // malloc() in the following test may be optimized to produce a compile-time // undef value. Check that we trap on the volatile assignment anyway. TEST(MemorySanitizer, DISABLED_MallocNoIdent) { S4 *x = (int*)malloc(sizeof(S4)); EXPECT_POISONED(*x); free(x); } TEST(MemorySanitizer, Malloc) { S4 *x = (int*)Ident(malloc(sizeof(S4))); EXPECT_POISONED(*x); free(x); } TEST(MemorySanitizer, Realloc) { S4 *x = (int*)Ident(realloc(0, sizeof(S4))); EXPECT_POISONED(x[0]); x[0] = 1; x = (int*)Ident(realloc(x, 2 * sizeof(S4))); EXPECT_NOT_POISONED(x[0]); // Ok, was inited before. EXPECT_POISONED(x[1]); x = (int*)Ident(realloc(x, 3 * sizeof(S4))); EXPECT_NOT_POISONED(x[0]); // Ok, was inited before. EXPECT_POISONED(x[2]); EXPECT_POISONED(x[1]); x[2] = 1; // Init this here. Check that after realloc it is poisoned again. x = (int*)Ident(realloc(x, 2 * sizeof(S4))); EXPECT_NOT_POISONED(x[0]); // Ok, was inited before. EXPECT_POISONED(x[1]); x = (int*)Ident(realloc(x, 3 * sizeof(S4))); EXPECT_POISONED(x[1]); EXPECT_POISONED(x[2]); free(x); } TEST(MemorySanitizer, Calloc) { S4 *x = (int*)Ident(calloc(1, sizeof(S4))); EXPECT_NOT_POISONED(*x); // Should not be poisoned. EXPECT_EQ(0, *x); free(x); } TEST(MemorySanitizer, CallocReturnsZeroMem) { size_t sizes[] = {16, 1000, 10000, 100000, 2100000}; for (size_t s = 0; s < sizeof(sizes)/sizeof(sizes[0]); s++) { size_t size = sizes[s]; for (size_t iter = 0; iter < 5; iter++) { char *x = Ident((char*)calloc(1, size)); EXPECT_EQ(x[0], 0); EXPECT_EQ(x[size - 1], 0); EXPECT_EQ(x[size / 2], 0); EXPECT_EQ(x[size / 3], 0); EXPECT_EQ(x[size / 4], 0); memset(x, 0x42, size); free(Ident(x)); } } } TEST(MemorySanitizer, AndOr) { U4 *p = GetPoisoned(); // We poison two bytes in the midle of a 4-byte word to make the test // correct regardless of endianness. ((U1*)p)[1] = 0; ((U1*)p)[2] = 0xff; EXPECT_NOT_POISONED(*p & 0x00ffff00); EXPECT_NOT_POISONED(*p & 0x00ff0000); EXPECT_NOT_POISONED(*p & 0x0000ff00); EXPECT_POISONED(*p & 0xff000000); EXPECT_POISONED(*p & 0x000000ff); EXPECT_POISONED(*p & 0x0000ffff); EXPECT_POISONED(*p & 0xffff0000); EXPECT_NOT_POISONED(*p | 0xff0000ff); EXPECT_NOT_POISONED(*p | 0xff00ffff); EXPECT_NOT_POISONED(*p | 0xffff00ff); EXPECT_POISONED(*p | 0xff000000); EXPECT_POISONED(*p | 0x000000ff); EXPECT_POISONED(*p | 0x0000ffff); EXPECT_POISONED(*p | 0xffff0000); EXPECT_POISONED(*GetPoisoned() & *GetPoisoned()); } template static bool applyNot(T value, T shadow) { __msan_partial_poison(&value, &shadow, sizeof(T)); return !value; } TEST(MemorySanitizer, Not) { EXPECT_NOT_POISONED(applyNot(0x0, 0x0)); EXPECT_NOT_POISONED(applyNot(0xFFFFFFFF, 0x0)); EXPECT_POISONED(applyNot(0xFFFFFFFF, 0xFFFFFFFF)); EXPECT_NOT_POISONED(applyNot(0xFF000000, 0x0FFFFFFF)); EXPECT_NOT_POISONED(applyNot(0xFF000000, 0x00FFFFFF)); EXPECT_NOT_POISONED(applyNot(0xFF000000, 0x0000FFFF)); EXPECT_NOT_POISONED(applyNot(0xFF000000, 0x00000000)); EXPECT_POISONED(applyNot(0xFF000000, 0xFF000000)); EXPECT_NOT_POISONED(applyNot(0xFF800000, 0xFF000000)); EXPECT_POISONED(applyNot(0x00008000, 0x00008000)); EXPECT_NOT_POISONED(applyNot(0x0, 0x0)); EXPECT_NOT_POISONED(applyNot(0xFF, 0xFE)); EXPECT_NOT_POISONED(applyNot(0xFF, 0x0)); EXPECT_POISONED(applyNot(0xFF, 0xFF)); EXPECT_POISONED(applyNot((void*)0xFFFFFF, (void*)(-1))); EXPECT_NOT_POISONED(applyNot((void*)0xFFFFFF, (void*)(-2))); } TEST(MemorySanitizer, Shift) { U4 *up = GetPoisoned(); ((U1*)up)[0] = 0; ((U1*)up)[3] = 0xff; EXPECT_NOT_POISONED(*up >> 30); EXPECT_NOT_POISONED(*up >> 24); EXPECT_POISONED(*up >> 23); EXPECT_POISONED(*up >> 10); EXPECT_NOT_POISONED(*up << 30); EXPECT_NOT_POISONED(*up << 24); EXPECT_POISONED(*up << 23); EXPECT_POISONED(*up << 10); S4 *sp = (S4*)up; EXPECT_NOT_POISONED(*sp >> 30); EXPECT_NOT_POISONED(*sp >> 24); EXPECT_POISONED(*sp >> 23); EXPECT_POISONED(*sp >> 10); sp = GetPoisoned(); ((S1*)sp)[1] = 0; ((S1*)sp)[2] = 0; EXPECT_POISONED(*sp >> 31); EXPECT_POISONED(100 >> *GetPoisoned()); EXPECT_POISONED(100U >> *GetPoisoned()); } NOINLINE static int GetPoisonedZero() { int *zero = new int; *zero = 0; __msan_poison(zero, sizeof(*zero)); int res = *zero; delete zero; return res; } TEST(MemorySanitizer, LoadFromDirtyAddress) { int *a = new int; *a = 0; EXPECT_UMR(break_optimization((void*)(U8)a[GetPoisonedZero()])); delete a; } TEST(MemorySanitizer, StoreToDirtyAddress) { int *a = new int; EXPECT_UMR(a[GetPoisonedZero()] = 0); break_optimization(a); delete a; } NOINLINE void StackTestFunc() { S4 p4; S4 ok4 = 1; S2 p2; S2 ok2 = 1; S1 p1; S1 ok1 = 1; break_optimization(&p4); break_optimization(&ok4); break_optimization(&p2); break_optimization(&ok2); break_optimization(&p1); break_optimization(&ok1); EXPECT_POISONED(p4); EXPECT_POISONED(p2); EXPECT_POISONED(p1); EXPECT_NOT_POISONED(ok1); EXPECT_NOT_POISONED(ok2); EXPECT_NOT_POISONED(ok4); } TEST(MemorySanitizer, StackTest) { StackTestFunc(); } NOINLINE void StackStressFunc() { int foo[10000]; break_optimization(foo); } TEST(MemorySanitizer, DISABLED_StackStressTest) { for (int i = 0; i < 1000000; i++) StackStressFunc(); } template void TestFloatingPoint() { static volatile T v; static T g[100]; break_optimization(&g); T *x = GetPoisoned(); T *y = GetPoisoned(1); EXPECT_POISONED(*x); EXPECT_POISONED((long long)*x); EXPECT_POISONED((int)*x); g[0] = *x; g[1] = *x + *y; g[2] = *x - *y; g[3] = *x * *y; } TEST(MemorySanitizer, FloatingPointTest) { TestFloatingPoint(); TestFloatingPoint(); } TEST(MemorySanitizer, DynMem) { S4 x = 0; S4 *y = GetPoisoned(); memcpy(y, &x, g_one * sizeof(S4)); EXPECT_NOT_POISONED(*y); } static char *DynRetTestStr; TEST(MemorySanitizer, DynRet) { ReturnPoisoned(); EXPECT_NOT_POISONED(atoi("0")); } TEST(MemorySanitizer, DynRet1) { ReturnPoisoned(); } struct LargeStruct { S4 x[10]; }; NOINLINE LargeStruct LargeRetTest() { LargeStruct res; res.x[0] = *GetPoisoned(); res.x[1] = *GetPoisoned(); res.x[2] = *GetPoisoned(); res.x[3] = *GetPoisoned(); res.x[4] = *GetPoisoned(); res.x[5] = *GetPoisoned(); res.x[6] = *GetPoisoned(); res.x[7] = *GetPoisoned(); res.x[8] = *GetPoisoned(); res.x[9] = *GetPoisoned(); return res; } TEST(MemorySanitizer, strcmp) { char s1[10]; char s2[10]; strncpy(s1, "foo", 10); s2[0] = 'f'; s2[1] = 'n'; EXPECT_GT(strcmp(s1, s2), 0); s2[1] = 'o'; int res; EXPECT_UMR(res = strcmp(s1, s2)); EXPECT_NOT_POISONED(res); EXPECT_EQ(strncmp(s1, s2, 1), 0); } TEST(MemorySanitizer, LargeRet) { LargeStruct a = LargeRetTest(); EXPECT_POISONED(a.x[0]); EXPECT_POISONED(a.x[9]); } TEST(MemorySanitizer, strerror) { char *buf = strerror(EINVAL); EXPECT_NOT_POISONED(strlen(buf)); buf = strerror(123456); EXPECT_NOT_POISONED(strlen(buf)); } TEST(MemorySanitizer, strerror_r) { errno = 0; char buf[1000]; char *res = (char*) (size_t) strerror_r(EINVAL, buf, sizeof(buf)); ASSERT_EQ(0, errno); if (!res) res = buf; // POSIX version success. EXPECT_NOT_POISONED(strlen(res)); } TEST(MemorySanitizer, fread) { char *x = new char[32]; FILE *f = fopen(FILE_TO_READ, "r"); ASSERT_TRUE(f != NULL); fread(x, 1, 32, f); EXPECT_NOT_POISONED(x[0]); EXPECT_NOT_POISONED(x[16]); EXPECT_NOT_POISONED(x[31]); fclose(f); delete[] x; } TEST(MemorySanitizer, read) { char *x = new char[32]; int fd = open(FILE_TO_READ, O_RDONLY); ASSERT_GT(fd, 0); int sz = read(fd, x, 32); ASSERT_EQ(sz, 32); EXPECT_NOT_POISONED(x[0]); EXPECT_NOT_POISONED(x[16]); EXPECT_NOT_POISONED(x[31]); close(fd); delete[] x; } TEST(MemorySanitizer, pread) { char *x = new char[32]; int fd = open(FILE_TO_READ, O_RDONLY); ASSERT_GT(fd, 0); int sz = pread(fd, x, 32, 0); ASSERT_EQ(sz, 32); EXPECT_NOT_POISONED(x[0]); EXPECT_NOT_POISONED(x[16]); EXPECT_NOT_POISONED(x[31]); close(fd); delete[] x; } TEST(MemorySanitizer, readv) { char buf[2011]; struct iovec iov[2]; iov[0].iov_base = buf + 1; iov[0].iov_len = 5; iov[1].iov_base = buf + 10; iov[1].iov_len = 2000; int fd = open(FILE_TO_READ, O_RDONLY); ASSERT_GT(fd, 0); int sz = readv(fd, iov, 2); ASSERT_GE(sz, 0); ASSERT_LE(sz, 5 + 2000); ASSERT_GT((size_t)sz, iov[0].iov_len); EXPECT_POISONED(buf[0]); EXPECT_NOT_POISONED(buf[1]); EXPECT_NOT_POISONED(buf[5]); EXPECT_POISONED(buf[6]); EXPECT_POISONED(buf[9]); EXPECT_NOT_POISONED(buf[10]); EXPECT_NOT_POISONED(buf[10 + (sz - 1) - 5]); EXPECT_POISONED(buf[11 + (sz - 1) - 5]); close(fd); } TEST(MemorySanitizer, preadv) { char buf[2011]; struct iovec iov[2]; iov[0].iov_base = buf + 1; iov[0].iov_len = 5; iov[1].iov_base = buf + 10; iov[1].iov_len = 2000; int fd = open(FILE_TO_READ, O_RDONLY); ASSERT_GT(fd, 0); int sz = preadv(fd, iov, 2, 3); ASSERT_GE(sz, 0); ASSERT_LE(sz, 5 + 2000); ASSERT_GT((size_t)sz, iov[0].iov_len); EXPECT_POISONED(buf[0]); EXPECT_NOT_POISONED(buf[1]); EXPECT_NOT_POISONED(buf[5]); EXPECT_POISONED(buf[6]); EXPECT_POISONED(buf[9]); EXPECT_NOT_POISONED(buf[10]); EXPECT_NOT_POISONED(buf[10 + (sz - 1) - 5]); EXPECT_POISONED(buf[11 + (sz - 1) - 5]); close(fd); } // FIXME: fails now. TEST(MemorySanitizer, DISABLED_ioctl) { struct winsize ws; EXPECT_EQ(ioctl(2, TIOCGWINSZ, &ws), 0); EXPECT_NOT_POISONED(ws.ws_col); } TEST(MemorySanitizer, readlink) { char *x = new char[1000]; readlink(SYMLINK_TO_READ, x, 1000); EXPECT_NOT_POISONED(x[0]); delete [] x; } TEST(MemorySanitizer, stat) { struct stat* st = new struct stat; int res = stat(FILE_TO_READ, st); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(st->st_dev); EXPECT_NOT_POISONED(st->st_mode); EXPECT_NOT_POISONED(st->st_size); } TEST(MemorySanitizer, fstatat) { struct stat* st = new struct stat; int dirfd = open(DIR_TO_READ, O_RDONLY); ASSERT_GT(dirfd, 0); int res = fstatat(dirfd, SUBFILE_TO_READ, st, 0); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(st->st_dev); EXPECT_NOT_POISONED(st->st_mode); EXPECT_NOT_POISONED(st->st_size); close(dirfd); } TEST(MemorySanitizer, statfs) { struct statfs st; int res = statfs("/", &st); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(st.f_type); EXPECT_NOT_POISONED(st.f_bfree); EXPECT_NOT_POISONED(st.f_namelen); } TEST(MemorySanitizer, statvfs) { struct statvfs st; int res = statvfs("/", &st); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(st.f_bsize); EXPECT_NOT_POISONED(st.f_blocks); EXPECT_NOT_POISONED(st.f_bfree); EXPECT_NOT_POISONED(st.f_namemax); } TEST(MemorySanitizer, fstatvfs) { struct statvfs st; int fd = open("/", O_RDONLY | O_DIRECTORY); int res = fstatvfs(fd, &st); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(st.f_bsize); EXPECT_NOT_POISONED(st.f_blocks); EXPECT_NOT_POISONED(st.f_bfree); EXPECT_NOT_POISONED(st.f_namemax); close(fd); } TEST(MemorySanitizer, pipe) { int* pipefd = new int[2]; int res = pipe(pipefd); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(pipefd[0]); EXPECT_NOT_POISONED(pipefd[1]); close(pipefd[0]); close(pipefd[1]); } TEST(MemorySanitizer, pipe2) { int* pipefd = new int[2]; int res = pipe2(pipefd, O_NONBLOCK); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(pipefd[0]); EXPECT_NOT_POISONED(pipefd[1]); close(pipefd[0]); close(pipefd[1]); } TEST(MemorySanitizer, socketpair) { int sv[2]; int res = socketpair(AF_UNIX, SOCK_STREAM, 0, sv); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(sv[0]); EXPECT_NOT_POISONED(sv[1]); close(sv[0]); close(sv[1]); } TEST(MemorySanitizer, poll) { int* pipefd = new int[2]; int res = pipe(pipefd); ASSERT_EQ(0, res); char data = 42; res = write(pipefd[1], &data, 1); ASSERT_EQ(1, res); pollfd fds[2]; fds[0].fd = pipefd[0]; fds[0].events = POLLIN; fds[1].fd = pipefd[1]; fds[1].events = POLLIN; res = poll(fds, 2, 500); ASSERT_EQ(1, res); EXPECT_NOT_POISONED(fds[0].revents); EXPECT_NOT_POISONED(fds[1].revents); close(pipefd[0]); close(pipefd[1]); } // There is no ppoll() on FreeBSD. #if !defined (__FreeBSD__) TEST(MemorySanitizer, ppoll) { int* pipefd = new int[2]; int res = pipe(pipefd); ASSERT_EQ(0, res); char data = 42; res = write(pipefd[1], &data, 1); ASSERT_EQ(1, res); pollfd fds[2]; fds[0].fd = pipefd[0]; fds[0].events = POLLIN; fds[1].fd = pipefd[1]; fds[1].events = POLLIN; sigset_t ss; sigemptyset(&ss); res = ppoll(fds, 2, NULL, &ss); ASSERT_EQ(1, res); EXPECT_NOT_POISONED(fds[0].revents); EXPECT_NOT_POISONED(fds[1].revents); close(pipefd[0]); close(pipefd[1]); } #endif TEST(MemorySanitizer, poll_positive) { int* pipefd = new int[2]; int res = pipe(pipefd); ASSERT_EQ(0, res); pollfd fds[2]; fds[0].fd = pipefd[0]; fds[0].events = POLLIN; // fds[1].fd uninitialized fds[1].events = POLLIN; EXPECT_UMR(poll(fds, 2, 0)); close(pipefd[0]); close(pipefd[1]); } TEST(MemorySanitizer, bind_getsockname) { int sock = socket(AF_UNIX, SOCK_STREAM, 0); struct sockaddr_in sai; memset(&sai, 0, sizeof(sai)); sai.sin_family = AF_UNIX; int res = bind(sock, (struct sockaddr *)&sai, sizeof(sai)); ASSERT_EQ(0, res); char buf[200]; socklen_t addrlen; EXPECT_UMR(getsockname(sock, (struct sockaddr *)&buf, &addrlen)); addrlen = sizeof(buf); res = getsockname(sock, (struct sockaddr *)&buf, &addrlen); EXPECT_NOT_POISONED(addrlen); EXPECT_NOT_POISONED(buf[0]); EXPECT_NOT_POISONED(buf[addrlen - 1]); EXPECT_POISONED(buf[addrlen]); close(sock); } class SocketAddr { public: virtual ~SocketAddr() = default; virtual struct sockaddr *ptr() = 0; virtual size_t size() const = 0; template static std::unique_ptr Create(int family, Args... args); }; class SocketAddr4 : public SocketAddr { public: SocketAddr4() { EXPECT_POISONED(sai_); } explicit SocketAddr4(uint16_t port) { memset(&sai_, 0, sizeof(sai_)); sai_.sin_family = AF_INET; sai_.sin_port = port; sai_.sin_addr.s_addr = htonl(INADDR_LOOPBACK); } sockaddr *ptr() override { return reinterpret_cast(&sai_); } size_t size() const override { return sizeof(sai_); } private: sockaddr_in sai_; }; class SocketAddr6 : public SocketAddr { public: SocketAddr6() { EXPECT_POISONED(sai_); } explicit SocketAddr6(uint16_t port) { memset(&sai_, 0, sizeof(sai_)); sai_.sin6_family = AF_INET6; sai_.sin6_port = port; sai_.sin6_addr = in6addr_loopback; } sockaddr *ptr() override { return reinterpret_cast(&sai_); } size_t size() const override { return sizeof(sai_); } private: sockaddr_in6 sai_; }; template std::unique_ptr SocketAddr::Create(int family, Args... args) { if (family == AF_INET) return std::unique_ptr(new SocketAddr4(args...)); return std::unique_ptr(new SocketAddr6(args...)); } class MemorySanitizerIpTest : public ::testing::TestWithParam { public: void SetUp() override { ASSERT_TRUE(GetParam() == AF_INET || GetParam() == AF_INET6); } template std::unique_ptr CreateSockAddr(Args... args) const { return SocketAddr::Create(GetParam(), args...); } int CreateSocket(int socket_type) const { return socket(GetParam(), socket_type, 0); } }; std::vector GetAvailableIpSocketFamilies() { std::vector result; for (int i : {AF_INET, AF_INET6}) { int s = socket(i, SOCK_STREAM, 0); if (s > 0) { auto sai = SocketAddr::Create(i, 0); if (bind(s, sai->ptr(), sai->size()) == 0) result.push_back(i); close(s); } } return result; } INSTANTIATE_TEST_CASE_P(IpTests, MemorySanitizerIpTest, ::testing::ValuesIn(GetAvailableIpSocketFamilies())); TEST_P(MemorySanitizerIpTest, accept) { int listen_socket = CreateSocket(SOCK_STREAM); ASSERT_LT(0, listen_socket); auto sai = CreateSockAddr(0); int res = bind(listen_socket, sai->ptr(), sai->size()); ASSERT_EQ(0, res); res = listen(listen_socket, 1); ASSERT_EQ(0, res); socklen_t sz = sai->size(); res = getsockname(listen_socket, sai->ptr(), &sz); ASSERT_EQ(0, res); ASSERT_EQ(sai->size(), sz); int connect_socket = CreateSocket(SOCK_STREAM); ASSERT_LT(0, connect_socket); res = fcntl(connect_socket, F_SETFL, O_NONBLOCK); ASSERT_EQ(0, res); res = connect(connect_socket, sai->ptr(), sai->size()); // On FreeBSD this connection completes immediately. if (res != 0) { ASSERT_EQ(-1, res); ASSERT_EQ(EINPROGRESS, errno); } __msan_poison(sai->ptr(), sai->size()); int new_sock = accept(listen_socket, sai->ptr(), &sz); ASSERT_LT(0, new_sock); ASSERT_EQ(sai->size(), sz); EXPECT_NOT_POISONED2(sai->ptr(), sai->size()); __msan_poison(sai->ptr(), sai->size()); res = getpeername(new_sock, sai->ptr(), &sz); ASSERT_EQ(0, res); ASSERT_EQ(sai->size(), sz); EXPECT_NOT_POISONED2(sai->ptr(), sai->size()); close(new_sock); close(connect_socket); close(listen_socket); } TEST_P(MemorySanitizerIpTest, recvmsg) { int server_socket = CreateSocket(SOCK_DGRAM); ASSERT_LT(0, server_socket); auto sai = CreateSockAddr(0); int res = bind(server_socket, sai->ptr(), sai->size()); ASSERT_EQ(0, res); socklen_t sz = sai->size(); res = getsockname(server_socket, sai->ptr(), &sz); ASSERT_EQ(0, res); ASSERT_EQ(sai->size(), sz); int client_socket = CreateSocket(SOCK_DGRAM); ASSERT_LT(0, client_socket); auto client_sai = CreateSockAddr(0); res = bind(client_socket, client_sai->ptr(), client_sai->size()); ASSERT_EQ(0, res); sz = client_sai->size(); res = getsockname(client_socket, client_sai->ptr(), &sz); ASSERT_EQ(0, res); ASSERT_EQ(client_sai->size(), sz); const char *s = "message text"; struct iovec iov; iov.iov_base = (void *)s; iov.iov_len = strlen(s) + 1; struct msghdr msg; memset(&msg, 0, sizeof(msg)); msg.msg_name = sai->ptr(); msg.msg_namelen = sai->size(); msg.msg_iov = &iov; msg.msg_iovlen = 1; res = sendmsg(client_socket, &msg, 0); ASSERT_LT(0, res); char buf[1000]; struct iovec recv_iov; recv_iov.iov_base = (void *)&buf; recv_iov.iov_len = sizeof(buf); auto recv_sai = CreateSockAddr(); struct msghdr recv_msg; memset(&recv_msg, 0, sizeof(recv_msg)); recv_msg.msg_name = recv_sai->ptr(); recv_msg.msg_namelen = recv_sai->size(); recv_msg.msg_iov = &recv_iov; recv_msg.msg_iovlen = 1; res = recvmsg(server_socket, &recv_msg, 0); ASSERT_LT(0, res); ASSERT_EQ(recv_sai->size(), recv_msg.msg_namelen); EXPECT_NOT_POISONED2(recv_sai->ptr(), recv_sai->size()); EXPECT_STREQ(s, buf); close(server_socket); close(client_socket); } #define EXPECT_HOSTENT_NOT_POISONED(he) \ do { \ EXPECT_NOT_POISONED(*(he)); \ ASSERT_NE((void *)0, (he)->h_name); \ ASSERT_NE((void *)0, (he)->h_aliases); \ ASSERT_NE((void *)0, (he)->h_addr_list); \ EXPECT_NOT_POISONED(strlen((he)->h_name)); \ char **p = (he)->h_aliases; \ while (*p) { \ EXPECT_NOT_POISONED(strlen(*p)); \ ++p; \ } \ char **q = (he)->h_addr_list; \ while (*q) { \ EXPECT_NOT_POISONED(*q[0]); \ ++q; \ } \ EXPECT_NOT_POISONED(*q); \ } while (0) TEST(MemorySanitizer, gethostent) { struct hostent *he = gethostent(); ASSERT_NE((void *)NULL, he); EXPECT_HOSTENT_NOT_POISONED(he); } #ifndef MSAN_TEST_DISABLE_GETHOSTBYNAME TEST(MemorySanitizer, gethostbyname) { struct hostent *he = gethostbyname("localhost"); ASSERT_NE((void *)NULL, he); EXPECT_HOSTENT_NOT_POISONED(he); } #endif // MSAN_TEST_DISABLE_GETHOSTBYNAME TEST(MemorySanitizer, getaddrinfo) { struct addrinfo *ai; struct addrinfo hints; memset(&hints, 0, sizeof(hints)); hints.ai_family = AF_INET; int res = getaddrinfo("localhost", NULL, &hints, &ai); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(*ai); ASSERT_EQ(sizeof(sockaddr_in), ai->ai_addrlen); EXPECT_NOT_POISONED(*(sockaddr_in *)ai->ai_addr); } TEST(MemorySanitizer, getnameinfo) { struct sockaddr_in sai; memset(&sai, 0, sizeof(sai)); sai.sin_family = AF_INET; sai.sin_port = 80; sai.sin_addr.s_addr = htonl(INADDR_LOOPBACK); char host[500]; char serv[500]; int res = getnameinfo((struct sockaddr *)&sai, sizeof(sai), host, sizeof(host), serv, sizeof(serv), 0); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(host[0]); EXPECT_POISONED(host[sizeof(host) - 1]); ASSERT_NE(0U, strlen(host)); EXPECT_NOT_POISONED(serv[0]); EXPECT_POISONED(serv[sizeof(serv) - 1]); ASSERT_NE(0U, strlen(serv)); } TEST(MemorySanitizer, gethostbyname2) { struct hostent *he = gethostbyname2("localhost", AF_INET); ASSERT_NE((void *)NULL, he); EXPECT_HOSTENT_NOT_POISONED(he); } TEST(MemorySanitizer, gethostbyaddr) { in_addr_t addr = inet_addr("127.0.0.1"); EXPECT_NOT_POISONED(addr); struct hostent *he = gethostbyaddr(&addr, sizeof(addr), AF_INET); ASSERT_NE((void *)NULL, he); EXPECT_HOSTENT_NOT_POISONED(he); } TEST(MemorySanitizer, gethostent_r) { char buf[2000]; struct hostent he; struct hostent *result; int err; int res = gethostent_r(&he, buf, sizeof(buf), &result, &err); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(result); ASSERT_NE((void *)NULL, result); EXPECT_HOSTENT_NOT_POISONED(result); EXPECT_NOT_POISONED(err); } TEST(MemorySanitizer, gethostbyname_r) { char buf[2000]; struct hostent he; struct hostent *result; int err; int res = gethostbyname_r("localhost", &he, buf, sizeof(buf), &result, &err); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(result); ASSERT_NE((void *)NULL, result); EXPECT_HOSTENT_NOT_POISONED(result); EXPECT_NOT_POISONED(err); } TEST(MemorySanitizer, gethostbyname_r_bad_host_name) { char buf[2000]; struct hostent he; struct hostent *result; int err; int res = gethostbyname_r("bad-host-name", &he, buf, sizeof(buf), &result, &err); ASSERT_EQ((struct hostent *)0, result); EXPECT_NOT_POISONED(err); } TEST(MemorySanitizer, gethostbyname_r_erange) { char buf[5]; struct hostent he; struct hostent *result; int err; gethostbyname_r("localhost", &he, buf, sizeof(buf), &result, &err); ASSERT_EQ(ERANGE, errno); EXPECT_NOT_POISONED(err); } TEST(MemorySanitizer, gethostbyname2_r) { char buf[2000]; struct hostent he; struct hostent *result; int err; int res = gethostbyname2_r("localhost", AF_INET, &he, buf, sizeof(buf), &result, &err); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(result); ASSERT_NE((void *)NULL, result); EXPECT_HOSTENT_NOT_POISONED(result); EXPECT_NOT_POISONED(err); } TEST(MemorySanitizer, gethostbyaddr_r) { char buf[2000]; struct hostent he; struct hostent *result; int err; in_addr_t addr = inet_addr("127.0.0.1"); EXPECT_NOT_POISONED(addr); int res = gethostbyaddr_r(&addr, sizeof(addr), AF_INET, &he, buf, sizeof(buf), &result, &err); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(result); ASSERT_NE((void *)NULL, result); EXPECT_HOSTENT_NOT_POISONED(result); EXPECT_NOT_POISONED(err); } TEST(MemorySanitizer, getsockopt) { int sock = socket(AF_UNIX, SOCK_STREAM, 0); struct linger l[2]; socklen_t sz = sizeof(l[0]); int res = getsockopt(sock, SOL_SOCKET, SO_LINGER, &l[0], &sz); ASSERT_EQ(0, res); ASSERT_EQ(sizeof(l[0]), sz); EXPECT_NOT_POISONED(l[0]); EXPECT_POISONED(*(char *)(l + 1)); } TEST(MemorySanitizer, getcwd) { char path[PATH_MAX + 1]; char* res = getcwd(path, sizeof(path)); ASSERT_TRUE(res != NULL); EXPECT_NOT_POISONED(path[0]); } TEST(MemorySanitizer, getcwd_gnu) { char* res = getcwd(NULL, 0); ASSERT_TRUE(res != NULL); EXPECT_NOT_POISONED(res[0]); free(res); } // There's no get_current_dir_name() on FreeBSD. #if !defined(__FreeBSD__) TEST(MemorySanitizer, get_current_dir_name) { char* res = get_current_dir_name(); ASSERT_TRUE(res != NULL); EXPECT_NOT_POISONED(res[0]); free(res); } #endif TEST(MemorySanitizer, shmctl) { int id = shmget(IPC_PRIVATE, 4096, 0644 | IPC_CREAT); ASSERT_GT(id, -1); struct shmid_ds ds; int res = shmctl(id, IPC_STAT, &ds); ASSERT_GT(res, -1); EXPECT_NOT_POISONED(ds); // FreeBSD does not support shmctl(IPC_INFO) and shmctl(SHM_INFO). #if !defined(__FreeBSD__) struct shminfo si; res = shmctl(id, IPC_INFO, (struct shmid_ds *)&si); ASSERT_GT(res, -1); EXPECT_NOT_POISONED(si); struct shm_info s_i; res = shmctl(id, SHM_INFO, (struct shmid_ds *)&s_i); ASSERT_GT(res, -1); EXPECT_NOT_POISONED(s_i); #endif res = shmctl(id, IPC_RMID, 0); ASSERT_GT(res, -1); } TEST(MemorySanitizer, shmat) { const int kShmSize = 4096; void *mapping_start = mmap(NULL, kShmSize + SHMLBA, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); ASSERT_NE(MAP_FAILED, mapping_start); void *p = (void *)(((unsigned long)mapping_start + SHMLBA - 1) / SHMLBA * SHMLBA); // p is now SHMLBA-aligned; ((char *)p)[10] = *GetPoisoned(); ((char *)p)[kShmSize - 1] = *GetPoisoned(); int res = munmap(mapping_start, kShmSize + SHMLBA); ASSERT_EQ(0, res); int id = shmget(IPC_PRIVATE, kShmSize, 0644 | IPC_CREAT); ASSERT_GT(id, -1); void *q = shmat(id, p, 0); ASSERT_EQ(p, q); EXPECT_NOT_POISONED(((char *)q)[0]); EXPECT_NOT_POISONED(((char *)q)[10]); EXPECT_NOT_POISONED(((char *)q)[kShmSize - 1]); res = shmdt(q); ASSERT_EQ(0, res); res = shmctl(id, IPC_RMID, 0); ASSERT_GT(res, -1); } // There's no random_r() on FreeBSD. #if !defined(__FreeBSD__) TEST(MemorySanitizer, random_r) { int32_t x; char z[64]; memset(z, 0, sizeof(z)); struct random_data buf; memset(&buf, 0, sizeof(buf)); int res = initstate_r(0, z, sizeof(z), &buf); ASSERT_EQ(0, res); res = random_r(&buf, &x); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(x); } #endif TEST(MemorySanitizer, confstr) { char buf[3]; size_t res = confstr(_CS_PATH, buf, sizeof(buf)); ASSERT_GT(res, sizeof(buf)); EXPECT_NOT_POISONED(buf[0]); EXPECT_NOT_POISONED(buf[sizeof(buf) - 1]); char buf2[1000]; res = confstr(_CS_PATH, buf2, sizeof(buf2)); ASSERT_LT(res, sizeof(buf2)); EXPECT_NOT_POISONED(buf2[0]); EXPECT_NOT_POISONED(buf2[res - 1]); EXPECT_POISONED(buf2[res]); ASSERT_EQ(res, strlen(buf2) + 1); } TEST(MemorySanitizer, opendir) { DIR *dir = opendir("."); closedir(dir); char name[10] = "."; __msan_poison(name, sizeof(name)); EXPECT_UMR(dir = opendir(name)); closedir(dir); } TEST(MemorySanitizer, readdir) { DIR *dir = opendir("."); struct dirent *d = readdir(dir); ASSERT_TRUE(d != NULL); EXPECT_NOT_POISONED(d->d_name[0]); closedir(dir); } TEST(MemorySanitizer, readdir_r) { DIR *dir = opendir("."); struct dirent d; struct dirent *pd; int res = readdir_r(dir, &d, &pd); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(pd); EXPECT_NOT_POISONED(d.d_name[0]); closedir(dir); } TEST(MemorySanitizer, realpath) { const char* relpath = "."; char path[PATH_MAX + 1]; char* res = realpath(relpath, path); ASSERT_TRUE(res != NULL); EXPECT_NOT_POISONED(path[0]); } TEST(MemorySanitizer, realpath_null) { const char* relpath = "."; char* res = realpath(relpath, NULL); printf("%d, %s\n", errno, strerror(errno)); ASSERT_TRUE(res != NULL); EXPECT_NOT_POISONED(res[0]); free(res); } // There's no canonicalize_file_name() on FreeBSD. #if !defined(__FreeBSD__) TEST(MemorySanitizer, canonicalize_file_name) { const char* relpath = "."; char* res = canonicalize_file_name(relpath); ASSERT_TRUE(res != NULL); EXPECT_NOT_POISONED(res[0]); free(res); } #endif extern char **environ; TEST(MemorySanitizer, setenv) { setenv("AAA", "BBB", 1); for (char **envp = environ; *envp; ++envp) { EXPECT_NOT_POISONED(*envp); EXPECT_NOT_POISONED(*envp[0]); } } TEST(MemorySanitizer, putenv) { char s[] = "AAA=BBB"; putenv(s); for (char **envp = environ; *envp; ++envp) { EXPECT_NOT_POISONED(*envp); EXPECT_NOT_POISONED(*envp[0]); } } TEST(MemorySanitizer, memcpy) { char* x = new char[2]; char* y = new char[2]; x[0] = 1; x[1] = *GetPoisoned(); memcpy(y, x, 2); EXPECT_NOT_POISONED(y[0]); EXPECT_POISONED(y[1]); } void TestUnalignedMemcpy(unsigned left, unsigned right, bool src_is_aligned, bool src_is_poisoned, bool dst_is_poisoned) { fprintf(stderr, "%s(%d, %d, %d, %d, %d)\n", __func__, left, right, src_is_aligned, src_is_poisoned, dst_is_poisoned); const unsigned sz = 20; U4 dst_origin, src_origin; char *dst = (char *)malloc(sz); if (dst_is_poisoned) dst_origin = __msan_get_origin(dst); else memset(dst, 0, sz); char *src = (char *)malloc(sz); if (src_is_poisoned) src_origin = __msan_get_origin(src); else memset(src, 0, sz); memcpy(dst + left, src_is_aligned ? src + left : src, sz - left - right); for (unsigned i = 0; i < (left & (~3U)); ++i) if (dst_is_poisoned) EXPECT_POISONED_O(dst[i], dst_origin); else EXPECT_NOT_POISONED(dst[i]); for (unsigned i = 0; i < (right & (~3U)); ++i) if (dst_is_poisoned) EXPECT_POISONED_O(dst[sz - i - 1], dst_origin); else EXPECT_NOT_POISONED(dst[sz - i - 1]); for (unsigned i = left; i < sz - right; ++i) if (src_is_poisoned) EXPECT_POISONED_O(dst[i], src_origin); else EXPECT_NOT_POISONED(dst[i]); free(dst); free(src); } TEST(MemorySanitizer, memcpy_unaligned) { for (int i = 0; i < 10; ++i) for (int j = 0; j < 10; ++j) for (int aligned = 0; aligned < 2; ++aligned) for (int srcp = 0; srcp < 2; ++srcp) for (int dstp = 0; dstp < 2; ++dstp) TestUnalignedMemcpy(i, j, aligned, srcp, dstp); } TEST(MemorySanitizer, memmove) { char* x = new char[2]; char* y = new char[2]; x[0] = 1; x[1] = *GetPoisoned(); memmove(y, x, 2); EXPECT_NOT_POISONED(y[0]); EXPECT_POISONED(y[1]); } TEST(MemorySanitizer, memccpy_nomatch) { char* x = new char[5]; char* y = new char[5]; strcpy(x, "abc"); memccpy(y, x, 'd', 4); EXPECT_NOT_POISONED(y[0]); EXPECT_NOT_POISONED(y[1]); EXPECT_NOT_POISONED(y[2]); EXPECT_NOT_POISONED(y[3]); EXPECT_POISONED(y[4]); delete[] x; delete[] y; } TEST(MemorySanitizer, memccpy_match) { char* x = new char[5]; char* y = new char[5]; strcpy(x, "abc"); memccpy(y, x, 'b', 4); EXPECT_NOT_POISONED(y[0]); EXPECT_NOT_POISONED(y[1]); EXPECT_POISONED(y[2]); EXPECT_POISONED(y[3]); EXPECT_POISONED(y[4]); delete[] x; delete[] y; } TEST(MemorySanitizer, memccpy_nomatch_positive) { char* x = new char[5]; char* y = new char[5]; strcpy(x, "abc"); EXPECT_UMR(memccpy(y, x, 'd', 5)); delete[] x; delete[] y; } TEST(MemorySanitizer, memccpy_match_positive) { char* x = new char[5]; char* y = new char[5]; x[0] = 'a'; x[2] = 'b'; EXPECT_UMR(memccpy(y, x, 'b', 5)); delete[] x; delete[] y; } TEST(MemorySanitizer, bcopy) { char* x = new char[2]; char* y = new char[2]; x[0] = 1; x[1] = *GetPoisoned(); bcopy(x, y, 2); EXPECT_NOT_POISONED(y[0]); EXPECT_POISONED(y[1]); } TEST(MemorySanitizer, strdup) { char buf[4] = "abc"; __msan_poison(buf + 2, sizeof(*buf)); char *x = strdup(buf); EXPECT_NOT_POISONED(x[0]); EXPECT_NOT_POISONED(x[1]); EXPECT_POISONED(x[2]); EXPECT_NOT_POISONED(x[3]); free(x); } TEST(MemorySanitizer, strndup) { char buf[4] = "abc"; __msan_poison(buf + 2, sizeof(*buf)); char *x; EXPECT_UMR(x = strndup(buf, 3)); EXPECT_NOT_POISONED(x[0]); EXPECT_NOT_POISONED(x[1]); EXPECT_POISONED(x[2]); EXPECT_NOT_POISONED(x[3]); free(x); // Check handling of non 0 terminated strings. buf[3] = 'z'; __msan_poison(buf + 3, sizeof(*buf)); EXPECT_UMR(x = strndup(buf + 3, 1)); EXPECT_POISONED(x[0]); EXPECT_NOT_POISONED(x[1]); free(x); } TEST(MemorySanitizer, strndup_short) { char buf[4] = "abc"; __msan_poison(buf + 1, sizeof(*buf)); __msan_poison(buf + 2, sizeof(*buf)); char *x; EXPECT_UMR(x = strndup(buf, 2)); EXPECT_NOT_POISONED(x[0]); EXPECT_POISONED(x[1]); EXPECT_NOT_POISONED(x[2]); free(x); } template void TestOverlapMemmove() { T *x = new T[size]; ASSERT_GE(size, 3); x[2] = 0; memmove(x, x + 1, (size - 1) * sizeof(T)); EXPECT_NOT_POISONED(x[1]); EXPECT_POISONED(x[0]); EXPECT_POISONED(x[2]); delete [] x; } TEST(MemorySanitizer, overlap_memmove) { TestOverlapMemmove(); TestOverlapMemmove(); TestOverlapMemmove(); TestOverlapMemmove(); } TEST(MemorySanitizer, strcpy) { // NOLINT char* x = new char[3]; char* y = new char[3]; x[0] = 'a'; x[1] = *GetPoisoned(1, 1); x[2] = 0; strcpy(y, x); // NOLINT EXPECT_NOT_POISONED(y[0]); EXPECT_POISONED(y[1]); EXPECT_NOT_POISONED(y[2]); } TEST(MemorySanitizer, strncpy) { // NOLINT char* x = new char[3]; char* y = new char[5]; x[0] = 'a'; x[1] = *GetPoisoned(1, 1); x[2] = '\0'; strncpy(y, x, 4); // NOLINT EXPECT_NOT_POISONED(y[0]); EXPECT_POISONED(y[1]); EXPECT_NOT_POISONED(y[2]); EXPECT_NOT_POISONED(y[3]); EXPECT_POISONED(y[4]); } TEST(MemorySanitizer, stpcpy) { // NOLINT char* x = new char[3]; char* y = new char[3]; x[0] = 'a'; x[1] = *GetPoisoned(1, 1); x[2] = 0; char *res = stpcpy(y, x); // NOLINT ASSERT_EQ(res, y + 2); EXPECT_NOT_POISONED(y[0]); EXPECT_POISONED(y[1]); EXPECT_NOT_POISONED(y[2]); } TEST(MemorySanitizer, strcat) { // NOLINT char a[10]; char b[] = "def"; strcpy(a, "abc"); __msan_poison(b + 1, 1); strcat(a, b); EXPECT_NOT_POISONED(a[3]); EXPECT_POISONED(a[4]); EXPECT_NOT_POISONED(a[5]); EXPECT_NOT_POISONED(a[6]); EXPECT_POISONED(a[7]); } TEST(MemorySanitizer, strncat) { // NOLINT char a[10]; char b[] = "def"; strcpy(a, "abc"); __msan_poison(b + 1, 1); strncat(a, b, 5); EXPECT_NOT_POISONED(a[3]); EXPECT_POISONED(a[4]); EXPECT_NOT_POISONED(a[5]); EXPECT_NOT_POISONED(a[6]); EXPECT_POISONED(a[7]); } TEST(MemorySanitizer, strncat_overflow) { // NOLINT char a[10]; char b[] = "def"; strcpy(a, "abc"); __msan_poison(b + 1, 1); strncat(a, b, 2); EXPECT_NOT_POISONED(a[3]); EXPECT_POISONED(a[4]); EXPECT_NOT_POISONED(a[5]); EXPECT_POISONED(a[6]); EXPECT_POISONED(a[7]); } +TEST(MemorySanitizer, wcscat) { + wchar_t a[10]; + wchar_t b[] = L"def"; + wcscpy(a, L"abc"); + + wcscat(a, b); + EXPECT_EQ(6U, wcslen(a)); + EXPECT_POISONED(a[7]); + + a[3] = 0; + __msan_poison(b + 1, sizeof(wchar_t)); + EXPECT_UMR(wcscat(a, b)); + + __msan_unpoison(b + 1, sizeof(wchar_t)); + __msan_poison(a + 2, sizeof(wchar_t)); + EXPECT_UMR(wcscat(a, b)); +} + +TEST(MemorySanitizer, wcsncat) { + wchar_t a[10]; + wchar_t b[] = L"def"; + wcscpy(a, L"abc"); + + wcsncat(a, b, 5); + EXPECT_EQ(6U, wcslen(a)); + EXPECT_POISONED(a[7]); + + a[3] = 0; + __msan_poison(a + 4, sizeof(wchar_t) * 6); + wcsncat(a, b, 2); + EXPECT_EQ(5U, wcslen(a)); + EXPECT_POISONED(a[6]); + + a[3] = 0; + __msan_poison(b + 1, sizeof(wchar_t)); + EXPECT_UMR(wcsncat(a, b, 2)); + + __msan_unpoison(b + 1, sizeof(wchar_t)); + __msan_poison(a + 2, sizeof(wchar_t)); + EXPECT_UMR(wcsncat(a, b, 2)); +} + #define TEST_STRTO_INT(func_name, char_type, str_prefix) \ TEST(MemorySanitizer, func_name) { \ char_type *e; \ EXPECT_EQ(1U, func_name(str_prefix##"1", &e, 10)); \ EXPECT_NOT_POISONED((S8)e); \ } #define TEST_STRTO_FLOAT(func_name, char_type, str_prefix) \ TEST(MemorySanitizer, func_name) { \ char_type *e; \ EXPECT_NE(0, func_name(str_prefix##"1.5", &e)); \ EXPECT_NOT_POISONED((S8)e); \ } #define TEST_STRTO_FLOAT_LOC(func_name, char_type, str_prefix) \ TEST(MemorySanitizer, func_name) { \ locale_t loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0); \ char_type *e; \ EXPECT_NE(0, func_name(str_prefix##"1.5", &e, loc)); \ EXPECT_NOT_POISONED((S8)e); \ freelocale(loc); \ } #define TEST_STRTO_INT_LOC(func_name, char_type, str_prefix) \ TEST(MemorySanitizer, func_name) { \ locale_t loc = newlocale(LC_NUMERIC_MASK, "C", (locale_t)0); \ char_type *e; \ ASSERT_EQ(1U, func_name(str_prefix##"1", &e, 10, loc)); \ EXPECT_NOT_POISONED((S8)e); \ freelocale(loc); \ } TEST_STRTO_INT(strtol, char, ) TEST_STRTO_INT(strtoll, char, ) TEST_STRTO_INT(strtoul, char, ) TEST_STRTO_INT(strtoull, char, ) TEST_STRTO_FLOAT(strtof, char, ) TEST_STRTO_FLOAT(strtod, char, ) TEST_STRTO_FLOAT(strtold, char, ) TEST_STRTO_FLOAT_LOC(strtof_l, char, ) TEST_STRTO_FLOAT_LOC(strtod_l, char, ) TEST_STRTO_FLOAT_LOC(strtold_l, char, ) TEST_STRTO_INT_LOC(strtol_l, char, ) TEST_STRTO_INT_LOC(strtoll_l, char, ) TEST_STRTO_INT_LOC(strtoul_l, char, ) TEST_STRTO_INT_LOC(strtoull_l, char, ) TEST_STRTO_INT(wcstol, wchar_t, L) TEST_STRTO_INT(wcstoll, wchar_t, L) TEST_STRTO_INT(wcstoul, wchar_t, L) TEST_STRTO_INT(wcstoull, wchar_t, L) TEST_STRTO_FLOAT(wcstof, wchar_t, L) TEST_STRTO_FLOAT(wcstod, wchar_t, L) TEST_STRTO_FLOAT(wcstold, wchar_t, L) TEST_STRTO_FLOAT_LOC(wcstof_l, wchar_t, L) TEST_STRTO_FLOAT_LOC(wcstod_l, wchar_t, L) TEST_STRTO_FLOAT_LOC(wcstold_l, wchar_t, L) TEST_STRTO_INT_LOC(wcstol_l, wchar_t, L) TEST_STRTO_INT_LOC(wcstoll_l, wchar_t, L) TEST_STRTO_INT_LOC(wcstoul_l, wchar_t, L) TEST_STRTO_INT_LOC(wcstoull_l, wchar_t, L) TEST(MemorySanitizer, strtoimax) { char *e; ASSERT_EQ(1, strtoimax("1", &e, 10)); EXPECT_NOT_POISONED((S8) e); } TEST(MemorySanitizer, strtoumax) { char *e; ASSERT_EQ(1U, strtoumax("1", &e, 10)); EXPECT_NOT_POISONED((S8) e); } #ifdef __GLIBC__ extern "C" float __strtof_l(const char *nptr, char **endptr, locale_t loc); TEST_STRTO_FLOAT_LOC(__strtof_l, char, ) extern "C" double __strtod_l(const char *nptr, char **endptr, locale_t loc); TEST_STRTO_FLOAT_LOC(__strtod_l, char, ) extern "C" long double __strtold_l(const char *nptr, char **endptr, locale_t loc); TEST_STRTO_FLOAT_LOC(__strtold_l, char, ) extern "C" float __wcstof_l(const wchar_t *nptr, wchar_t **endptr, locale_t loc); TEST_STRTO_FLOAT_LOC(__wcstof_l, wchar_t, L) extern "C" double __wcstod_l(const wchar_t *nptr, wchar_t **endptr, locale_t loc); TEST_STRTO_FLOAT_LOC(__wcstod_l, wchar_t, L) extern "C" long double __wcstold_l(const wchar_t *nptr, wchar_t **endptr, locale_t loc); TEST_STRTO_FLOAT_LOC(__wcstold_l, wchar_t, L) #endif // __GLIBC__ TEST(MemorySanitizer, modf) { double x, y; x = modf(2.1, &y); EXPECT_NOT_POISONED(y); } TEST(MemorySanitizer, modff) { float x, y; x = modff(2.1, &y); EXPECT_NOT_POISONED(y); } TEST(MemorySanitizer, modfl) { long double x, y; x = modfl(2.1, &y); EXPECT_NOT_POISONED(y); } // There's no sincos() on FreeBSD. #if !defined(__FreeBSD__) TEST(MemorySanitizer, sincos) { double s, c; sincos(0.2, &s, &c); EXPECT_NOT_POISONED(s); EXPECT_NOT_POISONED(c); } #endif // There's no sincosf() on FreeBSD. #if !defined(__FreeBSD__) TEST(MemorySanitizer, sincosf) { float s, c; sincosf(0.2, &s, &c); EXPECT_NOT_POISONED(s); EXPECT_NOT_POISONED(c); } #endif // There's no sincosl() on FreeBSD. #if !defined(__FreeBSD__) TEST(MemorySanitizer, sincosl) { long double s, c; sincosl(0.2, &s, &c); EXPECT_NOT_POISONED(s); EXPECT_NOT_POISONED(c); } #endif TEST(MemorySanitizer, remquo) { int quo; double res = remquo(29.0, 3.0, &quo); ASSERT_NE(0.0, res); EXPECT_NOT_POISONED(quo); } TEST(MemorySanitizer, remquof) { int quo; float res = remquof(29.0, 3.0, &quo); ASSERT_NE(0.0, res); EXPECT_NOT_POISONED(quo); } TEST(MemorySanitizer, remquol) { int quo; long double res = remquof(29.0, 3.0, &quo); ASSERT_NE(0.0, res); EXPECT_NOT_POISONED(quo); } TEST(MemorySanitizer, lgamma) { double res = lgamma(1.1); ASSERT_NE(0.0, res); EXPECT_NOT_POISONED(signgam); } TEST(MemorySanitizer, lgammaf) { float res = lgammaf(1.1); ASSERT_NE(0.0, res); EXPECT_NOT_POISONED(signgam); } TEST(MemorySanitizer, lgammal) { long double res = lgammal(1.1); ASSERT_NE(0.0, res); EXPECT_NOT_POISONED(signgam); } TEST(MemorySanitizer, lgamma_r) { int sgn; double res = lgamma_r(1.1, &sgn); ASSERT_NE(0.0, res); EXPECT_NOT_POISONED(sgn); } TEST(MemorySanitizer, lgammaf_r) { int sgn; float res = lgammaf_r(1.1, &sgn); ASSERT_NE(0.0, res); EXPECT_NOT_POISONED(sgn); } // There's no lgammal_r() on FreeBSD. #if !defined(__FreeBSD__) TEST(MemorySanitizer, lgammal_r) { int sgn; long double res = lgammal_r(1.1, &sgn); ASSERT_NE(0.0, res); EXPECT_NOT_POISONED(sgn); } #endif // There's no drand48_r() on FreeBSD. #if !defined(__FreeBSD__) TEST(MemorySanitizer, drand48_r) { struct drand48_data buf; srand48_r(0, &buf); double d; drand48_r(&buf, &d); EXPECT_NOT_POISONED(d); } #endif // There's no lrand48_r() on FreeBSD. #if !defined(__FreeBSD__) TEST(MemorySanitizer, lrand48_r) { struct drand48_data buf; srand48_r(0, &buf); long d; lrand48_r(&buf, &d); EXPECT_NOT_POISONED(d); } #endif TEST(MemorySanitizer, sprintf) { // NOLINT char buff[10]; break_optimization(buff); EXPECT_POISONED(buff[0]); int res = sprintf(buff, "%d", 1234567); // NOLINT ASSERT_EQ(res, 7); ASSERT_EQ(buff[0], '1'); ASSERT_EQ(buff[1], '2'); ASSERT_EQ(buff[2], '3'); ASSERT_EQ(buff[6], '7'); ASSERT_EQ(buff[7], 0); EXPECT_POISONED(buff[8]); } TEST(MemorySanitizer, snprintf) { char buff[10]; break_optimization(buff); EXPECT_POISONED(buff[0]); int res = snprintf(buff, sizeof(buff), "%d", 1234567); ASSERT_EQ(res, 7); ASSERT_EQ(buff[0], '1'); ASSERT_EQ(buff[1], '2'); ASSERT_EQ(buff[2], '3'); ASSERT_EQ(buff[6], '7'); ASSERT_EQ(buff[7], 0); EXPECT_POISONED(buff[8]); } TEST(MemorySanitizer, swprintf) { wchar_t buff[10]; ASSERT_EQ(4U, sizeof(wchar_t)); break_optimization(buff); EXPECT_POISONED(buff[0]); int res = swprintf(buff, 9, L"%d", 1234567); ASSERT_EQ(res, 7); ASSERT_EQ(buff[0], '1'); ASSERT_EQ(buff[1], '2'); ASSERT_EQ(buff[2], '3'); ASSERT_EQ(buff[6], '7'); ASSERT_EQ(buff[7], L'\0'); EXPECT_POISONED(buff[8]); } TEST(MemorySanitizer, asprintf) { // NOLINT char *pbuf; EXPECT_POISONED(pbuf); int res = asprintf(&pbuf, "%d", 1234567); // NOLINT ASSERT_EQ(res, 7); EXPECT_NOT_POISONED(pbuf); ASSERT_EQ(pbuf[0], '1'); ASSERT_EQ(pbuf[1], '2'); ASSERT_EQ(pbuf[2], '3'); ASSERT_EQ(pbuf[6], '7'); ASSERT_EQ(pbuf[7], 0); free(pbuf); } TEST(MemorySanitizer, mbstowcs) { const char *x = "abc"; wchar_t buff[10]; int res = mbstowcs(buff, x, 2); EXPECT_EQ(2, res); EXPECT_EQ(L'a', buff[0]); EXPECT_EQ(L'b', buff[1]); EXPECT_POISONED(buff[2]); res = mbstowcs(buff, x, 10); EXPECT_EQ(3, res); EXPECT_NOT_POISONED(buff[3]); } TEST(MemorySanitizer, wcstombs) { const wchar_t *x = L"abc"; char buff[10]; int res = wcstombs(buff, x, 4); EXPECT_EQ(res, 3); EXPECT_EQ(buff[0], 'a'); EXPECT_EQ(buff[1], 'b'); EXPECT_EQ(buff[2], 'c'); } TEST(MemorySanitizer, wcsrtombs) { const wchar_t *x = L"abc"; const wchar_t *p = x; char buff[10]; mbstate_t mbs; memset(&mbs, 0, sizeof(mbs)); int res = wcsrtombs(buff, &p, 4, &mbs); EXPECT_EQ(res, 3); EXPECT_EQ(buff[0], 'a'); EXPECT_EQ(buff[1], 'b'); EXPECT_EQ(buff[2], 'c'); EXPECT_EQ(buff[3], '\0'); EXPECT_POISONED(buff[4]); } TEST(MemorySanitizer, wcsnrtombs) { const wchar_t *x = L"abc"; const wchar_t *p = x; char buff[10]; mbstate_t mbs; memset(&mbs, 0, sizeof(mbs)); int res = wcsnrtombs(buff, &p, 2, 4, &mbs); EXPECT_EQ(res, 2); EXPECT_EQ(buff[0], 'a'); EXPECT_EQ(buff[1], 'b'); EXPECT_POISONED(buff[2]); } TEST(MemorySanitizer, wcrtomb) { wchar_t x = L'a'; char buff[10]; mbstate_t mbs; memset(&mbs, 0, sizeof(mbs)); size_t res = wcrtomb(buff, x, &mbs); EXPECT_EQ(res, (size_t)1); EXPECT_EQ(buff[0], 'a'); } TEST(MemorySanitizer, wmemset) { wchar_t x[25]; break_optimization(x); EXPECT_POISONED(x[0]); wmemset(x, L'A', 10); EXPECT_EQ(x[0], L'A'); EXPECT_EQ(x[9], L'A'); EXPECT_POISONED(x[10]); } TEST(MemorySanitizer, mbtowc) { const char *x = "abc"; wchar_t wx; int res = mbtowc(&wx, x, 3); EXPECT_GT(res, 0); EXPECT_NOT_POISONED(wx); } TEST(MemorySanitizer, mbrtowc) { const char *x = "abc"; wchar_t wx; mbstate_t mbs; memset(&mbs, 0, sizeof(mbs)); int res = mbrtowc(&wx, x, 3, &mbs); EXPECT_GT(res, 0); EXPECT_NOT_POISONED(wx); } TEST(MemorySanitizer, wcsftime) { wchar_t x[100]; time_t t = time(NULL); struct tm tms; struct tm *tmres = localtime_r(&t, &tms); ASSERT_NE((void *)0, tmres); size_t res = wcsftime(x, sizeof(x) / sizeof(x[0]), L"%Y-%m-%d", tmres); EXPECT_GT(res, 0UL); EXPECT_EQ(res, wcslen(x)); } TEST(MemorySanitizer, gettimeofday) { struct timeval tv; struct timezone tz; break_optimization(&tv); break_optimization(&tz); ASSERT_EQ(16U, sizeof(tv)); ASSERT_EQ(8U, sizeof(tz)); EXPECT_POISONED(tv.tv_sec); EXPECT_POISONED(tv.tv_usec); EXPECT_POISONED(tz.tz_minuteswest); EXPECT_POISONED(tz.tz_dsttime); ASSERT_EQ(0, gettimeofday(&tv, &tz)); EXPECT_NOT_POISONED(tv.tv_sec); EXPECT_NOT_POISONED(tv.tv_usec); EXPECT_NOT_POISONED(tz.tz_minuteswest); EXPECT_NOT_POISONED(tz.tz_dsttime); } TEST(MemorySanitizer, clock_gettime) { struct timespec tp; EXPECT_POISONED(tp.tv_sec); EXPECT_POISONED(tp.tv_nsec); ASSERT_EQ(0, clock_gettime(CLOCK_REALTIME, &tp)); EXPECT_NOT_POISONED(tp.tv_sec); EXPECT_NOT_POISONED(tp.tv_nsec); } TEST(MemorySanitizer, clock_getres) { struct timespec tp; EXPECT_POISONED(tp.tv_sec); EXPECT_POISONED(tp.tv_nsec); ASSERT_EQ(0, clock_getres(CLOCK_REALTIME, 0)); EXPECT_POISONED(tp.tv_sec); EXPECT_POISONED(tp.tv_nsec); ASSERT_EQ(0, clock_getres(CLOCK_REALTIME, &tp)); EXPECT_NOT_POISONED(tp.tv_sec); EXPECT_NOT_POISONED(tp.tv_nsec); } TEST(MemorySanitizer, getitimer) { struct itimerval it1, it2; int res; EXPECT_POISONED(it1.it_interval.tv_sec); EXPECT_POISONED(it1.it_interval.tv_usec); EXPECT_POISONED(it1.it_value.tv_sec); EXPECT_POISONED(it1.it_value.tv_usec); res = getitimer(ITIMER_VIRTUAL, &it1); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(it1.it_interval.tv_sec); EXPECT_NOT_POISONED(it1.it_interval.tv_usec); EXPECT_NOT_POISONED(it1.it_value.tv_sec); EXPECT_NOT_POISONED(it1.it_value.tv_usec); it1.it_interval.tv_sec = it1.it_value.tv_sec = 10000; it1.it_interval.tv_usec = it1.it_value.tv_usec = 0; res = setitimer(ITIMER_VIRTUAL, &it1, &it2); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(it2.it_interval.tv_sec); EXPECT_NOT_POISONED(it2.it_interval.tv_usec); EXPECT_NOT_POISONED(it2.it_value.tv_sec); EXPECT_NOT_POISONED(it2.it_value.tv_usec); // Check that old_value can be 0, and disable the timer. memset(&it1, 0, sizeof(it1)); res = setitimer(ITIMER_VIRTUAL, &it1, 0); ASSERT_EQ(0, res); } TEST(MemorySanitizer, setitimer_null) { setitimer(ITIMER_VIRTUAL, 0, 0); // Not testing the return value, since it the behaviour seems to differ // between libc implementations and POSIX. // Should never crash, though. } TEST(MemorySanitizer, time) { time_t t; EXPECT_POISONED(t); time_t t2 = time(&t); ASSERT_NE(t2, (time_t)-1); EXPECT_NOT_POISONED(t); } TEST(MemorySanitizer, strptime) { struct tm time; char *p = strptime("11/1/2013-05:39", "%m/%d/%Y-%H:%M", &time); ASSERT_TRUE(p != NULL); EXPECT_NOT_POISONED(time.tm_sec); EXPECT_NOT_POISONED(time.tm_hour); EXPECT_NOT_POISONED(time.tm_year); } TEST(MemorySanitizer, localtime) { time_t t = 123; struct tm *time = localtime(&t); ASSERT_TRUE(time != NULL); EXPECT_NOT_POISONED(time->tm_sec); EXPECT_NOT_POISONED(time->tm_hour); EXPECT_NOT_POISONED(time->tm_year); EXPECT_NOT_POISONED(time->tm_isdst); EXPECT_NE(0U, strlen(time->tm_zone)); } TEST(MemorySanitizer, localtime_r) { time_t t = 123; struct tm time; struct tm *res = localtime_r(&t, &time); ASSERT_TRUE(res != NULL); EXPECT_NOT_POISONED(time.tm_sec); EXPECT_NOT_POISONED(time.tm_hour); EXPECT_NOT_POISONED(time.tm_year); EXPECT_NOT_POISONED(time.tm_isdst); EXPECT_NE(0U, strlen(time.tm_zone)); } #if !defined(__FreeBSD__) /* Creates a temporary file with contents similar to /etc/fstab to be used with getmntent{_r}. */ class TempFstabFile { public: TempFstabFile() : fd (-1) { } ~TempFstabFile() { if (fd >= 0) close (fd); } bool Create(void) { snprintf(tmpfile, sizeof(tmpfile), "/tmp/msan.getmntent.tmp.XXXXXX"); fd = mkstemp(tmpfile); if (fd == -1) return false; const char entry[] = "/dev/root / ext4 errors=remount-ro 0 1"; size_t entrylen = sizeof(entry); size_t bytesWritten = write(fd, entry, entrylen); if (entrylen != bytesWritten) return false; return true; } const char* FileName(void) { return tmpfile; } private: char tmpfile[128]; int fd; }; #endif // There's no getmntent() on FreeBSD. #if !defined(__FreeBSD__) TEST(MemorySanitizer, getmntent) { TempFstabFile fstabtmp; ASSERT_TRUE(fstabtmp.Create()); FILE *fp = setmntent(fstabtmp.FileName(), "r"); struct mntent *mnt = getmntent(fp); ASSERT_TRUE(mnt != NULL); ASSERT_NE(0U, strlen(mnt->mnt_fsname)); ASSERT_NE(0U, strlen(mnt->mnt_dir)); ASSERT_NE(0U, strlen(mnt->mnt_type)); ASSERT_NE(0U, strlen(mnt->mnt_opts)); EXPECT_NOT_POISONED(mnt->mnt_freq); EXPECT_NOT_POISONED(mnt->mnt_passno); fclose(fp); } #endif // There's no getmntent_r() on FreeBSD. #if !defined(__FreeBSD__) TEST(MemorySanitizer, getmntent_r) { TempFstabFile fstabtmp; ASSERT_TRUE(fstabtmp.Create()); FILE *fp = setmntent(fstabtmp.FileName(), "r"); struct mntent mntbuf; char buf[1000]; struct mntent *mnt = getmntent_r(fp, &mntbuf, buf, sizeof(buf)); ASSERT_TRUE(mnt != NULL); ASSERT_NE(0U, strlen(mnt->mnt_fsname)); ASSERT_NE(0U, strlen(mnt->mnt_dir)); ASSERT_NE(0U, strlen(mnt->mnt_type)); ASSERT_NE(0U, strlen(mnt->mnt_opts)); EXPECT_NOT_POISONED(mnt->mnt_freq); EXPECT_NOT_POISONED(mnt->mnt_passno); fclose(fp); } #endif TEST(MemorySanitizer, ether) { const char *asc = "11:22:33:44:55:66"; struct ether_addr *paddr = ether_aton(asc); EXPECT_NOT_POISONED(*paddr); struct ether_addr addr; paddr = ether_aton_r(asc, &addr); ASSERT_EQ(paddr, &addr); EXPECT_NOT_POISONED(addr); char *s = ether_ntoa(&addr); ASSERT_NE(0U, strlen(s)); char buf[100]; s = ether_ntoa_r(&addr, buf); ASSERT_EQ(s, buf); ASSERT_NE(0U, strlen(buf)); } TEST(MemorySanitizer, mmap) { const int size = 4096; void *p1, *p2; p1 = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); __msan_poison(p1, size); munmap(p1, size); for (int i = 0; i < 1000; i++) { p2 = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0); if (p2 == p1) break; else munmap(p2, size); } if (p1 == p2) { EXPECT_NOT_POISONED(*(char*)p2); munmap(p2, size); } } // There's no fcvt() on FreeBSD. #if !defined(__FreeBSD__) // FIXME: enable and add ecvt. // FIXME: check why msandr does nt handle fcvt. TEST(MemorySanitizer, fcvt) { int a, b; break_optimization(&a); break_optimization(&b); EXPECT_POISONED(a); EXPECT_POISONED(b); char *str = fcvt(12345.6789, 10, &a, &b); EXPECT_NOT_POISONED(a); EXPECT_NOT_POISONED(b); ASSERT_NE(nullptr, str); EXPECT_NOT_POISONED(str[0]); ASSERT_NE(0U, strlen(str)); } #endif // There's no fcvt_long() on FreeBSD. #if !defined(__FreeBSD__) TEST(MemorySanitizer, fcvt_long) { int a, b; break_optimization(&a); break_optimization(&b); EXPECT_POISONED(a); EXPECT_POISONED(b); char *str = fcvt(111111112345.6789, 10, &a, &b); EXPECT_NOT_POISONED(a); EXPECT_NOT_POISONED(b); ASSERT_NE(nullptr, str); EXPECT_NOT_POISONED(str[0]); ASSERT_NE(0U, strlen(str)); } #endif TEST(MemorySanitizer, memchr) { char x[10]; break_optimization(x); EXPECT_POISONED(x[0]); x[2] = '2'; void *res; EXPECT_UMR(res = memchr(x, '2', 10)); EXPECT_NOT_POISONED(res); x[0] = '0'; x[1] = '1'; res = memchr(x, '2', 10); EXPECT_EQ(&x[2], res); EXPECT_UMR(res = memchr(x, '3', 10)); EXPECT_NOT_POISONED(res); } TEST(MemorySanitizer, memrchr) { char x[10]; break_optimization(x); EXPECT_POISONED(x[0]); x[9] = '9'; void *res; EXPECT_UMR(res = memrchr(x, '9', 10)); EXPECT_NOT_POISONED(res); x[0] = '0'; x[1] = '1'; res = memrchr(x, '0', 2); EXPECT_EQ(&x[0], res); EXPECT_UMR(res = memrchr(x, '7', 10)); EXPECT_NOT_POISONED(res); } TEST(MemorySanitizer, frexp) { int x; x = *GetPoisoned(); double r = frexp(1.1, &x); EXPECT_NOT_POISONED(r); EXPECT_NOT_POISONED(x); x = *GetPoisoned(); float rf = frexpf(1.1, &x); EXPECT_NOT_POISONED(rf); EXPECT_NOT_POISONED(x); x = *GetPoisoned(); double rl = frexpl(1.1, &x); EXPECT_NOT_POISONED(rl); EXPECT_NOT_POISONED(x); } namespace { static int cnt; void SigactionHandler(int signo, siginfo_t* si, void* uc) { ASSERT_EQ(signo, SIGPROF); ASSERT_TRUE(si != NULL); EXPECT_NOT_POISONED(si->si_errno); EXPECT_NOT_POISONED(si->si_pid); #if __linux__ # if defined(__x86_64__) EXPECT_NOT_POISONED(((ucontext_t*)uc)->uc_mcontext.gregs[REG_RIP]); # elif defined(__i386__) EXPECT_NOT_POISONED(((ucontext_t*)uc)->uc_mcontext.gregs[REG_EIP]); # endif #endif ++cnt; } TEST(MemorySanitizer, sigaction) { struct sigaction act = {}; struct sigaction oldact = {}; struct sigaction origact = {}; sigaction(SIGPROF, 0, &origact); act.sa_flags |= SA_SIGINFO; act.sa_sigaction = &SigactionHandler; sigaction(SIGPROF, &act, 0); kill(getpid(), SIGPROF); act.sa_flags &= ~SA_SIGINFO; act.sa_handler = SIG_DFL; sigaction(SIGPROF, &act, 0); act.sa_flags &= ~SA_SIGINFO; act.sa_handler = SIG_IGN; sigaction(SIGPROF, &act, &oldact); EXPECT_FALSE(oldact.sa_flags & SA_SIGINFO); EXPECT_EQ(SIG_DFL, oldact.sa_handler); kill(getpid(), SIGPROF); act.sa_flags |= SA_SIGINFO; act.sa_sigaction = &SigactionHandler; sigaction(SIGPROF, &act, &oldact); EXPECT_FALSE(oldact.sa_flags & SA_SIGINFO); EXPECT_EQ(SIG_IGN, oldact.sa_handler); kill(getpid(), SIGPROF); act.sa_flags &= ~SA_SIGINFO; act.sa_handler = SIG_DFL; sigaction(SIGPROF, &act, &oldact); EXPECT_TRUE(oldact.sa_flags & SA_SIGINFO); EXPECT_EQ(&SigactionHandler, oldact.sa_sigaction); EXPECT_EQ(2, cnt); sigaction(SIGPROF, &origact, 0); } } // namespace TEST(MemorySanitizer, sigemptyset) { sigset_t s; EXPECT_POISONED(s); int res = sigemptyset(&s); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(s); } TEST(MemorySanitizer, sigfillset) { sigset_t s; EXPECT_POISONED(s); int res = sigfillset(&s); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(s); } TEST(MemorySanitizer, sigpending) { sigset_t s; EXPECT_POISONED(s); int res = sigpending(&s); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(s); } TEST(MemorySanitizer, sigprocmask) { sigset_t s; EXPECT_POISONED(s); int res = sigprocmask(SIG_BLOCK, 0, &s); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(s); } struct StructWithDtor { ~StructWithDtor(); }; NOINLINE StructWithDtor::~StructWithDtor() { break_optimization(0); } TEST(MemorySanitizer, Invoke) { StructWithDtor s; // Will cause the calls to become invokes. EXPECT_NOT_POISONED(0); EXPECT_POISONED(*GetPoisoned()); EXPECT_NOT_POISONED(0); EXPECT_POISONED(*GetPoisoned()); EXPECT_POISONED(ReturnPoisoned()); } TEST(MemorySanitizer, ptrtoint) { // Test that shadow is propagated through pointer-to-integer conversion. unsigned char c = 0; __msan_poison(&c, 1); uintptr_t u = (uintptr_t)c << 8; EXPECT_NOT_POISONED(u & 0xFF00FF); EXPECT_POISONED(u & 0xFF00); break_optimization(&u); void* p = (void*)u; break_optimization(&p); EXPECT_POISONED(p); EXPECT_NOT_POISONED(((uintptr_t)p) & 0xFF00FF); EXPECT_POISONED(((uintptr_t)p) & 0xFF00); } static void vaargsfn2(int guard, ...) { va_list vl; va_start(vl, guard); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_POISONED(va_arg(vl, double)); va_end(vl); } static void vaargsfn(int guard, ...) { va_list vl; va_start(vl, guard); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_POISONED(va_arg(vl, int)); // The following call will overwrite __msan_param_tls. // Checks after it test that arg shadow was somehow saved across the call. vaargsfn2(1, 2, 3, 4, *GetPoisoned()); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_POISONED(va_arg(vl, int)); va_end(vl); } TEST(MemorySanitizer, VAArgTest) { int* x = GetPoisoned(); int* y = GetPoisoned(4); vaargsfn(1, 13, *x, 42, *y); } static void vaargsfn_many(int guard, ...) { va_list vl; va_start(vl, guard); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_POISONED(va_arg(vl, int)); va_end(vl); } TEST(MemorySanitizer, VAArgManyTest) { int* x = GetPoisoned(); int* y = GetPoisoned(4); vaargsfn_many(1, 2, *x, 3, 4, 5, 6, 7, 8, 9, *y); } static void vaargsfn_manyfix(int g1, int g2, int g3, int g4, int g5, int g6, int g7, int g8, int g9, ...) { va_list vl; va_start(vl, g9); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_POISONED(va_arg(vl, int)); va_end(vl); } TEST(MemorySanitizer, VAArgManyFixTest) { int* x = GetPoisoned(); int* y = GetPoisoned(); vaargsfn_manyfix(1, *x, 3, 4, 5, 6, 7, 8, 9, 10, *y); } static void vaargsfn_pass2(va_list vl) { EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_POISONED(va_arg(vl, int)); } static void vaargsfn_pass(int guard, ...) { va_list vl; va_start(vl, guard); EXPECT_POISONED(va_arg(vl, int)); vaargsfn_pass2(vl); va_end(vl); } TEST(MemorySanitizer, VAArgPass) { int* x = GetPoisoned(); int* y = GetPoisoned(4); vaargsfn_pass(1, *x, 2, 3, *y); } static void vaargsfn_copy2(va_list vl) { EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_POISONED(va_arg(vl, int)); } static void vaargsfn_copy(int guard, ...) { va_list vl; va_start(vl, guard); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_POISONED(va_arg(vl, int)); va_list vl2; va_copy(vl2, vl); vaargsfn_copy2(vl2); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_POISONED(va_arg(vl, int)); va_end(vl); } TEST(MemorySanitizer, VAArgCopy) { int* x = GetPoisoned(); int* y = GetPoisoned(4); vaargsfn_copy(1, 2, *x, 3, *y); } static void vaargsfn_ptr(int guard, ...) { va_list vl; va_start(vl, guard); EXPECT_NOT_POISONED(va_arg(vl, int*)); EXPECT_POISONED(va_arg(vl, int*)); EXPECT_NOT_POISONED(va_arg(vl, int*)); EXPECT_POISONED(va_arg(vl, double*)); va_end(vl); } TEST(MemorySanitizer, VAArgPtr) { int** x = GetPoisoned(); double** y = GetPoisoned(8); int z; vaargsfn_ptr(1, &z, *x, &z, *y); } static void vaargsfn_overflow(int guard, ...) { va_list vl; va_start(vl, guard); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, double)); EXPECT_NOT_POISONED(va_arg(vl, double)); EXPECT_NOT_POISONED(va_arg(vl, double)); EXPECT_POISONED(va_arg(vl, double)); EXPECT_NOT_POISONED(va_arg(vl, double)); EXPECT_POISONED(va_arg(vl, int*)); EXPECT_NOT_POISONED(va_arg(vl, double)); EXPECT_NOT_POISONED(va_arg(vl, double)); EXPECT_POISONED(va_arg(vl, int)); EXPECT_POISONED(va_arg(vl, double)); EXPECT_POISONED(va_arg(vl, int*)); EXPECT_NOT_POISONED(va_arg(vl, int)); EXPECT_NOT_POISONED(va_arg(vl, double)); EXPECT_NOT_POISONED(va_arg(vl, int*)); EXPECT_POISONED(va_arg(vl, int)); EXPECT_POISONED(va_arg(vl, double)); EXPECT_POISONED(va_arg(vl, int*)); va_end(vl); } TEST(MemorySanitizer, VAArgOverflow) { int* x = GetPoisoned(); double* y = GetPoisoned(8); int** p = GetPoisoned(16); int z; vaargsfn_overflow(1, 1, 2, *x, 4, 5, 6, 1.1, 2.2, 3.3, *y, 5.5, *p, 7.7, 8.8, // the following args will overflow for sure *x, *y, *p, 7, 9.9, &z, *x, *y, *p); } static void vaargsfn_tlsoverwrite2(int guard, ...) { va_list vl; va_start(vl, guard); for (int i = 0; i < 20; ++i) EXPECT_NOT_POISONED(va_arg(vl, int)); va_end(vl); } static void vaargsfn_tlsoverwrite(int guard, ...) { // This call will overwrite TLS contents unless it's backed up somewhere. vaargsfn_tlsoverwrite2(2, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42); // 20x va_list vl; va_start(vl, guard); for (int i = 0; i < 20; ++i) EXPECT_POISONED(va_arg(vl, int)); va_end(vl); } TEST(MemorySanitizer, VAArgTLSOverwrite) { int* x = GetPoisoned(); vaargsfn_tlsoverwrite(1, *x, *x, *x, *x, *x, *x, *x, *x, *x, *x, *x, *x, *x, *x, *x, *x, *x, *x, *x, *x); // 20x } struct StructByVal { int a, b, c, d, e, f; }; static void vaargsfn_structbyval(int guard, ...) { va_list vl; va_start(vl, guard); { StructByVal s = va_arg(vl, StructByVal); EXPECT_NOT_POISONED(s.a); EXPECT_POISONED(s.b); EXPECT_NOT_POISONED(s.c); EXPECT_POISONED(s.d); EXPECT_NOT_POISONED(s.e); EXPECT_POISONED(s.f); } { StructByVal s = va_arg(vl, StructByVal); EXPECT_NOT_POISONED(s.a); EXPECT_POISONED(s.b); EXPECT_NOT_POISONED(s.c); EXPECT_POISONED(s.d); EXPECT_NOT_POISONED(s.e); EXPECT_POISONED(s.f); } va_end(vl); } TEST(MemorySanitizer, VAArgStructByVal) { StructByVal s; s.a = 1; s.b = *GetPoisoned(); s.c = 2; s.d = *GetPoisoned(); s.e = 3; s.f = *GetPoisoned(); vaargsfn_structbyval(0, s, s); } NOINLINE void StructByValTestFunc(struct StructByVal s) { EXPECT_NOT_POISONED(s.a); EXPECT_POISONED(s.b); EXPECT_NOT_POISONED(s.c); EXPECT_POISONED(s.d); EXPECT_NOT_POISONED(s.e); EXPECT_POISONED(s.f); } NOINLINE void StructByValTestFunc1(struct StructByVal s) { StructByValTestFunc(s); } NOINLINE void StructByValTestFunc2(int z, struct StructByVal s) { StructByValTestFunc(s); } TEST(MemorySanitizer, StructByVal) { // Large aggregates are passed as "byval" pointer argument in LLVM. struct StructByVal s; s.a = 1; s.b = *GetPoisoned(); s.c = 2; s.d = *GetPoisoned(); s.e = 3; s.f = *GetPoisoned(); StructByValTestFunc(s); StructByValTestFunc1(s); StructByValTestFunc2(0, s); } #if MSAN_HAS_M128 NOINLINE __m128i m128Eq(__m128i *a, __m128i *b) { return _mm_cmpeq_epi16(*a, *b); } NOINLINE __m128i m128Lt(__m128i *a, __m128i *b) { return _mm_cmplt_epi16(*a, *b); } TEST(MemorySanitizer, m128) { __m128i a = _mm_set1_epi16(0x1234); __m128i b = _mm_set1_epi16(0x7890); EXPECT_NOT_POISONED(m128Eq(&a, &b)); EXPECT_NOT_POISONED(m128Lt(&a, &b)); } // FIXME: add more tests for __m128i. #endif // MSAN_HAS_M128 // We should not complain when copying this poisoned hole. struct StructWithHole { U4 a; // 4-byte hole. U8 b; }; NOINLINE StructWithHole ReturnStructWithHole() { StructWithHole res; __msan_poison(&res, sizeof(res)); res.a = 1; res.b = 2; return res; } TEST(MemorySanitizer, StructWithHole) { StructWithHole a = ReturnStructWithHole(); break_optimization(&a); } template NOINLINE T ReturnStruct() { T res; __msan_poison(&res, sizeof(res)); res.a = 1; return res; } template NOINLINE void TestReturnStruct() { T s1 = ReturnStruct(); EXPECT_NOT_POISONED(s1.a); EXPECT_POISONED(s1.b); } struct SSS1 { int a, b, c; }; struct SSS2 { int b, a, c; }; struct SSS3 { int b, c, a; }; struct SSS4 { int c, b, a; }; struct SSS5 { int a; float b; }; struct SSS6 { int a; double b; }; struct SSS7 { S8 b; int a; }; struct SSS8 { S2 b; S8 a; }; TEST(MemorySanitizer, IntStruct3) { TestReturnStruct(); TestReturnStruct(); TestReturnStruct(); TestReturnStruct(); TestReturnStruct(); TestReturnStruct(); TestReturnStruct(); TestReturnStruct(); } struct LongStruct { U1 a1, b1; U2 a2, b2; U4 a4, b4; U8 a8, b8; }; NOINLINE LongStruct ReturnLongStruct1() { LongStruct res; __msan_poison(&res, sizeof(res)); res.a1 = res.a2 = res.a4 = res.a8 = 111; // leaves b1, .., b8 poisoned. return res; } NOINLINE LongStruct ReturnLongStruct2() { LongStruct res; __msan_poison(&res, sizeof(res)); res.b1 = res.b2 = res.b4 = res.b8 = 111; // leaves a1, .., a8 poisoned. return res; } TEST(MemorySanitizer, LongStruct) { LongStruct s1 = ReturnLongStruct1(); __msan_print_shadow(&s1, sizeof(s1)); EXPECT_NOT_POISONED(s1.a1); EXPECT_NOT_POISONED(s1.a2); EXPECT_NOT_POISONED(s1.a4); EXPECT_NOT_POISONED(s1.a8); EXPECT_POISONED(s1.b1); EXPECT_POISONED(s1.b2); EXPECT_POISONED(s1.b4); EXPECT_POISONED(s1.b8); LongStruct s2 = ReturnLongStruct2(); __msan_print_shadow(&s2, sizeof(s2)); EXPECT_NOT_POISONED(s2.b1); EXPECT_NOT_POISONED(s2.b2); EXPECT_NOT_POISONED(s2.b4); EXPECT_NOT_POISONED(s2.b8); EXPECT_POISONED(s2.a1); EXPECT_POISONED(s2.a2); EXPECT_POISONED(s2.a4); EXPECT_POISONED(s2.a8); } #ifdef __GLIBC__ #define MSAN_TEST_PRLIMIT __GLIBC_PREREQ(2, 13) #else #define MSAN_TEST_PRLIMIT 1 #endif TEST(MemorySanitizer, getrlimit) { struct rlimit limit; __msan_poison(&limit, sizeof(limit)); int result = getrlimit(RLIMIT_DATA, &limit); ASSERT_EQ(result, 0); EXPECT_NOT_POISONED(limit.rlim_cur); EXPECT_NOT_POISONED(limit.rlim_max); #if MSAN_TEST_PRLIMIT struct rlimit limit2; __msan_poison(&limit2, sizeof(limit2)); result = prlimit(getpid(), RLIMIT_DATA, &limit, &limit2); ASSERT_EQ(result, 0); EXPECT_NOT_POISONED(limit2.rlim_cur); EXPECT_NOT_POISONED(limit2.rlim_max); __msan_poison(&limit, sizeof(limit)); result = prlimit(getpid(), RLIMIT_DATA, nullptr, &limit); ASSERT_EQ(result, 0); EXPECT_NOT_POISONED(limit.rlim_cur); EXPECT_NOT_POISONED(limit.rlim_max); result = prlimit(getpid(), RLIMIT_DATA, &limit, nullptr); ASSERT_EQ(result, 0); #endif } TEST(MemorySanitizer, getrusage) { struct rusage usage; __msan_poison(&usage, sizeof(usage)); int result = getrusage(RUSAGE_SELF, &usage); ASSERT_EQ(result, 0); EXPECT_NOT_POISONED(usage.ru_utime.tv_sec); EXPECT_NOT_POISONED(usage.ru_utime.tv_usec); EXPECT_NOT_POISONED(usage.ru_stime.tv_sec); EXPECT_NOT_POISONED(usage.ru_stime.tv_usec); EXPECT_NOT_POISONED(usage.ru_maxrss); EXPECT_NOT_POISONED(usage.ru_minflt); EXPECT_NOT_POISONED(usage.ru_majflt); EXPECT_NOT_POISONED(usage.ru_inblock); EXPECT_NOT_POISONED(usage.ru_oublock); EXPECT_NOT_POISONED(usage.ru_nvcsw); EXPECT_NOT_POISONED(usage.ru_nivcsw); } #if defined(__FreeBSD__) static void GetProgramPath(char *buf, size_t sz) { int mib[4] = { CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1 }; int res = sysctl(mib, 4, buf, &sz, NULL, 0); ASSERT_EQ(0, res); } #elif defined(__GLIBC__) static void GetProgramPath(char *buf, size_t sz) { extern char *program_invocation_name; int res = snprintf(buf, sz, "%s", program_invocation_name); ASSERT_GE(res, 0); ASSERT_LT((size_t)res, sz); } #else # error "TODO: port this" #endif static void dladdr_testfn() {} TEST(MemorySanitizer, dladdr) { Dl_info info; __msan_poison(&info, sizeof(info)); int result = dladdr((const void*)dladdr_testfn, &info); ASSERT_NE(result, 0); EXPECT_NOT_POISONED((unsigned long)info.dli_fname); if (info.dli_fname) EXPECT_NOT_POISONED(strlen(info.dli_fname)); EXPECT_NOT_POISONED((unsigned long)info.dli_fbase); EXPECT_NOT_POISONED((unsigned long)info.dli_sname); if (info.dli_sname) EXPECT_NOT_POISONED(strlen(info.dli_sname)); EXPECT_NOT_POISONED((unsigned long)info.dli_saddr); } #ifndef MSAN_TEST_DISABLE_DLOPEN static int dl_phdr_callback(struct dl_phdr_info *info, size_t size, void *data) { (*(int *)data)++; EXPECT_NOT_POISONED(info->dlpi_addr); EXPECT_NOT_POISONED(strlen(info->dlpi_name)); EXPECT_NOT_POISONED(info->dlpi_phnum); for (int i = 0; i < info->dlpi_phnum; ++i) EXPECT_NOT_POISONED(info->dlpi_phdr[i]); return 0; } // Compute the path to our loadable DSO. We assume it's in the same // directory. Only use string routines that we intercept so far to do this. static void GetPathToLoadable(char *buf, size_t sz) { char program_path[kMaxPathLength]; GetProgramPath(program_path, sizeof(program_path)); const char *last_slash = strrchr(program_path, '/'); ASSERT_NE(nullptr, last_slash); size_t dir_len = (size_t)(last_slash - program_path); #if defined(__x86_64__) static const char basename[] = "libmsan_loadable.x86_64.so"; #elif defined(__MIPSEB__) || defined(MIPSEB) static const char basename[] = "libmsan_loadable.mips64.so"; #elif defined(__mips64) static const char basename[] = "libmsan_loadable.mips64el.so"; #elif defined(__aarch64__) static const char basename[] = "libmsan_loadable.aarch64.so"; #elif defined(__powerpc64__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ static const char basename[] = "libmsan_loadable.powerpc64.so"; #elif defined(__powerpc64__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ static const char basename[] = "libmsan_loadable.powerpc64le.so"; #endif int res = snprintf(buf, sz, "%.*s/%s", (int)dir_len, program_path, basename); ASSERT_GE(res, 0); ASSERT_LT((size_t)res, sz); } TEST(MemorySanitizer, dl_iterate_phdr) { char path[kMaxPathLength]; GetPathToLoadable(path, sizeof(path)); // Having at least one dlopen'ed library in the process makes this more // entertaining. void *lib = dlopen(path, RTLD_LAZY); ASSERT_NE((void*)0, lib); int count = 0; int result = dl_iterate_phdr(dl_phdr_callback, &count); ASSERT_GT(count, 0); dlclose(lib); } TEST(MemorySanitizer, dlopen) { char path[kMaxPathLength]; GetPathToLoadable(path, sizeof(path)); // We need to clear shadow for globals when doing dlopen. In order to test // this, we have to poison the shadow for the DSO before we load it. In // general this is difficult, but the loader tends to reload things in the // same place, so we open, close, and then reopen. The global should always // start out clean after dlopen. for (int i = 0; i < 2; i++) { void *lib = dlopen(path, RTLD_LAZY); if (lib == NULL) { printf("dlerror: %s\n", dlerror()); ASSERT_TRUE(lib != NULL); } void **(*get_dso_global)() = (void **(*)())dlsym(lib, "get_dso_global"); ASSERT_TRUE(get_dso_global != NULL); void **dso_global = get_dso_global(); EXPECT_NOT_POISONED(*dso_global); __msan_poison(dso_global, sizeof(*dso_global)); EXPECT_POISONED(*dso_global); dlclose(lib); } } // Regression test for a crash in dlopen() interceptor. TEST(MemorySanitizer, dlopenFailed) { const char *path = "/libmsan_loadable_does_not_exist.so"; void *lib = dlopen(path, RTLD_LAZY); ASSERT_TRUE(lib == NULL); } #endif // MSAN_TEST_DISABLE_DLOPEN // There's no sched_getaffinity() on FreeBSD. #if !defined(__FreeBSD__) TEST(MemorySanitizer, sched_getaffinity) { cpu_set_t mask; int res = sched_getaffinity(getpid(), sizeof(mask), &mask); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(mask); } #endif TEST(MemorySanitizer, scanf) { const char *input = "42 hello"; int* d = new int; char* s = new char[7]; int res = sscanf(input, "%d %5s", d, s); printf("res %d\n", res); ASSERT_EQ(res, 2); EXPECT_NOT_POISONED(*d); EXPECT_NOT_POISONED(s[0]); EXPECT_NOT_POISONED(s[1]); EXPECT_NOT_POISONED(s[2]); EXPECT_NOT_POISONED(s[3]); EXPECT_NOT_POISONED(s[4]); EXPECT_NOT_POISONED(s[5]); EXPECT_POISONED(s[6]); delete[] s; delete d; } static void *SimpleThread_threadfn(void* data) { return new int; } TEST(MemorySanitizer, SimpleThread) { pthread_t t; void *p; int res = pthread_create(&t, NULL, SimpleThread_threadfn, NULL); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(t); res = pthread_join(t, &p); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(p); delete (int*)p; } static void *SmallStackThread_threadfn(void* data) { return 0; } #ifdef PTHREAD_STACK_MIN # define SMALLSTACKSIZE PTHREAD_STACK_MIN # define SMALLPRESTACKSIZE PTHREAD_STACK_MIN #else # define SMALLSTACKSIZE 64 * 1024 # define SMALLPRESTACKSIZE 16 * 1024 #endif TEST(MemorySanitizer, SmallStackThread) { pthread_attr_t attr; pthread_t t; void *p; int res; res = pthread_attr_init(&attr); ASSERT_EQ(0, res); res = pthread_attr_setstacksize(&attr, SMALLSTACKSIZE); ASSERT_EQ(0, res); res = pthread_create(&t, &attr, SmallStackThread_threadfn, NULL); ASSERT_EQ(0, res); res = pthread_join(t, &p); ASSERT_EQ(0, res); res = pthread_attr_destroy(&attr); ASSERT_EQ(0, res); } TEST(MemorySanitizer, SmallPreAllocatedStackThread) { pthread_attr_t attr; pthread_t t; int res; res = pthread_attr_init(&attr); ASSERT_EQ(0, res); void *stack; const size_t kStackSize = SMALLPRESTACKSIZE; res = posix_memalign(&stack, 4096, kStackSize); ASSERT_EQ(0, res); res = pthread_attr_setstack(&attr, stack, kStackSize); ASSERT_EQ(0, res); res = pthread_create(&t, &attr, SmallStackThread_threadfn, NULL); EXPECT_EQ(0, res); res = pthread_join(t, NULL); ASSERT_EQ(0, res); res = pthread_attr_destroy(&attr); ASSERT_EQ(0, res); } TEST(MemorySanitizer, pthread_attr_get) { pthread_attr_t attr; int res; res = pthread_attr_init(&attr); ASSERT_EQ(0, res); { int v; res = pthread_attr_getdetachstate(&attr, &v); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(v); } { size_t v; res = pthread_attr_getguardsize(&attr, &v); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(v); } { struct sched_param v; res = pthread_attr_getschedparam(&attr, &v); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(v); } { int v; res = pthread_attr_getschedpolicy(&attr, &v); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(v); } { int v; res = pthread_attr_getinheritsched(&attr, &v); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(v); } { int v; res = pthread_attr_getscope(&attr, &v); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(v); } { size_t v; res = pthread_attr_getstacksize(&attr, &v); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(v); } { void *v; size_t w; res = pthread_attr_getstack(&attr, &v, &w); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(v); EXPECT_NOT_POISONED(w); } { cpu_set_t v; res = pthread_attr_getaffinity_np(&attr, sizeof(v), &v); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(v); } res = pthread_attr_destroy(&attr); ASSERT_EQ(0, res); } TEST(MemorySanitizer, pthread_getschedparam) { int policy; struct sched_param param; int res = pthread_getschedparam(pthread_self(), &policy, ¶m); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(policy); EXPECT_NOT_POISONED(param.sched_priority); } TEST(MemorySanitizer, pthread_key_create) { pthread_key_t key; int res = pthread_key_create(&key, NULL); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(key); res = pthread_key_delete(key); ASSERT_EQ(0, res); } namespace { struct SignalCondArg { pthread_cond_t* cond; pthread_mutex_t* mu; bool broadcast; }; void *SignalCond(void *param) { SignalCondArg *arg = reinterpret_cast(param); pthread_mutex_lock(arg->mu); if (arg->broadcast) pthread_cond_broadcast(arg->cond); else pthread_cond_signal(arg->cond); pthread_mutex_unlock(arg->mu); return 0; } } // namespace TEST(MemorySanitizer, pthread_cond_wait) { pthread_cond_t cond; pthread_mutex_t mu; SignalCondArg args = {&cond, &mu, false}; pthread_cond_init(&cond, 0); pthread_mutex_init(&mu, 0); pthread_mutex_lock(&mu); // signal pthread_t thr; pthread_create(&thr, 0, SignalCond, &args); int res = pthread_cond_wait(&cond, &mu); ASSERT_EQ(0, res); pthread_join(thr, 0); // broadcast args.broadcast = true; pthread_create(&thr, 0, SignalCond, &args); res = pthread_cond_wait(&cond, &mu); ASSERT_EQ(0, res); pthread_join(thr, 0); pthread_mutex_unlock(&mu); pthread_mutex_destroy(&mu); pthread_cond_destroy(&cond); } TEST(MemorySanitizer, tmpnam) { char s[L_tmpnam]; char *res = tmpnam(s); ASSERT_EQ(s, res); EXPECT_NOT_POISONED(strlen(res)); } TEST(MemorySanitizer, tempnam) { char *res = tempnam(NULL, "zzz"); EXPECT_NOT_POISONED(strlen(res)); free(res); } TEST(MemorySanitizer, posix_memalign) { void *p; EXPECT_POISONED(p); int res = posix_memalign(&p, 4096, 13); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(p); EXPECT_EQ(0U, (uintptr_t)p % 4096); free(p); } // There's no memalign() on FreeBSD. #if !defined(__FreeBSD__) TEST(MemorySanitizer, memalign) { void *p = memalign(4096, 13); EXPECT_EQ(0U, (uintptr_t)p % 4096); free(p); } #endif TEST(MemorySanitizer, valloc) { void *a = valloc(100); uintptr_t PageSize = GetPageSize(); EXPECT_EQ(0U, (uintptr_t)a % PageSize); free(a); } // There's no pvalloc() on FreeBSD. #if !defined(__FreeBSD__) TEST(MemorySanitizer, pvalloc) { uintptr_t PageSize = GetPageSize(); void *p = pvalloc(PageSize + 100); EXPECT_EQ(0U, (uintptr_t)p % PageSize); EXPECT_EQ(2 * PageSize, __sanitizer_get_allocated_size(p)); free(p); p = pvalloc(0); // pvalloc(0) should allocate at least one page. EXPECT_EQ(0U, (uintptr_t)p % PageSize); EXPECT_EQ(PageSize, __sanitizer_get_allocated_size(p)); free(p); } #endif TEST(MemorySanitizer, inet_pton) { const char *s = "1:0:0:0:0:0:0:8"; unsigned char buf[sizeof(struct in6_addr)]; int res = inet_pton(AF_INET6, s, buf); ASSERT_EQ(1, res); EXPECT_NOT_POISONED(buf[0]); EXPECT_NOT_POISONED(buf[sizeof(struct in6_addr) - 1]); char s_out[INET6_ADDRSTRLEN]; EXPECT_POISONED(s_out[3]); const char *q = inet_ntop(AF_INET6, buf, s_out, INET6_ADDRSTRLEN); ASSERT_NE((void*)0, q); EXPECT_NOT_POISONED(s_out[3]); } TEST(MemorySanitizer, inet_aton) { const char *s = "127.0.0.1"; struct in_addr in[2]; int res = inet_aton(s, in); ASSERT_NE(0, res); EXPECT_NOT_POISONED(in[0]); EXPECT_POISONED(*(char *)(in + 1)); } TEST(MemorySanitizer, uname) { struct utsname u; int res = uname(&u); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(strlen(u.sysname)); EXPECT_NOT_POISONED(strlen(u.nodename)); EXPECT_NOT_POISONED(strlen(u.release)); EXPECT_NOT_POISONED(strlen(u.version)); EXPECT_NOT_POISONED(strlen(u.machine)); } TEST(MemorySanitizer, gethostname) { char buf[100]; int res = gethostname(buf, 100); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(strlen(buf)); } // There's no sysinfo() on FreeBSD. #if !defined(__FreeBSD__) TEST(MemorySanitizer, sysinfo) { struct sysinfo info; int res = sysinfo(&info); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(info); } #endif TEST(MemorySanitizer, getpwuid) { struct passwd *p = getpwuid(0); // root ASSERT_TRUE(p != NULL); EXPECT_NOT_POISONED(p->pw_name); ASSERT_TRUE(p->pw_name != NULL); EXPECT_NOT_POISONED(p->pw_name[0]); EXPECT_NOT_POISONED(p->pw_uid); ASSERT_EQ(0U, p->pw_uid); } TEST(MemorySanitizer, getpwuid_r) { struct passwd pwd; struct passwd *pwdres; char buf[10000]; int res = getpwuid_r(0, &pwd, buf, sizeof(buf), &pwdres); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(pwd.pw_name); ASSERT_TRUE(pwd.pw_name != NULL); EXPECT_NOT_POISONED(pwd.pw_name[0]); EXPECT_NOT_POISONED(pwd.pw_uid); ASSERT_EQ(0U, pwd.pw_uid); EXPECT_NOT_POISONED(pwdres); } TEST(MemorySanitizer, getpwnam_r) { struct passwd pwd; struct passwd *pwdres; char buf[10000]; int res = getpwnam_r("root", &pwd, buf, sizeof(buf), &pwdres); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(pwd.pw_name); ASSERT_TRUE(pwd.pw_name != NULL); EXPECT_NOT_POISONED(pwd.pw_name[0]); EXPECT_NOT_POISONED(pwd.pw_uid); ASSERT_EQ(0U, pwd.pw_uid); EXPECT_NOT_POISONED(pwdres); } TEST(MemorySanitizer, getpwnam_r_positive) { struct passwd pwd; struct passwd *pwdres; char s[5]; strncpy(s, "abcd", 5); __msan_poison(s, 5); char buf[10000]; int res; EXPECT_UMR(res = getpwnam_r(s, &pwd, buf, sizeof(buf), &pwdres)); } TEST(MemorySanitizer, getgrnam_r) { struct group grp; struct group *grpres; char buf[10000]; int res = getgrnam_r(SUPERUSER_GROUP, &grp, buf, sizeof(buf), &grpres); ASSERT_EQ(0, res); // Note that getgrnam_r() returns 0 if the matching group is not found. ASSERT_NE(nullptr, grpres); EXPECT_NOT_POISONED(grp.gr_name); ASSERT_TRUE(grp.gr_name != NULL); EXPECT_NOT_POISONED(grp.gr_name[0]); EXPECT_NOT_POISONED(grp.gr_gid); EXPECT_NOT_POISONED(grpres); } TEST(MemorySanitizer, getpwent) { setpwent(); struct passwd *p = getpwent(); ASSERT_TRUE(p != NULL); EXPECT_NOT_POISONED(p->pw_name); ASSERT_TRUE(p->pw_name != NULL); EXPECT_NOT_POISONED(p->pw_name[0]); EXPECT_NOT_POISONED(p->pw_uid); } TEST(MemorySanitizer, getpwent_r) { struct passwd pwd; struct passwd *pwdres; char buf[10000]; setpwent(); int res = getpwent_r(&pwd, buf, sizeof(buf), &pwdres); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(pwd.pw_name); ASSERT_TRUE(pwd.pw_name != NULL); EXPECT_NOT_POISONED(pwd.pw_name[0]); EXPECT_NOT_POISONED(pwd.pw_uid); EXPECT_NOT_POISONED(pwdres); } // There's no fgetpwent() on FreeBSD. #if !defined(__FreeBSD__) TEST(MemorySanitizer, fgetpwent) { FILE *fp = fopen("/etc/passwd", "r"); struct passwd *p = fgetpwent(fp); ASSERT_TRUE(p != NULL); EXPECT_NOT_POISONED(p->pw_name); ASSERT_TRUE(p->pw_name != NULL); EXPECT_NOT_POISONED(p->pw_name[0]); EXPECT_NOT_POISONED(p->pw_uid); fclose(fp); } #endif TEST(MemorySanitizer, getgrent) { setgrent(); struct group *p = getgrent(); ASSERT_TRUE(p != NULL); EXPECT_NOT_POISONED(p->gr_name); ASSERT_TRUE(p->gr_name != NULL); EXPECT_NOT_POISONED(p->gr_name[0]); EXPECT_NOT_POISONED(p->gr_gid); } // There's no fgetgrent() on FreeBSD. #if !defined(__FreeBSD__) TEST(MemorySanitizer, fgetgrent) { FILE *fp = fopen("/etc/group", "r"); struct group *grp = fgetgrent(fp); ASSERT_TRUE(grp != NULL); EXPECT_NOT_POISONED(grp->gr_name); ASSERT_TRUE(grp->gr_name != NULL); EXPECT_NOT_POISONED(grp->gr_name[0]); EXPECT_NOT_POISONED(grp->gr_gid); for (char **p = grp->gr_mem; *p; ++p) { EXPECT_NOT_POISONED((*p)[0]); EXPECT_TRUE(strlen(*p) > 0); } fclose(fp); } #endif TEST(MemorySanitizer, getgrent_r) { struct group grp; struct group *grpres; char buf[10000]; setgrent(); int res = getgrent_r(&grp, buf, sizeof(buf), &grpres); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(grp.gr_name); ASSERT_TRUE(grp.gr_name != NULL); EXPECT_NOT_POISONED(grp.gr_name[0]); EXPECT_NOT_POISONED(grp.gr_gid); EXPECT_NOT_POISONED(grpres); } // There's no fgetgrent_r() on FreeBSD. #if !defined(__FreeBSD__) TEST(MemorySanitizer, fgetgrent_r) { FILE *fp = fopen("/etc/group", "r"); struct group grp; struct group *grpres; char buf[10000]; setgrent(); int res = fgetgrent_r(fp, &grp, buf, sizeof(buf), &grpres); ASSERT_EQ(0, res); EXPECT_NOT_POISONED(grp.gr_name); ASSERT_TRUE(grp.gr_name != NULL); EXPECT_NOT_POISONED(grp.gr_name[0]); EXPECT_NOT_POISONED(grp.gr_gid); EXPECT_NOT_POISONED(grpres); fclose(fp); } #endif TEST(MemorySanitizer, getgroups) { int n = getgroups(0, 0); gid_t *gids = new gid_t[n]; int res = getgroups(n, gids); ASSERT_EQ(n, res); for (int i = 0; i < n; ++i) EXPECT_NOT_POISONED(gids[i]); } TEST(MemorySanitizer, getgroups_zero) { gid_t group; int n = getgroups(0, &group); ASSERT_GE(n, 0); } TEST(MemorySanitizer, getgroups_negative) { gid_t group; int n = getgroups(-1, 0); ASSERT_EQ(-1, n); n = getgroups(-1, 0); ASSERT_EQ(-1, n); } TEST(MemorySanitizer, wordexp) { wordexp_t w; int res = wordexp("a b c", &w, 0); ASSERT_EQ(0, res); ASSERT_EQ(3U, w.we_wordc); ASSERT_STREQ("a", w.we_wordv[0]); ASSERT_STREQ("b", w.we_wordv[1]); ASSERT_STREQ("c", w.we_wordv[2]); } template static bool applySlt(T value, T shadow) { __msan_partial_poison(&value, &shadow, sizeof(T)); volatile bool zzz = true; // This "|| zzz" trick somehow makes LLVM emit "icmp slt" instead of // a shift-and-trunc to get at the highest bit. volatile bool v = value < 0 || zzz; return v; } TEST(MemorySanitizer, SignedCompareWithZero) { EXPECT_NOT_POISONED(applySlt(0xF, 0xF)); EXPECT_NOT_POISONED(applySlt(0xF, 0xFF)); EXPECT_NOT_POISONED(applySlt(0xF, 0xFFFFFF)); EXPECT_NOT_POISONED(applySlt(0xF, 0x7FFFFFF)); EXPECT_UMR(applySlt(0xF, 0x80FFFFFF)); EXPECT_UMR(applySlt(0xF, 0xFFFFFFFF)); } template static T poisoned(T Va, S Sa) { char SIZE_CHECK1[(ssize_t)sizeof(T) - (ssize_t)sizeof(S)]; char SIZE_CHECK2[(ssize_t)sizeof(S) - (ssize_t)sizeof(T)]; T a; a = Va; __msan_partial_poison(&a, &Sa, sizeof(T)); return a; } TEST(MemorySanitizer, ICmpRelational) { EXPECT_NOT_POISONED(poisoned(0, 0) < poisoned(0, 0)); EXPECT_NOT_POISONED(poisoned(0U, 0) < poisoned(0U, 0)); EXPECT_NOT_POISONED(poisoned(0LL, 0LLU) < poisoned(0LL, 0LLU)); EXPECT_NOT_POISONED(poisoned(0LLU, 0LLU) < poisoned(0LLU, 0LLU)); EXPECT_POISONED(poisoned(0xFF, 0xFF) < poisoned(0xFF, 0xFF)); EXPECT_POISONED(poisoned(0xFFFFFFFFU, 0xFFFFFFFFU) < poisoned(0xFFFFFFFFU, 0xFFFFFFFFU)); EXPECT_POISONED(poisoned(-1, 0xFFFFFFFFU) < poisoned(-1, 0xFFFFFFFFU)); EXPECT_NOT_POISONED(poisoned(0, 0) <= poisoned(0, 0)); EXPECT_NOT_POISONED(poisoned(0U, 0) <= poisoned(0U, 0)); EXPECT_NOT_POISONED(poisoned(0LL, 0LLU) <= poisoned(0LL, 0LLU)); EXPECT_NOT_POISONED(poisoned(0LLU, 0LLU) <= poisoned(0LLU, 0LLU)); EXPECT_POISONED(poisoned(0xFF, 0xFF) <= poisoned(0xFF, 0xFF)); EXPECT_POISONED(poisoned(0xFFFFFFFFU, 0xFFFFFFFFU) <= poisoned(0xFFFFFFFFU, 0xFFFFFFFFU)); EXPECT_POISONED(poisoned(-1, 0xFFFFFFFFU) <= poisoned(-1, 0xFFFFFFFFU)); EXPECT_NOT_POISONED(poisoned(0, 0) > poisoned(0, 0)); EXPECT_NOT_POISONED(poisoned(0U, 0) > poisoned(0U, 0)); EXPECT_NOT_POISONED(poisoned(0LL, 0LLU) > poisoned(0LL, 0LLU)); EXPECT_NOT_POISONED(poisoned(0LLU, 0LLU) > poisoned(0LLU, 0LLU)); EXPECT_POISONED(poisoned(0xFF, 0xFF) > poisoned(0xFF, 0xFF)); EXPECT_POISONED(poisoned(0xFFFFFFFFU, 0xFFFFFFFFU) > poisoned(0xFFFFFFFFU, 0xFFFFFFFFU)); EXPECT_POISONED(poisoned(-1, 0xFFFFFFFFU) > poisoned(-1, 0xFFFFFFFFU)); EXPECT_NOT_POISONED(poisoned(0, 0) >= poisoned(0, 0)); EXPECT_NOT_POISONED(poisoned(0U, 0) >= poisoned(0U, 0)); EXPECT_NOT_POISONED(poisoned(0LL, 0LLU) >= poisoned(0LL, 0LLU)); EXPECT_NOT_POISONED(poisoned(0LLU, 0LLU) >= poisoned(0LLU, 0LLU)); EXPECT_POISONED(poisoned(0xFF, 0xFF) >= poisoned(0xFF, 0xFF)); EXPECT_POISONED(poisoned(0xFFFFFFFFU, 0xFFFFFFFFU) >= poisoned(0xFFFFFFFFU, 0xFFFFFFFFU)); EXPECT_POISONED(poisoned(-1, 0xFFFFFFFFU) >= poisoned(-1, 0xFFFFFFFFU)); EXPECT_POISONED(poisoned(6, 0xF) > poisoned(7, 0)); EXPECT_POISONED(poisoned(0xF, 0xF) > poisoned(7, 0)); // Note that "icmp op X, Y" is approximated with "or shadow(X), shadow(Y)" // and therefore may generate false positives in some cases, e.g. the // following one: // EXPECT_NOT_POISONED(poisoned(-1, 0x80000000U) >= poisoned(-1, 0U)); } #if MSAN_HAS_M128 TEST(MemorySanitizer, ICmpVectorRelational) { EXPECT_NOT_POISONED( _mm_cmplt_epi16(poisoned(_mm_set1_epi16(0), _mm_set1_epi16(0)), poisoned(_mm_set1_epi16(0), _mm_set1_epi16(0)))); EXPECT_NOT_POISONED( _mm_cmplt_epi16(poisoned(_mm_set1_epi32(0), _mm_set1_epi32(0)), poisoned(_mm_set1_epi32(0), _mm_set1_epi32(0)))); EXPECT_POISONED( _mm_cmplt_epi16(poisoned(_mm_set1_epi16(0), _mm_set1_epi16(0xFFFF)), poisoned(_mm_set1_epi16(0), _mm_set1_epi16(0xFFFF)))); EXPECT_POISONED(_mm_cmpgt_epi16(poisoned(_mm_set1_epi16(6), _mm_set1_epi16(0xF)), poisoned(_mm_set1_epi16(7), _mm_set1_epi16(0)))); } TEST(MemorySanitizer, stmxcsr_ldmxcsr) { U4 x = _mm_getcsr(); EXPECT_NOT_POISONED(x); _mm_setcsr(x); __msan_poison(&x, sizeof(x)); U4 origin = __LINE__; __msan_set_origin(&x, sizeof(x), origin); EXPECT_UMR_O(_mm_setcsr(x), origin); } #endif // Volatile bitfield store is implemented as load-mask-store // Test that we don't warn on the store of (uninitialized) padding. struct VolatileBitfieldStruct { volatile unsigned x : 1; unsigned y : 1; }; TEST(MemorySanitizer, VolatileBitfield) { VolatileBitfieldStruct *S = new VolatileBitfieldStruct; S->x = 1; EXPECT_NOT_POISONED((unsigned)S->x); EXPECT_POISONED((unsigned)S->y); } TEST(MemorySanitizer, UnalignedLoad) { char x[32] __attribute__((aligned(8))); U4 origin = __LINE__; for (unsigned i = 0; i < sizeof(x) / 4; ++i) __msan_set_origin(x + 4 * i, 4, origin + i); memset(x + 8, 0, 16); EXPECT_POISONED_O(__sanitizer_unaligned_load16(x + 6), origin + 1); EXPECT_POISONED_O(__sanitizer_unaligned_load16(x + 7), origin + 1); EXPECT_NOT_POISONED(__sanitizer_unaligned_load16(x + 8)); EXPECT_NOT_POISONED(__sanitizer_unaligned_load16(x + 9)); EXPECT_NOT_POISONED(__sanitizer_unaligned_load16(x + 22)); EXPECT_POISONED_O(__sanitizer_unaligned_load16(x + 23), origin + 6); EXPECT_POISONED_O(__sanitizer_unaligned_load16(x + 24), origin + 6); EXPECT_POISONED_O(__sanitizer_unaligned_load32(x + 4), origin + 1); EXPECT_POISONED_O(__sanitizer_unaligned_load32(x + 7), origin + 1); EXPECT_NOT_POISONED(__sanitizer_unaligned_load32(x + 8)); EXPECT_NOT_POISONED(__sanitizer_unaligned_load32(x + 9)); EXPECT_NOT_POISONED(__sanitizer_unaligned_load32(x + 20)); EXPECT_POISONED_O(__sanitizer_unaligned_load32(x + 21), origin + 6); EXPECT_POISONED_O(__sanitizer_unaligned_load32(x + 24), origin + 6); EXPECT_POISONED_O(__sanitizer_unaligned_load64(x), origin); EXPECT_POISONED_O(__sanitizer_unaligned_load64(x + 1), origin); EXPECT_POISONED_O(__sanitizer_unaligned_load64(x + 7), origin + 1); EXPECT_NOT_POISONED(__sanitizer_unaligned_load64(x + 8)); EXPECT_NOT_POISONED(__sanitizer_unaligned_load64(x + 9)); EXPECT_NOT_POISONED(__sanitizer_unaligned_load64(x + 16)); EXPECT_POISONED_O(__sanitizer_unaligned_load64(x + 17), origin + 6); EXPECT_POISONED_O(__sanitizer_unaligned_load64(x + 21), origin + 6); EXPECT_POISONED_O(__sanitizer_unaligned_load64(x + 24), origin + 6); } TEST(MemorySanitizer, UnalignedStore16) { char x[5] __attribute__((aligned(4))); U2 y2 = 0; U4 origin = __LINE__; __msan_poison(&y2, 1); __msan_set_origin(&y2, 1, origin); __sanitizer_unaligned_store16(x + 1, y2); EXPECT_POISONED_O(x[0], origin); EXPECT_POISONED_O(x[1], origin); EXPECT_NOT_POISONED(x[2]); EXPECT_POISONED_O(x[3], origin); } TEST(MemorySanitizer, UnalignedStore32) { char x[8] __attribute__((aligned(4))); U4 y4 = 0; U4 origin = __LINE__; __msan_poison(&y4, 2); __msan_set_origin(&y4, 2, origin); __sanitizer_unaligned_store32(x + 3, y4); EXPECT_POISONED_O(x[0], origin); EXPECT_POISONED_O(x[1], origin); EXPECT_POISONED_O(x[2], origin); EXPECT_POISONED_O(x[3], origin); EXPECT_POISONED_O(x[4], origin); EXPECT_NOT_POISONED(x[5]); EXPECT_NOT_POISONED(x[6]); EXPECT_POISONED_O(x[7], origin); } TEST(MemorySanitizer, UnalignedStore64) { char x[16] __attribute__((aligned(8))); U8 y8 = 0; U4 origin = __LINE__; __msan_poison(&y8, 3); __msan_poison(((char *)&y8) + sizeof(y8) - 2, 1); __msan_set_origin(&y8, 8, origin); __sanitizer_unaligned_store64(x + 3, y8); EXPECT_POISONED_O(x[0], origin); EXPECT_POISONED_O(x[1], origin); EXPECT_POISONED_O(x[2], origin); EXPECT_POISONED_O(x[3], origin); EXPECT_POISONED_O(x[4], origin); EXPECT_POISONED_O(x[5], origin); EXPECT_NOT_POISONED(x[6]); EXPECT_NOT_POISONED(x[7]); EXPECT_NOT_POISONED(x[8]); EXPECT_POISONED_O(x[9], origin); EXPECT_NOT_POISONED(x[10]); EXPECT_POISONED_O(x[11], origin); } TEST(MemorySanitizer, UnalignedStore16_precise) { char x[8] __attribute__((aligned(4))); U2 y = 0; U4 originx1 = __LINE__; U4 originx2 = __LINE__; U4 originy = __LINE__; __msan_poison(x, sizeof(x)); __msan_set_origin(x, 4, originx1); __msan_set_origin(x + 4, 4, originx2); __msan_poison(((char *)&y) + 1, 1); __msan_set_origin(&y, sizeof(y), originy); __sanitizer_unaligned_store16(x + 3, y); EXPECT_POISONED_O(x[0], originx1); EXPECT_POISONED_O(x[1], originx1); EXPECT_POISONED_O(x[2], originx1); EXPECT_NOT_POISONED(x[3]); EXPECT_POISONED_O(x[4], originy); EXPECT_POISONED_O(x[5], originy); EXPECT_POISONED_O(x[6], originy); EXPECT_POISONED_O(x[7], originy); } TEST(MemorySanitizer, UnalignedStore16_precise2) { char x[8] __attribute__((aligned(4))); U2 y = 0; U4 originx1 = __LINE__; U4 originx2 = __LINE__; U4 originy = __LINE__; __msan_poison(x, sizeof(x)); __msan_set_origin(x, 4, originx1); __msan_set_origin(x + 4, 4, originx2); __msan_poison(((char *)&y), 1); __msan_set_origin(&y, sizeof(y), originy); __sanitizer_unaligned_store16(x + 3, y); EXPECT_POISONED_O(x[0], originy); EXPECT_POISONED_O(x[1], originy); EXPECT_POISONED_O(x[2], originy); EXPECT_POISONED_O(x[3], originy); EXPECT_NOT_POISONED(x[4]); EXPECT_POISONED_O(x[5], originx2); EXPECT_POISONED_O(x[6], originx2); EXPECT_POISONED_O(x[7], originx2); } TEST(MemorySanitizer, UnalignedStore64_precise) { char x[12] __attribute__((aligned(8))); U8 y = 0; U4 originx1 = __LINE__; U4 originx2 = __LINE__; U4 originx3 = __LINE__; U4 originy = __LINE__; __msan_poison(x, sizeof(x)); __msan_set_origin(x, 4, originx1); __msan_set_origin(x + 4, 4, originx2); __msan_set_origin(x + 8, 4, originx3); __msan_poison(((char *)&y) + 1, 1); __msan_poison(((char *)&y) + 7, 1); __msan_set_origin(&y, sizeof(y), originy); __sanitizer_unaligned_store64(x + 2, y); EXPECT_POISONED_O(x[0], originy); EXPECT_POISONED_O(x[1], originy); EXPECT_NOT_POISONED(x[2]); EXPECT_POISONED_O(x[3], originy); EXPECT_NOT_POISONED(x[4]); EXPECT_NOT_POISONED(x[5]); EXPECT_NOT_POISONED(x[6]); EXPECT_NOT_POISONED(x[7]); EXPECT_NOT_POISONED(x[8]); EXPECT_POISONED_O(x[9], originy); EXPECT_POISONED_O(x[10], originy); EXPECT_POISONED_O(x[11], originy); } TEST(MemorySanitizer, UnalignedStore64_precise2) { char x[12] __attribute__((aligned(8))); U8 y = 0; U4 originx1 = __LINE__; U4 originx2 = __LINE__; U4 originx3 = __LINE__; U4 originy = __LINE__; __msan_poison(x, sizeof(x)); __msan_set_origin(x, 4, originx1); __msan_set_origin(x + 4, 4, originx2); __msan_set_origin(x + 8, 4, originx3); __msan_poison(((char *)&y) + 3, 3); __msan_set_origin(&y, sizeof(y), originy); __sanitizer_unaligned_store64(x + 2, y); EXPECT_POISONED_O(x[0], originx1); EXPECT_POISONED_O(x[1], originx1); EXPECT_NOT_POISONED(x[2]); EXPECT_NOT_POISONED(x[3]); EXPECT_NOT_POISONED(x[4]); EXPECT_POISONED_O(x[5], originy); EXPECT_POISONED_O(x[6], originy); EXPECT_POISONED_O(x[7], originy); EXPECT_NOT_POISONED(x[8]); EXPECT_NOT_POISONED(x[9]); EXPECT_POISONED_O(x[10], originx3); EXPECT_POISONED_O(x[11], originx3); } #if (defined(__x86_64__) && defined(__clang__)) namespace { typedef U1 V16x8 __attribute__((__vector_size__(16))); typedef U2 V8x16 __attribute__((__vector_size__(16))); typedef U4 V4x32 __attribute__((__vector_size__(16))); typedef U8 V2x64 __attribute__((__vector_size__(16))); typedef U4 V8x32 __attribute__((__vector_size__(32))); typedef U8 V4x64 __attribute__((__vector_size__(32))); typedef U4 V2x32 __attribute__((__vector_size__(8))); typedef U2 V4x16 __attribute__((__vector_size__(8))); typedef U1 V8x8 __attribute__((__vector_size__(8))); V8x16 shift_sse2_left_scalar(V8x16 x, U4 y) { return _mm_slli_epi16(x, y); } V8x16 shift_sse2_left(V8x16 x, V8x16 y) { return _mm_sll_epi16(x, y); } TEST(VectorShiftTest, sse2_left_scalar) { V8x16 v = {Poisoned(0, 3), Poisoned(0, 7), 2, 3, 4, 5, 6, 7}; V8x16 u = shift_sse2_left_scalar(v, 2); EXPECT_POISONED(u[0]); EXPECT_POISONED(u[1]); EXPECT_NOT_POISONED(u[0] | (3U << 2)); EXPECT_NOT_POISONED(u[1] | (7U << 2)); u[0] = u[1] = 0; EXPECT_NOT_POISONED(u); } TEST(VectorShiftTest, sse2_left_scalar_by_uninit) { V8x16 v = {0, 1, 2, 3, 4, 5, 6, 7}; V8x16 u = shift_sse2_left_scalar(v, Poisoned()); EXPECT_POISONED(u[0]); EXPECT_POISONED(u[1]); EXPECT_POISONED(u[2]); EXPECT_POISONED(u[3]); EXPECT_POISONED(u[4]); EXPECT_POISONED(u[5]); EXPECT_POISONED(u[6]); EXPECT_POISONED(u[7]); } TEST(VectorShiftTest, sse2_left) { V8x16 v = {Poisoned(0, 3), Poisoned(0, 7), 2, 3, 4, 5, 6, 7}; // Top 64 bits of shift count don't affect the result. V2x64 s = {2, Poisoned()}; V8x16 u = shift_sse2_left(v, s); EXPECT_POISONED(u[0]); EXPECT_POISONED(u[1]); EXPECT_NOT_POISONED(u[0] | (3U << 2)); EXPECT_NOT_POISONED(u[1] | (7U << 2)); u[0] = u[1] = 0; EXPECT_NOT_POISONED(u); } TEST(VectorShiftTest, sse2_left_by_uninit) { V8x16 v = {Poisoned(0, 3), Poisoned(0, 7), 2, 3, 4, 5, 6, 7}; V2x64 s = {Poisoned(), Poisoned()}; V8x16 u = shift_sse2_left(v, s); EXPECT_POISONED(u[0]); EXPECT_POISONED(u[1]); EXPECT_POISONED(u[2]); EXPECT_POISONED(u[3]); EXPECT_POISONED(u[4]); EXPECT_POISONED(u[5]); EXPECT_POISONED(u[6]); EXPECT_POISONED(u[7]); } #ifdef __AVX2__ V4x32 shift_avx2_left(V4x32 x, V4x32 y) { return _mm_sllv_epi32(x, y); } // This is variable vector shift that's only available starting with AVX2. // V4x32 shift_avx2_left(V4x32 x, V4x32 y) { TEST(VectorShiftTest, avx2_left) { V4x32 v = {Poisoned(0, 3), Poisoned(0, 7), 2, 3}; V4x32 s = {2, Poisoned(), 3, Poisoned()}; V4x32 u = shift_avx2_left(v, s); EXPECT_POISONED(u[0]); EXPECT_NOT_POISONED(u[0] | (~7U)); EXPECT_POISONED(u[1]); EXPECT_POISONED(u[1] | (~31U)); EXPECT_NOT_POISONED(u[2]); EXPECT_POISONED(u[3]); EXPECT_POISONED(u[3] | (~31U)); } #endif // __AVX2__ } // namespace TEST(VectorPackTest, sse2_packssdw_128) { const unsigned S2_max = (1 << 15) - 1; V4x32 a = {Poisoned(0, 0xFF0000), Poisoned(0, 0xFFFF0000), S2_max + 100, 4}; V4x32 b = {Poisoned(0, 0xFF), S2_max + 10000, Poisoned(0, 0xFF00), S2_max}; V8x16 c = _mm_packs_epi32(a, b); EXPECT_POISONED(c[0]); EXPECT_POISONED(c[1]); EXPECT_NOT_POISONED(c[2]); EXPECT_NOT_POISONED(c[3]); EXPECT_POISONED(c[4]); EXPECT_NOT_POISONED(c[5]); EXPECT_POISONED(c[6]); EXPECT_NOT_POISONED(c[7]); EXPECT_EQ(c[2], S2_max); EXPECT_EQ(c[3], 4); EXPECT_EQ(c[5], S2_max); EXPECT_EQ(c[7], S2_max); } TEST(VectorPackTest, mmx_packuswb) { const unsigned U1_max = (1 << 8) - 1; V4x16 a = {Poisoned(0, 0xFF00), Poisoned(0, 0xF000U), U1_max + 100, 4}; V4x16 b = {Poisoned(0, 0xFF), U1_max - 1, Poisoned(0, 0xF), U1_max}; V8x8 c = _mm_packs_pu16(a, b); EXPECT_POISONED(c[0]); EXPECT_POISONED(c[1]); EXPECT_NOT_POISONED(c[2]); EXPECT_NOT_POISONED(c[3]); EXPECT_POISONED(c[4]); EXPECT_NOT_POISONED(c[5]); EXPECT_POISONED(c[6]); EXPECT_NOT_POISONED(c[7]); EXPECT_EQ(c[2], U1_max); EXPECT_EQ(c[3], 4); EXPECT_EQ(c[5], U1_max - 1); EXPECT_EQ(c[7], U1_max); } TEST(VectorSadTest, sse2_psad_bw) { V16x8 a = {Poisoned(), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; V16x8 b = {100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115}; V2x64 c = _mm_sad_epu8(a, b); EXPECT_POISONED(c[0]); EXPECT_NOT_POISONED(c[1]); EXPECT_EQ(800U, c[1]); } TEST(VectorMaddTest, mmx_pmadd_wd) { V4x16 a = {Poisoned(), 1, 2, 3}; V4x16 b = {100, 101, 102, 103}; V2x32 c = _mm_madd_pi16(a, b); EXPECT_POISONED(c[0]); EXPECT_NOT_POISONED(c[1]); EXPECT_EQ((unsigned)(2 * 102 + 3 * 103), c[1]); } TEST(VectorCmpTest, mm_cmpneq_ps) { V4x32 c; c = _mm_cmpneq_ps(V4x32{Poisoned(), 1, 2, 3}, V4x32{4, 5, Poisoned(), 6}); EXPECT_POISONED(c[0]); EXPECT_NOT_POISONED(c[1]); EXPECT_POISONED(c[2]); EXPECT_NOT_POISONED(c[3]); c = _mm_cmpneq_ps(V4x32{0, 1, 2, 3}, V4x32{4, 5, 6, 7}); EXPECT_NOT_POISONED(c); } TEST(VectorCmpTest, mm_cmpneq_sd) { V2x64 c; c = _mm_cmpneq_sd(V2x64{Poisoned(), 1}, V2x64{2, 3}); EXPECT_POISONED(c[0]); c = _mm_cmpneq_sd(V2x64{1, 2}, V2x64{Poisoned(), 3}); EXPECT_POISONED(c[0]); c = _mm_cmpneq_sd(V2x64{1, 2}, V2x64{3, 4}); EXPECT_NOT_POISONED(c[0]); c = _mm_cmpneq_sd(V2x64{1, Poisoned()}, V2x64{2, Poisoned()}); EXPECT_NOT_POISONED(c[0]); c = _mm_cmpneq_sd(V2x64{1, Poisoned()}, V2x64{1, Poisoned()}); EXPECT_NOT_POISONED(c[0]); } TEST(VectorCmpTest, builtin_ia32_ucomisdlt) { U4 c; c = __builtin_ia32_ucomisdlt(V2x64{Poisoned(), 1}, V2x64{2, 3}); EXPECT_POISONED(c); c = __builtin_ia32_ucomisdlt(V2x64{1, 2}, V2x64{Poisoned(), 3}); EXPECT_POISONED(c); c = __builtin_ia32_ucomisdlt(V2x64{1, 2}, V2x64{3, 4}); EXPECT_NOT_POISONED(c); c = __builtin_ia32_ucomisdlt(V2x64{1, Poisoned()}, V2x64{2, Poisoned()}); EXPECT_NOT_POISONED(c); c = __builtin_ia32_ucomisdlt(V2x64{1, Poisoned()}, V2x64{1, Poisoned()}); EXPECT_NOT_POISONED(c); } #endif // defined(__x86_64__) && defined(__clang__) TEST(MemorySanitizerOrigins, SetGet) { EXPECT_EQ(TrackingOrigins(), !!__msan_get_track_origins()); if (!TrackingOrigins()) return; int x; __msan_set_origin(&x, sizeof(x), 1234); EXPECT_ORIGIN(1234U, __msan_get_origin(&x)); __msan_set_origin(&x, sizeof(x), 5678); EXPECT_ORIGIN(5678U, __msan_get_origin(&x)); __msan_set_origin(&x, sizeof(x), 0); EXPECT_ORIGIN(0U, __msan_get_origin(&x)); } namespace { struct S { U4 dummy; U2 a; U2 b; }; TEST(MemorySanitizerOrigins, InitializedStoreDoesNotChangeOrigin) { if (!TrackingOrigins()) return; S s; U4 origin = rand(); // NOLINT s.a = *GetPoisonedO(0, origin); EXPECT_ORIGIN(origin, __msan_get_origin(&s.a)); EXPECT_ORIGIN(origin, __msan_get_origin(&s.b)); s.b = 42; EXPECT_ORIGIN(origin, __msan_get_origin(&s.a)); EXPECT_ORIGIN(origin, __msan_get_origin(&s.b)); } } // namespace template INLINE void BinaryOpOriginTest(BinaryOp op) { U4 ox = rand(); //NOLINT U4 oy = rand(); //NOLINT T *x = GetPoisonedO(0, ox, 0); T *y = GetPoisonedO(1, oy, 0); T *z = GetPoisonedO(2, 0, 0); *z = op(*x, *y); U4 origin = __msan_get_origin(z); EXPECT_POISONED_O(*z, origin); EXPECT_EQ(true, __msan_origin_is_descendant_or_same(origin, ox) || __msan_origin_is_descendant_or_same(origin, oy)); // y is poisoned, x is not. *x = 10101; *y = *GetPoisonedO(1, oy); break_optimization(x); __msan_set_origin(z, sizeof(*z), 0); *z = op(*x, *y); EXPECT_POISONED_O(*z, oy); EXPECT_ORIGIN(oy, __msan_get_origin(z)); // x is poisoned, y is not. *x = *GetPoisonedO(0, ox); *y = 10101010; break_optimization(y); __msan_set_origin(z, sizeof(*z), 0); *z = op(*x, *y); EXPECT_POISONED_O(*z, ox); EXPECT_ORIGIN(ox, __msan_get_origin(z)); } template INLINE T XOR(const T &a, const T&b) { return a ^ b; } template INLINE T ADD(const T &a, const T&b) { return a + b; } template INLINE T SUB(const T &a, const T&b) { return a - b; } template INLINE T MUL(const T &a, const T&b) { return a * b; } template INLINE T AND(const T &a, const T&b) { return a & b; } template INLINE T OR (const T &a, const T&b) { return a | b; } TEST(MemorySanitizerOrigins, BinaryOp) { if (!TrackingOrigins()) return; BinaryOpOriginTest(XOR); BinaryOpOriginTest(ADD); BinaryOpOriginTest(SUB); BinaryOpOriginTest(MUL); BinaryOpOriginTest(OR); BinaryOpOriginTest(AND); BinaryOpOriginTest(ADD); BinaryOpOriginTest(ADD); BinaryOpOriginTest(ADD); BinaryOpOriginTest(ADD); } TEST(MemorySanitizerOrigins, Unary) { if (!TrackingOrigins()) return; EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O((void*)*GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O((U8)*GetPoisonedO(0, __LINE__), __LINE__); } TEST(MemorySanitizerOrigins, EQ) { if (!TrackingOrigins()) return; EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__) <= 11, __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__) == 11, __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__) == 1.1, __LINE__); } TEST(MemorySanitizerOrigins, DIV) { if (!TrackingOrigins()) return; EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__) / 100, __LINE__); unsigned o = __LINE__; EXPECT_UMR_O(volatile unsigned y = 100 / *GetPoisonedO(0, o, 1), o); } TEST(MemorySanitizerOrigins, SHIFT) { if (!TrackingOrigins()) return; EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__) >> 10, __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__) >> 10, __LINE__); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__) << 10, __LINE__); EXPECT_POISONED_O(10U << *GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(-10 >> *GetPoisonedO(0, __LINE__), __LINE__); EXPECT_POISONED_O(-10 << *GetPoisonedO(0, __LINE__), __LINE__); } template void MemCpyTest() { int ox = __LINE__; T *x = new T[N]; T *y = new T[N]; T *z = new T[N]; T *q = new T[N]; __msan_poison(x, N * sizeof(T)); __msan_set_origin(x, N * sizeof(T), ox); __msan_set_origin(y, N * sizeof(T), 777777); __msan_set_origin(z, N * sizeof(T), 888888); EXPECT_NOT_POISONED(x); memcpy(y, x, N * sizeof(T)); EXPECT_POISONED_O(y[0], ox); EXPECT_POISONED_O(y[N/2], ox); EXPECT_POISONED_O(y[N-1], ox); EXPECT_NOT_POISONED(x); void *res = mempcpy(q, x, N * sizeof(T)); ASSERT_EQ(q + N, res); EXPECT_POISONED_O(q[0], ox); EXPECT_POISONED_O(q[N/2], ox); EXPECT_POISONED_O(q[N-1], ox); EXPECT_NOT_POISONED(x); memmove(z, x, N * sizeof(T)); EXPECT_POISONED_O(z[0], ox); EXPECT_POISONED_O(z[N/2], ox); EXPECT_POISONED_O(z[N-1], ox); } TEST(MemorySanitizerOrigins, LargeMemCpy) { if (!TrackingOrigins()) return; MemCpyTest(); MemCpyTest(); } TEST(MemorySanitizerOrigins, SmallMemCpy) { if (!TrackingOrigins()) return; MemCpyTest(); MemCpyTest(); MemCpyTest(); } TEST(MemorySanitizerOrigins, Select) { if (!TrackingOrigins()) return; EXPECT_NOT_POISONED(g_one ? 1 : *GetPoisonedO(0, __LINE__)); EXPECT_POISONED_O(*GetPoisonedO(0, __LINE__), __LINE__); S4 x; break_optimization(&x); x = g_1 ? *GetPoisonedO(0, __LINE__) : 0; EXPECT_POISONED_O(g_1 ? *GetPoisonedO(0, __LINE__) : 1, __LINE__); EXPECT_POISONED_O(g_0 ? 1 : *GetPoisonedO(0, __LINE__), __LINE__); } NOINLINE int RetvalOriginTest(U4 origin) { int *a = new int; break_optimization(a); __msan_set_origin(a, sizeof(*a), origin); int res = *a; delete a; return res; } TEST(MemorySanitizerOrigins, Retval) { if (!TrackingOrigins()) return; EXPECT_POISONED_O(RetvalOriginTest(__LINE__), __LINE__); } NOINLINE void ParamOriginTest(int param, U4 origin) { EXPECT_POISONED_O(param, origin); } TEST(MemorySanitizerOrigins, Param) { if (!TrackingOrigins()) return; int *a = new int; U4 origin = __LINE__; break_optimization(a); __msan_set_origin(a, sizeof(*a), origin); ParamOriginTest(*a, origin); delete a; } TEST(MemorySanitizerOrigins, Invoke) { if (!TrackingOrigins()) return; StructWithDtor s; // Will cause the calls to become invokes. EXPECT_POISONED_O(RetvalOriginTest(__LINE__), __LINE__); } TEST(MemorySanitizerOrigins, strlen) { S8 alignment; break_optimization(&alignment); char x[4] = {'a', 'b', 0, 0}; __msan_poison(&x[2], 1); U4 origin = __LINE__; __msan_set_origin(x, sizeof(x), origin); EXPECT_UMR_O(volatile unsigned y = strlen(x), origin); } TEST(MemorySanitizerOrigins, wcslen) { wchar_t w[3] = {'a', 'b', 0}; U4 origin = __LINE__; __msan_set_origin(w, sizeof(w), origin); __msan_poison(&w[2], sizeof(wchar_t)); EXPECT_UMR_O(volatile unsigned y = wcslen(w), origin); } #if MSAN_HAS_M128 TEST(MemorySanitizerOrigins, StoreIntrinsic) { __m128 x, y; U4 origin = __LINE__; __msan_set_origin(&x, sizeof(x), origin); __msan_poison(&x, sizeof(x)); _mm_storeu_ps((float*)&y, x); EXPECT_POISONED_O(y, origin); } #endif NOINLINE void RecursiveMalloc(int depth) { static int count; count++; if ((count % (1024 * 1024)) == 0) printf("RecursiveMalloc: %d\n", count); int *x1 = new int; int *x2 = new int; break_optimization(x1); break_optimization(x2); if (depth > 0) { RecursiveMalloc(depth-1); RecursiveMalloc(depth-1); } delete x1; delete x2; } TEST(MemorySanitizer, Select) { int x; int volatile* p = &x; int z = *p ? 1 : 0; EXPECT_POISONED(z); } TEST(MemorySanitizer, SelectPartial) { // Precise instrumentation of select. // Some bits of the result do not depend on select condition, and must stay // initialized even if select condition is not. These are the bits that are // equal and initialized in both left and right select arguments. U4 x = 0xFFFFABCDU; U4 x_s = 0xFFFF0000U; __msan_partial_poison(&x, &x_s, sizeof(x)); U4 y = 0xAB00U; U1 cond = true; __msan_poison(&cond, sizeof(cond)); U4 z = cond ? x : y; __msan_print_shadow(&z, sizeof(z)); EXPECT_POISONED(z & 0xFFU); EXPECT_NOT_POISONED(z & 0xFF00U); EXPECT_POISONED(z & 0xFF0000U); EXPECT_POISONED(z & 0xFF000000U); EXPECT_EQ(0xAB00U, z & 0xFF00U); } TEST(MemorySanitizerStress, DISABLED_MallocStackTrace) { RecursiveMalloc(22); } TEST(MemorySanitizerAllocator, get_estimated_allocated_size) { size_t sizes[] = {0, 20, 5000, 1<<20}; for (size_t i = 0; i < sizeof(sizes) / sizeof(*sizes); ++i) { size_t alloc_size = __sanitizer_get_estimated_allocated_size(sizes[i]); EXPECT_EQ(alloc_size, sizes[i]); } } TEST(MemorySanitizerAllocator, get_allocated_size_and_ownership) { char *array = reinterpret_cast(malloc(100)); int *int_ptr = new int; EXPECT_TRUE(__sanitizer_get_ownership(array)); EXPECT_EQ(100U, __sanitizer_get_allocated_size(array)); EXPECT_TRUE(__sanitizer_get_ownership(int_ptr)); EXPECT_EQ(sizeof(*int_ptr), __sanitizer_get_allocated_size(int_ptr)); void *wild_addr = reinterpret_cast(0x1); EXPECT_FALSE(__sanitizer_get_ownership(wild_addr)); EXPECT_EQ(0U, __sanitizer_get_allocated_size(wild_addr)); EXPECT_FALSE(__sanitizer_get_ownership(array + 50)); EXPECT_EQ(0U, __sanitizer_get_allocated_size(array + 50)); // NULL is a valid argument for GetAllocatedSize but is not owned. EXPECT_FALSE(__sanitizer_get_ownership(NULL)); EXPECT_EQ(0U, __sanitizer_get_allocated_size(NULL)); free(array); EXPECT_FALSE(__sanitizer_get_ownership(array)); EXPECT_EQ(0U, __sanitizer_get_allocated_size(array)); delete int_ptr; } TEST(MemorySanitizer, MlockTest) { EXPECT_EQ(0, mlockall(MCL_CURRENT)); EXPECT_EQ(0, mlock((void*)0x12345, 0x5678)); EXPECT_EQ(0, munlockall()); EXPECT_EQ(0, munlock((void*)0x987, 0x654)); } // Test that LargeAllocator unpoisons memory before releasing it to the OS. TEST(MemorySanitizer, LargeAllocatorUnpoisonsOnFree) { void *p = malloc(1024 * 1024); free(p); typedef void *(*mmap_fn)(void *, size_t, int, int, int, off_t); mmap_fn real_mmap = (mmap_fn)dlsym(RTLD_NEXT, "mmap"); // Allocate the page that was released to the OS in free() with the real mmap, // bypassing the interceptor. char *q = (char *)real_mmap(p, 4096, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); ASSERT_NE((char *)0, q); ASSERT_TRUE(q <= p); ASSERT_TRUE(q + 4096 > p); EXPECT_NOT_POISONED(q[0]); EXPECT_NOT_POISONED(q[10]); EXPECT_NOT_POISONED(q[100]); munmap(q, 4096); } #if SANITIZER_TEST_HAS_MALLOC_USABLE_SIZE TEST(MemorySanitizer, MallocUsableSizeTest) { const size_t kArraySize = 100; char *array = Ident((char*)malloc(kArraySize)); int *int_ptr = Ident(new int); EXPECT_EQ(0U, malloc_usable_size(NULL)); EXPECT_EQ(kArraySize, malloc_usable_size(array)); EXPECT_EQ(sizeof(int), malloc_usable_size(int_ptr)); free(array); delete int_ptr; } #endif // SANITIZER_TEST_HAS_MALLOC_USABLE_SIZE Index: vendor/compiler-rt/dist/lib/profile/CMakeLists.txt =================================================================== --- vendor/compiler-rt/dist/lib/profile/CMakeLists.txt (revision 320536) +++ vendor/compiler-rt/dist/lib/profile/CMakeLists.txt (revision 320537) @@ -1,100 +1,101 @@ CHECK_CXX_SOURCE_COMPILES(" #ifdef _MSC_VER #include /* Workaround for PR19898. */ #include #endif int main() { #ifdef _MSC_VER volatile LONG val = 1; MemoryBarrier(); InterlockedCompareExchange(&val, 0, 1); InterlockedIncrement(&val); InterlockedDecrement(&val); #else volatile unsigned long val = 1; __sync_synchronize(); __sync_val_compare_and_swap(&val, 1, 0); __sync_add_and_fetch(&val, 1); __sync_sub_and_fetch(&val, 1); #endif return 0; } " COMPILER_RT_TARGET_HAS_ATOMICS) CHECK_CXX_SOURCE_COMPILES(" #if defined(__linux__) #include #endif #include int fd; int main() { struct flock s_flock; s_flock.l_type = F_WRLCK; fcntl(fd, F_SETLKW, &s_flock); return 0; } " COMPILER_RT_TARGET_HAS_FCNTL_LCK) add_compiler_rt_component(profile) set(PROFILE_SOURCES GCDAProfiling.c InstrProfiling.c InstrProfilingValue.c InstrProfilingBuffer.c InstrProfilingFile.c InstrProfilingMerge.c InstrProfilingMergeFile.c + InstrProfilingNameVar.c InstrProfilingWriter.c InstrProfilingPlatformDarwin.c InstrProfilingPlatformLinux.c InstrProfilingPlatformOther.c InstrProfilingRuntime.cc InstrProfilingUtil.c) if(WIN32) list(APPEND PROFILE_SOURCES WindowsMMap.c) endif() if(UNIX) set(EXTRA_FLAGS -fPIC -Wno-pedantic) endif() if(COMPILER_RT_TARGET_HAS_ATOMICS) set(EXTRA_FLAGS ${EXTRA_FLAGS} -DCOMPILER_RT_HAS_ATOMICS=1) endif() if(COMPILER_RT_TARGET_HAS_FCNTL_LCK) set(EXTRA_FLAGS ${EXTRA_FLAGS} -DCOMPILER_RT_HAS_FCNTL_LCK=1) endif() # This appears to be a C-only warning banning the use of locals in aggregate # initializers. All other compilers accept this, though. # nonstandard extension used : 'identifier' : cannot be initialized using address of automatic variable append_list_if(COMPILER_RT_HAS_WD4221_FLAG /wd4221 EXTRA_FLAGS) if(APPLE) add_compiler_rt_runtime(clang_rt.profile STATIC OS ${PROFILE_SUPPORTED_OS} ARCHS ${PROFILE_SUPPORTED_ARCH} CFLAGS ${EXTRA_FLAGS} SOURCES ${PROFILE_SOURCES} PARENT_TARGET profile) else() add_compiler_rt_runtime(clang_rt.profile STATIC ARCHS ${PROFILE_SUPPORTED_ARCH} CFLAGS ${EXTRA_FLAGS} SOURCES ${PROFILE_SOURCES} PARENT_TARGET profile) endif() Index: vendor/compiler-rt/dist/lib/profile/InstrProfiling.c =================================================================== --- vendor/compiler-rt/dist/lib/profile/InstrProfiling.c (revision 320536) +++ vendor/compiler-rt/dist/lib/profile/InstrProfiling.c (revision 320537) @@ -1,81 +1,79 @@ /*===- InstrProfiling.c - Support library for PGO instrumentation ---------===*\ |* |* The LLVM Compiler Infrastructure |* |* This file is distributed under the University of Illinois Open Source |* License. See LICENSE.TXT for details. |* \*===----------------------------------------------------------------------===*/ #include "InstrProfiling.h" #include "InstrProfilingInternal.h" #include #include #include #include #define INSTR_PROF_VALUE_PROF_DATA #include "InstrProfData.inc" COMPILER_RT_WEAK uint64_t INSTR_PROF_RAW_VERSION_VAR = INSTR_PROF_RAW_VERSION; -COMPILER_RT_WEAK char INSTR_PROF_PROFILE_NAME_VAR[1] = {0}; - COMPILER_RT_VISIBILITY uint64_t __llvm_profile_get_magic(void) { return sizeof(void *) == sizeof(uint64_t) ? (INSTR_PROF_RAW_MAGIC_64) : (INSTR_PROF_RAW_MAGIC_32); } static unsigned ProfileDumped = 0; COMPILER_RT_VISIBILITY unsigned lprofProfileDumped() { return ProfileDumped; } COMPILER_RT_VISIBILITY void lprofSetProfileDumped() { ProfileDumped = 1; } /* Return the number of bytes needed to add to SizeInBytes to make it * the result a multiple of 8. */ COMPILER_RT_VISIBILITY uint8_t __llvm_profile_get_num_padding_bytes(uint64_t SizeInBytes) { return 7 & (sizeof(uint64_t) - SizeInBytes % sizeof(uint64_t)); } COMPILER_RT_VISIBILITY uint64_t __llvm_profile_get_version(void) { return __llvm_profile_raw_version; } COMPILER_RT_VISIBILITY void __llvm_profile_reset_counters(void) { uint64_t *I = __llvm_profile_begin_counters(); uint64_t *E = __llvm_profile_end_counters(); memset(I, 0, sizeof(uint64_t) * (E - I)); const __llvm_profile_data *DataBegin = __llvm_profile_begin_data(); const __llvm_profile_data *DataEnd = __llvm_profile_end_data(); const __llvm_profile_data *DI; for (DI = DataBegin; DI < DataEnd; ++DI) { uint64_t CurrentVSiteCount = 0; uint32_t VKI, i; if (!DI->Values) continue; ValueProfNode **ValueCounters = (ValueProfNode **)DI->Values; for (VKI = IPVK_First; VKI <= IPVK_Last; ++VKI) CurrentVSiteCount += DI->NumValueSites[VKI]; for (i = 0; i < CurrentVSiteCount; ++i) { ValueProfNode *CurrentVNode = ValueCounters[i]; while (CurrentVNode) { CurrentVNode->Count = 0; CurrentVNode = CurrentVNode->Next; } } } ProfileDumped = 0; } Index: vendor/compiler-rt/dist/lib/profile/InstrProfilingBuffer.c =================================================================== --- vendor/compiler-rt/dist/lib/profile/InstrProfilingBuffer.c (revision 320536) +++ vendor/compiler-rt/dist/lib/profile/InstrProfilingBuffer.c (revision 320537) @@ -1,59 +1,68 @@ /*===- InstrProfilingBuffer.c - Write instrumentation to a memory buffer --===*\ |* |* The LLVM Compiler Infrastructure |* |* This file is distributed under the University of Illinois Open Source |* License. See LICENSE.TXT for details. |* \*===----------------------------------------------------------------------===*/ #include "InstrProfiling.h" #include "InstrProfilingInternal.h" COMPILER_RT_VISIBILITY uint64_t __llvm_profile_get_size_for_buffer(void) { const __llvm_profile_data *DataBegin = __llvm_profile_begin_data(); const __llvm_profile_data *DataEnd = __llvm_profile_end_data(); const uint64_t *CountersBegin = __llvm_profile_begin_counters(); const uint64_t *CountersEnd = __llvm_profile_end_counters(); const char *NamesBegin = __llvm_profile_begin_names(); const char *NamesEnd = __llvm_profile_end_names(); return __llvm_profile_get_size_for_buffer_internal( DataBegin, DataEnd, CountersBegin, CountersEnd, NamesBegin, NamesEnd); } COMPILER_RT_VISIBILITY uint64_t __llvm_profile_get_data_size(const __llvm_profile_data *Begin, const __llvm_profile_data *End) { intptr_t BeginI = (intptr_t)Begin, EndI = (intptr_t)End; return ((EndI + sizeof(__llvm_profile_data) - 1) - BeginI) / sizeof(__llvm_profile_data); } COMPILER_RT_VISIBILITY uint64_t __llvm_profile_get_size_for_buffer_internal( const __llvm_profile_data *DataBegin, const __llvm_profile_data *DataEnd, const uint64_t *CountersBegin, const uint64_t *CountersEnd, const char *NamesBegin, const char *NamesEnd) { /* Match logic in __llvm_profile_write_buffer(). */ const uint64_t NamesSize = (NamesEnd - NamesBegin) * sizeof(char); const uint8_t Padding = __llvm_profile_get_num_padding_bytes(NamesSize); return sizeof(__llvm_profile_header) + (__llvm_profile_get_data_size(DataBegin, DataEnd) * sizeof(__llvm_profile_data)) + (CountersEnd - CountersBegin) * sizeof(uint64_t) + NamesSize + Padding; } +COMPILER_RT_VISIBILITY +void initBufferWriter(ProfDataWriter *BufferWriter, char *Buffer) { + BufferWriter->Write = lprofBufferWriter; + BufferWriter->WriterCtx = Buffer; +} + COMPILER_RT_VISIBILITY int __llvm_profile_write_buffer(char *Buffer) { - return lprofWriteData(lprofBufferWriter, Buffer, 0); + ProfDataWriter BufferWriter; + initBufferWriter(&BufferWriter, Buffer); + return lprofWriteData(&BufferWriter, 0, 0); } COMPILER_RT_VISIBILITY int __llvm_profile_write_buffer_internal( char *Buffer, const __llvm_profile_data *DataBegin, const __llvm_profile_data *DataEnd, const uint64_t *CountersBegin, const uint64_t *CountersEnd, const char *NamesBegin, const char *NamesEnd) { - return lprofWriteDataImpl(lprofBufferWriter, Buffer, DataBegin, DataEnd, - CountersBegin, CountersEnd, 0, NamesBegin, - NamesEnd); + ProfDataWriter BufferWriter; + initBufferWriter(&BufferWriter, Buffer); + return lprofWriteDataImpl(&BufferWriter, DataBegin, DataEnd, CountersBegin, + CountersEnd, 0, NamesBegin, NamesEnd, 0); } Index: vendor/compiler-rt/dist/lib/profile/InstrProfilingFile.c =================================================================== --- vendor/compiler-rt/dist/lib/profile/InstrProfilingFile.c (revision 320536) +++ vendor/compiler-rt/dist/lib/profile/InstrProfilingFile.c (revision 320537) @@ -1,600 +1,620 @@ /*===- InstrProfilingFile.c - Write instrumentation to a file -------------===*\ |* |* The LLVM Compiler Infrastructure |* |* This file is distributed under the University of Illinois Open Source |* License. See LICENSE.TXT for details. |* \*===----------------------------------------------------------------------===*/ #include "InstrProfiling.h" #include "InstrProfilingInternal.h" #include "InstrProfilingUtil.h" #include #include #include #include #ifdef _MSC_VER /* For _alloca. */ #include #endif #if defined(_WIN32) #include "WindowsMMap.h" /* For _chsize_s */ #include #else #include #include #include #if defined(__linux__) #include #endif #endif /* From where is profile name specified. * The order the enumerators define their * precedence. Re-order them may lead to * runtime behavior change. */ typedef enum ProfileNameSpecifier { PNS_unknown = 0, PNS_default, PNS_command_line, PNS_environment, PNS_runtime_api } ProfileNameSpecifier; static const char *getPNSStr(ProfileNameSpecifier PNS) { switch (PNS) { case PNS_default: return "default setting"; case PNS_command_line: return "command line"; case PNS_environment: return "environment variable"; case PNS_runtime_api: return "runtime API"; default: return "Unknown"; } } #define MAX_PID_SIZE 16 /* Data structure holding the result of parsed filename pattern. */ typedef struct lprofFilename { /* File name string possibly with %p or %h specifiers. */ const char *FilenamePat; /* A flag indicating if FilenamePat's memory is allocated * by runtime. */ unsigned OwnsFilenamePat; const char *ProfilePathPrefix; char PidChars[MAX_PID_SIZE]; char Hostname[COMPILER_RT_MAX_HOSTLEN]; unsigned NumPids; unsigned NumHosts; /* When in-process merging is enabled, this parameter specifies * the total number of profile data files shared by all the processes * spawned from the same binary. By default the value is 1. If merging * is not enabled, its value should be 0. This parameter is specified * by the %[0-9]m specifier. For instance %2m enables merging using * 2 profile data files. %1m is equivalent to %m. Also %m specifier * can only appear once at the end of the name pattern. */ unsigned MergePoolSize; ProfileNameSpecifier PNS; } lprofFilename; COMPILER_RT_WEAK lprofFilename lprofCurFilename = {0, 0, 0, {0}, {0}, 0, 0, 0, PNS_unknown}; int getpid(void); static int getCurFilenameLength(); static const char *getCurFilename(char *FilenameBuf); static unsigned doMerging() { return lprofCurFilename.MergePoolSize; } /* Return 1 if there is an error, otherwise return 0. */ -static uint32_t fileWriter(ProfDataIOVec *IOVecs, uint32_t NumIOVecs, - void **WriterCtx) { +static uint32_t fileWriter(ProfDataWriter *This, ProfDataIOVec *IOVecs, + uint32_t NumIOVecs) { uint32_t I; - FILE *File = (FILE *)*WriterCtx; + FILE *File = (FILE *)This->WriterCtx; for (I = 0; I < NumIOVecs; I++) { - if (fwrite(IOVecs[I].Data, IOVecs[I].ElmSize, IOVecs[I].NumElm, File) != - IOVecs[I].NumElm) - return 1; + if (IOVecs[I].Data) { + if (fwrite(IOVecs[I].Data, IOVecs[I].ElmSize, IOVecs[I].NumElm, File) != + IOVecs[I].NumElm) + return 1; + } else { + if (fseek(File, IOVecs[I].ElmSize * IOVecs[I].NumElm, SEEK_CUR) == -1) + return 1; + } } return 0; } +static void initFileWriter(ProfDataWriter *This, FILE *File) { + This->Write = fileWriter; + This->WriterCtx = File; +} + COMPILER_RT_VISIBILITY ProfBufferIO * lprofCreateBufferIOInternal(void *File, uint32_t BufferSz) { FreeHook = &free; DynamicBufferIOBuffer = (uint8_t *)calloc(BufferSz, 1); VPBufferSize = BufferSz; - return lprofCreateBufferIO(fileWriter, File); + ProfDataWriter *fileWriter = + (ProfDataWriter *)calloc(sizeof(ProfDataWriter), 1); + initFileWriter(fileWriter, File); + ProfBufferIO *IO = lprofCreateBufferIO(fileWriter); + IO->OwnFileWriter = 1; + return IO; } static void setupIOBuffer() { const char *BufferSzStr = 0; BufferSzStr = getenv("LLVM_VP_BUFFER_SIZE"); if (BufferSzStr && BufferSzStr[0]) { VPBufferSize = atoi(BufferSzStr); DynamicBufferIOBuffer = (uint8_t *)calloc(VPBufferSize, 1); } } /* Read profile data in \c ProfileFile and merge with in-memory profile counters. Returns -1 if there is fatal error, otheriwse - 0 is returned. + 0 is returned. Returning 0 does not mean merge is actually + performed. If merge is actually done, *MergeDone is set to 1. */ -static int doProfileMerging(FILE *ProfileFile) { +static int doProfileMerging(FILE *ProfileFile, int *MergeDone) { uint64_t ProfileFileSize; char *ProfileBuffer; if (fseek(ProfileFile, 0L, SEEK_END) == -1) { PROF_ERR("Unable to merge profile data, unable to get size: %s\n", strerror(errno)); return -1; } ProfileFileSize = ftell(ProfileFile); /* Restore file offset. */ if (fseek(ProfileFile, 0L, SEEK_SET) == -1) { PROF_ERR("Unable to merge profile data, unable to rewind: %s\n", strerror(errno)); return -1; } /* Nothing to merge. */ if (ProfileFileSize < sizeof(__llvm_profile_header)) { if (ProfileFileSize) PROF_WARN("Unable to merge profile data: %s\n", "source profile file is too small."); return 0; } ProfileBuffer = mmap(NULL, ProfileFileSize, PROT_READ, MAP_SHARED | MAP_FILE, fileno(ProfileFile), 0); if (ProfileBuffer == MAP_FAILED) { PROF_ERR("Unable to merge profile data, mmap failed: %s\n", strerror(errno)); return -1; } if (__llvm_profile_check_compatibility(ProfileBuffer, ProfileFileSize)) { (void)munmap(ProfileBuffer, ProfileFileSize); PROF_WARN("Unable to merge profile data: %s\n", "source profile file is not compatible."); return 0; } /* Now start merging */ __llvm_profile_merge_from_buffer(ProfileBuffer, ProfileFileSize); (void)munmap(ProfileBuffer, ProfileFileSize); + *MergeDone = 1; + return 0; } /* Create the directory holding the file, if needed. */ static void createProfileDir(const char *Filename) { size_t Length = strlen(Filename); if (lprofFindFirstDirSeparator(Filename)) { char *Copy = (char *)COMPILER_RT_ALLOCA(Length + 1); strncpy(Copy, Filename, Length + 1); __llvm_profile_recursive_mkdir(Copy); } } /* Open the profile data for merging. It opens the file in r+b mode with * file locking. If the file has content which is compatible with the * current process, it also reads in the profile data in the file and merge * it with in-memory counters. After the profile data is merged in memory, * the original profile data is truncated and gets ready for the profile * dumper. With profile merging enabled, each executable as well as any of * its instrumented shared libraries dump profile data into their own data file. */ -static FILE *openFileForMerging(const char *ProfileFileName) { +static FILE *openFileForMerging(const char *ProfileFileName, int *MergeDone) { FILE *ProfileFile; int rc; createProfileDir(ProfileFileName); ProfileFile = lprofOpenFileEx(ProfileFileName); if (!ProfileFile) return NULL; - rc = doProfileMerging(ProfileFile); - if (rc || COMPILER_RT_FTRUNCATE(ProfileFile, 0L) || + rc = doProfileMerging(ProfileFile, MergeDone); + if (rc || (!*MergeDone && COMPILER_RT_FTRUNCATE(ProfileFile, 0L)) || fseek(ProfileFile, 0L, SEEK_SET) == -1) { PROF_ERR("Profile Merging of file %s failed: %s\n", ProfileFileName, strerror(errno)); fclose(ProfileFile); return NULL; } - fseek(ProfileFile, 0L, SEEK_SET); return ProfileFile; } /* Write profile data to file \c OutputName. */ static int writeFile(const char *OutputName) { int RetVal; FILE *OutputFile; + int MergeDone = 0; if (!doMerging()) OutputFile = fopen(OutputName, "ab"); else - OutputFile = openFileForMerging(OutputName); + OutputFile = openFileForMerging(OutputName, &MergeDone); if (!OutputFile) return -1; FreeHook = &free; setupIOBuffer(); - RetVal = lprofWriteData(fileWriter, OutputFile, lprofGetVPDataReader()); + ProfDataWriter fileWriter; + initFileWriter(&fileWriter, OutputFile); + RetVal = lprofWriteData(&fileWriter, lprofGetVPDataReader(), MergeDone); fclose(OutputFile); return RetVal; } static void truncateCurrentFile(void) { const char *Filename; char *FilenameBuf; FILE *File; int Length; Length = getCurFilenameLength(); FilenameBuf = (char *)COMPILER_RT_ALLOCA(Length + 1); Filename = getCurFilename(FilenameBuf); if (!Filename) return; /* By pass file truncation to allow online raw profile * merging. */ if (lprofCurFilename.MergePoolSize) return; createProfileDir(Filename); /* Truncate the file. Later we'll reopen and append. */ File = fopen(Filename, "w"); if (!File) return; fclose(File); } static const char *DefaultProfileName = "default.profraw"; static void resetFilenameToDefault(void) { if (lprofCurFilename.FilenamePat && lprofCurFilename.OwnsFilenamePat) { free((void *)lprofCurFilename.FilenamePat); } memset(&lprofCurFilename, 0, sizeof(lprofCurFilename)); lprofCurFilename.FilenamePat = DefaultProfileName; lprofCurFilename.PNS = PNS_default; } static int containsMergeSpecifier(const char *FilenamePat, int I) { return (FilenamePat[I] == 'm' || (FilenamePat[I] >= '1' && FilenamePat[I] <= '9' && /* If FilenamePat[I] is not '\0', the next byte is guaranteed * to be in-bound as the string is null terminated. */ FilenamePat[I + 1] == 'm')); } /* Parses the pattern string \p FilenamePat and stores the result to * lprofcurFilename structure. */ static int parseFilenamePattern(const char *FilenamePat, unsigned CopyFilenamePat) { int NumPids = 0, NumHosts = 0, I; char *PidChars = &lprofCurFilename.PidChars[0]; char *Hostname = &lprofCurFilename.Hostname[0]; int MergingEnabled = 0; /* Clean up cached prefix. */ if (lprofCurFilename.ProfilePathPrefix) free((void *)lprofCurFilename.ProfilePathPrefix); memset(&lprofCurFilename, 0, sizeof(lprofCurFilename)); if (lprofCurFilename.FilenamePat && lprofCurFilename.OwnsFilenamePat) { free((void *)lprofCurFilename.FilenamePat); } if (!CopyFilenamePat) lprofCurFilename.FilenamePat = FilenamePat; else { lprofCurFilename.FilenamePat = strdup(FilenamePat); lprofCurFilename.OwnsFilenamePat = 1; } /* Check the filename for "%p", which indicates a pid-substitution. */ for (I = 0; FilenamePat[I]; ++I) if (FilenamePat[I] == '%') { if (FilenamePat[++I] == 'p') { if (!NumPids++) { if (snprintf(PidChars, MAX_PID_SIZE, "%d", getpid()) <= 0) { PROF_WARN("Unable to get pid for filename pattern %s. Using the " "default name.", FilenamePat); return -1; } } } else if (FilenamePat[I] == 'h') { if (!NumHosts++) if (COMPILER_RT_GETHOSTNAME(Hostname, COMPILER_RT_MAX_HOSTLEN)) { PROF_WARN("Unable to get hostname for filename pattern %s. Using " "the default name.", FilenamePat); return -1; } } else if (containsMergeSpecifier(FilenamePat, I)) { if (MergingEnabled) { PROF_WARN("%%m specifier can only be specified once in %s.\n", FilenamePat); return -1; } MergingEnabled = 1; if (FilenamePat[I] == 'm') lprofCurFilename.MergePoolSize = 1; else { lprofCurFilename.MergePoolSize = FilenamePat[I] - '0'; I++; /* advance to 'm' */ } } } lprofCurFilename.NumPids = NumPids; lprofCurFilename.NumHosts = NumHosts; return 0; } static void parseAndSetFilename(const char *FilenamePat, ProfileNameSpecifier PNS, unsigned CopyFilenamePat) { const char *OldFilenamePat = lprofCurFilename.FilenamePat; ProfileNameSpecifier OldPNS = lprofCurFilename.PNS; if (PNS < OldPNS) return; if (!FilenamePat) FilenamePat = DefaultProfileName; if (OldFilenamePat && !strcmp(OldFilenamePat, FilenamePat)) { lprofCurFilename.PNS = PNS; return; } /* When PNS >= OldPNS, the last one wins. */ if (!FilenamePat || parseFilenamePattern(FilenamePat, CopyFilenamePat)) resetFilenameToDefault(); lprofCurFilename.PNS = PNS; if (!OldFilenamePat) { if (getenv("LLVM_PROFILE_VERBOSE")) PROF_NOTE("Set profile file path to \"%s\" via %s.\n", lprofCurFilename.FilenamePat, getPNSStr(PNS)); } else { if (getenv("LLVM_PROFILE_VERBOSE")) PROF_NOTE("Override old profile path \"%s\" via %s to \"%s\" via %s.\n", OldFilenamePat, getPNSStr(OldPNS), lprofCurFilename.FilenamePat, getPNSStr(PNS)); } truncateCurrentFile(); } /* Return buffer length that is required to store the current profile * filename with PID and hostname substitutions. */ /* The length to hold uint64_t followed by 2 digit pool id including '_' */ #define SIGLEN 24 static int getCurFilenameLength() { int Len; if (!lprofCurFilename.FilenamePat || !lprofCurFilename.FilenamePat[0]) return 0; if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts || lprofCurFilename.MergePoolSize)) return strlen(lprofCurFilename.FilenamePat); Len = strlen(lprofCurFilename.FilenamePat) + lprofCurFilename.NumPids * (strlen(lprofCurFilename.PidChars) - 2) + lprofCurFilename.NumHosts * (strlen(lprofCurFilename.Hostname) - 2); if (lprofCurFilename.MergePoolSize) Len += SIGLEN; return Len; } /* Return the pointer to the current profile file name (after substituting * PIDs and Hostnames in filename pattern. \p FilenameBuf is the buffer * to store the resulting filename. If no substitution is needed, the * current filename pattern string is directly returned. */ static const char *getCurFilename(char *FilenameBuf) { int I, J, PidLength, HostNameLength; const char *FilenamePat = lprofCurFilename.FilenamePat; if (!lprofCurFilename.FilenamePat || !lprofCurFilename.FilenamePat[0]) return 0; if (!(lprofCurFilename.NumPids || lprofCurFilename.NumHosts || lprofCurFilename.MergePoolSize)) return lprofCurFilename.FilenamePat; PidLength = strlen(lprofCurFilename.PidChars); HostNameLength = strlen(lprofCurFilename.Hostname); /* Construct the new filename. */ for (I = 0, J = 0; FilenamePat[I]; ++I) if (FilenamePat[I] == '%') { if (FilenamePat[++I] == 'p') { memcpy(FilenameBuf + J, lprofCurFilename.PidChars, PidLength); J += PidLength; } else if (FilenamePat[I] == 'h') { memcpy(FilenameBuf + J, lprofCurFilename.Hostname, HostNameLength); J += HostNameLength; } else if (containsMergeSpecifier(FilenamePat, I)) { char LoadModuleSignature[SIGLEN]; int S; int ProfilePoolId = getpid() % lprofCurFilename.MergePoolSize; S = snprintf(LoadModuleSignature, SIGLEN, "%" PRIu64 "_%d", lprofGetLoadModuleSignature(), ProfilePoolId); if (S == -1 || S > SIGLEN) S = SIGLEN; memcpy(FilenameBuf + J, LoadModuleSignature, S); J += S; if (FilenamePat[I] != 'm') I++; } /* Drop any unknown substitutions. */ } else FilenameBuf[J++] = FilenamePat[I]; FilenameBuf[J] = 0; return FilenameBuf; } /* Returns the pointer to the environment variable * string. Returns null if the env var is not set. */ static const char *getFilenamePatFromEnv(void) { const char *Filename = getenv("LLVM_PROFILE_FILE"); if (!Filename || !Filename[0]) return 0; return Filename; } COMPILER_RT_VISIBILITY const char *__llvm_profile_get_path_prefix(void) { int Length; char *FilenameBuf, *Prefix; const char *Filename, *PrefixEnd; if (lprofCurFilename.ProfilePathPrefix) return lprofCurFilename.ProfilePathPrefix; Length = getCurFilenameLength(); FilenameBuf = (char *)COMPILER_RT_ALLOCA(Length + 1); Filename = getCurFilename(FilenameBuf); if (!Filename) return "\0"; PrefixEnd = lprofFindLastDirSeparator(Filename); if (!PrefixEnd) return "\0"; Length = PrefixEnd - Filename + 1; Prefix = (char *)malloc(Length + 1); if (!Prefix) { PROF_ERR("Failed to %s\n", "allocate memory."); return "\0"; } memcpy(Prefix, Filename, Length); Prefix[Length] = '\0'; lprofCurFilename.ProfilePathPrefix = Prefix; return Prefix; } /* This method is invoked by the runtime initialization hook * InstrProfilingRuntime.o if it is linked in. Both user specified * profile path via -fprofile-instr-generate= and LLVM_PROFILE_FILE * environment variable can override this default value. */ COMPILER_RT_VISIBILITY void __llvm_profile_initialize_file(void) { const char *EnvFilenamePat; const char *SelectedPat = NULL; ProfileNameSpecifier PNS = PNS_unknown; int hasCommandLineOverrider = (INSTR_PROF_PROFILE_NAME_VAR[0] != 0); EnvFilenamePat = getFilenamePatFromEnv(); if (EnvFilenamePat) { SelectedPat = EnvFilenamePat; PNS = PNS_environment; } else if (hasCommandLineOverrider) { SelectedPat = INSTR_PROF_PROFILE_NAME_VAR; PNS = PNS_command_line; } else { SelectedPat = NULL; PNS = PNS_default; } parseAndSetFilename(SelectedPat, PNS, 0); } /* This API is directly called by the user application code. It has the * highest precedence compared with LLVM_PROFILE_FILE environment variable * and command line option -fprofile-instr-generate=. */ COMPILER_RT_VISIBILITY void __llvm_profile_set_filename(const char *FilenamePat) { parseAndSetFilename(FilenamePat, PNS_runtime_api, 1); } /* The public API for writing profile data into the file with name * set by previous calls to __llvm_profile_set_filename or * __llvm_profile_override_default_filename or * __llvm_profile_initialize_file. */ COMPILER_RT_VISIBILITY int __llvm_profile_write_file(void) { int rc, Length; const char *Filename; char *FilenameBuf; int PDeathSig = 0; if (lprofProfileDumped()) { PROF_NOTE("Profile data not written to file: %s.\n", "already written"); return 0; } Length = getCurFilenameLength(); FilenameBuf = (char *)COMPILER_RT_ALLOCA(Length + 1); Filename = getCurFilename(FilenameBuf); /* Check the filename. */ if (!Filename) { PROF_ERR("Failed to write file : %s\n", "Filename not set"); return -1; } /* Check if there is llvm/runtime version mismatch. */ if (GET_VERSION(__llvm_profile_get_version()) != INSTR_PROF_RAW_VERSION) { PROF_ERR("Runtime and instrumentation version mismatch : " "expected %d, but get %d\n", INSTR_PROF_RAW_VERSION, (int)GET_VERSION(__llvm_profile_get_version())); return -1; } // Temporarily suspend getting SIGKILL when the parent exits. PDeathSig = lprofSuspendSigKill(); /* Write profile data to the file. */ rc = writeFile(Filename); if (rc) PROF_ERR("Failed to write file \"%s\": %s\n", Filename, strerror(errno)); // Restore SIGKILL. if (PDeathSig == 1) lprofRestoreSigKill(); return rc; } COMPILER_RT_VISIBILITY int __llvm_profile_dump(void) { if (!doMerging()) PROF_WARN("Later invocation of __llvm_profile_dump can lead to clobbering " " of previously dumped profile data : %s. Either use %%m " "in profile name or change profile name before dumping.\n", "online profile merging is not on"); int rc = __llvm_profile_write_file(); lprofSetProfileDumped(); return rc; } static void writeFileWithoutReturn(void) { __llvm_profile_write_file(); } COMPILER_RT_VISIBILITY int __llvm_profile_register_write_file_atexit(void) { static int HasBeenRegistered = 0; if (HasBeenRegistered) return 0; lprofSetupValueProfiler(); HasBeenRegistered = 1; return atexit(writeFileWithoutReturn); } Index: vendor/compiler-rt/dist/lib/profile/InstrProfilingInternal.h =================================================================== --- vendor/compiler-rt/dist/lib/profile/InstrProfilingInternal.h (revision 320536) +++ vendor/compiler-rt/dist/lib/profile/InstrProfilingInternal.h (revision 320537) @@ -1,182 +1,189 @@ /*===- InstrProfiling.h- Support library for PGO instrumentation ----------===*\ |* |* The LLVM Compiler Infrastructure |* |* This file is distributed under the University of Illinois Open Source |* License. See LICENSE.TXT for details. |* \*===----------------------------------------------------------------------===*/ #ifndef PROFILE_INSTRPROFILING_INTERNALH_ #define PROFILE_INSTRPROFILING_INTERNALH_ #include "InstrProfiling.h" #include "stddef.h" /*! * \brief Write instrumentation data to the given buffer, given explicit * pointers to the live data in memory. This function is probably not what you * want. Use __llvm_profile_get_size_for_buffer instead. Use this function if * your program has a custom memory layout. */ uint64_t __llvm_profile_get_size_for_buffer_internal( const __llvm_profile_data *DataBegin, const __llvm_profile_data *DataEnd, const uint64_t *CountersBegin, const uint64_t *CountersEnd, const char *NamesBegin, const char *NamesEnd); /*! * \brief Write instrumentation data to the given buffer, given explicit * pointers to the live data in memory. This function is probably not what you * want. Use __llvm_profile_write_buffer instead. Use this function if your * program has a custom memory layout. * * \pre \c Buffer is the start of a buffer at least as big as \a * __llvm_profile_get_size_for_buffer_internal(). */ int __llvm_profile_write_buffer_internal( char *Buffer, const __llvm_profile_data *DataBegin, const __llvm_profile_data *DataEnd, const uint64_t *CountersBegin, const uint64_t *CountersEnd, const char *NamesBegin, const char *NamesEnd); /*! * The data structure describing the data to be written by the * low level writer callback function. */ typedef struct ProfDataIOVec { const void *Data; size_t ElmSize; size_t NumElm; } ProfDataIOVec; -typedef uint32_t (*WriterCallback)(ProfDataIOVec *, uint32_t NumIOVecs, - void **WriterCtx); +struct ProfDataWriter; +typedef uint32_t (*WriterCallback)(struct ProfDataWriter *This, ProfDataIOVec *, + uint32_t NumIOVecs); +typedef struct ProfDataWriter { + WriterCallback Write; + void *WriterCtx; +} ProfDataWriter; + /*! * The data structure for buffered IO of profile data. */ typedef struct ProfBufferIO { - /* File handle. */ - void *File; - /* Low level IO callback. */ - WriterCallback FileWriter; + ProfDataWriter *FileWriter; + uint32_t OwnFileWriter; /* The start of the buffer. */ uint8_t *BufferStart; /* Total size of the buffer. */ uint32_t BufferSz; /* Current byte offset from the start of the buffer. */ uint32_t CurOffset; } ProfBufferIO; /* The creator interface used by testing. */ ProfBufferIO *lprofCreateBufferIOInternal(void *File, uint32_t BufferSz); /*! * This is the interface to create a handle for buffered IO. */ -ProfBufferIO *lprofCreateBufferIO(WriterCallback FileWriter, void *File); +ProfBufferIO *lprofCreateBufferIO(ProfDataWriter *FileWriter); /*! * The interface to destroy the bufferIO handle and reclaim * the memory. */ void lprofDeleteBufferIO(ProfBufferIO *BufferIO); /*! * This is the interface to write \c Data of \c Size bytes through * \c BufferIO. Returns 0 if successful, otherwise return -1. */ int lprofBufferIOWrite(ProfBufferIO *BufferIO, const uint8_t *Data, uint32_t Size); /*! * The interface to flush the remaining data in the buffer. * through the low level writer callback. */ int lprofBufferIOFlush(ProfBufferIO *BufferIO); /* The low level interface to write data into a buffer. It is used as the * callback by other high level writer methods such as buffered IO writer * and profile data writer. */ -uint32_t lprofBufferWriter(ProfDataIOVec *IOVecs, uint32_t NumIOVecs, - void **WriterCtx); +uint32_t lprofBufferWriter(ProfDataWriter *This, ProfDataIOVec *IOVecs, + uint32_t NumIOVecs); +void initBufferWriter(ProfDataWriter *BufferWriter, char *Buffer); struct ValueProfData; struct ValueProfRecord; struct InstrProfValueData; struct ValueProfNode; /*! * The class that defines a set of methods to read value profile * data for streaming/serialization from the instrumentation runtime. */ typedef struct VPDataReaderType { uint32_t (*InitRTRecord)(const __llvm_profile_data *Data, uint8_t *SiteCountArray[]); /* Function pointer to getValueProfRecordHeader method. */ uint32_t (*GetValueProfRecordHeaderSize)(uint32_t NumSites); /* Function pointer to getFristValueProfRecord method. */ struct ValueProfRecord *(*GetFirstValueProfRecord)(struct ValueProfData *); /* Return the number of value data for site \p Site. */ uint32_t (*GetNumValueDataForSite)(uint32_t VK, uint32_t Site); /* Return the total size of the value profile data of the * current function. */ uint32_t (*GetValueProfDataSize)(void); /*! * Read the next \p N value data for site \p Site and store the data * in \p Dst. \p StartNode is the first value node to start with if * it is not null. The function returns the pointer to the value * node pointer to be used as the \p StartNode of the next batch reading. * If there is nothing left, it returns NULL. */ struct ValueProfNode *(*GetValueData)(uint32_t ValueKind, uint32_t Site, struct InstrProfValueData *Dst, struct ValueProfNode *StartNode, uint32_t N); } VPDataReaderType; -int lprofWriteData(WriterCallback Writer, void *WriterCtx, - VPDataReaderType *VPDataReader); -int lprofWriteDataImpl(WriterCallback Writer, void *WriterCtx, +/* Write profile data to destinitation. If SkipNameDataWrite is set to 1, + the name data is already in destintation, we just skip over it. */ +int lprofWriteData(ProfDataWriter *Writer, VPDataReaderType *VPDataReader, + int SkipNameDataWrite); +int lprofWriteDataImpl(ProfDataWriter *Writer, const __llvm_profile_data *DataBegin, const __llvm_profile_data *DataEnd, const uint64_t *CountersBegin, const uint64_t *CountersEnd, VPDataReaderType *VPDataReader, const char *NamesBegin, - const char *NamesEnd); + const char *NamesEnd, int SkipNameDataWrite); /* Merge value profile data pointed to by SrcValueProfData into * in-memory profile counters pointed by to DstData. */ void lprofMergeValueProfData(struct ValueProfData *SrcValueProfData, __llvm_profile_data *DstData); VPDataReaderType *lprofGetVPDataReader(); /* Internal interface used by test to reset the max number of * tracked values per value site to be \p MaxVals. */ void lprofSetMaxValsPerSite(uint32_t MaxVals); void lprofSetupValueProfiler(); /* Return the profile header 'signature' value associated with the current * executable or shared library. The signature value can be used to for * a profile name that is unique to this load module so that it does not * collide with profiles from other binaries. It also allows shared libraries * to dump merged profile data into its own profile file. */ uint64_t lprofGetLoadModuleSignature(); /* * Return non zero value if the profile data has already been * dumped to the file. */ unsigned lprofProfileDumped(); void lprofSetProfileDumped(); COMPILER_RT_VISIBILITY extern void (*FreeHook)(void *); COMPILER_RT_VISIBILITY extern uint8_t *DynamicBufferIOBuffer; COMPILER_RT_VISIBILITY extern uint32_t VPBufferSize; COMPILER_RT_VISIBILITY extern uint32_t VPMaxNumValsPerSite; /* Pointer to the start of static value counters to be allocted. */ COMPILER_RT_VISIBILITY extern ValueProfNode *CurrentVNode; COMPILER_RT_VISIBILITY extern ValueProfNode *EndVNode; extern void (*VPMergeHook)(struct ValueProfData *, __llvm_profile_data *); #endif Index: vendor/compiler-rt/dist/lib/profile/InstrProfilingNameVar.c =================================================================== --- vendor/compiler-rt/dist/lib/profile/InstrProfilingNameVar.c (nonexistent) +++ vendor/compiler-rt/dist/lib/profile/InstrProfilingNameVar.c (revision 320537) @@ -0,0 +1,18 @@ +//===- InstrProfilingNameVar.c - profile name variable setup --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "InstrProfiling.h" + +/* char __llvm_profile_filename[1] + * + * The runtime should only provide its own definition of this symbol when the + * user has not specified one. Set this up by moving the runtime's copy of this + * symbol to an object file within the archive. + */ +COMPILER_RT_WEAK char INSTR_PROF_PROFILE_NAME_VAR[1] = {0}; Property changes on: vendor/compiler-rt/dist/lib/profile/InstrProfilingNameVar.c ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/compiler-rt/dist/lib/profile/InstrProfilingWriter.c =================================================================== --- vendor/compiler-rt/dist/lib/profile/InstrProfilingWriter.c (revision 320536) +++ vendor/compiler-rt/dist/lib/profile/InstrProfilingWriter.c (revision 320537) @@ -1,281 +1,286 @@ /*===- InstrProfilingWriter.c - Write instrumentation to a file or buffer -===*\ |* |* The LLVM Compiler Infrastructure |* |* This file is distributed under the University of Illinois Open Source |* License. See LICENSE.TXT for details. |* \*===----------------------------------------------------------------------===*/ #include "InstrProfiling.h" #include "InstrProfilingInternal.h" #ifdef _MSC_VER /* For _alloca */ #include #endif #include #define INSTR_PROF_VALUE_PROF_DATA #include "InstrProfData.inc" COMPILER_RT_VISIBILITY void (*FreeHook)(void *) = NULL; static ProfBufferIO TheBufferIO; #define VP_BUFFER_SIZE 8 * 1024 static uint8_t BufferIOBuffer[VP_BUFFER_SIZE]; static InstrProfValueData VPDataArray[16]; static uint32_t VPDataArraySize = sizeof(VPDataArray) / sizeof(*VPDataArray); COMPILER_RT_VISIBILITY uint8_t *DynamicBufferIOBuffer = 0; COMPILER_RT_VISIBILITY uint32_t VPBufferSize = 0; /* The buffer writer is reponsponsible in keeping writer state * across the call. */ -COMPILER_RT_VISIBILITY uint32_t lprofBufferWriter(ProfDataIOVec *IOVecs, - uint32_t NumIOVecs, - void **WriterCtx) { +COMPILER_RT_VISIBILITY uint32_t lprofBufferWriter(ProfDataWriter *This, + ProfDataIOVec *IOVecs, + uint32_t NumIOVecs) { uint32_t I; - char **Buffer = (char **)WriterCtx; + char **Buffer = (char **)&This->WriterCtx; for (I = 0; I < NumIOVecs; I++) { size_t Length = IOVecs[I].ElmSize * IOVecs[I].NumElm; - memcpy(*Buffer, IOVecs[I].Data, Length); + if (IOVecs[I].Data) + memcpy(*Buffer, IOVecs[I].Data, Length); *Buffer += Length; } return 0; } -static void llvmInitBufferIO(ProfBufferIO *BufferIO, WriterCallback FileWriter, - void *File, uint8_t *Buffer, uint32_t BufferSz) { - BufferIO->File = File; +static void llvmInitBufferIO(ProfBufferIO *BufferIO, ProfDataWriter *FileWriter, + uint8_t *Buffer, uint32_t BufferSz) { BufferIO->FileWriter = FileWriter; + BufferIO->OwnFileWriter = 0; BufferIO->BufferStart = Buffer; BufferIO->BufferSz = BufferSz; BufferIO->CurOffset = 0; } COMPILER_RT_VISIBILITY ProfBufferIO * -lprofCreateBufferIO(WriterCallback FileWriter, void *File) { +lprofCreateBufferIO(ProfDataWriter *FileWriter) { uint8_t *Buffer = DynamicBufferIOBuffer; uint32_t BufferSize = VPBufferSize; if (!Buffer) { Buffer = &BufferIOBuffer[0]; BufferSize = sizeof(BufferIOBuffer); } - llvmInitBufferIO(&TheBufferIO, FileWriter, File, Buffer, BufferSize); + llvmInitBufferIO(&TheBufferIO, FileWriter, Buffer, BufferSize); return &TheBufferIO; } COMPILER_RT_VISIBILITY void lprofDeleteBufferIO(ProfBufferIO *BufferIO) { + if (BufferIO->OwnFileWriter) + FreeHook(BufferIO->FileWriter); if (DynamicBufferIOBuffer) { FreeHook(DynamicBufferIOBuffer); DynamicBufferIOBuffer = 0; VPBufferSize = 0; } } COMPILER_RT_VISIBILITY int lprofBufferIOWrite(ProfBufferIO *BufferIO, const uint8_t *Data, uint32_t Size) { /* Buffer is not large enough, it is time to flush. */ if (Size + BufferIO->CurOffset > BufferIO->BufferSz) { if (lprofBufferIOFlush(BufferIO) != 0) return -1; } /* Special case, bypass the buffer completely. */ ProfDataIOVec IO[] = {{Data, sizeof(uint8_t), Size}}; if (Size > BufferIO->BufferSz) { - if (BufferIO->FileWriter(IO, 1, &BufferIO->File)) + if (BufferIO->FileWriter->Write(BufferIO->FileWriter, IO, 1)) return -1; } else { /* Write the data to buffer */ uint8_t *Buffer = BufferIO->BufferStart + BufferIO->CurOffset; - lprofBufferWriter(IO, 1, (void **)&Buffer); - BufferIO->CurOffset = Buffer - BufferIO->BufferStart; + ProfDataWriter BufferWriter; + initBufferWriter(&BufferWriter, (char *)Buffer); + lprofBufferWriter(&BufferWriter, IO, 1); + BufferIO->CurOffset = + (uint8_t *)BufferWriter.WriterCtx - BufferIO->BufferStart; } return 0; } COMPILER_RT_VISIBILITY int lprofBufferIOFlush(ProfBufferIO *BufferIO) { if (BufferIO->CurOffset) { ProfDataIOVec IO[] = { {BufferIO->BufferStart, sizeof(uint8_t), BufferIO->CurOffset}}; - if (BufferIO->FileWriter(IO, 1, &BufferIO->File)) + if (BufferIO->FileWriter->Write(BufferIO->FileWriter, IO, 1)) return -1; BufferIO->CurOffset = 0; } return 0; } /* Write out value profile data for function specified with \c Data. * The implementation does not use the method \c serializeValueProfData * which depends on dynamic memory allocation. In this implementation, * value profile data is written out to \c BufferIO piecemeal. */ static int writeOneValueProfData(ProfBufferIO *BufferIO, VPDataReaderType *VPDataReader, const __llvm_profile_data *Data) { unsigned I, NumValueKinds = 0; ValueProfData VPHeader; uint8_t *SiteCountArray[IPVK_Last + 1]; for (I = 0; I <= IPVK_Last; I++) { if (!Data->NumValueSites[I]) SiteCountArray[I] = 0; else { uint32_t Sz = VPDataReader->GetValueProfRecordHeaderSize(Data->NumValueSites[I]) - offsetof(ValueProfRecord, SiteCountArray); /* Only use alloca for this small byte array to avoid excessive * stack growth. */ SiteCountArray[I] = (uint8_t *)COMPILER_RT_ALLOCA(Sz); memset(SiteCountArray[I], 0, Sz); } } /* If NumValueKinds returned is 0, there is nothing to write, report success and return. This should match the raw profile reader's behavior. */ if (!(NumValueKinds = VPDataReader->InitRTRecord(Data, SiteCountArray))) return 0; /* First write the header structure. */ VPHeader.TotalSize = VPDataReader->GetValueProfDataSize(); VPHeader.NumValueKinds = NumValueKinds; if (lprofBufferIOWrite(BufferIO, (const uint8_t *)&VPHeader, sizeof(ValueProfData))) return -1; /* Make sure nothing else needs to be written before value profile * records. */ if ((void *)VPDataReader->GetFirstValueProfRecord(&VPHeader) != (void *)(&VPHeader + 1)) return -1; /* Write out the value profile record for each value kind * one by one. */ for (I = 0; I <= IPVK_Last; I++) { uint32_t J; ValueProfRecord RecordHeader; /* The size of the value prof record header without counting the * site count array .*/ uint32_t RecordHeaderSize = offsetof(ValueProfRecord, SiteCountArray); uint32_t SiteCountArraySize; if (!Data->NumValueSites[I]) continue; /* Write out the record header. */ RecordHeader.Kind = I; RecordHeader.NumValueSites = Data->NumValueSites[I]; if (lprofBufferIOWrite(BufferIO, (const uint8_t *)&RecordHeader, RecordHeaderSize)) return -1; /* Write out the site value count array including padding space. */ SiteCountArraySize = VPDataReader->GetValueProfRecordHeaderSize(Data->NumValueSites[I]) - RecordHeaderSize; if (lprofBufferIOWrite(BufferIO, SiteCountArray[I], SiteCountArraySize)) return -1; /* Write out the value profile data for each value site. */ for (J = 0; J < Data->NumValueSites[I]; J++) { uint32_t NRead, NRemain; ValueProfNode *NextStartNode = 0; NRemain = VPDataReader->GetNumValueDataForSite(I, J); if (!NRemain) continue; /* Read and write out value data in small chunks till it is done. */ do { NRead = (NRemain > VPDataArraySize ? VPDataArraySize : NRemain); NextStartNode = VPDataReader->GetValueData(I, /* ValueKind */ J, /* Site */ &VPDataArray[0], NextStartNode, NRead); if (lprofBufferIOWrite(BufferIO, (const uint8_t *)&VPDataArray[0], NRead * sizeof(InstrProfValueData))) return -1; NRemain -= NRead; } while (NRemain != 0); } } /* All done report success. */ return 0; } -static int writeValueProfData(WriterCallback Writer, void *WriterCtx, +static int writeValueProfData(ProfDataWriter *Writer, VPDataReaderType *VPDataReader, const __llvm_profile_data *DataBegin, const __llvm_profile_data *DataEnd) { ProfBufferIO *BufferIO; const __llvm_profile_data *DI = 0; if (!VPDataReader) return 0; - BufferIO = lprofCreateBufferIO(Writer, WriterCtx); + BufferIO = lprofCreateBufferIO(Writer); for (DI = DataBegin; DI < DataEnd; DI++) { if (writeOneValueProfData(BufferIO, VPDataReader, DI)) return -1; } if (lprofBufferIOFlush(BufferIO) != 0) return -1; lprofDeleteBufferIO(BufferIO); return 0; } -COMPILER_RT_VISIBILITY int lprofWriteData(WriterCallback Writer, - void *WriterCtx, - VPDataReaderType *VPDataReader) { +COMPILER_RT_VISIBILITY int lprofWriteData(ProfDataWriter *Writer, + VPDataReaderType *VPDataReader, + int SkipNameDataWrite) { /* Match logic in __llvm_profile_write_buffer(). */ const __llvm_profile_data *DataBegin = __llvm_profile_begin_data(); const __llvm_profile_data *DataEnd = __llvm_profile_end_data(); const uint64_t *CountersBegin = __llvm_profile_begin_counters(); const uint64_t *CountersEnd = __llvm_profile_end_counters(); const char *NamesBegin = __llvm_profile_begin_names(); const char *NamesEnd = __llvm_profile_end_names(); - return lprofWriteDataImpl(Writer, WriterCtx, DataBegin, DataEnd, - CountersBegin, CountersEnd, VPDataReader, - NamesBegin, NamesEnd); + return lprofWriteDataImpl(Writer, DataBegin, DataEnd, CountersBegin, + CountersEnd, VPDataReader, NamesBegin, NamesEnd, + SkipNameDataWrite); } COMPILER_RT_VISIBILITY int -lprofWriteDataImpl(WriterCallback Writer, void *WriterCtx, - const __llvm_profile_data *DataBegin, +lprofWriteDataImpl(ProfDataWriter *Writer, const __llvm_profile_data *DataBegin, const __llvm_profile_data *DataEnd, const uint64_t *CountersBegin, const uint64_t *CountersEnd, VPDataReaderType *VPDataReader, const char *NamesBegin, - const char *NamesEnd) { + const char *NamesEnd, int SkipNameDataWrite) { /* Calculate size of sections. */ const uint64_t DataSize = __llvm_profile_get_data_size(DataBegin, DataEnd); const uint64_t CountersSize = CountersEnd - CountersBegin; const uint64_t NamesSize = NamesEnd - NamesBegin; const uint64_t Padding = __llvm_profile_get_num_padding_bytes(NamesSize); /* Enough zeroes for padding. */ const char Zeroes[sizeof(uint64_t)] = {0}; /* Create the header. */ __llvm_profile_header Header; if (!DataSize) return 0; /* Initialize header structure. */ #define INSTR_PROF_RAW_HEADER(Type, Name, Init) Header.Name = Init; #include "InstrProfData.inc" /* Write the data. */ - ProfDataIOVec IOVec[] = {{&Header, sizeof(__llvm_profile_header), 1}, - {DataBegin, sizeof(__llvm_profile_data), DataSize}, - {CountersBegin, sizeof(uint64_t), CountersSize}, - {NamesBegin, sizeof(uint8_t), NamesSize}, - {Zeroes, sizeof(uint8_t), Padding}}; - if (Writer(IOVec, sizeof(IOVec) / sizeof(*IOVec), &WriterCtx)) + ProfDataIOVec IOVec[] = { + {&Header, sizeof(__llvm_profile_header), 1}, + {DataBegin, sizeof(__llvm_profile_data), DataSize}, + {CountersBegin, sizeof(uint64_t), CountersSize}, + {SkipNameDataWrite ? NULL : NamesBegin, sizeof(uint8_t), NamesSize}, + {Zeroes, sizeof(uint8_t), Padding}}; + if (Writer->Write(Writer, IOVec, sizeof(IOVec) / sizeof(*IOVec))) return -1; - return writeValueProfData(Writer, WriterCtx, VPDataReader, DataBegin, - DataEnd); + return writeValueProfData(Writer, VPDataReader, DataBegin, DataEnd); } Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_allocator.cc =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_allocator.cc (revision 320536) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_allocator.cc (revision 320537) @@ -1,258 +1,258 @@ //===-- sanitizer_allocator.cc --------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is shared between AddressSanitizer and ThreadSanitizer // run-time libraries. // This allocator is used inside run-times. //===----------------------------------------------------------------------===// #include "sanitizer_allocator.h" #include "sanitizer_allocator_internal.h" #include "sanitizer_atomic.h" #include "sanitizer_common.h" namespace __sanitizer { // ThreadSanitizer for Go uses libc malloc/free. #if SANITIZER_GO || defined(SANITIZER_USE_MALLOC) # if SANITIZER_LINUX && !SANITIZER_ANDROID extern "C" void *__libc_malloc(uptr size); # if !SANITIZER_GO extern "C" void *__libc_memalign(uptr alignment, uptr size); # endif extern "C" void *__libc_realloc(void *ptr, uptr size); extern "C" void __libc_free(void *ptr); # else # include # define __libc_malloc malloc # if !SANITIZER_GO static void *__libc_memalign(uptr alignment, uptr size) { void *p; uptr error = posix_memalign(&p, alignment, size); if (error) return nullptr; return p; } # endif # define __libc_realloc realloc # define __libc_free free # endif static void *RawInternalAlloc(uptr size, InternalAllocatorCache *cache, uptr alignment) { (void)cache; #if !SANITIZER_GO if (alignment == 0) return __libc_malloc(size); else return __libc_memalign(alignment, size); #else // Windows does not provide __libc_memalign/posix_memalign. It provides // __aligned_malloc, but the allocated blocks can't be passed to free, // they need to be passed to __aligned_free. InternalAlloc interface does // not account for such requirement. Alignemnt does not seem to be used // anywhere in runtime, so just call __libc_malloc for now. DCHECK_EQ(alignment, 0); return __libc_malloc(size); #endif } static void *RawInternalRealloc(void *ptr, uptr size, InternalAllocatorCache *cache) { (void)cache; return __libc_realloc(ptr, size); } static void RawInternalFree(void *ptr, InternalAllocatorCache *cache) { (void)cache; __libc_free(ptr); } InternalAllocator *internal_allocator() { return 0; } #else // SANITIZER_GO || defined(SANITIZER_USE_MALLOC) static ALIGNED(64) char internal_alloc_placeholder[sizeof(InternalAllocator)]; static atomic_uint8_t internal_allocator_initialized; static StaticSpinMutex internal_alloc_init_mu; static InternalAllocatorCache internal_allocator_cache; static StaticSpinMutex internal_allocator_cache_mu; InternalAllocator *internal_allocator() { InternalAllocator *internal_allocator_instance = reinterpret_cast(&internal_alloc_placeholder); if (atomic_load(&internal_allocator_initialized, memory_order_acquire) == 0) { SpinMutexLock l(&internal_alloc_init_mu); if (atomic_load(&internal_allocator_initialized, memory_order_relaxed) == 0) { internal_allocator_instance->Init(kReleaseToOSIntervalNever); atomic_store(&internal_allocator_initialized, 1, memory_order_release); } } return internal_allocator_instance; } static void *RawInternalAlloc(uptr size, InternalAllocatorCache *cache, uptr alignment) { if (alignment == 0) alignment = 8; if (cache == 0) { SpinMutexLock l(&internal_allocator_cache_mu); return internal_allocator()->Allocate(&internal_allocator_cache, size, alignment); } return internal_allocator()->Allocate(cache, size, alignment); } static void *RawInternalRealloc(void *ptr, uptr size, InternalAllocatorCache *cache) { uptr alignment = 8; if (cache == 0) { SpinMutexLock l(&internal_allocator_cache_mu); return internal_allocator()->Reallocate(&internal_allocator_cache, ptr, size, alignment); } return internal_allocator()->Reallocate(cache, ptr, size, alignment); } static void RawInternalFree(void *ptr, InternalAllocatorCache *cache) { if (!cache) { SpinMutexLock l(&internal_allocator_cache_mu); return internal_allocator()->Deallocate(&internal_allocator_cache, ptr); } internal_allocator()->Deallocate(cache, ptr); } #endif // SANITIZER_GO || defined(SANITIZER_USE_MALLOC) const u64 kBlockMagic = 0x6A6CB03ABCEBC041ull; void *InternalAlloc(uptr size, InternalAllocatorCache *cache, uptr alignment) { if (size + sizeof(u64) < size) return nullptr; void *p = RawInternalAlloc(size + sizeof(u64), cache, alignment); if (!p) return nullptr; ((u64*)p)[0] = kBlockMagic; return (char*)p + sizeof(u64); } void *InternalRealloc(void *addr, uptr size, InternalAllocatorCache *cache) { if (!addr) return InternalAlloc(size, cache); if (size + sizeof(u64) < size) return nullptr; addr = (char*)addr - sizeof(u64); size = size + sizeof(u64); CHECK_EQ(kBlockMagic, ((u64*)addr)[0]); void *p = RawInternalRealloc(addr, size, cache); if (!p) return nullptr; return (char*)p + sizeof(u64); } void *InternalCalloc(uptr count, uptr size, InternalAllocatorCache *cache) { - if (CallocShouldReturnNullDueToOverflow(count, size)) + if (CheckForCallocOverflow(count, size)) return InternalAllocator::FailureHandler::OnBadRequest(); void *p = InternalAlloc(count * size, cache); if (p) internal_memset(p, 0, count * size); return p; } void InternalFree(void *addr, InternalAllocatorCache *cache) { if (!addr) return; addr = (char*)addr - sizeof(u64); CHECK_EQ(kBlockMagic, ((u64*)addr)[0]); ((u64*)addr)[0] = 0; RawInternalFree(addr, cache); } // LowLevelAllocator static LowLevelAllocateCallback low_level_alloc_callback; void *LowLevelAllocator::Allocate(uptr size) { // Align allocation size. size = RoundUpTo(size, 8); if (allocated_end_ - allocated_current_ < (sptr)size) { uptr size_to_allocate = Max(size, GetPageSizeCached()); allocated_current_ = (char*)MmapOrDie(size_to_allocate, __func__); allocated_end_ = allocated_current_ + size_to_allocate; if (low_level_alloc_callback) { low_level_alloc_callback((uptr)allocated_current_, size_to_allocate); } } CHECK(allocated_end_ - allocated_current_ >= (sptr)size); void *res = allocated_current_; allocated_current_ += size; return res; } void SetLowLevelAllocateCallback(LowLevelAllocateCallback callback) { low_level_alloc_callback = callback; } -bool CallocShouldReturnNullDueToOverflow(uptr size, uptr n) { +bool CheckForCallocOverflow(uptr size, uptr n) { if (!size) return false; uptr max = (uptr)-1L; return (max / size) < n; } static atomic_uint8_t allocator_out_of_memory = {0}; static atomic_uint8_t allocator_may_return_null = {0}; bool IsAllocatorOutOfMemory() { return atomic_load_relaxed(&allocator_out_of_memory); } // Prints error message and kills the program. void NORETURN ReportAllocatorCannotReturnNull() { Report("%s's allocator is terminating the process instead of returning 0\n", SanitizerToolName); Report("If you don't like this behavior set allocator_may_return_null=1\n"); CHECK(0); Die(); } bool AllocatorMayReturnNull() { return atomic_load(&allocator_may_return_null, memory_order_relaxed); } void SetAllocatorMayReturnNull(bool may_return_null) { atomic_store(&allocator_may_return_null, may_return_null, memory_order_relaxed); } void *ReturnNullOrDieOnFailure::OnBadRequest() { if (AllocatorMayReturnNull()) return nullptr; ReportAllocatorCannotReturnNull(); } void *ReturnNullOrDieOnFailure::OnOOM() { atomic_store_relaxed(&allocator_out_of_memory, 1); if (AllocatorMayReturnNull()) return nullptr; ReportAllocatorCannotReturnNull(); } -void *DieOnFailure::OnBadRequest() { +void NORETURN *DieOnFailure::OnBadRequest() { ReportAllocatorCannotReturnNull(); } -void *DieOnFailure::OnOOM() { +void NORETURN *DieOnFailure::OnOOM() { atomic_store_relaxed(&allocator_out_of_memory, 1); ReportAllocatorCannotReturnNull(); } } // namespace __sanitizer Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_allocator.h =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_allocator.h (revision 320536) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_allocator.h (revision 320537) @@ -1,73 +1,75 @@ //===-- sanitizer_allocator.h -----------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Specialized memory allocator for ThreadSanitizer, MemorySanitizer, etc. // //===----------------------------------------------------------------------===// #ifndef SANITIZER_ALLOCATOR_H #define SANITIZER_ALLOCATOR_H #include "sanitizer_internal_defs.h" #include "sanitizer_common.h" #include "sanitizer_libc.h" #include "sanitizer_list.h" #include "sanitizer_mutex.h" #include "sanitizer_lfstack.h" #include "sanitizer_procmaps.h" namespace __sanitizer { // Since flags are immutable and allocator behavior can be changed at runtime // (unit tests or ASan on Android are some examples), allocator_may_return_null // flag value is cached here and can be altered later. bool AllocatorMayReturnNull(); void SetAllocatorMayReturnNull(bool may_return_null); // Allocator failure handling policies: // Implements AllocatorMayReturnNull policy, returns null when the flag is set, // dies otherwise. struct ReturnNullOrDieOnFailure { static void *OnBadRequest(); static void *OnOOM(); }; // Always dies on the failure. struct DieOnFailure { - static void *OnBadRequest(); - static void *OnOOM(); + static void NORETURN *OnBadRequest(); + static void NORETURN *OnOOM(); }; // Returns true if allocator detected OOM condition. Can be used to avoid memory // hungry operations. Set when AllocatorReturnNullOrDieOnOOM() is called. bool IsAllocatorOutOfMemory(); // Allocators call these callbacks on mmap/munmap. struct NoOpMapUnmapCallback { void OnMap(uptr p, uptr size) const { } void OnUnmap(uptr p, uptr size) const { } }; // Callback type for iterating over chunks. typedef void (*ForEachChunkCallback)(uptr chunk, void *arg); -// Returns true if calloc(size, n) should return 0 due to overflow in size*n. -bool CallocShouldReturnNullDueToOverflow(uptr size, uptr n); +// Returns true if calloc(size, n) call overflows on size*n calculation. +// The caller should "return POLICY::OnBadRequest();" where POLICY is the +// current allocator failure handling policy. +bool CheckForCallocOverflow(uptr size, uptr n); #include "sanitizer_allocator_size_class_map.h" #include "sanitizer_allocator_stats.h" #include "sanitizer_allocator_primary64.h" #include "sanitizer_allocator_bytemap.h" #include "sanitizer_allocator_primary32.h" #include "sanitizer_allocator_local_cache.h" #include "sanitizer_allocator_secondary.h" #include "sanitizer_allocator_combined.h" } // namespace __sanitizer #endif // SANITIZER_ALLOCATOR_H Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_allocator_local_cache.h =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_allocator_local_cache.h (revision 320536) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_allocator_local_cache.h (revision 320537) @@ -1,262 +1,266 @@ //===-- sanitizer_allocator_local_cache.h -----------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Part of the Sanitizer Allocator. // //===----------------------------------------------------------------------===// #ifndef SANITIZER_ALLOCATOR_H #error This file must be included inside sanitizer_allocator.h #endif // Objects of this type should be used as local caches for SizeClassAllocator64 // or SizeClassAllocator32. Since the typical use of this class is to have one // object per thread in TLS, is has to be POD. template struct SizeClassAllocatorLocalCache : SizeClassAllocator::AllocatorCache { }; // Cache used by SizeClassAllocator64. template struct SizeClassAllocator64LocalCache { typedef SizeClassAllocator Allocator; static const uptr kNumClasses = SizeClassAllocator::kNumClasses; typedef typename Allocator::SizeClassMapT SizeClassMap; typedef typename Allocator::CompactPtrT CompactPtrT; void Init(AllocatorGlobalStats *s) { stats_.Init(); if (s) s->Register(&stats_); } void Destroy(SizeClassAllocator *allocator, AllocatorGlobalStats *s) { Drain(allocator); if (s) s->Unregister(&stats_); } void *Allocate(SizeClassAllocator *allocator, uptr class_id) { CHECK_NE(class_id, 0UL); CHECK_LT(class_id, kNumClasses); PerClass *c = &per_class_[class_id]; - if (UNLIKELY(c->count == 0)) - Refill(c, allocator, class_id); + if (UNLIKELY(c->count == 0)) { + if (UNLIKELY(!Refill(c, allocator, class_id))) + return nullptr; + } stats_.Add(AllocatorStatAllocated, c->class_size); CHECK_GT(c->count, 0); CompactPtrT chunk = c->chunks[--c->count]; void *res = reinterpret_cast(allocator->CompactPtrToPointer( allocator->GetRegionBeginBySizeClass(class_id), chunk)); return res; } void Deallocate(SizeClassAllocator *allocator, uptr class_id, void *p) { CHECK_NE(class_id, 0UL); CHECK_LT(class_id, kNumClasses); // If the first allocator call on a new thread is a deallocation, then // max_count will be zero, leading to check failure. InitCache(); PerClass *c = &per_class_[class_id]; stats_.Sub(AllocatorStatAllocated, c->class_size); CHECK_NE(c->max_count, 0UL); if (UNLIKELY(c->count == c->max_count)) Drain(c, allocator, class_id, c->max_count / 2); CompactPtrT chunk = allocator->PointerToCompactPtr( allocator->GetRegionBeginBySizeClass(class_id), reinterpret_cast(p)); c->chunks[c->count++] = chunk; } void Drain(SizeClassAllocator *allocator) { for (uptr class_id = 0; class_id < kNumClasses; class_id++) { PerClass *c = &per_class_[class_id]; while (c->count > 0) Drain(c, allocator, class_id, c->count); } } // private: struct PerClass { u32 count; u32 max_count; uptr class_size; CompactPtrT chunks[2 * SizeClassMap::kMaxNumCachedHint]; }; PerClass per_class_[kNumClasses]; AllocatorStats stats_; void InitCache() { if (per_class_[1].max_count) return; for (uptr i = 0; i < kNumClasses; i++) { PerClass *c = &per_class_[i]; c->max_count = 2 * SizeClassMap::MaxCachedHint(i); c->class_size = Allocator::ClassIdToSize(i); } } - NOINLINE void Refill(PerClass *c, SizeClassAllocator *allocator, + NOINLINE bool Refill(PerClass *c, SizeClassAllocator *allocator, uptr class_id) { InitCache(); uptr num_requested_chunks = c->max_count / 2; - allocator->GetFromAllocator(&stats_, class_id, c->chunks, - num_requested_chunks); + if (UNLIKELY(!allocator->GetFromAllocator(&stats_, class_id, c->chunks, + num_requested_chunks))) + return false; c->count = num_requested_chunks; + return true; } NOINLINE void Drain(PerClass *c, SizeClassAllocator *allocator, uptr class_id, uptr count) { InitCache(); CHECK_GE(c->count, count); uptr first_idx_to_drain = c->count - count; c->count -= count; allocator->ReturnToAllocator(&stats_, class_id, &c->chunks[first_idx_to_drain], count); } }; // Cache used by SizeClassAllocator32. template struct SizeClassAllocator32LocalCache { typedef SizeClassAllocator Allocator; typedef typename Allocator::TransferBatch TransferBatch; static const uptr kNumClasses = SizeClassAllocator::kNumClasses; void Init(AllocatorGlobalStats *s) { stats_.Init(); if (s) s->Register(&stats_); } void Destroy(SizeClassAllocator *allocator, AllocatorGlobalStats *s) { Drain(allocator); if (s) s->Unregister(&stats_); } void *Allocate(SizeClassAllocator *allocator, uptr class_id) { CHECK_NE(class_id, 0UL); CHECK_LT(class_id, kNumClasses); PerClass *c = &per_class_[class_id]; if (UNLIKELY(c->count == 0)) { if (UNLIKELY(!Refill(allocator, class_id))) return nullptr; } stats_.Add(AllocatorStatAllocated, c->class_size); void *res = c->batch[--c->count]; PREFETCH(c->batch[c->count - 1]); return res; } void Deallocate(SizeClassAllocator *allocator, uptr class_id, void *p) { CHECK_NE(class_id, 0UL); CHECK_LT(class_id, kNumClasses); // If the first allocator call on a new thread is a deallocation, then // max_count will be zero, leading to check failure. InitCache(); PerClass *c = &per_class_[class_id]; stats_.Sub(AllocatorStatAllocated, c->class_size); CHECK_NE(c->max_count, 0UL); if (UNLIKELY(c->count == c->max_count)) Drain(allocator, class_id); c->batch[c->count++] = p; } void Drain(SizeClassAllocator *allocator) { for (uptr class_id = 0; class_id < kNumClasses; class_id++) { PerClass *c = &per_class_[class_id]; while (c->count > 0) Drain(allocator, class_id); } } // private: typedef typename SizeClassAllocator::SizeClassMapT SizeClassMap; struct PerClass { uptr count; uptr max_count; uptr class_size; uptr class_id_for_transfer_batch; void *batch[2 * TransferBatch::kMaxNumCached]; }; PerClass per_class_[kNumClasses]; AllocatorStats stats_; void InitCache() { if (per_class_[1].max_count) return; // TransferBatch class is declared in SizeClassAllocator. uptr class_id_for_transfer_batch = SizeClassMap::ClassID(sizeof(TransferBatch)); for (uptr i = 0; i < kNumClasses; i++) { PerClass *c = &per_class_[i]; uptr max_cached = TransferBatch::MaxCached(i); c->max_count = 2 * max_cached; c->class_size = Allocator::ClassIdToSize(i); // We transfer chunks between central and thread-local free lists in // batches. For small size classes we allocate batches separately. For // large size classes we may use one of the chunks to store the batch. // sizeof(TransferBatch) must be a power of 2 for more efficient // allocation. c->class_id_for_transfer_batch = (c->class_size < TransferBatch::AllocationSizeRequiredForNElements(max_cached)) ? class_id_for_transfer_batch : 0; } } // Returns a TransferBatch suitable for class_id. // For small size classes allocates the batch from the allocator. // For large size classes simply returns b. TransferBatch *CreateBatch(uptr class_id, SizeClassAllocator *allocator, TransferBatch *b) { if (uptr batch_class_id = per_class_[class_id].class_id_for_transfer_batch) return (TransferBatch*)Allocate(allocator, batch_class_id); return b; } // Destroys TransferBatch b. // For small size classes deallocates b to the allocator. // Does notthing for large size classes. void DestroyBatch(uptr class_id, SizeClassAllocator *allocator, TransferBatch *b) { if (uptr batch_class_id = per_class_[class_id].class_id_for_transfer_batch) Deallocate(allocator, batch_class_id, b); } NOINLINE bool Refill(SizeClassAllocator *allocator, uptr class_id) { InitCache(); PerClass *c = &per_class_[class_id]; TransferBatch *b = allocator->AllocateBatch(&stats_, this, class_id); if (UNLIKELY(!b)) return false; CHECK_GT(b->Count(), 0); b->CopyToArray(c->batch); c->count = b->Count(); DestroyBatch(class_id, allocator, b); return true; } NOINLINE void Drain(SizeClassAllocator *allocator, uptr class_id) { InitCache(); PerClass *c = &per_class_[class_id]; uptr cnt = Min(c->max_count / 2, c->count); uptr first_idx_to_drain = c->count - cnt; TransferBatch *b = CreateBatch( class_id, allocator, (TransferBatch *)c->batch[first_idx_to_drain]); // Failure to allocate a batch while releasing memory is non recoverable. // TODO(alekseys): Figure out how to do it without allocating a new batch. if (UNLIKELY(!b)) DieOnFailure::OnOOM(); b->SetFromArray(allocator->GetRegionBeginBySizeClass(class_id), &c->batch[first_idx_to_drain], cnt); c->count -= cnt; allocator->DeallocateBatch(&stats_, class_id, b); } }; Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_allocator_primary32.h =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_allocator_primary32.h (revision 320536) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_allocator_primary32.h (revision 320537) @@ -1,330 +1,329 @@ //===-- sanitizer_allocator_primary32.h -------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Part of the Sanitizer Allocator. // //===----------------------------------------------------------------------===// #ifndef SANITIZER_ALLOCATOR_H #error This file must be included inside sanitizer_allocator.h #endif template struct SizeClassAllocator32LocalCache; // SizeClassAllocator32 -- allocator for 32-bit address space. // This allocator can theoretically be used on 64-bit arch, but there it is less // efficient than SizeClassAllocator64. // // [kSpaceBeg, kSpaceBeg + kSpaceSize) is the range of addresses which can // be returned by MmapOrDie(). // // Region: // a result of a single call to MmapAlignedOrDieOnFatalError(kRegionSize, // kRegionSize). // Since the regions are aligned by kRegionSize, there are exactly // kNumPossibleRegions possible regions in the address space and so we keep // a ByteMap possible_regions to store the size classes of each Region. // 0 size class means the region is not used by the allocator. // // One Region is used to allocate chunks of a single size class. // A Region looks like this: // UserChunk1 .. UserChunkN MetaChunkN .. MetaChunk1 // // In order to avoid false sharing the objects of this class should be // chache-line aligned. struct SizeClassAllocator32FlagMasks { // Bit masks. enum { kRandomShuffleChunks = 1, }; }; template class SizeClassAllocator32 { public: static const uptr kSpaceBeg = Params::kSpaceBeg; static const u64 kSpaceSize = Params::kSpaceSize; static const uptr kMetadataSize = Params::kMetadataSize; typedef typename Params::SizeClassMap SizeClassMap; static const uptr kRegionSizeLog = Params::kRegionSizeLog; typedef typename Params::ByteMap ByteMap; typedef typename Params::MapUnmapCallback MapUnmapCallback; static const bool kRandomShuffleChunks = Params::kFlags & SizeClassAllocator32FlagMasks::kRandomShuffleChunks; struct TransferBatch { static const uptr kMaxNumCached = SizeClassMap::kMaxNumCachedHint - 2; void SetFromArray(uptr region_beg_unused, void *batch[], uptr count) { count_ = count; CHECK_LE(count_, kMaxNumCached); for (uptr i = 0; i < count; i++) batch_[i] = batch[i]; } uptr Count() const { return count_; } void Clear() { count_ = 0; } void Add(void *ptr) { batch_[count_++] = ptr; CHECK_LE(count_, kMaxNumCached); } void CopyToArray(void *to_batch[]) { for (uptr i = 0, n = Count(); i < n; i++) to_batch[i] = batch_[i]; } // How much memory do we need for a batch containing n elements. static uptr AllocationSizeRequiredForNElements(uptr n) { return sizeof(uptr) * 2 + sizeof(void *) * n; } static uptr MaxCached(uptr class_id) { return Min(kMaxNumCached, SizeClassMap::MaxCachedHint(class_id)); } TransferBatch *next; private: uptr count_; void *batch_[kMaxNumCached]; }; static const uptr kBatchSize = sizeof(TransferBatch); COMPILER_CHECK((kBatchSize & (kBatchSize - 1)) == 0); COMPILER_CHECK(sizeof(TransferBatch) == SizeClassMap::kMaxNumCachedHint * sizeof(uptr)); static uptr ClassIdToSize(uptr class_id) { return SizeClassMap::Size(class_id); } typedef SizeClassAllocator32 ThisT; typedef SizeClassAllocator32LocalCache AllocatorCache; void Init(s32 release_to_os_interval_ms) { possible_regions.TestOnlyInit(); internal_memset(size_class_info_array, 0, sizeof(size_class_info_array)); } s32 ReleaseToOSIntervalMs() const { return kReleaseToOSIntervalNever; } void SetReleaseToOSIntervalMs(s32 release_to_os_interval_ms) { // This is empty here. Currently only implemented in 64-bit allocator. } void *MapWithCallback(uptr size) { - size = RoundUpTo(size, GetPageSizeCached()); void *res = MmapOrDie(size, "SizeClassAllocator32"); MapUnmapCallback().OnMap((uptr)res, size); return res; } void UnmapWithCallback(uptr beg, uptr size) { MapUnmapCallback().OnUnmap(beg, size); UnmapOrDie(reinterpret_cast(beg), size); } static bool CanAllocate(uptr size, uptr alignment) { return size <= SizeClassMap::kMaxSize && alignment <= SizeClassMap::kMaxSize; } void *GetMetaData(const void *p) { CHECK(PointerIsMine(p)); uptr mem = reinterpret_cast(p); uptr beg = ComputeRegionBeg(mem); uptr size = ClassIdToSize(GetSizeClass(p)); u32 offset = mem - beg; uptr n = offset / (u32)size; // 32-bit division uptr meta = (beg + kRegionSize) - (n + 1) * kMetadataSize; return reinterpret_cast(meta); } NOINLINE TransferBatch *AllocateBatch(AllocatorStats *stat, AllocatorCache *c, uptr class_id) { CHECK_LT(class_id, kNumClasses); SizeClassInfo *sci = GetSizeClassInfo(class_id); SpinMutexLock l(&sci->mutex); if (sci->free_list.empty() && UNLIKELY(!PopulateFreeList(stat, c, sci, class_id))) return nullptr; CHECK(!sci->free_list.empty()); TransferBatch *b = sci->free_list.front(); sci->free_list.pop_front(); return b; } NOINLINE void DeallocateBatch(AllocatorStats *stat, uptr class_id, TransferBatch *b) { CHECK_LT(class_id, kNumClasses); SizeClassInfo *sci = GetSizeClassInfo(class_id); SpinMutexLock l(&sci->mutex); CHECK_GT(b->Count(), 0); sci->free_list.push_front(b); } uptr GetRegionBeginBySizeClass(uptr class_id) { return 0; } bool PointerIsMine(const void *p) { uptr mem = reinterpret_cast(p); if (mem < kSpaceBeg || mem >= kSpaceBeg + kSpaceSize) return false; return GetSizeClass(p) != 0; } uptr GetSizeClass(const void *p) { return possible_regions[ComputeRegionId(reinterpret_cast(p))]; } void *GetBlockBegin(const void *p) { CHECK(PointerIsMine(p)); uptr mem = reinterpret_cast(p); uptr beg = ComputeRegionBeg(mem); uptr size = ClassIdToSize(GetSizeClass(p)); u32 offset = mem - beg; u32 n = offset / (u32)size; // 32-bit division uptr res = beg + (n * (u32)size); return reinterpret_cast(res); } uptr GetActuallyAllocatedSize(void *p) { CHECK(PointerIsMine(p)); return ClassIdToSize(GetSizeClass(p)); } uptr ClassID(uptr size) { return SizeClassMap::ClassID(size); } uptr TotalMemoryUsed() { // No need to lock here. uptr res = 0; for (uptr i = 0; i < kNumPossibleRegions; i++) if (possible_regions[i]) res += kRegionSize; return res; } void TestOnlyUnmap() { for (uptr i = 0; i < kNumPossibleRegions; i++) if (possible_regions[i]) UnmapWithCallback((i * kRegionSize), kRegionSize); } // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone // introspection API. void ForceLock() { for (uptr i = 0; i < kNumClasses; i++) { GetSizeClassInfo(i)->mutex.Lock(); } } void ForceUnlock() { for (int i = kNumClasses - 1; i >= 0; i--) { GetSizeClassInfo(i)->mutex.Unlock(); } } // Iterate over all existing chunks. // The allocator must be locked when calling this function. void ForEachChunk(ForEachChunkCallback callback, void *arg) { for (uptr region = 0; region < kNumPossibleRegions; region++) if (possible_regions[region]) { uptr chunk_size = ClassIdToSize(possible_regions[region]); uptr max_chunks_in_region = kRegionSize / (chunk_size + kMetadataSize); uptr region_beg = region * kRegionSize; for (uptr chunk = region_beg; chunk < region_beg + max_chunks_in_region * chunk_size; chunk += chunk_size) { // Too slow: CHECK_EQ((void *)chunk, GetBlockBegin((void *)chunk)); callback(chunk, arg); } } } void PrintStats() { } static uptr AdditionalSize() { return 0; } typedef SizeClassMap SizeClassMapT; static const uptr kNumClasses = SizeClassMap::kNumClasses; private: static const uptr kRegionSize = 1 << kRegionSizeLog; static const uptr kNumPossibleRegions = kSpaceSize / kRegionSize; struct SizeClassInfo { SpinMutex mutex; IntrusiveList free_list; char padding[kCacheLineSize - sizeof(uptr) - sizeof(IntrusiveList)]; }; COMPILER_CHECK(sizeof(SizeClassInfo) == kCacheLineSize); uptr ComputeRegionId(uptr mem) { uptr res = mem >> kRegionSizeLog; CHECK_LT(res, kNumPossibleRegions); return res; } uptr ComputeRegionBeg(uptr mem) { return mem & ~(kRegionSize - 1); } uptr AllocateRegion(AllocatorStats *stat, uptr class_id) { CHECK_LT(class_id, kNumClasses); uptr res = reinterpret_cast(MmapAlignedOrDieOnFatalError( kRegionSize, kRegionSize, "SizeClassAllocator32")); if (UNLIKELY(!res)) return 0; MapUnmapCallback().OnMap(res, kRegionSize); stat->Add(AllocatorStatMapped, kRegionSize); - CHECK_EQ(0U, (res & (kRegionSize - 1))); + CHECK(IsAligned(res, kRegionSize)); possible_regions.set(ComputeRegionId(res), static_cast(class_id)); return res; } SizeClassInfo *GetSizeClassInfo(uptr class_id) { CHECK_LT(class_id, kNumClasses); return &size_class_info_array[class_id]; } bool PopulateFreeList(AllocatorStats *stat, AllocatorCache *c, SizeClassInfo *sci, uptr class_id) { uptr size = ClassIdToSize(class_id); uptr reg = AllocateRegion(stat, class_id); if (UNLIKELY(!reg)) return false; uptr n_chunks = kRegionSize / (size + kMetadataSize); uptr max_count = TransferBatch::MaxCached(class_id); + CHECK_GT(max_count, 0); TransferBatch *b = nullptr; for (uptr i = reg; i < reg + n_chunks * size; i += size) { if (!b) { b = c->CreateBatch(class_id, this, (TransferBatch*)i); - if (!b) + if (UNLIKELY(!b)) return false; b->Clear(); } b->Add((void*)i); if (b->Count() == max_count) { - CHECK_GT(b->Count(), 0); sci->free_list.push_back(b); b = nullptr; } } if (b) { CHECK_GT(b->Count(), 0); sci->free_list.push_back(b); } return true; } ByteMap possible_regions; SizeClassInfo size_class_info_array[kNumClasses]; }; Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_allocator_primary64.h =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_allocator_primary64.h (revision 320536) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_allocator_primary64.h (revision 320537) @@ -1,521 +1,560 @@ //===-- sanitizer_allocator_primary64.h -------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Part of the Sanitizer Allocator. // //===----------------------------------------------------------------------===// #ifndef SANITIZER_ALLOCATOR_H #error This file must be included inside sanitizer_allocator.h #endif template struct SizeClassAllocator64LocalCache; // SizeClassAllocator64 -- allocator for 64-bit address space. // The template parameter Params is a class containing the actual parameters. // // Space: a portion of address space of kSpaceSize bytes starting at SpaceBeg. // If kSpaceBeg is ~0 then SpaceBeg is chosen dynamically my mmap. // Otherwise SpaceBeg=kSpaceBeg (fixed address). // kSpaceSize is a power of two. // At the beginning the entire space is mprotect-ed, then small parts of it // are mapped on demand. // // Region: a part of Space dedicated to a single size class. // There are kNumClasses Regions of equal size. // // UserChunk: a piece of memory returned to user. // MetaChunk: kMetadataSize bytes of metadata associated with a UserChunk. // FreeArray is an array free-d chunks (stored as 4-byte offsets) // // A Region looks like this: // UserChunk1 ... UserChunkN MetaChunkN ... MetaChunk1 FreeArray struct SizeClassAllocator64FlagMasks { // Bit masks. enum { kRandomShuffleChunks = 1, }; }; template class SizeClassAllocator64 { public: static const uptr kSpaceBeg = Params::kSpaceBeg; static const uptr kSpaceSize = Params::kSpaceSize; static const uptr kMetadataSize = Params::kMetadataSize; typedef typename Params::SizeClassMap SizeClassMap; typedef typename Params::MapUnmapCallback MapUnmapCallback; static const bool kRandomShuffleChunks = Params::kFlags & SizeClassAllocator64FlagMasks::kRandomShuffleChunks; typedef SizeClassAllocator64 ThisT; typedef SizeClassAllocator64LocalCache AllocatorCache; // When we know the size class (the region base) we can represent a pointer // as a 4-byte integer (offset from the region start shifted right by 4). typedef u32 CompactPtrT; static const uptr kCompactPtrScale = 4; CompactPtrT PointerToCompactPtr(uptr base, uptr ptr) { return static_cast((ptr - base) >> kCompactPtrScale); } uptr CompactPtrToPointer(uptr base, CompactPtrT ptr32) { return base + (static_cast(ptr32) << kCompactPtrScale); } void Init(s32 release_to_os_interval_ms) { uptr TotalSpaceSize = kSpaceSize + AdditionalSize(); if (kUsingConstantSpaceBeg) { CHECK_EQ(kSpaceBeg, reinterpret_cast( MmapFixedNoAccess(kSpaceBeg, TotalSpaceSize))); } else { NonConstSpaceBeg = reinterpret_cast(MmapNoAccess(TotalSpaceSize)); CHECK_NE(NonConstSpaceBeg, ~(uptr)0); } SetReleaseToOSIntervalMs(release_to_os_interval_ms); - MapWithCallback(SpaceEnd(), AdditionalSize()); + MapWithCallbackOrDie(SpaceEnd(), AdditionalSize()); } s32 ReleaseToOSIntervalMs() const { return atomic_load(&release_to_os_interval_ms_, memory_order_relaxed); } void SetReleaseToOSIntervalMs(s32 release_to_os_interval_ms) { atomic_store(&release_to_os_interval_ms_, release_to_os_interval_ms, memory_order_relaxed); } - void MapWithCallback(uptr beg, uptr size) { - CHECK_EQ(beg, reinterpret_cast(MmapFixedOrDie(beg, size))); - MapUnmapCallback().OnMap(beg, size); - } - - void UnmapWithCallback(uptr beg, uptr size) { - MapUnmapCallback().OnUnmap(beg, size); - UnmapOrDie(reinterpret_cast(beg), size); - } - static bool CanAllocate(uptr size, uptr alignment) { return size <= SizeClassMap::kMaxSize && alignment <= SizeClassMap::kMaxSize; } NOINLINE void ReturnToAllocator(AllocatorStats *stat, uptr class_id, const CompactPtrT *chunks, uptr n_chunks) { RegionInfo *region = GetRegionInfo(class_id); uptr region_beg = GetRegionBeginBySizeClass(class_id); CompactPtrT *free_array = GetFreeArray(region_beg); BlockingMutexLock l(®ion->mutex); uptr old_num_chunks = region->num_freed_chunks; uptr new_num_freed_chunks = old_num_chunks + n_chunks; - EnsureFreeArraySpace(region, region_beg, new_num_freed_chunks); + // Failure to allocate free array space while releasing memory is non + // recoverable. + if (UNLIKELY(!EnsureFreeArraySpace(region, region_beg, + new_num_freed_chunks))) + DieOnFailure::OnOOM(); for (uptr i = 0; i < n_chunks; i++) free_array[old_num_chunks + i] = chunks[i]; region->num_freed_chunks = new_num_freed_chunks; - region->n_freed += n_chunks; + region->stats.n_freed += n_chunks; MaybeReleaseToOS(class_id); } - NOINLINE void GetFromAllocator(AllocatorStats *stat, uptr class_id, + NOINLINE bool GetFromAllocator(AllocatorStats *stat, uptr class_id, CompactPtrT *chunks, uptr n_chunks) { RegionInfo *region = GetRegionInfo(class_id); uptr region_beg = GetRegionBeginBySizeClass(class_id); CompactPtrT *free_array = GetFreeArray(region_beg); BlockingMutexLock l(®ion->mutex); if (UNLIKELY(region->num_freed_chunks < n_chunks)) { - PopulateFreeArray(stat, class_id, region, - n_chunks - region->num_freed_chunks); + if (UNLIKELY(!PopulateFreeArray(stat, class_id, region, + n_chunks - region->num_freed_chunks))) + return false; CHECK_GE(region->num_freed_chunks, n_chunks); } region->num_freed_chunks -= n_chunks; uptr base_idx = region->num_freed_chunks; for (uptr i = 0; i < n_chunks; i++) chunks[i] = free_array[base_idx + i]; - region->n_allocated += n_chunks; + region->stats.n_allocated += n_chunks; + return true; } - bool PointerIsMine(const void *p) { uptr P = reinterpret_cast(p); if (kUsingConstantSpaceBeg && (kSpaceBeg % kSpaceSize) == 0) return P / kSpaceSize == kSpaceBeg / kSpaceSize; return P >= SpaceBeg() && P < SpaceEnd(); } uptr GetRegionBegin(const void *p) { if (kUsingConstantSpaceBeg) return reinterpret_cast(p) & ~(kRegionSize - 1); uptr space_beg = SpaceBeg(); return ((reinterpret_cast(p) - space_beg) & ~(kRegionSize - 1)) + space_beg; } uptr GetRegionBeginBySizeClass(uptr class_id) { return SpaceBeg() + kRegionSize * class_id; } uptr GetSizeClass(const void *p) { if (kUsingConstantSpaceBeg && (kSpaceBeg % kSpaceSize) == 0) return ((reinterpret_cast(p)) / kRegionSize) % kNumClassesRounded; return ((reinterpret_cast(p) - SpaceBeg()) / kRegionSize) % kNumClassesRounded; } void *GetBlockBegin(const void *p) { uptr class_id = GetSizeClass(p); uptr size = ClassIdToSize(class_id); if (!size) return nullptr; uptr chunk_idx = GetChunkIdx((uptr)p, size); uptr reg_beg = GetRegionBegin(p); uptr beg = chunk_idx * size; uptr next_beg = beg + size; if (class_id >= kNumClasses) return nullptr; RegionInfo *region = GetRegionInfo(class_id); if (region->mapped_user >= next_beg) return reinterpret_cast(reg_beg + beg); return nullptr; } uptr GetActuallyAllocatedSize(void *p) { CHECK(PointerIsMine(p)); return ClassIdToSize(GetSizeClass(p)); } uptr ClassID(uptr size) { return SizeClassMap::ClassID(size); } void *GetMetaData(const void *p) { uptr class_id = GetSizeClass(p); uptr size = ClassIdToSize(class_id); uptr chunk_idx = GetChunkIdx(reinterpret_cast(p), size); uptr region_beg = GetRegionBeginBySizeClass(class_id); return reinterpret_cast(GetMetadataEnd(region_beg) - (1 + chunk_idx) * kMetadataSize); } uptr TotalMemoryUsed() { uptr res = 0; for (uptr i = 0; i < kNumClasses; i++) res += GetRegionInfo(i)->allocated_user; return res; } // Test-only. void TestOnlyUnmap() { - UnmapWithCallback(SpaceBeg(), kSpaceSize + AdditionalSize()); + UnmapWithCallbackOrDie(SpaceBeg(), kSpaceSize + AdditionalSize()); } static void FillMemoryProfile(uptr start, uptr rss, bool file, uptr *stats, uptr stats_size) { for (uptr class_id = 0; class_id < stats_size; class_id++) if (stats[class_id] == start) stats[class_id] = rss; } void PrintStats(uptr class_id, uptr rss) { RegionInfo *region = GetRegionInfo(class_id); if (region->mapped_user == 0) return; - uptr in_use = region->n_allocated - region->n_freed; + uptr in_use = region->stats.n_allocated - region->stats.n_freed; uptr avail_chunks = region->allocated_user / ClassIdToSize(class_id); Printf( - " %02zd (%6zd): mapped: %6zdK allocs: %7zd frees: %7zd inuse: %6zd " + "%s %02zd (%6zd): mapped: %6zdK allocs: %7zd frees: %7zd inuse: %6zd " "num_freed_chunks %7zd avail: %6zd rss: %6zdK releases: %6zd\n", - class_id, ClassIdToSize(class_id), region->mapped_user >> 10, - region->n_allocated, region->n_freed, in_use, - region->num_freed_chunks, avail_chunks, rss >> 10, - region->rtoi.num_releases); + region->exhausted ? "F" : " ", class_id, ClassIdToSize(class_id), + region->mapped_user >> 10, region->stats.n_allocated, + region->stats.n_freed, in_use, region->num_freed_chunks, avail_chunks, + rss >> 10, region->rtoi.num_releases); } void PrintStats() { uptr total_mapped = 0; uptr n_allocated = 0; uptr n_freed = 0; for (uptr class_id = 1; class_id < kNumClasses; class_id++) { RegionInfo *region = GetRegionInfo(class_id); total_mapped += region->mapped_user; - n_allocated += region->n_allocated; - n_freed += region->n_freed; + n_allocated += region->stats.n_allocated; + n_freed += region->stats.n_freed; } Printf("Stats: SizeClassAllocator64: %zdM mapped in %zd allocations; " "remains %zd\n", total_mapped >> 20, n_allocated, n_allocated - n_freed); uptr rss_stats[kNumClasses]; for (uptr class_id = 0; class_id < kNumClasses; class_id++) rss_stats[class_id] = SpaceBeg() + kRegionSize * class_id; GetMemoryProfile(FillMemoryProfile, rss_stats, kNumClasses); for (uptr class_id = 1; class_id < kNumClasses; class_id++) PrintStats(class_id, rss_stats[class_id]); } // ForceLock() and ForceUnlock() are needed to implement Darwin malloc zone // introspection API. void ForceLock() { for (uptr i = 0; i < kNumClasses; i++) { GetRegionInfo(i)->mutex.Lock(); } } void ForceUnlock() { for (int i = (int)kNumClasses - 1; i >= 0; i--) { GetRegionInfo(i)->mutex.Unlock(); } } // Iterate over all existing chunks. // The allocator must be locked when calling this function. void ForEachChunk(ForEachChunkCallback callback, void *arg) { for (uptr class_id = 1; class_id < kNumClasses; class_id++) { RegionInfo *region = GetRegionInfo(class_id); uptr chunk_size = ClassIdToSize(class_id); uptr region_beg = SpaceBeg() + class_id * kRegionSize; for (uptr chunk = region_beg; chunk < region_beg + region->allocated_user; chunk += chunk_size) { // Too slow: CHECK_EQ((void *)chunk, GetBlockBegin((void *)chunk)); callback(chunk, arg); } } } static uptr ClassIdToSize(uptr class_id) { return SizeClassMap::Size(class_id); } static uptr AdditionalSize() { return RoundUpTo(sizeof(RegionInfo) * kNumClassesRounded, GetPageSizeCached()); } typedef SizeClassMap SizeClassMapT; static const uptr kNumClasses = SizeClassMap::kNumClasses; static const uptr kNumClassesRounded = SizeClassMap::kNumClassesRounded; private: static const uptr kRegionSize = kSpaceSize / kNumClassesRounded; // FreeArray is the array of free-d chunks (stored as 4-byte offsets). // In the worst case it may reguire kRegionSize/SizeClassMap::kMinSize // elements, but in reality this will not happen. For simplicity we // dedicate 1/8 of the region's virtual space to FreeArray. static const uptr kFreeArraySize = kRegionSize / 8; static const bool kUsingConstantSpaceBeg = kSpaceBeg != ~(uptr)0; uptr NonConstSpaceBeg; uptr SpaceBeg() const { return kUsingConstantSpaceBeg ? kSpaceBeg : NonConstSpaceBeg; } uptr SpaceEnd() const { return SpaceBeg() + kSpaceSize; } // kRegionSize must be >= 2^32. COMPILER_CHECK((kRegionSize) >= (1ULL << (SANITIZER_WORDSIZE / 2))); // kRegionSize must be <= 2^36, see CompactPtrT. COMPILER_CHECK((kRegionSize) <= (1ULL << (SANITIZER_WORDSIZE / 2 + 4))); // Call mmap for user memory with at least this size. static const uptr kUserMapSize = 1 << 16; // Call mmap for metadata memory with at least this size. static const uptr kMetaMapSize = 1 << 16; // Call mmap for free array memory with at least this size. static const uptr kFreeArrayMapSize = 1 << 16; atomic_sint32_t release_to_os_interval_ms_; + struct Stats { + uptr n_allocated; + uptr n_freed; + }; + struct ReleaseToOsInfo { uptr n_freed_at_last_release; uptr num_releases; u64 last_release_at_ns; }; struct RegionInfo { BlockingMutex mutex; uptr num_freed_chunks; // Number of elements in the freearray. uptr mapped_free_array; // Bytes mapped for freearray. uptr allocated_user; // Bytes allocated for user memory. uptr allocated_meta; // Bytes allocated for metadata. uptr mapped_user; // Bytes mapped for user memory. uptr mapped_meta; // Bytes mapped for metadata. - u32 rand_state; // Seed for random shuffle, used if kRandomShuffleChunks. - uptr n_allocated, n_freed; // Just stats. + u32 rand_state; // Seed for random shuffle, used if kRandomShuffleChunks. + bool exhausted; // Whether region is out of space for new chunks. + Stats stats; ReleaseToOsInfo rtoi; }; COMPILER_CHECK(sizeof(RegionInfo) >= kCacheLineSize); u32 Rand(u32 *state) { // ANSI C linear congruential PRNG. return (*state = *state * 1103515245 + 12345) >> 16; } u32 RandN(u32 *state, u32 n) { return Rand(state) % n; } // [0, n) void RandomShuffle(u32 *a, u32 n, u32 *rand_state) { if (n <= 1) return; for (u32 i = n - 1; i > 0; i--) Swap(a[i], a[RandN(rand_state, i + 1)]); } RegionInfo *GetRegionInfo(uptr class_id) { CHECK_LT(class_id, kNumClasses); RegionInfo *regions = reinterpret_cast(SpaceBeg() + kSpaceSize); return ®ions[class_id]; } uptr GetMetadataEnd(uptr region_beg) { return region_beg + kRegionSize - kFreeArraySize; } uptr GetChunkIdx(uptr chunk, uptr size) { if (!kUsingConstantSpaceBeg) chunk -= SpaceBeg(); uptr offset = chunk % kRegionSize; // Here we divide by a non-constant. This is costly. // size always fits into 32-bits. If the offset fits too, use 32-bit div. if (offset >> (SANITIZER_WORDSIZE / 2)) return offset / size; return (u32)offset / (u32)size; } CompactPtrT *GetFreeArray(uptr region_beg) { return reinterpret_cast(region_beg + kRegionSize - kFreeArraySize); } - void EnsureFreeArraySpace(RegionInfo *region, uptr region_beg, + bool MapWithCallback(uptr beg, uptr size) { + uptr mapped = reinterpret_cast(MmapFixedOrDieOnFatalError(beg, size)); + if (UNLIKELY(!mapped)) + return false; + CHECK_EQ(beg, mapped); + MapUnmapCallback().OnMap(beg, size); + return true; + } + + void MapWithCallbackOrDie(uptr beg, uptr size) { + CHECK_EQ(beg, reinterpret_cast(MmapFixedOrDie(beg, size))); + MapUnmapCallback().OnMap(beg, size); + } + + void UnmapWithCallbackOrDie(uptr beg, uptr size) { + MapUnmapCallback().OnUnmap(beg, size); + UnmapOrDie(reinterpret_cast(beg), size); + } + + bool EnsureFreeArraySpace(RegionInfo *region, uptr region_beg, uptr num_freed_chunks) { uptr needed_space = num_freed_chunks * sizeof(CompactPtrT); if (region->mapped_free_array < needed_space) { CHECK_LE(needed_space, kFreeArraySize); uptr new_mapped_free_array = RoundUpTo(needed_space, kFreeArrayMapSize); uptr current_map_end = reinterpret_cast(GetFreeArray(region_beg)) + region->mapped_free_array; uptr new_map_size = new_mapped_free_array - region->mapped_free_array; - MapWithCallback(current_map_end, new_map_size); + if (UNLIKELY(!MapWithCallback(current_map_end, new_map_size))) + return false; region->mapped_free_array = new_mapped_free_array; } + return true; } - - NOINLINE void PopulateFreeArray(AllocatorStats *stat, uptr class_id, + NOINLINE bool PopulateFreeArray(AllocatorStats *stat, uptr class_id, RegionInfo *region, uptr requested_count) { // region->mutex is held. - uptr size = ClassIdToSize(class_id); - uptr beg_idx = region->allocated_user; - uptr end_idx = beg_idx + requested_count * size; - uptr region_beg = GetRegionBeginBySizeClass(class_id); - if (end_idx > region->mapped_user) { + const uptr size = ClassIdToSize(class_id); + const uptr new_space_beg = region->allocated_user; + const uptr new_space_end = new_space_beg + requested_count * size; + const uptr region_beg = GetRegionBeginBySizeClass(class_id); + + // Map more space for chunks, if necessary. + if (new_space_end > region->mapped_user) { if (!kUsingConstantSpaceBeg && region->mapped_user == 0) region->rand_state = static_cast(region_beg >> 12); // From ASLR. // Do the mmap for the user memory. uptr map_size = kUserMapSize; - while (end_idx > region->mapped_user + map_size) + while (new_space_end > region->mapped_user + map_size) map_size += kUserMapSize; - CHECK_GE(region->mapped_user + map_size, end_idx); - MapWithCallback(region_beg + region->mapped_user, map_size); + CHECK_GE(region->mapped_user + map_size, new_space_end); + if (UNLIKELY(!MapWithCallback(region_beg + region->mapped_user, + map_size))) + return false; stat->Add(AllocatorStatMapped, map_size); region->mapped_user += map_size; } - CompactPtrT *free_array = GetFreeArray(region_beg); - uptr total_count = (region->mapped_user - beg_idx) / size; - uptr num_freed_chunks = region->num_freed_chunks; - EnsureFreeArraySpace(region, region_beg, num_freed_chunks + total_count); - for (uptr i = 0; i < total_count; i++) { - uptr chunk = beg_idx + i * size; - free_array[num_freed_chunks + total_count - 1 - i] = - PointerToCompactPtr(0, chunk); + const uptr new_chunks_count = (region->mapped_user - new_space_beg) / size; + + // Calculate the required space for metadata. + const uptr requested_allocated_meta = + region->allocated_meta + new_chunks_count * kMetadataSize; + uptr requested_mapped_meta = region->mapped_meta; + while (requested_allocated_meta > requested_mapped_meta) + requested_mapped_meta += kMetaMapSize; + // Check whether this size class is exhausted. + if (region->mapped_user + requested_mapped_meta > + kRegionSize - kFreeArraySize) { + if (!region->exhausted) { + region->exhausted = true; + Printf("%s: Out of memory. ", SanitizerToolName); + Printf("The process has exhausted %zuMB for size class %zu.\n", + kRegionSize >> 20, size); + } + return false; } + // Map more space for metadata, if necessary. + if (requested_mapped_meta > region->mapped_meta) { + if (UNLIKELY(!MapWithCallback( + GetMetadataEnd(region_beg) - requested_mapped_meta, + requested_mapped_meta - region->mapped_meta))) + return false; + region->mapped_meta = requested_mapped_meta; + } + + // If necessary, allocate more space for the free array and populate it with + // newly allocated chunks. + const uptr total_freed_chunks = region->num_freed_chunks + new_chunks_count; + if (UNLIKELY(!EnsureFreeArraySpace(region, region_beg, total_freed_chunks))) + return false; + CompactPtrT *free_array = GetFreeArray(region_beg); + for (uptr i = 0, chunk = new_space_beg; i < new_chunks_count; + i++, chunk += size) + free_array[total_freed_chunks - 1 - i] = PointerToCompactPtr(0, chunk); if (kRandomShuffleChunks) - RandomShuffle(&free_array[num_freed_chunks], total_count, + RandomShuffle(&free_array[region->num_freed_chunks], new_chunks_count, ®ion->rand_state); - region->num_freed_chunks += total_count; - region->allocated_user += total_count * size; - CHECK_LE(region->allocated_user, region->mapped_user); - region->allocated_meta += total_count * kMetadataSize; - if (region->allocated_meta > region->mapped_meta) { - uptr map_size = kMetaMapSize; - while (region->allocated_meta > region->mapped_meta + map_size) - map_size += kMetaMapSize; - // Do the mmap for the metadata. - CHECK_GE(region->mapped_meta + map_size, region->allocated_meta); - MapWithCallback(GetMetadataEnd(region_beg) - - region->mapped_meta - map_size, map_size); - region->mapped_meta += map_size; - } + // All necessary memory is mapped and now it is safe to advance all + // 'allocated_*' counters. + region->num_freed_chunks += new_chunks_count; + region->allocated_user += new_chunks_count * size; + CHECK_LE(region->allocated_user, region->mapped_user); + region->allocated_meta = requested_allocated_meta; CHECK_LE(region->allocated_meta, region->mapped_meta); - if (region->mapped_user + region->mapped_meta > - kRegionSize - kFreeArraySize) { - Printf("%s: Out of memory. Dying. ", SanitizerToolName); - Printf("The process has exhausted %zuMB for size class %zu.\n", - kRegionSize / 1024 / 1024, size); - Die(); - } + region->exhausted = false; + + return true; } void MaybeReleaseChunkRange(uptr region_beg, uptr chunk_size, CompactPtrT first, CompactPtrT last) { uptr beg_ptr = CompactPtrToPointer(region_beg, first); uptr end_ptr = CompactPtrToPointer(region_beg, last) + chunk_size; ReleaseMemoryPagesToOS(beg_ptr, end_ptr); } // Attempts to release some RAM back to OS. The region is expected to be // locked. // Algorithm: // * Sort the chunks. // * Find ranges fully covered by free-d chunks // * Release them to OS with madvise. void MaybeReleaseToOS(uptr class_id) { RegionInfo *region = GetRegionInfo(class_id); const uptr chunk_size = ClassIdToSize(class_id); const uptr page_size = GetPageSizeCached(); uptr n = region->num_freed_chunks; if (n * chunk_size < page_size) return; // No chance to release anything. - if ((region->n_freed - region->rtoi.n_freed_at_last_release) * chunk_size < - page_size) { + if ((region->stats.n_freed - + region->rtoi.n_freed_at_last_release) * chunk_size < page_size) { return; // Nothing new to release. } s32 interval_ms = ReleaseToOSIntervalMs(); if (interval_ms < 0) return; u64 now_ns = NanoTime(); if (region->rtoi.last_release_at_ns + interval_ms * 1000000ULL > now_ns) return; // Memory was returned recently. region->rtoi.last_release_at_ns = now_ns; uptr region_beg = GetRegionBeginBySizeClass(class_id); CompactPtrT *free_array = GetFreeArray(region_beg); SortArray(free_array, n); const uptr scaled_chunk_size = chunk_size >> kCompactPtrScale; const uptr kScaledGranularity = page_size >> kCompactPtrScale; uptr range_beg = free_array[0]; uptr prev = free_array[0]; for (uptr i = 1; i < n; i++) { uptr chunk = free_array[i]; CHECK_GT(chunk, prev); if (chunk - prev != scaled_chunk_size) { CHECK_GT(chunk - prev, scaled_chunk_size); if (prev + scaled_chunk_size - range_beg >= kScaledGranularity) { MaybeReleaseChunkRange(region_beg, chunk_size, range_beg, prev); - region->rtoi.n_freed_at_last_release = region->n_freed; + region->rtoi.n_freed_at_last_release = region->stats.n_freed; region->rtoi.num_releases++; } range_beg = chunk; } prev = chunk; } } }; - - Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_common.h =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_common.h (revision 320536) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_common.h (revision 320537) @@ -1,938 +1,941 @@ //===-- sanitizer_common.h --------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is shared between run-time libraries of sanitizers. // // It declares common functions and classes that are used in both runtimes. // Implementation of some functions are provided in sanitizer_common, while // others must be defined by run-time library itself. //===----------------------------------------------------------------------===// #ifndef SANITIZER_COMMON_H #define SANITIZER_COMMON_H #include "sanitizer_flags.h" #include "sanitizer_interface_internal.h" #include "sanitizer_internal_defs.h" #include "sanitizer_libc.h" #include "sanitizer_list.h" #include "sanitizer_mutex.h" #if defined(_MSC_VER) && !defined(__clang__) extern "C" void _ReadWriteBarrier(); #pragma intrinsic(_ReadWriteBarrier) #endif namespace __sanitizer { struct StackTrace; struct AddressInfo; // Constants. const uptr kWordSize = SANITIZER_WORDSIZE / 8; const uptr kWordSizeInBits = 8 * kWordSize; #if defined(__powerpc__) || defined(__powerpc64__) const uptr kCacheLineSize = 128; #else const uptr kCacheLineSize = 64; #endif const uptr kMaxPathLength = 4096; const uptr kMaxThreadStackSize = 1 << 30; // 1Gb static const uptr kErrorMessageBufferSize = 1 << 16; // Denotes fake PC values that come from JIT/JAVA/etc. // For such PC values __tsan_symbolize_external() will be called. const u64 kExternalPCBit = 1ULL << 60; extern const char *SanitizerToolName; // Can be changed by the tool. extern atomic_uint32_t current_verbosity; INLINE void SetVerbosity(int verbosity) { atomic_store(¤t_verbosity, verbosity, memory_order_relaxed); } INLINE int Verbosity() { return atomic_load(¤t_verbosity, memory_order_relaxed); } uptr GetPageSize(); extern uptr PageSizeCached; INLINE uptr GetPageSizeCached() { if (!PageSizeCached) PageSizeCached = GetPageSize(); return PageSizeCached; } uptr GetMmapGranularity(); uptr GetMaxVirtualAddress(); // Threads tid_t GetTid(); uptr GetThreadSelf(); void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, uptr *stack_bottom); void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, uptr *tls_addr, uptr *tls_size); // Memory management void *MmapOrDie(uptr size, const char *mem_type, bool raw_report = false); INLINE void *MmapOrDieQuietly(uptr size, const char *mem_type) { return MmapOrDie(size, mem_type, /*raw_report*/ true); } void UnmapOrDie(void *addr, uptr size); // Behaves just like MmapOrDie, but tolerates out of memory condition, in that // case returns nullptr. void *MmapOrDieOnFatalError(uptr size, const char *mem_type); void *MmapFixedNoReserve(uptr fixed_addr, uptr size, const char *name = nullptr); void *MmapNoReserveOrDie(uptr size, const char *mem_type); void *MmapFixedOrDie(uptr fixed_addr, uptr size); +// Behaves just like MmapFixedOrDie, but tolerates out of memory condition, in +// that case returns nullptr. +void *MmapFixedOrDieOnFatalError(uptr fixed_addr, uptr size); void *MmapFixedNoAccess(uptr fixed_addr, uptr size, const char *name = nullptr); void *MmapNoAccess(uptr size); // Map aligned chunk of address space; size and alignment are powers of two. // Dies on all but out of memory errors, in the latter case returns nullptr. void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment, const char *mem_type); // Disallow access to a memory range. Use MmapFixedNoAccess to allocate an // unaccessible memory. bool MprotectNoAccess(uptr addr, uptr size); bool MprotectReadOnly(uptr addr, uptr size); // Find an available address space. uptr FindAvailableMemoryRange(uptr size, uptr alignment, uptr left_padding); // Used to check if we can map shadow memory to a fixed location. bool MemoryRangeIsAvailable(uptr range_start, uptr range_end); // Releases memory pages entirely within the [beg, end] address range. Noop if // the provided range does not contain at least one entire page. void ReleaseMemoryPagesToOS(uptr beg, uptr end); void IncreaseTotalMmap(uptr size); void DecreaseTotalMmap(uptr size); uptr GetRSS(); void NoHugePagesInRegion(uptr addr, uptr length); void DontDumpShadowMemory(uptr addr, uptr length); // Check if the built VMA size matches the runtime one. void CheckVMASize(); void RunMallocHooks(const void *ptr, uptr size); void RunFreeHooks(const void *ptr); // InternalScopedBuffer can be used instead of large stack arrays to // keep frame size low. // FIXME: use InternalAlloc instead of MmapOrDie once // InternalAlloc is made libc-free. template class InternalScopedBuffer { public: explicit InternalScopedBuffer(uptr cnt) { cnt_ = cnt; ptr_ = (T *)MmapOrDie(cnt * sizeof(T), "InternalScopedBuffer"); } ~InternalScopedBuffer() { UnmapOrDie(ptr_, cnt_ * sizeof(T)); } T &operator[](uptr i) { return ptr_[i]; } T *data() { return ptr_; } uptr size() { return cnt_ * sizeof(T); } private: T *ptr_; uptr cnt_; // Disallow copies and moves. InternalScopedBuffer(const InternalScopedBuffer &) = delete; InternalScopedBuffer &operator=(const InternalScopedBuffer &) = delete; InternalScopedBuffer(InternalScopedBuffer &&) = delete; InternalScopedBuffer &operator=(InternalScopedBuffer &&) = delete; }; class InternalScopedString : public InternalScopedBuffer { public: explicit InternalScopedString(uptr max_length) : InternalScopedBuffer(max_length), length_(0) { (*this)[0] = '\0'; } uptr length() { return length_; } void clear() { (*this)[0] = '\0'; length_ = 0; } void append(const char *format, ...); private: uptr length_; }; // Simple low-level (mmap-based) allocator for internal use. Doesn't have // constructor, so all instances of LowLevelAllocator should be // linker initialized. class LowLevelAllocator { public: // Requires an external lock. void *Allocate(uptr size); private: char *allocated_end_; char *allocated_current_; }; typedef void (*LowLevelAllocateCallback)(uptr ptr, uptr size); // Allows to register tool-specific callbacks for LowLevelAllocator. // Passing NULL removes the callback. void SetLowLevelAllocateCallback(LowLevelAllocateCallback callback); // IO void RawWrite(const char *buffer); bool ColorizeReports(); void RemoveANSIEscapeSequencesFromString(char *buffer); void Printf(const char *format, ...); void Report(const char *format, ...); void SetPrintfAndReportCallback(void (*callback)(const char *)); #define VReport(level, ...) \ do { \ if ((uptr)Verbosity() >= (level)) Report(__VA_ARGS__); \ } while (0) #define VPrintf(level, ...) \ do { \ if ((uptr)Verbosity() >= (level)) Printf(__VA_ARGS__); \ } while (0) // Can be used to prevent mixing error reports from different sanitizers. extern StaticSpinMutex CommonSanitizerReportMutex; struct ReportFile { void Write(const char *buffer, uptr length); bool SupportsColors(); void SetReportPath(const char *path); // Don't use fields directly. They are only declared public to allow // aggregate initialization. // Protects fields below. StaticSpinMutex *mu; // Opened file descriptor. Defaults to stderr. It may be equal to // kInvalidFd, in which case new file will be opened when necessary. fd_t fd; // Path prefix of report file, set via __sanitizer_set_report_path. char path_prefix[kMaxPathLength]; // Full path to report, obtained as .PID char full_path[kMaxPathLength]; // PID of the process that opened fd. If a fork() occurs, // the PID of child will be different from fd_pid. uptr fd_pid; private: void ReopenIfNecessary(); }; extern ReportFile report_file; extern uptr stoptheworld_tracer_pid; extern uptr stoptheworld_tracer_ppid; enum FileAccessMode { RdOnly, WrOnly, RdWr }; // Returns kInvalidFd on error. fd_t OpenFile(const char *filename, FileAccessMode mode, error_t *errno_p = nullptr); void CloseFile(fd_t); // Return true on success, false on error. bool ReadFromFile(fd_t fd, void *buff, uptr buff_size, uptr *bytes_read = nullptr, error_t *error_p = nullptr); bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written = nullptr, error_t *error_p = nullptr); bool RenameFile(const char *oldpath, const char *newpath, error_t *error_p = nullptr); // Scoped file handle closer. struct FileCloser { explicit FileCloser(fd_t fd) : fd(fd) {} ~FileCloser() { CloseFile(fd); } fd_t fd; }; bool SupportsColoredOutput(fd_t fd); // Opens the file 'file_name" and reads up to 'max_len' bytes. // The resulting buffer is mmaped and stored in '*buff'. // The size of the mmaped region is stored in '*buff_size'. // The total number of read bytes is stored in '*read_len'. // Returns true if file was successfully opened and read. bool ReadFileToBuffer(const char *file_name, char **buff, uptr *buff_size, uptr *read_len, uptr max_len = 1 << 26, error_t *errno_p = nullptr); // Maps given file to virtual memory, and returns pointer to it // (or NULL if mapping fails). Stores the size of mmaped region // in '*buff_size'. void *MapFileToMemory(const char *file_name, uptr *buff_size); void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset); bool IsAccessibleMemoryRange(uptr beg, uptr size); // Error report formatting. const char *StripPathPrefix(const char *filepath, const char *strip_file_prefix); // Strip the directories from the module name. const char *StripModuleName(const char *module); // OS uptr ReadBinaryName(/*out*/char *buf, uptr buf_len); uptr ReadBinaryNameCached(/*out*/char *buf, uptr buf_len); uptr ReadLongProcessName(/*out*/ char *buf, uptr buf_len); const char *GetProcessName(); void UpdateProcessName(); void CacheBinaryName(); void DisableCoreDumperIfNecessary(); void DumpProcessMap(); void PrintModuleMap(); bool FileExists(const char *filename); const char *GetEnv(const char *name); bool SetEnv(const char *name, const char *value); const char *GetPwd(); char *FindPathToBinary(const char *name); bool IsPathSeparator(const char c); bool IsAbsolutePath(const char *path); // Starts a subprocess and returs its pid. // If *_fd parameters are not kInvalidFd their corresponding input/output // streams will be redirect to the file. The files will always be closed // in parent process even in case of an error. // The child process will close all fds after STDERR_FILENO // before passing control to a program. pid_t StartSubprocess(const char *filename, const char *const argv[], fd_t stdin_fd = kInvalidFd, fd_t stdout_fd = kInvalidFd, fd_t stderr_fd = kInvalidFd); // Checks if specified process is still running bool IsProcessRunning(pid_t pid); // Waits for the process to finish and returns its exit code. // Returns -1 in case of an error. int WaitForProcess(pid_t pid); u32 GetUid(); void ReExec(); char **GetArgv(); void PrintCmdline(); bool StackSizeIsUnlimited(); uptr GetStackSizeLimitInBytes(); void SetStackSizeLimitInBytes(uptr limit); bool AddressSpaceIsUnlimited(); void SetAddressSpaceUnlimited(); void AdjustStackSize(void *attr); void PrepareForSandboxing(__sanitizer_sandbox_arguments *args); void SetSandboxingCallback(void (*f)()); void InitializeCoverage(bool enabled, const char *coverage_dir); void InitTlsSize(); uptr GetTlsSize(); // Other void SleepForSeconds(int seconds); void SleepForMillis(int millis); u64 NanoTime(); int Atexit(void (*function)(void)); void SortArray(uptr *array, uptr size); void SortArray(u32 *array, uptr size); bool TemplateMatch(const char *templ, const char *str); // Exit void NORETURN Abort(); void NORETURN Die(); void NORETURN CheckFailed(const char *file, int line, const char *cond, u64 v1, u64 v2); void NORETURN ReportMmapFailureAndDie(uptr size, const char *mem_type, const char *mmap_type, error_t err, bool raw_report = false); // Set the name of the current thread to 'name', return true on succees. // The name may be truncated to a system-dependent limit. bool SanitizerSetThreadName(const char *name); // Get the name of the current thread (no more than max_len bytes), // return true on succees. name should have space for at least max_len+1 bytes. bool SanitizerGetThreadName(char *name, int max_len); // Specific tools may override behavior of "Die" and "CheckFailed" functions // to do tool-specific job. typedef void (*DieCallbackType)(void); // It's possible to add several callbacks that would be run when "Die" is // called. The callbacks will be run in the opposite order. The tools are // strongly recommended to setup all callbacks during initialization, when there // is only a single thread. bool AddDieCallback(DieCallbackType callback); bool RemoveDieCallback(DieCallbackType callback); void SetUserDieCallback(DieCallbackType callback); typedef void (*CheckFailedCallbackType)(const char *, int, const char *, u64, u64); void SetCheckFailedCallback(CheckFailedCallbackType callback); // Callback will be called if soft_rss_limit_mb is given and the limit is // exceeded (exceeded==true) or if rss went down below the limit // (exceeded==false). // The callback should be registered once at the tool init time. void SetSoftRssLimitExceededCallback(void (*Callback)(bool exceeded)); // Functions related to signal handling. typedef void (*SignalHandlerType)(int, void *, void *); HandleSignalMode GetHandleSignalMode(int signum); void InstallDeadlySignalHandlers(SignalHandlerType handler); const char *DescribeSignalOrException(int signo); // Alternative signal stack (POSIX-only). void SetAlternateSignalStack(); void UnsetAlternateSignalStack(); // We don't want a summary too long. const int kMaxSummaryLength = 1024; // Construct a one-line string: // SUMMARY: SanitizerToolName: error_message // and pass it to __sanitizer_report_error_summary. // If alt_tool_name is provided, it's used in place of SanitizerToolName. void ReportErrorSummary(const char *error_message, const char *alt_tool_name = nullptr); // Same as above, but construct error_message as: // error_type file:line[:column][ function] void ReportErrorSummary(const char *error_type, const AddressInfo &info, const char *alt_tool_name = nullptr); // Same as above, but obtains AddressInfo by symbolizing top stack trace frame. void ReportErrorSummary(const char *error_type, const StackTrace *trace, const char *alt_tool_name = nullptr); // Math #if SANITIZER_WINDOWS && !defined(__clang__) && !defined(__GNUC__) extern "C" { unsigned char _BitScanForward(unsigned long *index, unsigned long mask); // NOLINT unsigned char _BitScanReverse(unsigned long *index, unsigned long mask); // NOLINT #if defined(_WIN64) unsigned char _BitScanForward64(unsigned long *index, unsigned __int64 mask); // NOLINT unsigned char _BitScanReverse64(unsigned long *index, unsigned __int64 mask); // NOLINT #endif } #endif INLINE uptr MostSignificantSetBitIndex(uptr x) { CHECK_NE(x, 0U); unsigned long up; // NOLINT #if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__) # ifdef _WIN64 up = SANITIZER_WORDSIZE - 1 - __builtin_clzll(x); # else up = SANITIZER_WORDSIZE - 1 - __builtin_clzl(x); # endif #elif defined(_WIN64) _BitScanReverse64(&up, x); #else _BitScanReverse(&up, x); #endif return up; } INLINE uptr LeastSignificantSetBitIndex(uptr x) { CHECK_NE(x, 0U); unsigned long up; // NOLINT #if !SANITIZER_WINDOWS || defined(__clang__) || defined(__GNUC__) # ifdef _WIN64 up = __builtin_ctzll(x); # else up = __builtin_ctzl(x); # endif #elif defined(_WIN64) _BitScanForward64(&up, x); #else _BitScanForward(&up, x); #endif return up; } INLINE bool IsPowerOfTwo(uptr x) { return (x & (x - 1)) == 0; } INLINE uptr RoundUpToPowerOfTwo(uptr size) { CHECK(size); if (IsPowerOfTwo(size)) return size; uptr up = MostSignificantSetBitIndex(size); CHECK_LT(size, (1ULL << (up + 1))); CHECK_GT(size, (1ULL << up)); return 1ULL << (up + 1); } INLINE uptr RoundUpTo(uptr size, uptr boundary) { RAW_CHECK(IsPowerOfTwo(boundary)); return (size + boundary - 1) & ~(boundary - 1); } INLINE uptr RoundDownTo(uptr x, uptr boundary) { return x & ~(boundary - 1); } INLINE bool IsAligned(uptr a, uptr alignment) { return (a & (alignment - 1)) == 0; } INLINE uptr Log2(uptr x) { CHECK(IsPowerOfTwo(x)); return LeastSignificantSetBitIndex(x); } // Don't use std::min, std::max or std::swap, to minimize dependency // on libstdc++. template T Min(T a, T b) { return a < b ? a : b; } template T Max(T a, T b) { return a > b ? a : b; } template void Swap(T& a, T& b) { T tmp = a; a = b; b = tmp; } // Char handling INLINE bool IsSpace(int c) { return (c == ' ') || (c == '\n') || (c == '\t') || (c == '\f') || (c == '\r') || (c == '\v'); } INLINE bool IsDigit(int c) { return (c >= '0') && (c <= '9'); } INLINE int ToLower(int c) { return (c >= 'A' && c <= 'Z') ? (c + 'a' - 'A') : c; } // A low-level vector based on mmap. May incur a significant memory overhead for // small vectors. // WARNING: The current implementation supports only POD types. template class InternalMmapVectorNoCtor { public: void Initialize(uptr initial_capacity) { capacity_ = Max(initial_capacity, (uptr)1); size_ = 0; data_ = (T *)MmapOrDie(capacity_ * sizeof(T), "InternalMmapVectorNoCtor"); } void Destroy() { UnmapOrDie(data_, capacity_ * sizeof(T)); } T &operator[](uptr i) { CHECK_LT(i, size_); return data_[i]; } const T &operator[](uptr i) const { CHECK_LT(i, size_); return data_[i]; } void push_back(const T &element) { CHECK_LE(size_, capacity_); if (size_ == capacity_) { uptr new_capacity = RoundUpToPowerOfTwo(size_ + 1); Resize(new_capacity); } internal_memcpy(&data_[size_++], &element, sizeof(T)); } T &back() { CHECK_GT(size_, 0); return data_[size_ - 1]; } void pop_back() { CHECK_GT(size_, 0); size_--; } uptr size() const { return size_; } const T *data() const { return data_; } T *data() { return data_; } uptr capacity() const { return capacity_; } void resize(uptr new_size) { Resize(new_size); if (new_size > size_) { internal_memset(&data_[size_], 0, sizeof(T) * (new_size - size_)); } size_ = new_size; } void clear() { size_ = 0; } bool empty() const { return size() == 0; } const T *begin() const { return data(); } T *begin() { return data(); } const T *end() const { return data() + size(); } T *end() { return data() + size(); } private: void Resize(uptr new_capacity) { CHECK_GT(new_capacity, 0); CHECK_LE(size_, new_capacity); T *new_data = (T *)MmapOrDie(new_capacity * sizeof(T), "InternalMmapVector"); internal_memcpy(new_data, data_, size_ * sizeof(T)); T *old_data = data_; data_ = new_data; UnmapOrDie(old_data, capacity_ * sizeof(T)); capacity_ = new_capacity; } T *data_; uptr capacity_; uptr size_; }; template class InternalMmapVector : public InternalMmapVectorNoCtor { public: explicit InternalMmapVector(uptr initial_capacity) { InternalMmapVectorNoCtor::Initialize(initial_capacity); } ~InternalMmapVector() { InternalMmapVectorNoCtor::Destroy(); } // Disallow evil constructors. InternalMmapVector(const InternalMmapVector&); void operator=(const InternalMmapVector&); }; // HeapSort for arrays and InternalMmapVector. template void InternalSort(Container *v, uptr size, Compare comp) { if (size < 2) return; // Stage 1: insert elements to the heap. for (uptr i = 1; i < size; i++) { uptr j, p; for (j = i; j > 0; j = p) { p = (j - 1) / 2; if (comp((*v)[p], (*v)[j])) Swap((*v)[j], (*v)[p]); else break; } } // Stage 2: swap largest element with the last one, // and sink the new top. for (uptr i = size - 1; i > 0; i--) { Swap((*v)[0], (*v)[i]); uptr j, max_ind; for (j = 0; j < i; j = max_ind) { uptr left = 2 * j + 1; uptr right = 2 * j + 2; max_ind = j; if (left < i && comp((*v)[max_ind], (*v)[left])) max_ind = left; if (right < i && comp((*v)[max_ind], (*v)[right])) max_ind = right; if (max_ind != j) Swap((*v)[j], (*v)[max_ind]); else break; } } } // Works like std::lower_bound: finds the first element that is not less // than the val. template uptr InternalLowerBound(const Container &v, uptr first, uptr last, const Value &val, Compare comp) { while (last > first) { uptr mid = (first + last) / 2; if (comp(v[mid], val)) first = mid + 1; else last = mid; } return first; } enum ModuleArch { kModuleArchUnknown, kModuleArchI386, kModuleArchX86_64, kModuleArchX86_64H, kModuleArchARMV6, kModuleArchARMV7, kModuleArchARMV7S, kModuleArchARMV7K, kModuleArchARM64 }; // When adding a new architecture, don't forget to also update // script/asan_symbolize.py and sanitizer_symbolizer_libcdep.cc. inline const char *ModuleArchToString(ModuleArch arch) { switch (arch) { case kModuleArchUnknown: return ""; case kModuleArchI386: return "i386"; case kModuleArchX86_64: return "x86_64"; case kModuleArchX86_64H: return "x86_64h"; case kModuleArchARMV6: return "armv6"; case kModuleArchARMV7: return "armv7"; case kModuleArchARMV7S: return "armv7s"; case kModuleArchARMV7K: return "armv7k"; case kModuleArchARM64: return "arm64"; } CHECK(0 && "Invalid module arch"); return ""; } const uptr kModuleUUIDSize = 16; // Represents a binary loaded into virtual memory (e.g. this can be an // executable or a shared object). class LoadedModule { public: LoadedModule() : full_name_(nullptr), base_address_(0), max_executable_address_(0), arch_(kModuleArchUnknown), instrumented_(false) { internal_memset(uuid_, 0, kModuleUUIDSize); ranges_.clear(); } void set(const char *module_name, uptr base_address); void set(const char *module_name, uptr base_address, ModuleArch arch, u8 uuid[kModuleUUIDSize], bool instrumented); void clear(); void addAddressRange(uptr beg, uptr end, bool executable, bool writable); bool containsAddress(uptr address) const; const char *full_name() const { return full_name_; } uptr base_address() const { return base_address_; } uptr max_executable_address() const { return max_executable_address_; } ModuleArch arch() const { return arch_; } const u8 *uuid() const { return uuid_; } bool instrumented() const { return instrumented_; } struct AddressRange { AddressRange *next; uptr beg; uptr end; bool executable; bool writable; AddressRange(uptr beg, uptr end, bool executable, bool writable) : next(nullptr), beg(beg), end(end), executable(executable), writable(writable) {} }; const IntrusiveList &ranges() const { return ranges_; } private: char *full_name_; // Owned. uptr base_address_; uptr max_executable_address_; ModuleArch arch_; u8 uuid_[kModuleUUIDSize]; bool instrumented_; IntrusiveList ranges_; }; // List of LoadedModules. OS-dependent implementation is responsible for // filling this information. class ListOfModules { public: ListOfModules() : modules_(kInitialCapacity) {} ~ListOfModules() { clear(); } void init(); const LoadedModule *begin() const { return modules_.begin(); } LoadedModule *begin() { return modules_.begin(); } const LoadedModule *end() const { return modules_.end(); } LoadedModule *end() { return modules_.end(); } uptr size() const { return modules_.size(); } const LoadedModule &operator[](uptr i) const { CHECK_LT(i, modules_.size()); return modules_[i]; } private: void clear() { for (auto &module : modules_) module.clear(); modules_.clear(); } InternalMmapVector modules_; // We rarely have more than 16K loaded modules. static const uptr kInitialCapacity = 1 << 14; }; // Callback type for iterating over a set of memory ranges. typedef void (*RangeIteratorCallback)(uptr begin, uptr end, void *arg); enum AndroidApiLevel { ANDROID_NOT_ANDROID = 0, ANDROID_KITKAT = 19, ANDROID_LOLLIPOP_MR1 = 22, ANDROID_POST_LOLLIPOP = 23 }; void WriteToSyslog(const char *buffer); #if SANITIZER_MAC void LogFullErrorReport(const char *buffer); #else INLINE void LogFullErrorReport(const char *buffer) {} #endif #if SANITIZER_LINUX || SANITIZER_MAC void WriteOneLineToSyslog(const char *s); void LogMessageOnPrintf(const char *str); #else INLINE void WriteOneLineToSyslog(const char *s) {} INLINE void LogMessageOnPrintf(const char *str) {} #endif #if SANITIZER_LINUX // Initialize Android logging. Any writes before this are silently lost. void AndroidLogInit(); void SetAbortMessage(const char *); #else INLINE void AndroidLogInit() {} // FIXME: MacOS implementation could use CRSetCrashLogMessage. INLINE void SetAbortMessage(const char *) {} #endif #if SANITIZER_ANDROID void SanitizerInitializeUnwinder(); AndroidApiLevel AndroidGetApiLevel(); #else INLINE void AndroidLogWrite(const char *buffer_unused) {} INLINE void SanitizerInitializeUnwinder() {} INLINE AndroidApiLevel AndroidGetApiLevel() { return ANDROID_NOT_ANDROID; } #endif INLINE uptr GetPthreadDestructorIterations() { #if SANITIZER_ANDROID return (AndroidGetApiLevel() == ANDROID_LOLLIPOP_MR1) ? 8 : 4; #elif SANITIZER_POSIX return 4; #else // Unused on Windows. return 0; #endif } void *internal_start_thread(void(*func)(void*), void *arg); void internal_join_thread(void *th); void MaybeStartBackgroudThread(); // Make the compiler think that something is going on there. // Use this inside a loop that looks like memset/memcpy/etc to prevent the // compiler from recognising it and turning it into an actual call to // memset/memcpy/etc. static inline void SanitizerBreakOptimization(void *arg) { #if defined(_MSC_VER) && !defined(__clang__) _ReadWriteBarrier(); #else __asm__ __volatile__("" : : "r" (arg) : "memory"); #endif } struct SignalContext { void *context; uptr addr; uptr pc; uptr sp; uptr bp; bool is_memory_access; enum WriteFlag { UNKNOWN, READ, WRITE } write_flag; SignalContext(void *context, uptr addr, uptr pc, uptr sp, uptr bp, bool is_memory_access, WriteFlag write_flag) : context(context), addr(addr), pc(pc), sp(sp), bp(bp), is_memory_access(is_memory_access), write_flag(write_flag) {} static void DumpAllRegisters(void *context); // Creates signal context in a platform-specific manner. static SignalContext Create(void *siginfo, void *context); // Returns true if the "context" indicates a memory write. static WriteFlag GetWriteFlag(void *context); }; void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp); void MaybeReexec(); template class RunOnDestruction { public: explicit RunOnDestruction(Fn fn) : fn_(fn) {} ~RunOnDestruction() { fn_(); } private: Fn fn_; }; // A simple scope guard. Usage: // auto cleanup = at_scope_exit([]{ do_cleanup; }); template RunOnDestruction at_scope_exit(Fn fn) { return RunOnDestruction(fn); } // Linux on 64-bit s390 had a nasty bug that crashes the whole machine // if a process uses virtual memory over 4TB (as many sanitizers like // to do). This function will abort the process if running on a kernel // that looks vulnerable. #if SANITIZER_LINUX && SANITIZER_S390_64 void AvoidCVE_2016_2143(); #else INLINE void AvoidCVE_2016_2143() {} #endif struct StackDepotStats { uptr n_uniq_ids; uptr allocated; }; // The default value for allocator_release_to_os_interval_ms common flag to // indicate that sanitizer allocator should not attempt to release memory to OS. const s32 kReleaseToOSIntervalNever = -1; void CheckNoDeepBind(const char *filename, int flag); // Returns the requested amount of random data (up to 256 bytes) that can then // be used to seed a PRNG. bool GetRandom(void *buffer, uptr length); } // namespace __sanitizer inline void *operator new(__sanitizer::operator_new_size_type size, __sanitizer::LowLevelAllocator &alloc) { return alloc.Allocate(size); } #endif // SANITIZER_COMMON_H Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_common_interceptors.inc =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_common_interceptors.inc (revision 320536) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_common_interceptors.inc (revision 320537) @@ -1,6406 +1,6459 @@ //===-- sanitizer_common_interceptors.inc -----------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Common function interceptors for tools like AddressSanitizer, // ThreadSanitizer, MemorySanitizer, etc. // // This file should be included into the tool's interceptor file, // which has to define its own macros: // COMMON_INTERCEPTOR_ENTER // COMMON_INTERCEPTOR_ENTER_NOIGNORE // COMMON_INTERCEPTOR_READ_RANGE // COMMON_INTERCEPTOR_WRITE_RANGE // COMMON_INTERCEPTOR_INITIALIZE_RANGE // COMMON_INTERCEPTOR_DIR_ACQUIRE // COMMON_INTERCEPTOR_FD_ACQUIRE // COMMON_INTERCEPTOR_FD_RELEASE // COMMON_INTERCEPTOR_FD_ACCESS // COMMON_INTERCEPTOR_SET_THREAD_NAME // COMMON_INTERCEPTOR_ON_DLOPEN // COMMON_INTERCEPTOR_ON_EXIT // COMMON_INTERCEPTOR_MUTEX_PRE_LOCK // COMMON_INTERCEPTOR_MUTEX_POST_LOCK // COMMON_INTERCEPTOR_MUTEX_UNLOCK // COMMON_INTERCEPTOR_MUTEX_REPAIR // COMMON_INTERCEPTOR_SET_PTHREAD_NAME // COMMON_INTERCEPTOR_HANDLE_RECVMSG // COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED // COMMON_INTERCEPTOR_MEMSET_IMPL // COMMON_INTERCEPTOR_MEMMOVE_IMPL // COMMON_INTERCEPTOR_MEMCPY_IMPL // COMMON_INTERCEPTOR_COPY_STRING // COMMON_INTERCEPTOR_STRNDUP_IMPL //===----------------------------------------------------------------------===// #include "interception/interception.h" #include "sanitizer_addrhashmap.h" #include "sanitizer_placement_new.h" #include "sanitizer_platform_interceptors.h" #include "sanitizer_tls_get_addr.h" #include #if SANITIZER_INTERCEPTOR_HOOKS #define CALL_WEAK_INTERCEPTOR_HOOK(f, ...) f(__VA_ARGS__); #define DECLARE_WEAK_INTERCEPTOR_HOOK(f, ...) \ SANITIZER_INTERFACE_WEAK_DEF(void, f, __VA_ARGS__) {} #else #define DECLARE_WEAK_INTERCEPTOR_HOOK(f, ...) #define CALL_WEAK_INTERCEPTOR_HOOK(f, ...) #endif // SANITIZER_INTERCEPTOR_HOOKS #if SANITIZER_WINDOWS && !defined(va_copy) #define va_copy(dst, src) ((dst) = (src)) #endif // _WIN32 #if SANITIZER_FREEBSD #define pthread_setname_np pthread_set_name_np #define inet_aton __inet_aton #define inet_pton __inet_pton #define iconv __bsd_iconv #endif // Platform-specific options. #if SANITIZER_MAC namespace __sanitizer { bool PlatformHasDifferentMemcpyAndMemmove(); } #define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE \ (__sanitizer::PlatformHasDifferentMemcpyAndMemmove()) #elif SANITIZER_WINDOWS64 #define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE false #else #define PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE true #endif // SANITIZER_MAC #ifndef COMMON_INTERCEPTOR_INITIALIZE_RANGE #define COMMON_INTERCEPTOR_INITIALIZE_RANGE(p, size) {} #endif #ifndef COMMON_INTERCEPTOR_UNPOISON_PARAM #define COMMON_INTERCEPTOR_UNPOISON_PARAM(count) {} #endif #ifndef COMMON_INTERCEPTOR_FD_ACCESS #define COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd) {} #endif #ifndef COMMON_INTERCEPTOR_MUTEX_PRE_LOCK #define COMMON_INTERCEPTOR_MUTEX_PRE_LOCK(ctx, m) {} #endif #ifndef COMMON_INTERCEPTOR_MUTEX_POST_LOCK #define COMMON_INTERCEPTOR_MUTEX_POST_LOCK(ctx, m) {} #endif #ifndef COMMON_INTERCEPTOR_MUTEX_UNLOCK #define COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m) {} #endif #ifndef COMMON_INTERCEPTOR_MUTEX_REPAIR #define COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m) {} #endif #ifndef COMMON_INTERCEPTOR_MUTEX_INVALID #define COMMON_INTERCEPTOR_MUTEX_INVALID(ctx, m) {} #endif #ifndef COMMON_INTERCEPTOR_HANDLE_RECVMSG #define COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg) ((void)(msg)) #endif #ifndef COMMON_INTERCEPTOR_FILE_OPEN #define COMMON_INTERCEPTOR_FILE_OPEN(ctx, file, path) {} #endif #ifndef COMMON_INTERCEPTOR_FILE_CLOSE #define COMMON_INTERCEPTOR_FILE_CLOSE(ctx, file) {} #endif #ifndef COMMON_INTERCEPTOR_LIBRARY_LOADED #define COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, handle) {} #endif #ifndef COMMON_INTERCEPTOR_LIBRARY_UNLOADED #define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() {} #endif #ifndef COMMON_INTERCEPTOR_ENTER_NOIGNORE #define COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, ...) \ COMMON_INTERCEPTOR_ENTER(ctx, __VA_ARGS__) #endif #ifndef COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED #define COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED (0) #endif #define COMMON_INTERCEPTOR_READ_STRING(ctx, s, n) \ COMMON_INTERCEPTOR_READ_RANGE((ctx), (s), \ common_flags()->strict_string_checks ? (REAL(strlen)(s)) + 1 : (n) ) #ifndef COMMON_INTERCEPTOR_ON_DLOPEN #define COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag) \ CheckNoDeepBind(filename, flag); #endif #ifndef COMMON_INTERCEPTOR_GET_TLS_RANGE #define COMMON_INTERCEPTOR_GET_TLS_RANGE(begin, end) *begin = *end = 0; #endif #ifndef COMMON_INTERCEPTOR_ACQUIRE #define COMMON_INTERCEPTOR_ACQUIRE(ctx, u) {} #endif #ifndef COMMON_INTERCEPTOR_RELEASE #define COMMON_INTERCEPTOR_RELEASE(ctx, u) {} #endif #ifndef COMMON_INTERCEPTOR_USER_CALLBACK_START #define COMMON_INTERCEPTOR_USER_CALLBACK_START() {} #endif #ifndef COMMON_INTERCEPTOR_USER_CALLBACK_END #define COMMON_INTERCEPTOR_USER_CALLBACK_END() {} #endif #ifdef SANITIZER_NLDBL_VERSION #define COMMON_INTERCEPT_FUNCTION_LDBL(fn) \ COMMON_INTERCEPT_FUNCTION_VER(fn, SANITIZER_NLDBL_VERSION) #else #define COMMON_INTERCEPT_FUNCTION_LDBL(fn) \ COMMON_INTERCEPT_FUNCTION(fn) #endif #ifndef COMMON_INTERCEPTOR_MEMSET_IMPL #define COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, dst, v, size) \ { \ if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) \ return internal_memset(dst, v, size); \ COMMON_INTERCEPTOR_ENTER(ctx, memset, dst, v, size); \ if (common_flags()->intercept_intrin) \ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, size); \ return REAL(memset)(dst, v, size); \ } #endif #ifndef COMMON_INTERCEPTOR_MEMMOVE_IMPL #define COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, dst, src, size) \ { \ if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) \ return internal_memmove(dst, src, size); \ COMMON_INTERCEPTOR_ENTER(ctx, memmove, dst, src, size); \ if (common_flags()->intercept_intrin) { \ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, size); \ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, size); \ } \ return REAL(memmove)(dst, src, size); \ } #endif #ifndef COMMON_INTERCEPTOR_MEMCPY_IMPL #define COMMON_INTERCEPTOR_MEMCPY_IMPL(ctx, dst, src, size) \ { \ if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) { \ return internal_memmove(dst, src, size); \ } \ COMMON_INTERCEPTOR_ENTER(ctx, memcpy, dst, src, size); \ if (common_flags()->intercept_intrin) { \ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, size); \ COMMON_INTERCEPTOR_READ_RANGE(ctx, src, size); \ } \ return REAL(memcpy)(dst, src, size); \ } #endif #ifndef COMMON_INTERCEPTOR_COPY_STRING #define COMMON_INTERCEPTOR_COPY_STRING(ctx, to, from, size) {} #endif #ifndef COMMON_INTERCEPTOR_STRNDUP_IMPL -#define COMMON_INTERCEPTOR_STRNDUP_IMPL(ctx, s, size) \ - COMMON_INTERCEPTOR_ENTER(ctx, strndup, s, size); \ - uptr copy_length = internal_strnlen(s, size); \ - char *new_mem = (char *)WRAP(malloc)(copy_length + 1); \ - if (common_flags()->intercept_strndup) { \ - COMMON_INTERCEPTOR_READ_STRING(ctx, s, Min(size, copy_length + 1)); \ - } \ - COMMON_INTERCEPTOR_COPY_STRING(ctx, new_mem, s, copy_length); \ - internal_memcpy(new_mem, s, copy_length); \ - new_mem[copy_length] = '\0'; \ +#define COMMON_INTERCEPTOR_STRNDUP_IMPL(ctx, s, size) \ + COMMON_INTERCEPTOR_ENTER(ctx, strndup, s, size); \ + uptr copy_length = internal_strnlen(s, size); \ + char *new_mem = (char *)WRAP(malloc)(copy_length + 1); \ + if (common_flags()->intercept_strndup) { \ + COMMON_INTERCEPTOR_READ_STRING(ctx, s, Min(size, copy_length + 1)); \ + } \ + COMMON_INTERCEPTOR_COPY_STRING(ctx, new_mem, s, copy_length); \ + internal_memcpy(new_mem, s, copy_length); \ + new_mem[copy_length] = '\0'; \ return new_mem; #endif struct FileMetadata { // For open_memstream(). char **addr; SIZE_T *size; }; struct CommonInterceptorMetadata { enum { CIMT_INVALID = 0, CIMT_FILE } type; union { FileMetadata file; }; }; typedef AddrHashMap MetadataHashMap; static MetadataHashMap *interceptor_metadata_map; #if SI_NOT_WINDOWS UNUSED static void SetInterceptorMetadata(__sanitizer_FILE *addr, const FileMetadata &file) { MetadataHashMap::Handle h(interceptor_metadata_map, (uptr)addr); CHECK(h.created()); h->type = CommonInterceptorMetadata::CIMT_FILE; h->file = file; } UNUSED static const FileMetadata *GetInterceptorMetadata( __sanitizer_FILE *addr) { MetadataHashMap::Handle h(interceptor_metadata_map, (uptr)addr, /* remove */ false, /* create */ false); if (h.exists()) { CHECK(!h.created()); CHECK(h->type == CommonInterceptorMetadata::CIMT_FILE); return &h->file; } else { return 0; } } UNUSED static void DeleteInterceptorMetadata(void *addr) { MetadataHashMap::Handle h(interceptor_metadata_map, (uptr)addr, true); CHECK(h.exists()); } #endif // SI_NOT_WINDOWS #if SANITIZER_INTERCEPT_STRLEN INTERCEPTOR(SIZE_T, strlen, const char *s) { // Sometimes strlen is called prior to InitializeCommonInterceptors, // in which case the REAL(strlen) typically used in // COMMON_INTERCEPTOR_ENTER will fail. We use internal_strlen here // to handle that. if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) return internal_strlen(s); void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strlen, s); SIZE_T result = REAL(strlen)(s); if (common_flags()->intercept_strlen) COMMON_INTERCEPTOR_READ_RANGE(ctx, s, result + 1); return result; } #define INIT_STRLEN COMMON_INTERCEPT_FUNCTION(strlen) #else #define INIT_STRLEN #endif #if SANITIZER_INTERCEPT_STRNLEN INTERCEPTOR(SIZE_T, strnlen, const char *s, SIZE_T maxlen) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strnlen, s, maxlen); SIZE_T length = REAL(strnlen)(s, maxlen); if (common_flags()->intercept_strlen) COMMON_INTERCEPTOR_READ_RANGE(ctx, s, Min(length + 1, maxlen)); return length; } #define INIT_STRNLEN COMMON_INTERCEPT_FUNCTION(strnlen) #else #define INIT_STRNLEN #endif #if SANITIZER_INTERCEPT_STRNDUP INTERCEPTOR(char*, strndup, const char *s, uptr size) { void *ctx; COMMON_INTERCEPTOR_STRNDUP_IMPL(ctx, s, size); } #define INIT_STRNDUP COMMON_INTERCEPT_FUNCTION(strndup) #else #define INIT_STRNDUP #endif // SANITIZER_INTERCEPT_STRNDUP #if SANITIZER_INTERCEPT___STRNDUP INTERCEPTOR(char*, __strndup, const char *s, uptr size) { void *ctx; COMMON_INTERCEPTOR_STRNDUP_IMPL(ctx, s, size); } #define INIT___STRNDUP COMMON_INTERCEPT_FUNCTION(__strndup) #else #define INIT___STRNDUP #endif // SANITIZER_INTERCEPT___STRNDUP #if SANITIZER_INTERCEPT_TEXTDOMAIN INTERCEPTOR(char*, textdomain, const char *domainname) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, textdomain, domainname); if (domainname) COMMON_INTERCEPTOR_READ_STRING(ctx, domainname, 0); char *domain = REAL(textdomain)(domainname); if (domain) { COMMON_INTERCEPTOR_INITIALIZE_RANGE(domain, REAL(strlen)(domain) + 1); } return domain; } #define INIT_TEXTDOMAIN COMMON_INTERCEPT_FUNCTION(textdomain) #else #define INIT_TEXTDOMAIN #endif #if SANITIZER_INTERCEPT_STRCMP static inline int CharCmpX(unsigned char c1, unsigned char c2) { return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1; } DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strcmp, uptr called_pc, const char *s1, const char *s2, int result) INTERCEPTOR(int, strcmp, const char *s1, const char *s2) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strcmp, s1, s2); unsigned char c1, c2; uptr i; for (i = 0;; i++) { c1 = (unsigned char)s1[i]; c2 = (unsigned char)s2[i]; if (c1 != c2 || c1 == '\0') break; } COMMON_INTERCEPTOR_READ_STRING(ctx, s1, i + 1); COMMON_INTERCEPTOR_READ_STRING(ctx, s2, i + 1); int result = CharCmpX(c1, c2); CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strcmp, GET_CALLER_PC(), s1, s2, result); return result; } DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strncmp, uptr called_pc, const char *s1, const char *s2, uptr n, int result) INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr size) { if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) return internal_strncmp(s1, s2, size); void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strncmp, s1, s2, size); unsigned char c1 = 0, c2 = 0; uptr i; for (i = 0; i < size; i++) { c1 = (unsigned char)s1[i]; c2 = (unsigned char)s2[i]; if (c1 != c2 || c1 == '\0') break; } uptr i1 = i; uptr i2 = i; if (common_flags()->strict_string_checks) { for (; i1 < size && s1[i1]; i1++) {} for (; i2 < size && s2[i2]; i2++) {} } COMMON_INTERCEPTOR_READ_RANGE((ctx), (s1), Min(i1 + 1, size)); COMMON_INTERCEPTOR_READ_RANGE((ctx), (s2), Min(i2 + 1, size)); int result = CharCmpX(c1, c2); CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strncmp, GET_CALLER_PC(), s1, s2, size, result); return result; } #define INIT_STRCMP COMMON_INTERCEPT_FUNCTION(strcmp) #define INIT_STRNCMP COMMON_INTERCEPT_FUNCTION(strncmp) #else #define INIT_STRCMP #define INIT_STRNCMP #endif #if SANITIZER_INTERCEPT_STRCASECMP static inline int CharCaseCmp(unsigned char c1, unsigned char c2) { int c1_low = ToLower(c1); int c2_low = ToLower(c2); return c1_low - c2_low; } DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strcasecmp, uptr called_pc, const char *s1, const char *s2, int result) INTERCEPTOR(int, strcasecmp, const char *s1, const char *s2) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strcasecmp, s1, s2); unsigned char c1 = 0, c2 = 0; uptr i; for (i = 0;; i++) { c1 = (unsigned char)s1[i]; c2 = (unsigned char)s2[i]; if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break; } COMMON_INTERCEPTOR_READ_STRING(ctx, s1, i + 1); COMMON_INTERCEPTOR_READ_STRING(ctx, s2, i + 1); int result = CharCaseCmp(c1, c2); CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strcasecmp, GET_CALLER_PC(), s1, s2, result); return result; } DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strncasecmp, uptr called_pc, const char *s1, const char *s2, uptr size, int result) INTERCEPTOR(int, strncasecmp, const char *s1, const char *s2, SIZE_T size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strncasecmp, s1, s2, size); unsigned char c1 = 0, c2 = 0; uptr i; for (i = 0; i < size; i++) { c1 = (unsigned char)s1[i]; c2 = (unsigned char)s2[i]; if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break; } uptr i1 = i; uptr i2 = i; if (common_flags()->strict_string_checks) { for (; i1 < size && s1[i1]; i1++) {} for (; i2 < size && s2[i2]; i2++) {} } COMMON_INTERCEPTOR_READ_RANGE((ctx), (s1), Min(i1 + 1, size)); COMMON_INTERCEPTOR_READ_RANGE((ctx), (s2), Min(i2 + 1, size)); int result = CharCaseCmp(c1, c2); CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strncasecmp, GET_CALLER_PC(), s1, s2, size, result); return result; } #define INIT_STRCASECMP COMMON_INTERCEPT_FUNCTION(strcasecmp) #define INIT_STRNCASECMP COMMON_INTERCEPT_FUNCTION(strncasecmp) #else #define INIT_STRCASECMP #define INIT_STRNCASECMP #endif #if SANITIZER_INTERCEPT_STRSTR || SANITIZER_INTERCEPT_STRCASESTR static inline void StrstrCheck(void *ctx, char *r, const char *s1, const char *s2) { uptr len1 = REAL(strlen)(s1); uptr len2 = REAL(strlen)(s2); COMMON_INTERCEPTOR_READ_STRING(ctx, s1, r ? r - s1 + len2 : len1 + 1); COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, len2 + 1); } #endif #if SANITIZER_INTERCEPT_STRSTR DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strstr, uptr called_pc, const char *s1, const char *s2, char *result) INTERCEPTOR(char*, strstr, const char *s1, const char *s2) { if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) return internal_strstr(s1, s2); void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strstr, s1, s2); char *r = REAL(strstr)(s1, s2); if (common_flags()->intercept_strstr) StrstrCheck(ctx, r, s1, s2); CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strstr, GET_CALLER_PC(), s1, s2, r); return r; } #define INIT_STRSTR COMMON_INTERCEPT_FUNCTION(strstr); #else #define INIT_STRSTR #endif #if SANITIZER_INTERCEPT_STRCASESTR DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strcasestr, uptr called_pc, const char *s1, const char *s2, char *result) INTERCEPTOR(char*, strcasestr, const char *s1, const char *s2) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strcasestr, s1, s2); char *r = REAL(strcasestr)(s1, s2); if (common_flags()->intercept_strstr) StrstrCheck(ctx, r, s1, s2); CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_strcasestr, GET_CALLER_PC(), s1, s2, r); return r; } #define INIT_STRCASESTR COMMON_INTERCEPT_FUNCTION(strcasestr); #else #define INIT_STRCASESTR #endif #if SANITIZER_INTERCEPT_STRTOK INTERCEPTOR(char*, strtok, char *str, const char *delimiters) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strtok, str, delimiters); if (!common_flags()->intercept_strtok) { return REAL(strtok)(str, delimiters); } if (common_flags()->strict_string_checks) { // If strict_string_checks is enabled, we check the whole first argument // string on the first call (strtok saves this string in a static buffer // for subsequent calls). We do not need to check strtok's result. // As the delimiters can change, we check them every call. if (str != nullptr) { COMMON_INTERCEPTOR_READ_RANGE(ctx, str, REAL(strlen)(str) + 1); } COMMON_INTERCEPTOR_READ_RANGE(ctx, delimiters, REAL(strlen)(delimiters) + 1); return REAL(strtok)(str, delimiters); } else { // However, when strict_string_checks is disabled we cannot check the // whole string on the first call. Instead, we check the result string // which is guaranteed to be a NULL-terminated substring of the first // argument. We also conservatively check one character of str and the // delimiters. if (str != nullptr) { COMMON_INTERCEPTOR_READ_STRING(ctx, str, 1); } COMMON_INTERCEPTOR_READ_RANGE(ctx, delimiters, 1); char *result = REAL(strtok)(str, delimiters); if (result != nullptr) { COMMON_INTERCEPTOR_READ_RANGE(ctx, result, REAL(strlen)(result) + 1); } else if (str != nullptr) { // No delimiter were found, it's safe to assume that the entire str was // scanned. COMMON_INTERCEPTOR_READ_RANGE(ctx, str, REAL(strlen)(str) + 1); } return result; } } #define INIT_STRTOK COMMON_INTERCEPT_FUNCTION(strtok) #else #define INIT_STRTOK #endif #if SANITIZER_INTERCEPT_MEMMEM DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_memmem, uptr called_pc, const void *s1, SIZE_T len1, const void *s2, SIZE_T len2, void *result) INTERCEPTOR(void*, memmem, const void *s1, SIZE_T len1, const void *s2, SIZE_T len2) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, memmem, s1, len1, s2, len2); void *r = REAL(memmem)(s1, len1, s2, len2); if (common_flags()->intercept_memmem) { COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, len1); COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, len2); } CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_memmem, GET_CALLER_PC(), s1, len1, s2, len2, r); return r; } #define INIT_MEMMEM COMMON_INTERCEPT_FUNCTION(memmem); #else #define INIT_MEMMEM #endif // SANITIZER_INTERCEPT_MEMMEM #if SANITIZER_INTERCEPT_STRCHR INTERCEPTOR(char*, strchr, const char *s, int c) { void *ctx; if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) return internal_strchr(s, c); COMMON_INTERCEPTOR_ENTER(ctx, strchr, s, c); char *result = REAL(strchr)(s, c); if (common_flags()->intercept_strchr) { // Keep strlen as macro argument, as macro may ignore it. COMMON_INTERCEPTOR_READ_STRING(ctx, s, (result ? result - s : REAL(strlen)(s)) + 1); } return result; } #define INIT_STRCHR COMMON_INTERCEPT_FUNCTION(strchr) #else #define INIT_STRCHR #endif #if SANITIZER_INTERCEPT_STRCHRNUL INTERCEPTOR(char*, strchrnul, const char *s, int c) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strchrnul, s, c); char *result = REAL(strchrnul)(s, c); uptr len = result - s + 1; if (common_flags()->intercept_strchr) COMMON_INTERCEPTOR_READ_STRING(ctx, s, len); return result; } #define INIT_STRCHRNUL COMMON_INTERCEPT_FUNCTION(strchrnul) #else #define INIT_STRCHRNUL #endif #if SANITIZER_INTERCEPT_STRRCHR INTERCEPTOR(char*, strrchr, const char *s, int c) { void *ctx; if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) return internal_strrchr(s, c); COMMON_INTERCEPTOR_ENTER(ctx, strrchr, s, c); if (common_flags()->intercept_strchr) COMMON_INTERCEPTOR_READ_RANGE(ctx, s, REAL(strlen)(s) + 1); return REAL(strrchr)(s, c); } #define INIT_STRRCHR COMMON_INTERCEPT_FUNCTION(strrchr) #else #define INIT_STRRCHR #endif #if SANITIZER_INTERCEPT_STRSPN INTERCEPTOR(SIZE_T, strspn, const char *s1, const char *s2) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strspn, s1, s2); SIZE_T r = REAL(strspn)(s1, s2); if (common_flags()->intercept_strspn) { COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, REAL(strlen)(s2) + 1); COMMON_INTERCEPTOR_READ_STRING(ctx, s1, r + 1); } return r; } INTERCEPTOR(SIZE_T, strcspn, const char *s1, const char *s2) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strcspn, s1, s2); SIZE_T r = REAL(strcspn)(s1, s2); if (common_flags()->intercept_strspn) { COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, REAL(strlen)(s2) + 1); COMMON_INTERCEPTOR_READ_STRING(ctx, s1, r + 1); } return r; } #define INIT_STRSPN \ COMMON_INTERCEPT_FUNCTION(strspn); \ COMMON_INTERCEPT_FUNCTION(strcspn); #else #define INIT_STRSPN #endif #if SANITIZER_INTERCEPT_STRPBRK INTERCEPTOR(char *, strpbrk, const char *s1, const char *s2) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strpbrk, s1, s2); char *r = REAL(strpbrk)(s1, s2); if (common_flags()->intercept_strpbrk) { COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, REAL(strlen)(s2) + 1); COMMON_INTERCEPTOR_READ_STRING(ctx, s1, r ? r - s1 + 1 : REAL(strlen)(s1) + 1); } return r; } #define INIT_STRPBRK COMMON_INTERCEPT_FUNCTION(strpbrk); #else #define INIT_STRPBRK #endif #if SANITIZER_INTERCEPT_MEMSET INTERCEPTOR(void *, memset, void *dst, int v, uptr size) { void *ctx; COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, dst, v, size); } #define INIT_MEMSET COMMON_INTERCEPT_FUNCTION(memset) #else #define INIT_MEMSET #endif #if SANITIZER_INTERCEPT_MEMMOVE INTERCEPTOR(void *, memmove, void *dst, const void *src, uptr size) { void *ctx; COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, dst, src, size); } #define INIT_MEMMOVE COMMON_INTERCEPT_FUNCTION(memmove) #else #define INIT_MEMMOVE #endif #if SANITIZER_INTERCEPT_MEMCPY INTERCEPTOR(void *, memcpy, void *dst, const void *src, uptr size) { // On OS X, calling internal_memcpy here will cause memory corruptions, // because memcpy and memmove are actually aliases of the same // implementation. We need to use internal_memmove here. // N.B.: If we switch this to internal_ we'll have to use internal_memmove // due to memcpy being an alias of memmove on OS X. void *ctx; if (PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE) { COMMON_INTERCEPTOR_MEMCPY_IMPL(ctx, dst, src, size); } else { COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, dst, src, size); } } #define INIT_MEMCPY \ do { \ if (PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE) { \ COMMON_INTERCEPT_FUNCTION(memcpy); \ } else { \ ASSIGN_REAL(memcpy, memmove); \ } \ CHECK(REAL(memcpy)); \ } while (false) #else #define INIT_MEMCPY #endif #if SANITIZER_INTERCEPT_MEMCMP DECLARE_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_memcmp, uptr called_pc, const void *s1, const void *s2, uptr n, int result) INTERCEPTOR(int, memcmp, const void *a1, const void *a2, uptr size) { if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) return internal_memcmp(a1, a2, size); void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, memcmp, a1, a2, size); if (common_flags()->intercept_memcmp) { if (common_flags()->strict_memcmp) { // Check the entire regions even if the first bytes of the buffers are // different. COMMON_INTERCEPTOR_READ_RANGE(ctx, a1, size); COMMON_INTERCEPTOR_READ_RANGE(ctx, a2, size); // Fallthrough to REAL(memcmp) below. } else { unsigned char c1 = 0, c2 = 0; const unsigned char *s1 = (const unsigned char*)a1; const unsigned char *s2 = (const unsigned char*)a2; uptr i; for (i = 0; i < size; i++) { c1 = s1[i]; c2 = s2[i]; if (c1 != c2) break; } COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, Min(i + 1, size)); COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, Min(i + 1, size)); int r = CharCmpX(c1, c2); CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_memcmp, GET_CALLER_PC(), a1, a2, size, r); return r; } } int result = REAL(memcmp(a1, a2, size)); CALL_WEAK_INTERCEPTOR_HOOK(__sanitizer_weak_hook_memcmp, GET_CALLER_PC(), a1, a2, size, result); return result; } #define INIT_MEMCMP COMMON_INTERCEPT_FUNCTION(memcmp) #else #define INIT_MEMCMP #endif #if SANITIZER_INTERCEPT_MEMCHR INTERCEPTOR(void*, memchr, const void *s, int c, SIZE_T n) { if (COMMON_INTERCEPTOR_NOTHING_IS_INITIALIZED) return internal_memchr(s, c, n); void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, memchr, s, c, n); #if SANITIZER_WINDOWS void *res; if (REAL(memchr)) { res = REAL(memchr)(s, c, n); } else { res = internal_memchr(s, c, n); } #else void *res = REAL(memchr)(s, c, n); #endif uptr len = res ? (char *)res - (const char *)s + 1 : n; COMMON_INTERCEPTOR_READ_RANGE(ctx, s, len); return res; } #define INIT_MEMCHR COMMON_INTERCEPT_FUNCTION(memchr) #else #define INIT_MEMCHR #endif #if SANITIZER_INTERCEPT_MEMRCHR INTERCEPTOR(void*, memrchr, const void *s, int c, SIZE_T n) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, memrchr, s, c, n); COMMON_INTERCEPTOR_READ_RANGE(ctx, s, n); return REAL(memrchr)(s, c, n); } #define INIT_MEMRCHR COMMON_INTERCEPT_FUNCTION(memrchr) #else #define INIT_MEMRCHR #endif #if SANITIZER_INTERCEPT_FREXP INTERCEPTOR(double, frexp, double x, int *exp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, frexp, x, exp); // Assuming frexp() always writes to |exp|. COMMON_INTERCEPTOR_WRITE_RANGE(ctx, exp, sizeof(*exp)); double res = REAL(frexp)(x, exp); return res; } #define INIT_FREXP COMMON_INTERCEPT_FUNCTION(frexp); #else #define INIT_FREXP #endif // SANITIZER_INTERCEPT_FREXP #if SANITIZER_INTERCEPT_FREXPF_FREXPL INTERCEPTOR(float, frexpf, float x, int *exp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, frexpf, x, exp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. float res = REAL(frexpf)(x, exp); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, exp, sizeof(*exp)); return res; } INTERCEPTOR(long double, frexpl, long double x, int *exp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, frexpl, x, exp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. long double res = REAL(frexpl)(x, exp); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, exp, sizeof(*exp)); return res; } #define INIT_FREXPF_FREXPL \ COMMON_INTERCEPT_FUNCTION(frexpf); \ COMMON_INTERCEPT_FUNCTION_LDBL(frexpl) #else #define INIT_FREXPF_FREXPL #endif // SANITIZER_INTERCEPT_FREXPF_FREXPL #if SI_NOT_WINDOWS static void write_iovec(void *ctx, struct __sanitizer_iovec *iovec, SIZE_T iovlen, SIZE_T maxlen) { for (SIZE_T i = 0; i < iovlen && maxlen; ++i) { SSIZE_T sz = Min(iovec[i].iov_len, maxlen); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iovec[i].iov_base, sz); maxlen -= sz; } } static void read_iovec(void *ctx, struct __sanitizer_iovec *iovec, SIZE_T iovlen, SIZE_T maxlen) { COMMON_INTERCEPTOR_READ_RANGE(ctx, iovec, sizeof(*iovec) * iovlen); for (SIZE_T i = 0; i < iovlen && maxlen; ++i) { SSIZE_T sz = Min(iovec[i].iov_len, maxlen); COMMON_INTERCEPTOR_READ_RANGE(ctx, iovec[i].iov_base, sz); maxlen -= sz; } } #endif #if SANITIZER_INTERCEPT_READ INTERCEPTOR(SSIZE_T, read, int fd, void *ptr, SIZE_T count) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, read, fd, ptr, count); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(read)(fd, ptr, count); if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } #define INIT_READ COMMON_INTERCEPT_FUNCTION(read) #else #define INIT_READ #endif #if SANITIZER_INTERCEPT_FREAD INTERCEPTOR(SIZE_T, fread, void *ptr, SIZE_T size, SIZE_T nmemb, void *file) { // libc file streams can call user-supplied functions, see fopencookie. void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fread, ptr, size, nmemb, file); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SIZE_T res = REAL(fread)(ptr, size, nmemb, file); if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res * size); return res; } #define INIT_FREAD COMMON_INTERCEPT_FUNCTION(fread) #else #define INIT_FREAD #endif #if SANITIZER_INTERCEPT_PREAD INTERCEPTOR(SSIZE_T, pread, int fd, void *ptr, SIZE_T count, OFF_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pread, fd, ptr, count, offset); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(pread)(fd, ptr, count, offset); if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } #define INIT_PREAD COMMON_INTERCEPT_FUNCTION(pread) #else #define INIT_PREAD #endif #if SANITIZER_INTERCEPT_PREAD64 INTERCEPTOR(SSIZE_T, pread64, int fd, void *ptr, SIZE_T count, OFF64_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pread64, fd, ptr, count, offset); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(pread64)(fd, ptr, count, offset); if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } #define INIT_PREAD64 COMMON_INTERCEPT_FUNCTION(pread64) #else #define INIT_PREAD64 #endif #if SANITIZER_INTERCEPT_READV INTERCEPTOR_WITH_SUFFIX(SSIZE_T, readv, int fd, __sanitizer_iovec *iov, int iovcnt) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, readv, fd, iov, iovcnt); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); SSIZE_T res = REAL(readv)(fd, iov, iovcnt); if (res > 0) write_iovec(ctx, iov, iovcnt, res); if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } #define INIT_READV COMMON_INTERCEPT_FUNCTION(readv) #else #define INIT_READV #endif #if SANITIZER_INTERCEPT_PREADV INTERCEPTOR(SSIZE_T, preadv, int fd, __sanitizer_iovec *iov, int iovcnt, OFF_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, preadv, fd, iov, iovcnt, offset); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); SSIZE_T res = REAL(preadv)(fd, iov, iovcnt, offset); if (res > 0) write_iovec(ctx, iov, iovcnt, res); if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } #define INIT_PREADV COMMON_INTERCEPT_FUNCTION(preadv) #else #define INIT_PREADV #endif #if SANITIZER_INTERCEPT_PREADV64 INTERCEPTOR(SSIZE_T, preadv64, int fd, __sanitizer_iovec *iov, int iovcnt, OFF64_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, preadv64, fd, iov, iovcnt, offset); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); SSIZE_T res = REAL(preadv64)(fd, iov, iovcnt, offset); if (res > 0) write_iovec(ctx, iov, iovcnt, res); if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } #define INIT_PREADV64 COMMON_INTERCEPT_FUNCTION(preadv64) #else #define INIT_PREADV64 #endif #if SANITIZER_INTERCEPT_WRITE INTERCEPTOR(SSIZE_T, write, int fd, void *ptr, SIZE_T count) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, write, fd, ptr, count); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); SSIZE_T res = REAL(write)(fd, ptr, count); // FIXME: this check should be _before_ the call to REAL(write), not after if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); return res; } #define INIT_WRITE COMMON_INTERCEPT_FUNCTION(write) #else #define INIT_WRITE #endif #if SANITIZER_INTERCEPT_FWRITE INTERCEPTOR(SIZE_T, fwrite, const void *p, uptr size, uptr nmemb, void *file) { // libc file streams can call user-supplied functions, see fopencookie. void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fwrite, p, size, nmemb, file); SIZE_T res = REAL(fwrite)(p, size, nmemb, file); if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, p, res * size); return res; } #define INIT_FWRITE COMMON_INTERCEPT_FUNCTION(fwrite) #else #define INIT_FWRITE #endif #if SANITIZER_INTERCEPT_PWRITE INTERCEPTOR(SSIZE_T, pwrite, int fd, void *ptr, SIZE_T count, OFF_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pwrite, fd, ptr, count, offset); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); SSIZE_T res = REAL(pwrite)(fd, ptr, count, offset); if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); return res; } #define INIT_PWRITE COMMON_INTERCEPT_FUNCTION(pwrite) #else #define INIT_PWRITE #endif #if SANITIZER_INTERCEPT_PWRITE64 INTERCEPTOR(SSIZE_T, pwrite64, int fd, void *ptr, OFF64_T count, OFF64_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pwrite64, fd, ptr, count, offset); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); SSIZE_T res = REAL(pwrite64)(fd, ptr, count, offset); if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); return res; } #define INIT_PWRITE64 COMMON_INTERCEPT_FUNCTION(pwrite64) #else #define INIT_PWRITE64 #endif #if SANITIZER_INTERCEPT_WRITEV INTERCEPTOR_WITH_SUFFIX(SSIZE_T, writev, int fd, __sanitizer_iovec *iov, int iovcnt) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, writev, fd, iov, iovcnt); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); SSIZE_T res = REAL(writev)(fd, iov, iovcnt); if (res > 0) read_iovec(ctx, iov, iovcnt, res); return res; } #define INIT_WRITEV COMMON_INTERCEPT_FUNCTION(writev) #else #define INIT_WRITEV #endif #if SANITIZER_INTERCEPT_PWRITEV INTERCEPTOR(SSIZE_T, pwritev, int fd, __sanitizer_iovec *iov, int iovcnt, OFF_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pwritev, fd, iov, iovcnt, offset); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); SSIZE_T res = REAL(pwritev)(fd, iov, iovcnt, offset); if (res > 0) read_iovec(ctx, iov, iovcnt, res); return res; } #define INIT_PWRITEV COMMON_INTERCEPT_FUNCTION(pwritev) #else #define INIT_PWRITEV #endif #if SANITIZER_INTERCEPT_PWRITEV64 INTERCEPTOR(SSIZE_T, pwritev64, int fd, __sanitizer_iovec *iov, int iovcnt, OFF64_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pwritev64, fd, iov, iovcnt, offset); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); SSIZE_T res = REAL(pwritev64)(fd, iov, iovcnt, offset); if (res > 0) read_iovec(ctx, iov, iovcnt, res); return res; } #define INIT_PWRITEV64 COMMON_INTERCEPT_FUNCTION(pwritev64) #else #define INIT_PWRITEV64 #endif #if SANITIZER_INTERCEPT_PRCTL INTERCEPTOR(int, prctl, int option, unsigned long arg2, unsigned long arg3, // NOLINT unsigned long arg4, unsigned long arg5) { // NOLINT void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, prctl, option, arg2, arg3, arg4, arg5); static const int PR_SET_NAME = 15; int res = REAL(prctl(option, arg2, arg3, arg4, arg5)); if (option == PR_SET_NAME) { char buff[16]; internal_strncpy(buff, (char *)arg2, 15); buff[15] = 0; COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, buff); } return res; } #define INIT_PRCTL COMMON_INTERCEPT_FUNCTION(prctl) #else #define INIT_PRCTL #endif // SANITIZER_INTERCEPT_PRCTL #if SANITIZER_INTERCEPT_TIME INTERCEPTOR(unsigned long, time, unsigned long *t) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, time, t); unsigned long local_t; unsigned long res = REAL(time)(&local_t); if (t && res != (unsigned long)-1) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, t, sizeof(*t)); *t = local_t; } return res; } #define INIT_TIME COMMON_INTERCEPT_FUNCTION(time); #else #define INIT_TIME #endif // SANITIZER_INTERCEPT_TIME #if SANITIZER_INTERCEPT_LOCALTIME_AND_FRIENDS static void unpoison_tm(void *ctx, __sanitizer_tm *tm) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tm, sizeof(*tm)); if (tm->tm_zone) { // Can not use COMMON_INTERCEPTOR_WRITE_RANGE here, because tm->tm_zone // can point to shared memory and tsan would report a data race. COMMON_INTERCEPTOR_INITIALIZE_RANGE(tm->tm_zone, REAL(strlen(tm->tm_zone)) + 1); } } INTERCEPTOR(__sanitizer_tm *, localtime, unsigned long *timep) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, localtime, timep); __sanitizer_tm *res = REAL(localtime)(timep); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); unpoison_tm(ctx, res); } return res; } INTERCEPTOR(__sanitizer_tm *, localtime_r, unsigned long *timep, void *result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, localtime_r, timep, result); __sanitizer_tm *res = REAL(localtime_r)(timep, result); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); unpoison_tm(ctx, res); } return res; } INTERCEPTOR(__sanitizer_tm *, gmtime, unsigned long *timep) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gmtime, timep); __sanitizer_tm *res = REAL(gmtime)(timep); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); unpoison_tm(ctx, res); } return res; } INTERCEPTOR(__sanitizer_tm *, gmtime_r, unsigned long *timep, void *result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gmtime_r, timep, result); __sanitizer_tm *res = REAL(gmtime_r)(timep, result); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); unpoison_tm(ctx, res); } return res; } INTERCEPTOR(char *, ctime, unsigned long *timep) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ctime, timep); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. char *res = REAL(ctime)(timep); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); } return res; } INTERCEPTOR(char *, ctime_r, unsigned long *timep, char *result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ctime_r, timep, result); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. char *res = REAL(ctime_r)(timep, result); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); } return res; } INTERCEPTOR(char *, asctime, __sanitizer_tm *tm) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, asctime, tm); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. char *res = REAL(asctime)(tm); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, tm, sizeof(*tm)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); } return res; } INTERCEPTOR(char *, asctime_r, __sanitizer_tm *tm, char *result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, asctime_r, tm, result); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. char *res = REAL(asctime_r)(tm, result); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, tm, sizeof(*tm)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); } return res; } INTERCEPTOR(long, mktime, __sanitizer_tm *tm) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, mktime, tm); COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_sec, sizeof(tm->tm_sec)); COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_min, sizeof(tm->tm_min)); COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_hour, sizeof(tm->tm_hour)); COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_mday, sizeof(tm->tm_mday)); COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_mon, sizeof(tm->tm_mon)); COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_year, sizeof(tm->tm_year)); COMMON_INTERCEPTOR_READ_RANGE(ctx, &tm->tm_isdst, sizeof(tm->tm_isdst)); long res = REAL(mktime)(tm); if (res != -1) unpoison_tm(ctx, tm); return res; } #define INIT_LOCALTIME_AND_FRIENDS \ COMMON_INTERCEPT_FUNCTION(localtime); \ COMMON_INTERCEPT_FUNCTION(localtime_r); \ COMMON_INTERCEPT_FUNCTION(gmtime); \ COMMON_INTERCEPT_FUNCTION(gmtime_r); \ COMMON_INTERCEPT_FUNCTION(ctime); \ COMMON_INTERCEPT_FUNCTION(ctime_r); \ COMMON_INTERCEPT_FUNCTION(asctime); \ COMMON_INTERCEPT_FUNCTION(asctime_r); \ COMMON_INTERCEPT_FUNCTION(mktime); #else #define INIT_LOCALTIME_AND_FRIENDS #endif // SANITIZER_INTERCEPT_LOCALTIME_AND_FRIENDS #if SANITIZER_INTERCEPT_STRPTIME INTERCEPTOR(char *, strptime, char *s, char *format, __sanitizer_tm *tm) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strptime, s, format, tm); if (format) COMMON_INTERCEPTOR_READ_RANGE(ctx, format, REAL(strlen)(format) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. char *res = REAL(strptime)(s, format, tm); COMMON_INTERCEPTOR_READ_STRING(ctx, s, res ? res - s : 0); if (res && tm) { // Do not call unpoison_tm here, because strptime does not, in fact, // initialize the entire struct tm. For example, tm_zone pointer is left // uninitialized. COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tm, sizeof(*tm)); } return res; } #define INIT_STRPTIME COMMON_INTERCEPT_FUNCTION(strptime); #else #define INIT_STRPTIME #endif #if SANITIZER_INTERCEPT_SCANF || SANITIZER_INTERCEPT_PRINTF #include "sanitizer_common_interceptors_format.inc" #define FORMAT_INTERCEPTOR_IMPL(name, vname, ...) \ { \ void *ctx; \ va_list ap; \ va_start(ap, format); \ COMMON_INTERCEPTOR_ENTER(ctx, vname, __VA_ARGS__, ap); \ int res = WRAP(vname)(__VA_ARGS__, ap); \ va_end(ap); \ return res; \ } #endif #if SANITIZER_INTERCEPT_SCANF #define VSCANF_INTERCEPTOR_IMPL(vname, allowGnuMalloc, ...) \ { \ void *ctx; \ COMMON_INTERCEPTOR_ENTER(ctx, vname, __VA_ARGS__); \ va_list aq; \ va_copy(aq, ap); \ int res = REAL(vname)(__VA_ARGS__); \ if (res > 0) \ scanf_common(ctx, res, allowGnuMalloc, format, aq); \ va_end(aq); \ return res; \ } INTERCEPTOR(int, vscanf, const char *format, va_list ap) VSCANF_INTERCEPTOR_IMPL(vscanf, true, format, ap) INTERCEPTOR(int, vsscanf, const char *str, const char *format, va_list ap) VSCANF_INTERCEPTOR_IMPL(vsscanf, true, str, format, ap) INTERCEPTOR(int, vfscanf, void *stream, const char *format, va_list ap) VSCANF_INTERCEPTOR_IMPL(vfscanf, true, stream, format, ap) #if SANITIZER_INTERCEPT_ISOC99_SCANF INTERCEPTOR(int, __isoc99_vscanf, const char *format, va_list ap) VSCANF_INTERCEPTOR_IMPL(__isoc99_vscanf, false, format, ap) INTERCEPTOR(int, __isoc99_vsscanf, const char *str, const char *format, va_list ap) VSCANF_INTERCEPTOR_IMPL(__isoc99_vsscanf, false, str, format, ap) INTERCEPTOR(int, __isoc99_vfscanf, void *stream, const char *format, va_list ap) VSCANF_INTERCEPTOR_IMPL(__isoc99_vfscanf, false, stream, format, ap) #endif // SANITIZER_INTERCEPT_ISOC99_SCANF INTERCEPTOR(int, scanf, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(scanf, vscanf, format) INTERCEPTOR(int, fscanf, void *stream, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(fscanf, vfscanf, stream, format) INTERCEPTOR(int, sscanf, const char *str, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(sscanf, vsscanf, str, format) #if SANITIZER_INTERCEPT_ISOC99_SCANF INTERCEPTOR(int, __isoc99_scanf, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(__isoc99_scanf, __isoc99_vscanf, format) INTERCEPTOR(int, __isoc99_fscanf, void *stream, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(__isoc99_fscanf, __isoc99_vfscanf, stream, format) INTERCEPTOR(int, __isoc99_sscanf, const char *str, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(__isoc99_sscanf, __isoc99_vsscanf, str, format) #endif #endif #if SANITIZER_INTERCEPT_SCANF #define INIT_SCANF \ COMMON_INTERCEPT_FUNCTION_LDBL(scanf); \ COMMON_INTERCEPT_FUNCTION_LDBL(sscanf); \ COMMON_INTERCEPT_FUNCTION_LDBL(fscanf); \ COMMON_INTERCEPT_FUNCTION_LDBL(vscanf); \ COMMON_INTERCEPT_FUNCTION_LDBL(vsscanf); \ COMMON_INTERCEPT_FUNCTION_LDBL(vfscanf); #else #define INIT_SCANF #endif #if SANITIZER_INTERCEPT_ISOC99_SCANF #define INIT_ISOC99_SCANF \ COMMON_INTERCEPT_FUNCTION(__isoc99_scanf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_sscanf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_fscanf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_vscanf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_vsscanf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_vfscanf); #else #define INIT_ISOC99_SCANF #endif #if SANITIZER_INTERCEPT_PRINTF #define VPRINTF_INTERCEPTOR_ENTER(vname, ...) \ void *ctx; \ COMMON_INTERCEPTOR_ENTER(ctx, vname, __VA_ARGS__); \ va_list aq; \ va_copy(aq, ap); #define VPRINTF_INTERCEPTOR_RETURN() \ va_end(aq); #define VPRINTF_INTERCEPTOR_IMPL(vname, ...) \ { \ VPRINTF_INTERCEPTOR_ENTER(vname, __VA_ARGS__); \ if (common_flags()->check_printf) \ printf_common(ctx, format, aq); \ int res = REAL(vname)(__VA_ARGS__); \ VPRINTF_INTERCEPTOR_RETURN(); \ return res; \ } // FIXME: under ASan the REAL() call below may write to freed memory and // corrupt its metadata. See // https://github.com/google/sanitizers/issues/321. #define VSPRINTF_INTERCEPTOR_IMPL(vname, str, ...) \ { \ VPRINTF_INTERCEPTOR_ENTER(vname, str, __VA_ARGS__) \ if (common_flags()->check_printf) { \ printf_common(ctx, format, aq); \ } \ int res = REAL(vname)(str, __VA_ARGS__); \ if (res >= 0) { \ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, str, res + 1); \ } \ VPRINTF_INTERCEPTOR_RETURN(); \ return res; \ } // FIXME: under ASan the REAL() call below may write to freed memory and // corrupt its metadata. See // https://github.com/google/sanitizers/issues/321. #define VSNPRINTF_INTERCEPTOR_IMPL(vname, str, size, ...) \ { \ VPRINTF_INTERCEPTOR_ENTER(vname, str, size, __VA_ARGS__) \ if (common_flags()->check_printf) { \ printf_common(ctx, format, aq); \ } \ int res = REAL(vname)(str, size, __VA_ARGS__); \ if (res >= 0) { \ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, str, Min(size, (SIZE_T)(res + 1))); \ } \ VPRINTF_INTERCEPTOR_RETURN(); \ return res; \ } // FIXME: under ASan the REAL() call below may write to freed memory and // corrupt its metadata. See // https://github.com/google/sanitizers/issues/321. #define VASPRINTF_INTERCEPTOR_IMPL(vname, strp, ...) \ { \ VPRINTF_INTERCEPTOR_ENTER(vname, strp, __VA_ARGS__) \ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, strp, sizeof(char *)); \ if (common_flags()->check_printf) { \ printf_common(ctx, format, aq); \ } \ int res = REAL(vname)(strp, __VA_ARGS__); \ if (res >= 0) { \ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *strp, res + 1); \ } \ VPRINTF_INTERCEPTOR_RETURN(); \ return res; \ } INTERCEPTOR(int, vprintf, const char *format, va_list ap) VPRINTF_INTERCEPTOR_IMPL(vprintf, format, ap) INTERCEPTOR(int, vfprintf, __sanitizer_FILE *stream, const char *format, va_list ap) VPRINTF_INTERCEPTOR_IMPL(vfprintf, stream, format, ap) INTERCEPTOR(int, vsnprintf, char *str, SIZE_T size, const char *format, va_list ap) VSNPRINTF_INTERCEPTOR_IMPL(vsnprintf, str, size, format, ap) #if SANITIZER_INTERCEPT_PRINTF_L INTERCEPTOR(int, vsnprintf_l, char *str, SIZE_T size, void *loc, const char *format, va_list ap) VSNPRINTF_INTERCEPTOR_IMPL(vsnprintf_l, str, size, loc, format, ap) INTERCEPTOR(int, snprintf_l, char *str, SIZE_T size, void *loc, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(snprintf_l, vsnprintf_l, str, size, loc, format) #endif // SANITIZER_INTERCEPT_PRINTF_L INTERCEPTOR(int, vsprintf, char *str, const char *format, va_list ap) VSPRINTF_INTERCEPTOR_IMPL(vsprintf, str, format, ap) INTERCEPTOR(int, vasprintf, char **strp, const char *format, va_list ap) VASPRINTF_INTERCEPTOR_IMPL(vasprintf, strp, format, ap) #if SANITIZER_INTERCEPT_ISOC99_PRINTF INTERCEPTOR(int, __isoc99_vprintf, const char *format, va_list ap) VPRINTF_INTERCEPTOR_IMPL(__isoc99_vprintf, format, ap) INTERCEPTOR(int, __isoc99_vfprintf, __sanitizer_FILE *stream, const char *format, va_list ap) VPRINTF_INTERCEPTOR_IMPL(__isoc99_vfprintf, stream, format, ap) INTERCEPTOR(int, __isoc99_vsnprintf, char *str, SIZE_T size, const char *format, va_list ap) VSNPRINTF_INTERCEPTOR_IMPL(__isoc99_vsnprintf, str, size, format, ap) INTERCEPTOR(int, __isoc99_vsprintf, char *str, const char *format, va_list ap) VSPRINTF_INTERCEPTOR_IMPL(__isoc99_vsprintf, str, format, ap) #endif // SANITIZER_INTERCEPT_ISOC99_PRINTF INTERCEPTOR(int, printf, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(printf, vprintf, format) INTERCEPTOR(int, fprintf, __sanitizer_FILE *stream, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(fprintf, vfprintf, stream, format) INTERCEPTOR(int, sprintf, char *str, const char *format, ...) // NOLINT FORMAT_INTERCEPTOR_IMPL(sprintf, vsprintf, str, format) // NOLINT INTERCEPTOR(int, snprintf, char *str, SIZE_T size, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(snprintf, vsnprintf, str, size, format) INTERCEPTOR(int, asprintf, char **strp, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(asprintf, vasprintf, strp, format) #if SANITIZER_INTERCEPT_ISOC99_PRINTF INTERCEPTOR(int, __isoc99_printf, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(__isoc99_printf, __isoc99_vprintf, format) INTERCEPTOR(int, __isoc99_fprintf, __sanitizer_FILE *stream, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(__isoc99_fprintf, __isoc99_vfprintf, stream, format) INTERCEPTOR(int, __isoc99_sprintf, char *str, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(__isoc99_sprintf, __isoc99_vsprintf, str, format) INTERCEPTOR(int, __isoc99_snprintf, char *str, SIZE_T size, const char *format, ...) FORMAT_INTERCEPTOR_IMPL(__isoc99_snprintf, __isoc99_vsnprintf, str, size, format) #endif // SANITIZER_INTERCEPT_ISOC99_PRINTF #endif // SANITIZER_INTERCEPT_PRINTF #if SANITIZER_INTERCEPT_PRINTF #define INIT_PRINTF \ COMMON_INTERCEPT_FUNCTION_LDBL(printf); \ COMMON_INTERCEPT_FUNCTION_LDBL(sprintf); \ COMMON_INTERCEPT_FUNCTION_LDBL(snprintf); \ COMMON_INTERCEPT_FUNCTION_LDBL(asprintf); \ COMMON_INTERCEPT_FUNCTION_LDBL(fprintf); \ COMMON_INTERCEPT_FUNCTION_LDBL(vprintf); \ COMMON_INTERCEPT_FUNCTION_LDBL(vsprintf); \ COMMON_INTERCEPT_FUNCTION_LDBL(vsnprintf); \ COMMON_INTERCEPT_FUNCTION_LDBL(vasprintf); \ COMMON_INTERCEPT_FUNCTION_LDBL(vfprintf); #else #define INIT_PRINTF #endif #if SANITIZER_INTERCEPT_PRINTF_L #define INIT_PRINTF_L \ COMMON_INTERCEPT_FUNCTION(snprintf_l); \ COMMON_INTERCEPT_FUNCTION(vsnprintf_l); #else #define INIT_PRINTF_L #endif #if SANITIZER_INTERCEPT_ISOC99_PRINTF #define INIT_ISOC99_PRINTF \ COMMON_INTERCEPT_FUNCTION(__isoc99_printf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_sprintf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_snprintf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_fprintf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_vprintf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_vsprintf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_vsnprintf); \ COMMON_INTERCEPT_FUNCTION(__isoc99_vfprintf); #else #define INIT_ISOC99_PRINTF #endif #if SANITIZER_INTERCEPT_IOCTL #include "sanitizer_common_interceptors_ioctl.inc" INTERCEPTOR(int, ioctl, int d, unsigned long request, ...) { // We need a frame pointer, because we call into ioctl_common_[pre|post] which // can trigger a report and we need to be able to unwind through this // function. On Mac in debug mode we might not have a frame pointer, because // ioctl_common_[pre|post] doesn't get inlined here. ENABLE_FRAME_POINTER; void *ctx; va_list ap; va_start(ap, request); void *arg = va_arg(ap, void *); va_end(ap); COMMON_INTERCEPTOR_ENTER(ctx, ioctl, d, request, arg); CHECK(ioctl_initialized); // Note: TSan does not use common flags, and they are zero-initialized. // This effectively disables ioctl handling in TSan. if (!common_flags()->handle_ioctl) return REAL(ioctl)(d, request, arg); // Although request is unsigned long, the rest of the interceptor uses it // as just "unsigned" to save space, because we know that all values fit in // "unsigned" - they are compile-time constants. const ioctl_desc *desc = ioctl_lookup(request); ioctl_desc decoded_desc; if (!desc) { VPrintf(2, "Decoding unknown ioctl 0x%x\n", request); if (!ioctl_decode(request, &decoded_desc)) Printf("WARNING: failed decoding unknown ioctl 0x%x\n", request); else desc = &decoded_desc; } if (desc) ioctl_common_pre(ctx, desc, d, request, arg); int res = REAL(ioctl)(d, request, arg); // FIXME: some ioctls have different return values for success and failure. if (desc && res != -1) ioctl_common_post(ctx, desc, res, d, request, arg); return res; } #define INIT_IOCTL \ ioctl_init(); \ COMMON_INTERCEPT_FUNCTION(ioctl); #else #define INIT_IOCTL #endif #if SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS || \ SANITIZER_INTERCEPT_GETPWENT || SANITIZER_INTERCEPT_FGETPWENT || \ SANITIZER_INTERCEPT_GETPWENT_R || SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS static void unpoison_passwd(void *ctx, __sanitizer_passwd *pwd) { if (pwd) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwd, sizeof(*pwd)); if (pwd->pw_name) COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_name, REAL(strlen)(pwd->pw_name) + 1); if (pwd->pw_passwd) COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_passwd, REAL(strlen)(pwd->pw_passwd) + 1); #if !SANITIZER_ANDROID if (pwd->pw_gecos) COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_gecos, REAL(strlen)(pwd->pw_gecos) + 1); #endif #if SANITIZER_MAC if (pwd->pw_class) COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_class, REAL(strlen)(pwd->pw_class) + 1); #endif if (pwd->pw_dir) COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_dir, REAL(strlen)(pwd->pw_dir) + 1); if (pwd->pw_shell) COMMON_INTERCEPTOR_INITIALIZE_RANGE(pwd->pw_shell, REAL(strlen)(pwd->pw_shell) + 1); } } static void unpoison_group(void *ctx, __sanitizer_group *grp) { if (grp) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, grp, sizeof(*grp)); if (grp->gr_name) COMMON_INTERCEPTOR_INITIALIZE_RANGE(grp->gr_name, REAL(strlen)(grp->gr_name) + 1); if (grp->gr_passwd) COMMON_INTERCEPTOR_INITIALIZE_RANGE(grp->gr_passwd, REAL(strlen)(grp->gr_passwd) + 1); char **p = grp->gr_mem; for (; *p; ++p) { COMMON_INTERCEPTOR_INITIALIZE_RANGE(*p, REAL(strlen)(*p) + 1); } COMMON_INTERCEPTOR_INITIALIZE_RANGE(grp->gr_mem, (p - grp->gr_mem + 1) * sizeof(*p)); } } #endif // SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS || // SANITIZER_INTERCEPT_GETPWENT || SANITIZER_INTERCEPT_FGETPWENT || // SANITIZER_INTERCEPT_GETPWENT_R || // SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS #if SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS INTERCEPTOR(__sanitizer_passwd *, getpwnam, const char *name) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpwnam, name); COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); __sanitizer_passwd *res = REAL(getpwnam)(name); if (res) unpoison_passwd(ctx, res); return res; } INTERCEPTOR(__sanitizer_passwd *, getpwuid, u32 uid) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpwuid, uid); __sanitizer_passwd *res = REAL(getpwuid)(uid); if (res) unpoison_passwd(ctx, res); return res; } INTERCEPTOR(__sanitizer_group *, getgrnam, const char *name) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getgrnam, name); COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); __sanitizer_group *res = REAL(getgrnam)(name); if (res) unpoison_group(ctx, res); return res; } INTERCEPTOR(__sanitizer_group *, getgrgid, u32 gid) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getgrgid, gid); __sanitizer_group *res = REAL(getgrgid)(gid); if (res) unpoison_group(ctx, res); return res; } #define INIT_GETPWNAM_AND_FRIENDS \ COMMON_INTERCEPT_FUNCTION(getpwnam); \ COMMON_INTERCEPT_FUNCTION(getpwuid); \ COMMON_INTERCEPT_FUNCTION(getgrnam); \ COMMON_INTERCEPT_FUNCTION(getgrgid); #else #define INIT_GETPWNAM_AND_FRIENDS #endif #if SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS INTERCEPTOR(int, getpwnam_r, const char *name, __sanitizer_passwd *pwd, char *buf, SIZE_T buflen, __sanitizer_passwd **result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpwnam_r, name, pwd, buf, buflen, result); COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getpwnam_r)(name, pwd, buf, buflen, result); if (!res) { if (result && *result) unpoison_passwd(ctx, *result); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); } if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); return res; } INTERCEPTOR(int, getpwuid_r, u32 uid, __sanitizer_passwd *pwd, char *buf, SIZE_T buflen, __sanitizer_passwd **result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpwuid_r, uid, pwd, buf, buflen, result); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getpwuid_r)(uid, pwd, buf, buflen, result); if (!res) { if (result && *result) unpoison_passwd(ctx, *result); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); } if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); return res; } INTERCEPTOR(int, getgrnam_r, const char *name, __sanitizer_group *grp, char *buf, SIZE_T buflen, __sanitizer_group **result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getgrnam_r, name, grp, buf, buflen, result); COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getgrnam_r)(name, grp, buf, buflen, result); if (!res) { if (result && *result) unpoison_group(ctx, *result); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); } if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); return res; } INTERCEPTOR(int, getgrgid_r, u32 gid, __sanitizer_group *grp, char *buf, SIZE_T buflen, __sanitizer_group **result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getgrgid_r, gid, grp, buf, buflen, result); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getgrgid_r)(gid, grp, buf, buflen, result); if (!res) { if (result && *result) unpoison_group(ctx, *result); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); } if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); return res; } #define INIT_GETPWNAM_R_AND_FRIENDS \ COMMON_INTERCEPT_FUNCTION(getpwnam_r); \ COMMON_INTERCEPT_FUNCTION(getpwuid_r); \ COMMON_INTERCEPT_FUNCTION(getgrnam_r); \ COMMON_INTERCEPT_FUNCTION(getgrgid_r); #else #define INIT_GETPWNAM_R_AND_FRIENDS #endif #if SANITIZER_INTERCEPT_GETPWENT INTERCEPTOR(__sanitizer_passwd *, getpwent, int dummy) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpwent, dummy); __sanitizer_passwd *res = REAL(getpwent)(dummy); if (res) unpoison_passwd(ctx, res); return res; } INTERCEPTOR(__sanitizer_group *, getgrent, int dummy) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getgrent, dummy); __sanitizer_group *res = REAL(getgrent)(dummy); if (res) unpoison_group(ctx, res);; return res; } #define INIT_GETPWENT \ COMMON_INTERCEPT_FUNCTION(getpwent); \ COMMON_INTERCEPT_FUNCTION(getgrent); #else #define INIT_GETPWENT #endif #if SANITIZER_INTERCEPT_FGETPWENT INTERCEPTOR(__sanitizer_passwd *, fgetpwent, void *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fgetpwent, fp); __sanitizer_passwd *res = REAL(fgetpwent)(fp); if (res) unpoison_passwd(ctx, res); return res; } INTERCEPTOR(__sanitizer_group *, fgetgrent, void *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fgetgrent, fp); __sanitizer_group *res = REAL(fgetgrent)(fp); if (res) unpoison_group(ctx, res); return res; } #define INIT_FGETPWENT \ COMMON_INTERCEPT_FUNCTION(fgetpwent); \ COMMON_INTERCEPT_FUNCTION(fgetgrent); #else #define INIT_FGETPWENT #endif #if SANITIZER_INTERCEPT_GETPWENT_R INTERCEPTOR(int, getpwent_r, __sanitizer_passwd *pwbuf, char *buf, SIZE_T buflen, __sanitizer_passwd **pwbufp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpwent_r, pwbuf, buf, buflen, pwbufp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getpwent_r)(pwbuf, buf, buflen, pwbufp); if (!res) { if (pwbufp && *pwbufp) unpoison_passwd(ctx, *pwbufp); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); } if (pwbufp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwbufp, sizeof(*pwbufp)); return res; } INTERCEPTOR(int, fgetpwent_r, void *fp, __sanitizer_passwd *pwbuf, char *buf, SIZE_T buflen, __sanitizer_passwd **pwbufp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fgetpwent_r, fp, pwbuf, buf, buflen, pwbufp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(fgetpwent_r)(fp, pwbuf, buf, buflen, pwbufp); if (!res) { if (pwbufp && *pwbufp) unpoison_passwd(ctx, *pwbufp); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); } if (pwbufp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwbufp, sizeof(*pwbufp)); return res; } INTERCEPTOR(int, getgrent_r, __sanitizer_group *pwbuf, char *buf, SIZE_T buflen, __sanitizer_group **pwbufp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getgrent_r, pwbuf, buf, buflen, pwbufp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getgrent_r)(pwbuf, buf, buflen, pwbufp); if (!res) { if (pwbufp && *pwbufp) unpoison_group(ctx, *pwbufp); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); } if (pwbufp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwbufp, sizeof(*pwbufp)); return res; } INTERCEPTOR(int, fgetgrent_r, void *fp, __sanitizer_group *pwbuf, char *buf, SIZE_T buflen, __sanitizer_group **pwbufp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fgetgrent_r, fp, pwbuf, buf, buflen, pwbufp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(fgetgrent_r)(fp, pwbuf, buf, buflen, pwbufp); if (!res) { if (pwbufp && *pwbufp) unpoison_group(ctx, *pwbufp); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, buflen); } if (pwbufp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pwbufp, sizeof(*pwbufp)); return res; } #define INIT_GETPWENT_R \ COMMON_INTERCEPT_FUNCTION(getpwent_r); \ COMMON_INTERCEPT_FUNCTION(fgetpwent_r); \ COMMON_INTERCEPT_FUNCTION(getgrent_r); \ COMMON_INTERCEPT_FUNCTION(fgetgrent_r); #else #define INIT_GETPWENT_R #endif #if SANITIZER_INTERCEPT_SETPWENT // The only thing these interceptors do is disable any nested interceptors. // These functions may open nss modules and call uninstrumented functions from // them, and we don't want things like strlen() to trigger. INTERCEPTOR(void, setpwent, int dummy) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, setpwent, dummy); REAL(setpwent)(dummy); } INTERCEPTOR(void, endpwent, int dummy) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, endpwent, dummy); REAL(endpwent)(dummy); } INTERCEPTOR(void, setgrent, int dummy) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, setgrent, dummy); REAL(setgrent)(dummy); } INTERCEPTOR(void, endgrent, int dummy) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, endgrent, dummy); REAL(endgrent)(dummy); } #define INIT_SETPWENT \ COMMON_INTERCEPT_FUNCTION(setpwent); \ COMMON_INTERCEPT_FUNCTION(endpwent); \ COMMON_INTERCEPT_FUNCTION(setgrent); \ COMMON_INTERCEPT_FUNCTION(endgrent); #else #define INIT_SETPWENT #endif #if SANITIZER_INTERCEPT_CLOCK_GETTIME INTERCEPTOR(int, clock_getres, u32 clk_id, void *tp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, clock_getres, clk_id, tp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(clock_getres)(clk_id, tp); if (!res && tp) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tp, struct_timespec_sz); } return res; } INTERCEPTOR(int, clock_gettime, u32 clk_id, void *tp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, clock_gettime, clk_id, tp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(clock_gettime)(clk_id, tp); if (!res) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tp, struct_timespec_sz); } return res; } INTERCEPTOR(int, clock_settime, u32 clk_id, const void *tp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, clock_settime, clk_id, tp); COMMON_INTERCEPTOR_READ_RANGE(ctx, tp, struct_timespec_sz); return REAL(clock_settime)(clk_id, tp); } #define INIT_CLOCK_GETTIME \ COMMON_INTERCEPT_FUNCTION(clock_getres); \ COMMON_INTERCEPT_FUNCTION(clock_gettime); \ COMMON_INTERCEPT_FUNCTION(clock_settime); #else #define INIT_CLOCK_GETTIME #endif #if SANITIZER_INTERCEPT_GETITIMER INTERCEPTOR(int, getitimer, int which, void *curr_value) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getitimer, which, curr_value); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getitimer)(which, curr_value); if (!res && curr_value) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, curr_value, struct_itimerval_sz); } return res; } INTERCEPTOR(int, setitimer, int which, const void *new_value, void *old_value) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, setitimer, which, new_value, old_value); if (new_value) COMMON_INTERCEPTOR_READ_RANGE(ctx, new_value, struct_itimerval_sz); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(setitimer)(which, new_value, old_value); if (!res && old_value) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, old_value, struct_itimerval_sz); } return res; } #define INIT_GETITIMER \ COMMON_INTERCEPT_FUNCTION(getitimer); \ COMMON_INTERCEPT_FUNCTION(setitimer); #else #define INIT_GETITIMER #endif #if SANITIZER_INTERCEPT_GLOB static void unpoison_glob_t(void *ctx, __sanitizer_glob_t *pglob) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, pglob, sizeof(*pglob)); // +1 for NULL pointer at the end. if (pglob->gl_pathv) COMMON_INTERCEPTOR_WRITE_RANGE( ctx, pglob->gl_pathv, (pglob->gl_pathc + 1) * sizeof(*pglob->gl_pathv)); for (SIZE_T i = 0; i < pglob->gl_pathc; ++i) { char *p = pglob->gl_pathv[i]; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, REAL(strlen)(p) + 1); } } static THREADLOCAL __sanitizer_glob_t *pglob_copy; static void wrapped_gl_closedir(void *dir) { COMMON_INTERCEPTOR_UNPOISON_PARAM(1); pglob_copy->gl_closedir(dir); } static void *wrapped_gl_readdir(void *dir) { COMMON_INTERCEPTOR_UNPOISON_PARAM(1); return pglob_copy->gl_readdir(dir); } static void *wrapped_gl_opendir(const char *s) { COMMON_INTERCEPTOR_UNPOISON_PARAM(1); COMMON_INTERCEPTOR_INITIALIZE_RANGE(s, REAL(strlen)(s) + 1); return pglob_copy->gl_opendir(s); } static int wrapped_gl_lstat(const char *s, void *st) { COMMON_INTERCEPTOR_UNPOISON_PARAM(2); COMMON_INTERCEPTOR_INITIALIZE_RANGE(s, REAL(strlen)(s) + 1); return pglob_copy->gl_lstat(s, st); } static int wrapped_gl_stat(const char *s, void *st) { COMMON_INTERCEPTOR_UNPOISON_PARAM(2); COMMON_INTERCEPTOR_INITIALIZE_RANGE(s, REAL(strlen)(s) + 1); return pglob_copy->gl_stat(s, st); } static const __sanitizer_glob_t kGlobCopy = { 0, 0, 0, 0, wrapped_gl_closedir, wrapped_gl_readdir, wrapped_gl_opendir, wrapped_gl_lstat, wrapped_gl_stat}; INTERCEPTOR(int, glob, const char *pattern, int flags, int (*errfunc)(const char *epath, int eerrno), __sanitizer_glob_t *pglob) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, glob, pattern, flags, errfunc, pglob); COMMON_INTERCEPTOR_READ_STRING(ctx, pattern, 0); __sanitizer_glob_t glob_copy; internal_memcpy(&glob_copy, &kGlobCopy, sizeof(glob_copy)); if (flags & glob_altdirfunc) { Swap(pglob->gl_closedir, glob_copy.gl_closedir); Swap(pglob->gl_readdir, glob_copy.gl_readdir); Swap(pglob->gl_opendir, glob_copy.gl_opendir); Swap(pglob->gl_lstat, glob_copy.gl_lstat); Swap(pglob->gl_stat, glob_copy.gl_stat); pglob_copy = &glob_copy; } int res = REAL(glob)(pattern, flags, errfunc, pglob); if (flags & glob_altdirfunc) { Swap(pglob->gl_closedir, glob_copy.gl_closedir); Swap(pglob->gl_readdir, glob_copy.gl_readdir); Swap(pglob->gl_opendir, glob_copy.gl_opendir); Swap(pglob->gl_lstat, glob_copy.gl_lstat); Swap(pglob->gl_stat, glob_copy.gl_stat); } pglob_copy = 0; if ((!res || res == glob_nomatch) && pglob) unpoison_glob_t(ctx, pglob); return res; } INTERCEPTOR(int, glob64, const char *pattern, int flags, int (*errfunc)(const char *epath, int eerrno), __sanitizer_glob_t *pglob) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, glob64, pattern, flags, errfunc, pglob); COMMON_INTERCEPTOR_READ_STRING(ctx, pattern, 0); __sanitizer_glob_t glob_copy; internal_memcpy(&glob_copy, &kGlobCopy, sizeof(glob_copy)); if (flags & glob_altdirfunc) { Swap(pglob->gl_closedir, glob_copy.gl_closedir); Swap(pglob->gl_readdir, glob_copy.gl_readdir); Swap(pglob->gl_opendir, glob_copy.gl_opendir); Swap(pglob->gl_lstat, glob_copy.gl_lstat); Swap(pglob->gl_stat, glob_copy.gl_stat); pglob_copy = &glob_copy; } int res = REAL(glob64)(pattern, flags, errfunc, pglob); if (flags & glob_altdirfunc) { Swap(pglob->gl_closedir, glob_copy.gl_closedir); Swap(pglob->gl_readdir, glob_copy.gl_readdir); Swap(pglob->gl_opendir, glob_copy.gl_opendir); Swap(pglob->gl_lstat, glob_copy.gl_lstat); Swap(pglob->gl_stat, glob_copy.gl_stat); } pglob_copy = 0; if ((!res || res == glob_nomatch) && pglob) unpoison_glob_t(ctx, pglob); return res; } #define INIT_GLOB \ COMMON_INTERCEPT_FUNCTION(glob); \ COMMON_INTERCEPT_FUNCTION(glob64); #else // SANITIZER_INTERCEPT_GLOB #define INIT_GLOB #endif // SANITIZER_INTERCEPT_GLOB #if SANITIZER_INTERCEPT_WAIT // According to sys/wait.h, wait(), waitid(), waitpid() may have symbol version // suffixes on Darwin. See the declaration of INTERCEPTOR_WITH_SUFFIX for // details. INTERCEPTOR_WITH_SUFFIX(int, wait, int *status) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, wait, status); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(wait)(status); if (res != -1 && status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); return res; } // On FreeBSD id_t is always 64-bit wide. #if SANITIZER_FREEBSD && (SANITIZER_WORDSIZE == 32) INTERCEPTOR_WITH_SUFFIX(int, waitid, int idtype, long long id, void *infop, int options) { #else INTERCEPTOR_WITH_SUFFIX(int, waitid, int idtype, int id, void *infop, int options) { #endif void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, waitid, idtype, id, infop, options); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(waitid)(idtype, id, infop, options); if (res != -1 && infop) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, infop, siginfo_t_sz); return res; } INTERCEPTOR_WITH_SUFFIX(int, waitpid, int pid, int *status, int options) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, waitpid, pid, status, options); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(waitpid)(pid, status, options); if (res != -1 && status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); return res; } INTERCEPTOR(int, wait3, int *status, int options, void *rusage) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, wait3, status, options, rusage); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(wait3)(status, options, rusage); if (res != -1) { if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); if (rusage) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz); } return res; } #if SANITIZER_ANDROID INTERCEPTOR(int, __wait4, int pid, int *status, int options, void *rusage) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __wait4, pid, status, options, rusage); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(__wait4)(pid, status, options, rusage); if (res != -1) { if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); if (rusage) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz); } return res; } #define INIT_WAIT4 COMMON_INTERCEPT_FUNCTION(__wait4); #else INTERCEPTOR(int, wait4, int pid, int *status, int options, void *rusage) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, wait4, pid, status, options, rusage); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(wait4)(pid, status, options, rusage); if (res != -1) { if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); if (rusage) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz); } return res; } #define INIT_WAIT4 COMMON_INTERCEPT_FUNCTION(wait4); #endif // SANITIZER_ANDROID #define INIT_WAIT \ COMMON_INTERCEPT_FUNCTION(wait); \ COMMON_INTERCEPT_FUNCTION(waitid); \ COMMON_INTERCEPT_FUNCTION(waitpid); \ COMMON_INTERCEPT_FUNCTION(wait3); #else #define INIT_WAIT #define INIT_WAIT4 #endif #if SANITIZER_INTERCEPT_INET INTERCEPTOR(char *, inet_ntop, int af, const void *src, char *dst, u32 size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, inet_ntop, af, src, dst, size); uptr sz = __sanitizer_in_addr_sz(af); if (sz) COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sz); // FIXME: figure out read size based on the address family. // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. char *res = REAL(inet_ntop)(af, src, dst, size); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; } INTERCEPTOR(int, inet_pton, int af, const char *src, void *dst) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, inet_pton, af, src, dst); COMMON_INTERCEPTOR_READ_STRING(ctx, src, 0); // FIXME: figure out read size based on the address family. // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(inet_pton)(af, src, dst); if (res == 1) { uptr sz = __sanitizer_in_addr_sz(af); if (sz) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, sz); } return res; } #define INIT_INET \ COMMON_INTERCEPT_FUNCTION(inet_ntop); \ COMMON_INTERCEPT_FUNCTION(inet_pton); #else #define INIT_INET #endif #if SANITIZER_INTERCEPT_INET INTERCEPTOR(int, inet_aton, const char *cp, void *dst) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, inet_aton, cp, dst); if (cp) COMMON_INTERCEPTOR_READ_RANGE(ctx, cp, REAL(strlen)(cp) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(inet_aton)(cp, dst); if (res != 0) { uptr sz = __sanitizer_in_addr_sz(af_inet); if (sz) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst, sz); } return res; } #define INIT_INET_ATON COMMON_INTERCEPT_FUNCTION(inet_aton); #else #define INIT_INET_ATON #endif #if SANITIZER_INTERCEPT_PTHREAD_GETSCHEDPARAM INTERCEPTOR(int, pthread_getschedparam, uptr thread, int *policy, int *param) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pthread_getschedparam, thread, policy, param); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(pthread_getschedparam)(thread, policy, param); if (res == 0) { if (policy) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, policy, sizeof(*policy)); if (param) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, param, sizeof(*param)); } return res; } #define INIT_PTHREAD_GETSCHEDPARAM \ COMMON_INTERCEPT_FUNCTION(pthread_getschedparam); #else #define INIT_PTHREAD_GETSCHEDPARAM #endif #if SANITIZER_INTERCEPT_GETADDRINFO INTERCEPTOR(int, getaddrinfo, char *node, char *service, struct __sanitizer_addrinfo *hints, struct __sanitizer_addrinfo **out) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getaddrinfo, node, service, hints, out); if (node) COMMON_INTERCEPTOR_READ_RANGE(ctx, node, REAL(strlen)(node) + 1); if (service) COMMON_INTERCEPTOR_READ_RANGE(ctx, service, REAL(strlen)(service) + 1); if (hints) COMMON_INTERCEPTOR_READ_RANGE(ctx, hints, sizeof(__sanitizer_addrinfo)); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getaddrinfo)(node, service, hints, out); if (res == 0 && out) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, out, sizeof(*out)); struct __sanitizer_addrinfo *p = *out; while (p) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p)); if (p->ai_addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ai_addr, p->ai_addrlen); if (p->ai_canonname) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ai_canonname, REAL(strlen)(p->ai_canonname) + 1); p = p->ai_next; } } return res; } #define INIT_GETADDRINFO COMMON_INTERCEPT_FUNCTION(getaddrinfo); #else #define INIT_GETADDRINFO #endif #if SANITIZER_INTERCEPT_GETNAMEINFO INTERCEPTOR(int, getnameinfo, void *sockaddr, unsigned salen, char *host, unsigned hostlen, char *serv, unsigned servlen, int flags) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getnameinfo, sockaddr, salen, host, hostlen, serv, servlen, flags); // FIXME: consider adding READ_RANGE(sockaddr, salen) // There is padding in in_addr that may make this too noisy // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getnameinfo)(sockaddr, salen, host, hostlen, serv, servlen, flags); if (res == 0) { if (host && hostlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, host, REAL(strlen)(host) + 1); if (serv && servlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, serv, REAL(strlen)(serv) + 1); } return res; } #define INIT_GETNAMEINFO COMMON_INTERCEPT_FUNCTION(getnameinfo); #else #define INIT_GETNAMEINFO #endif #if SANITIZER_INTERCEPT_GETSOCKNAME INTERCEPTOR(int, getsockname, int sock_fd, void *addr, int *addrlen) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getsockname, sock_fd, addr, addrlen); COMMON_INTERCEPTOR_READ_RANGE(ctx, addrlen, sizeof(*addrlen)); int addrlen_in = *addrlen; // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getsockname)(sock_fd, addr, addrlen); if (res == 0) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(addrlen_in, *addrlen)); } return res; } #define INIT_GETSOCKNAME COMMON_INTERCEPT_FUNCTION(getsockname); #else #define INIT_GETSOCKNAME #endif #if SANITIZER_INTERCEPT_GETHOSTBYNAME || SANITIZER_INTERCEPT_GETHOSTBYNAME_R static void write_hostent(void *ctx, struct __sanitizer_hostent *h) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h, sizeof(__sanitizer_hostent)); if (h->h_name) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h->h_name, REAL(strlen)(h->h_name) + 1); char **p = h->h_aliases; while (*p) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *p, REAL(strlen)(*p) + 1); ++p; } COMMON_INTERCEPTOR_WRITE_RANGE( ctx, h->h_aliases, (p - h->h_aliases + 1) * sizeof(*h->h_aliases)); p = h->h_addr_list; while (*p) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *p, h->h_length); ++p; } COMMON_INTERCEPTOR_WRITE_RANGE( ctx, h->h_addr_list, (p - h->h_addr_list + 1) * sizeof(*h->h_addr_list)); } #endif #if SANITIZER_INTERCEPT_GETHOSTBYNAME INTERCEPTOR(struct __sanitizer_hostent *, gethostbyname, char *name) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gethostbyname, name); struct __sanitizer_hostent *res = REAL(gethostbyname)(name); if (res) write_hostent(ctx, res); return res; } INTERCEPTOR(struct __sanitizer_hostent *, gethostbyaddr, void *addr, int len, int type) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gethostbyaddr, addr, len, type); COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, len); struct __sanitizer_hostent *res = REAL(gethostbyaddr)(addr, len, type); if (res) write_hostent(ctx, res); return res; } INTERCEPTOR(struct __sanitizer_hostent *, gethostent, int fake) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gethostent, fake); struct __sanitizer_hostent *res = REAL(gethostent)(fake); if (res) write_hostent(ctx, res); return res; } INTERCEPTOR(struct __sanitizer_hostent *, gethostbyname2, char *name, int af) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gethostbyname2, name, af); struct __sanitizer_hostent *res = REAL(gethostbyname2)(name, af); if (res) write_hostent(ctx, res); return res; } #define INIT_GETHOSTBYNAME \ COMMON_INTERCEPT_FUNCTION(gethostent); \ COMMON_INTERCEPT_FUNCTION(gethostbyaddr); \ COMMON_INTERCEPT_FUNCTION(gethostbyname); \ COMMON_INTERCEPT_FUNCTION(gethostbyname2); #else #define INIT_GETHOSTBYNAME #endif #if SANITIZER_INTERCEPT_GETHOSTBYNAME_R INTERCEPTOR(int, gethostbyname_r, char *name, struct __sanitizer_hostent *ret, char *buf, SIZE_T buflen, __sanitizer_hostent **result, int *h_errnop) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gethostbyname_r, name, ret, buf, buflen, result, h_errnop); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(gethostbyname_r)(name, ret, buf, buflen, result, h_errnop); if (result) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); if (res == 0 && *result) write_hostent(ctx, *result); } if (h_errnop) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h_errnop, sizeof(*h_errnop)); return res; } #define INIT_GETHOSTBYNAME_R COMMON_INTERCEPT_FUNCTION(gethostbyname_r); #else #define INIT_GETHOSTBYNAME_R #endif #if SANITIZER_INTERCEPT_GETHOSTENT_R INTERCEPTOR(int, gethostent_r, struct __sanitizer_hostent *ret, char *buf, SIZE_T buflen, __sanitizer_hostent **result, int *h_errnop) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gethostent_r, ret, buf, buflen, result, h_errnop); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(gethostent_r)(ret, buf, buflen, result, h_errnop); if (result) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); if (res == 0 && *result) write_hostent(ctx, *result); } if (h_errnop) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h_errnop, sizeof(*h_errnop)); return res; } #define INIT_GETHOSTENT_R \ COMMON_INTERCEPT_FUNCTION(gethostent_r); #else #define INIT_GETHOSTENT_R #endif #if SANITIZER_INTERCEPT_GETHOSTBYADDR_R INTERCEPTOR(int, gethostbyaddr_r, void *addr, int len, int type, struct __sanitizer_hostent *ret, char *buf, SIZE_T buflen, __sanitizer_hostent **result, int *h_errnop) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gethostbyaddr_r, addr, len, type, ret, buf, buflen, result, h_errnop); COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, len); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(gethostbyaddr_r)(addr, len, type, ret, buf, buflen, result, h_errnop); if (result) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); if (res == 0 && *result) write_hostent(ctx, *result); } if (h_errnop) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h_errnop, sizeof(*h_errnop)); return res; } #define INIT_GETHOSTBYADDR_R \ COMMON_INTERCEPT_FUNCTION(gethostbyaddr_r); #else #define INIT_GETHOSTBYADDR_R #endif #if SANITIZER_INTERCEPT_GETHOSTBYNAME2_R INTERCEPTOR(int, gethostbyname2_r, char *name, int af, struct __sanitizer_hostent *ret, char *buf, SIZE_T buflen, __sanitizer_hostent **result, int *h_errnop) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gethostbyname2_r, name, af, ret, buf, buflen, result, h_errnop); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(gethostbyname2_r)(name, af, ret, buf, buflen, result, h_errnop); if (result) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); if (res == 0 && *result) write_hostent(ctx, *result); } if (h_errnop) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, h_errnop, sizeof(*h_errnop)); return res; } #define INIT_GETHOSTBYNAME2_R \ COMMON_INTERCEPT_FUNCTION(gethostbyname2_r); #else #define INIT_GETHOSTBYNAME2_R #endif #if SANITIZER_INTERCEPT_GETSOCKOPT INTERCEPTOR(int, getsockopt, int sockfd, int level, int optname, void *optval, int *optlen) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getsockopt, sockfd, level, optname, optval, optlen); if (optlen) COMMON_INTERCEPTOR_READ_RANGE(ctx, optlen, sizeof(*optlen)); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getsockopt)(sockfd, level, optname, optval, optlen); if (res == 0) if (optval && optlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, optval, *optlen); return res; } #define INIT_GETSOCKOPT COMMON_INTERCEPT_FUNCTION(getsockopt); #else #define INIT_GETSOCKOPT #endif #if SANITIZER_INTERCEPT_ACCEPT INTERCEPTOR(int, accept, int fd, void *addr, unsigned *addrlen) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, accept, fd, addr, addrlen); unsigned addrlen0 = 0; if (addrlen) { COMMON_INTERCEPTOR_READ_RANGE(ctx, addrlen, sizeof(*addrlen)); addrlen0 = *addrlen; } int fd2 = REAL(accept)(fd, addr, addrlen); if (fd2 >= 0) { if (fd >= 0) COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, fd2); if (addr && addrlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(*addrlen, addrlen0)); } return fd2; } #define INIT_ACCEPT COMMON_INTERCEPT_FUNCTION(accept); #else #define INIT_ACCEPT #endif #if SANITIZER_INTERCEPT_ACCEPT4 INTERCEPTOR(int, accept4, int fd, void *addr, unsigned *addrlen, int f) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, accept4, fd, addr, addrlen, f); unsigned addrlen0 = 0; if (addrlen) { COMMON_INTERCEPTOR_READ_RANGE(ctx, addrlen, sizeof(*addrlen)); addrlen0 = *addrlen; } // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int fd2 = REAL(accept4)(fd, addr, addrlen, f); if (fd2 >= 0) { if (fd >= 0) COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, fd2); if (addr && addrlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(*addrlen, addrlen0)); } return fd2; } #define INIT_ACCEPT4 COMMON_INTERCEPT_FUNCTION(accept4); #else #define INIT_ACCEPT4 #endif #if SANITIZER_INTERCEPT_MODF INTERCEPTOR(double, modf, double x, double *iptr) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, modf, x, iptr); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. double res = REAL(modf)(x, iptr); if (iptr) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iptr, sizeof(*iptr)); } return res; } INTERCEPTOR(float, modff, float x, float *iptr) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, modff, x, iptr); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. float res = REAL(modff)(x, iptr); if (iptr) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iptr, sizeof(*iptr)); } return res; } INTERCEPTOR(long double, modfl, long double x, long double *iptr) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, modfl, x, iptr); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. long double res = REAL(modfl)(x, iptr); if (iptr) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iptr, sizeof(*iptr)); } return res; } #define INIT_MODF \ COMMON_INTERCEPT_FUNCTION(modf); \ COMMON_INTERCEPT_FUNCTION(modff); \ COMMON_INTERCEPT_FUNCTION_LDBL(modfl); #else #define INIT_MODF #endif #if SANITIZER_INTERCEPT_RECVMSG static void write_msghdr(void *ctx, struct __sanitizer_msghdr *msg, SSIZE_T maxlen) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg, sizeof(*msg)); if (msg->msg_name && msg->msg_namelen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg->msg_name, msg->msg_namelen); if (msg->msg_iov && msg->msg_iovlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg->msg_iov, sizeof(*msg->msg_iov) * msg->msg_iovlen); write_iovec(ctx, msg->msg_iov, msg->msg_iovlen, maxlen); if (msg->msg_control && msg->msg_controllen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg->msg_control, msg->msg_controllen); } INTERCEPTOR(SSIZE_T, recvmsg, int fd, struct __sanitizer_msghdr *msg, int flags) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, recvmsg, fd, msg, flags); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(recvmsg)(fd, msg, flags); if (res >= 0) { if (fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); if (msg) { write_msghdr(ctx, msg, res); COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg); } } return res; } #define INIT_RECVMSG COMMON_INTERCEPT_FUNCTION(recvmsg); #else #define INIT_RECVMSG #endif #if SANITIZER_INTERCEPT_SENDMSG static void read_msghdr_control(void *ctx, void *control, uptr controllen) { const unsigned kCmsgDataOffset = RoundUpTo(sizeof(__sanitizer_cmsghdr), sizeof(uptr)); char *p = (char *)control; char *const control_end = p + controllen; while (true) { if (p + sizeof(__sanitizer_cmsghdr) > control_end) break; __sanitizer_cmsghdr *cmsg = (__sanitizer_cmsghdr *)p; COMMON_INTERCEPTOR_READ_RANGE(ctx, &cmsg->cmsg_len, sizeof(cmsg->cmsg_len)); if (p + RoundUpTo(cmsg->cmsg_len, sizeof(uptr)) > control_end) break; COMMON_INTERCEPTOR_READ_RANGE(ctx, &cmsg->cmsg_level, sizeof(cmsg->cmsg_level)); COMMON_INTERCEPTOR_READ_RANGE(ctx, &cmsg->cmsg_type, sizeof(cmsg->cmsg_type)); if (cmsg->cmsg_len > kCmsgDataOffset) { char *data = p + kCmsgDataOffset; unsigned data_len = cmsg->cmsg_len - kCmsgDataOffset; if (data_len > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, data, data_len); } p += RoundUpTo(cmsg->cmsg_len, sizeof(uptr)); } } static void read_msghdr(void *ctx, struct __sanitizer_msghdr *msg, SSIZE_T maxlen) { #define R(f) \ COMMON_INTERCEPTOR_READ_RANGE(ctx, &msg->msg_##f, sizeof(msg->msg_##f)) R(name); R(namelen); R(iov); R(iovlen); R(control); R(controllen); R(flags); #undef R if (msg->msg_name && msg->msg_namelen) COMMON_INTERCEPTOR_READ_RANGE(ctx, msg->msg_name, msg->msg_namelen); if (msg->msg_iov && msg->msg_iovlen) COMMON_INTERCEPTOR_READ_RANGE(ctx, msg->msg_iov, sizeof(*msg->msg_iov) * msg->msg_iovlen); read_iovec(ctx, msg->msg_iov, msg->msg_iovlen, maxlen); if (msg->msg_control && msg->msg_controllen) read_msghdr_control(ctx, msg->msg_control, msg->msg_controllen); } INTERCEPTOR(SSIZE_T, sendmsg, int fd, struct __sanitizer_msghdr *msg, int flags) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sendmsg, fd, msg, flags); if (fd >= 0) { COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); } SSIZE_T res = REAL(sendmsg)(fd, msg, flags); if (common_flags()->intercept_send && res >= 0 && msg) read_msghdr(ctx, msg, res); return res; } #define INIT_SENDMSG COMMON_INTERCEPT_FUNCTION(sendmsg); #else #define INIT_SENDMSG #endif #if SANITIZER_INTERCEPT_GETPEERNAME INTERCEPTOR(int, getpeername, int sockfd, void *addr, unsigned *addrlen) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpeername, sockfd, addr, addrlen); unsigned addr_sz; if (addrlen) addr_sz = *addrlen; // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getpeername)(sockfd, addr, addrlen); if (!res && addr && addrlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(addr_sz, *addrlen)); return res; } #define INIT_GETPEERNAME COMMON_INTERCEPT_FUNCTION(getpeername); #else #define INIT_GETPEERNAME #endif #if SANITIZER_INTERCEPT_SYSINFO INTERCEPTOR(int, sysinfo, void *info) { void *ctx; // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. COMMON_INTERCEPTOR_ENTER(ctx, sysinfo, info); int res = REAL(sysinfo)(info); if (!res && info) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, struct_sysinfo_sz); return res; } #define INIT_SYSINFO COMMON_INTERCEPT_FUNCTION(sysinfo); #else #define INIT_SYSINFO #endif #if SANITIZER_INTERCEPT_READDIR INTERCEPTOR(__sanitizer_dirent *, opendir, const char *path) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, opendir, path); COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); __sanitizer_dirent *res = REAL(opendir)(path); if (res) COMMON_INTERCEPTOR_DIR_ACQUIRE(ctx, path); return res; } INTERCEPTOR(__sanitizer_dirent *, readdir, void *dirp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, readdir, dirp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. __sanitizer_dirent *res = REAL(readdir)(dirp); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, res->d_reclen); return res; } INTERCEPTOR(int, readdir_r, void *dirp, __sanitizer_dirent *entry, __sanitizer_dirent **result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, readdir_r, dirp, entry, result); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(readdir_r)(dirp, entry, result); if (!res) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); if (*result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *result, (*result)->d_reclen); } return res; } #define INIT_READDIR \ COMMON_INTERCEPT_FUNCTION(opendir); \ COMMON_INTERCEPT_FUNCTION(readdir); \ COMMON_INTERCEPT_FUNCTION(readdir_r); #else #define INIT_READDIR #endif #if SANITIZER_INTERCEPT_READDIR64 INTERCEPTOR(__sanitizer_dirent64 *, readdir64, void *dirp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, readdir64, dirp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. __sanitizer_dirent64 *res = REAL(readdir64)(dirp); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, res->d_reclen); return res; } INTERCEPTOR(int, readdir64_r, void *dirp, __sanitizer_dirent64 *entry, __sanitizer_dirent64 **result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, readdir64_r, dirp, entry, result); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(readdir64_r)(dirp, entry, result); if (!res) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); if (*result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *result, (*result)->d_reclen); } return res; } #define INIT_READDIR64 \ COMMON_INTERCEPT_FUNCTION(readdir64); \ COMMON_INTERCEPT_FUNCTION(readdir64_r); #else #define INIT_READDIR64 #endif #if SANITIZER_INTERCEPT_PTRACE INTERCEPTOR(uptr, ptrace, int request, int pid, void *addr, void *data) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ptrace, request, pid, addr, data); __sanitizer_iovec local_iovec; if (data) { if (request == ptrace_setregs) COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_regs_struct_sz); else if (request == ptrace_setfpregs) COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_fpregs_struct_sz); else if (request == ptrace_setfpxregs) COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_fpxregs_struct_sz); else if (request == ptrace_setvfpregs) COMMON_INTERCEPTOR_READ_RANGE(ctx, data, struct_user_vfpregs_struct_sz); else if (request == ptrace_setsiginfo) COMMON_INTERCEPTOR_READ_RANGE(ctx, data, siginfo_t_sz); // Some kernel might zero the iovec::iov_base in case of invalid // write access. In this case copy the invalid address for further // inspection. else if (request == ptrace_setregset || request == ptrace_getregset) { __sanitizer_iovec *iovec = (__sanitizer_iovec*)data; COMMON_INTERCEPTOR_READ_RANGE(ctx, iovec, sizeof(*iovec)); local_iovec = *iovec; if (request == ptrace_setregset) COMMON_INTERCEPTOR_READ_RANGE(ctx, iovec->iov_base, iovec->iov_len); } } // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. uptr res = REAL(ptrace)(request, pid, addr, data); if (!res && data) { // Note that PEEK* requests assign different meaning to the return value. // This function does not handle them (nor does it need to). if (request == ptrace_getregs) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_regs_struct_sz); else if (request == ptrace_getfpregs) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_fpregs_struct_sz); else if (request == ptrace_getfpxregs) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_fpxregs_struct_sz); else if (request == ptrace_getvfpregs) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, struct_user_vfpregs_struct_sz); else if (request == ptrace_getsiginfo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, siginfo_t_sz); else if (request == ptrace_geteventmsg) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, data, sizeof(unsigned long)); else if (request == ptrace_getregset) { __sanitizer_iovec *iovec = (__sanitizer_iovec*)data; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, iovec, sizeof(*iovec)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, local_iovec.iov_base, local_iovec.iov_len); } } return res; } #define INIT_PTRACE COMMON_INTERCEPT_FUNCTION(ptrace); #else #define INIT_PTRACE #endif #if SANITIZER_INTERCEPT_SETLOCALE INTERCEPTOR(char *, setlocale, int category, char *locale) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, setlocale, category, locale); if (locale) COMMON_INTERCEPTOR_READ_RANGE(ctx, locale, REAL(strlen)(locale) + 1); char *res = REAL(setlocale)(category, locale); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; } #define INIT_SETLOCALE COMMON_INTERCEPT_FUNCTION(setlocale); #else #define INIT_SETLOCALE #endif #if SANITIZER_INTERCEPT_GETCWD INTERCEPTOR(char *, getcwd, char *buf, SIZE_T size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getcwd, buf, size); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. char *res = REAL(getcwd)(buf, size); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; } #define INIT_GETCWD COMMON_INTERCEPT_FUNCTION(getcwd); #else #define INIT_GETCWD #endif #if SANITIZER_INTERCEPT_GET_CURRENT_DIR_NAME INTERCEPTOR(char *, get_current_dir_name, int fake) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, get_current_dir_name, fake); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. char *res = REAL(get_current_dir_name)(fake); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; } #define INIT_GET_CURRENT_DIR_NAME \ COMMON_INTERCEPT_FUNCTION(get_current_dir_name); #else #define INIT_GET_CURRENT_DIR_NAME #endif UNUSED static inline void FixRealStrtolEndptr(const char *nptr, char **endptr) { CHECK(endptr); if (nptr == *endptr) { // No digits were found at strtol call, we need to find out the last // symbol accessed by strtoll on our own. // We get this symbol by skipping leading blanks and optional +/- sign. while (IsSpace(*nptr)) nptr++; if (*nptr == '+' || *nptr == '-') nptr++; *endptr = const_cast(nptr); } CHECK(*endptr >= nptr); } UNUSED static inline void StrtolFixAndCheck(void *ctx, const char *nptr, char **endptr, char *real_endptr, int base) { if (endptr) { *endptr = real_endptr; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, endptr, sizeof(*endptr)); } // If base has unsupported value, strtol can exit with EINVAL // without reading any characters. So do additional checks only // if base is valid. bool is_valid_base = (base == 0) || (2 <= base && base <= 36); if (is_valid_base) { FixRealStrtolEndptr(nptr, &real_endptr); } COMMON_INTERCEPTOR_READ_STRING(ctx, nptr, is_valid_base ? (real_endptr - nptr) + 1 : 0); } #if SANITIZER_INTERCEPT_STRTOIMAX INTERCEPTOR(INTMAX_T, strtoimax, const char *nptr, char **endptr, int base) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strtoimax, nptr, endptr, base); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. char *real_endptr; INTMAX_T res = REAL(strtoimax)(nptr, &real_endptr, base); StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base); return res; } INTERCEPTOR(INTMAX_T, strtoumax, const char *nptr, char **endptr, int base) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strtoumax, nptr, endptr, base); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. char *real_endptr; INTMAX_T res = REAL(strtoumax)(nptr, &real_endptr, base); StrtolFixAndCheck(ctx, nptr, endptr, real_endptr, base); return res; } #define INIT_STRTOIMAX \ COMMON_INTERCEPT_FUNCTION(strtoimax); \ COMMON_INTERCEPT_FUNCTION(strtoumax); #else #define INIT_STRTOIMAX #endif #if SANITIZER_INTERCEPT_MBSTOWCS INTERCEPTOR(SIZE_T, mbstowcs, wchar_t *dest, const char *src, SIZE_T len) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, mbstowcs, dest, src, len); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SIZE_T res = REAL(mbstowcs)(dest, src, len); if (res != (SIZE_T) - 1 && dest) { SIZE_T write_cnt = res + (res < len); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt * sizeof(wchar_t)); } return res; } INTERCEPTOR(SIZE_T, mbsrtowcs, wchar_t *dest, const char **src, SIZE_T len, void *ps) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, mbsrtowcs, dest, src, len, ps); if (src) COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src)); if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SIZE_T res = REAL(mbsrtowcs)(dest, src, len, ps); if (res != (SIZE_T)(-1) && dest && src) { // This function, and several others, may or may not write the terminating // \0 character. They write it iff they clear *src. SIZE_T write_cnt = res + !*src; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt * sizeof(wchar_t)); } return res; } #define INIT_MBSTOWCS \ COMMON_INTERCEPT_FUNCTION(mbstowcs); \ COMMON_INTERCEPT_FUNCTION(mbsrtowcs); #else #define INIT_MBSTOWCS #endif #if SANITIZER_INTERCEPT_MBSNRTOWCS INTERCEPTOR(SIZE_T, mbsnrtowcs, wchar_t *dest, const char **src, SIZE_T nms, SIZE_T len, void *ps) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, mbsnrtowcs, dest, src, nms, len, ps); if (src) { COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src)); if (nms) COMMON_INTERCEPTOR_READ_RANGE(ctx, *src, nms); } if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SIZE_T res = REAL(mbsnrtowcs)(dest, src, nms, len, ps); if (res != (SIZE_T)(-1) && dest && src) { SIZE_T write_cnt = res + !*src; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt * sizeof(wchar_t)); } return res; } #define INIT_MBSNRTOWCS COMMON_INTERCEPT_FUNCTION(mbsnrtowcs); #else #define INIT_MBSNRTOWCS #endif #if SANITIZER_INTERCEPT_WCSTOMBS INTERCEPTOR(SIZE_T, wcstombs, char *dest, const wchar_t *src, SIZE_T len) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, wcstombs, dest, src, len); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SIZE_T res = REAL(wcstombs)(dest, src, len); if (res != (SIZE_T) - 1 && dest) { SIZE_T write_cnt = res + (res < len); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt); } return res; } INTERCEPTOR(SIZE_T, wcsrtombs, char *dest, const wchar_t **src, SIZE_T len, void *ps) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, wcsrtombs, dest, src, len, ps); if (src) COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src)); if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SIZE_T res = REAL(wcsrtombs)(dest, src, len, ps); if (res != (SIZE_T) - 1 && dest && src) { SIZE_T write_cnt = res + !*src; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt); } return res; } #define INIT_WCSTOMBS \ COMMON_INTERCEPT_FUNCTION(wcstombs); \ COMMON_INTERCEPT_FUNCTION(wcsrtombs); #else #define INIT_WCSTOMBS #endif #if SANITIZER_INTERCEPT_WCSNRTOMBS INTERCEPTOR(SIZE_T, wcsnrtombs, char *dest, const wchar_t **src, SIZE_T nms, SIZE_T len, void *ps) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, wcsnrtombs, dest, src, nms, len, ps); if (src) { COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sizeof(*src)); if (nms) COMMON_INTERCEPTOR_READ_RANGE(ctx, *src, nms); } if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SIZE_T res = REAL(wcsnrtombs)(dest, src, nms, len, ps); if (res != ((SIZE_T)-1) && dest && src) { SIZE_T write_cnt = res + !*src; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt); } return res; } #define INIT_WCSNRTOMBS COMMON_INTERCEPT_FUNCTION(wcsnrtombs); #else #define INIT_WCSNRTOMBS #endif #if SANITIZER_INTERCEPT_WCRTOMB INTERCEPTOR(SIZE_T, wcrtomb, char *dest, wchar_t src, void *ps) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, wcrtomb, dest, src, ps); if (ps) COMMON_INTERCEPTOR_READ_RANGE(ctx, ps, mbstate_t_sz); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SIZE_T res = REAL(wcrtomb)(dest, src, ps); if (res != ((SIZE_T)-1) && dest) { SIZE_T write_cnt = res; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dest, write_cnt); } return res; } #define INIT_WCRTOMB COMMON_INTERCEPT_FUNCTION(wcrtomb); #else #define INIT_WCRTOMB #endif #if SANITIZER_INTERCEPT_TCGETATTR INTERCEPTOR(int, tcgetattr, int fd, void *termios_p) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, tcgetattr, fd, termios_p); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(tcgetattr)(fd, termios_p); if (!res && termios_p) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, termios_p, struct_termios_sz); return res; } #define INIT_TCGETATTR COMMON_INTERCEPT_FUNCTION(tcgetattr); #else #define INIT_TCGETATTR #endif #if SANITIZER_INTERCEPT_REALPATH INTERCEPTOR(char *, realpath, const char *path, char *resolved_path) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, realpath, path, resolved_path); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); // Workaround a bug in glibc where dlsym(RTLD_NEXT, ...) returns the oldest // version of a versioned symbol. For realpath(), this gives us something // (called __old_realpath) that does not handle NULL in the second argument. // Handle it as part of the interceptor. char *allocated_path = nullptr; if (!resolved_path) allocated_path = resolved_path = (char *)WRAP(malloc)(path_max + 1); char *res = REAL(realpath)(path, resolved_path); if (allocated_path && !res) WRAP(free)(allocated_path); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; } #define INIT_REALPATH COMMON_INTERCEPT_FUNCTION(realpath); #else #define INIT_REALPATH #endif #if SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME INTERCEPTOR(char *, canonicalize_file_name, const char *path) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, canonicalize_file_name, path); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); char *res = REAL(canonicalize_file_name)(path); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; } #define INIT_CANONICALIZE_FILE_NAME \ COMMON_INTERCEPT_FUNCTION(canonicalize_file_name); #else #define INIT_CANONICALIZE_FILE_NAME #endif #if SANITIZER_INTERCEPT_CONFSTR INTERCEPTOR(SIZE_T, confstr, int name, char *buf, SIZE_T len) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, confstr, name, buf, len); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SIZE_T res = REAL(confstr)(name, buf, len); if (buf && res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, res < len ? res : len); return res; } #define INIT_CONFSTR COMMON_INTERCEPT_FUNCTION(confstr); #else #define INIT_CONFSTR #endif #if SANITIZER_INTERCEPT_SCHED_GETAFFINITY INTERCEPTOR(int, sched_getaffinity, int pid, SIZE_T cpusetsize, void *mask) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sched_getaffinity, pid, cpusetsize, mask); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(sched_getaffinity)(pid, cpusetsize, mask); if (mask && !res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mask, cpusetsize); return res; } #define INIT_SCHED_GETAFFINITY COMMON_INTERCEPT_FUNCTION(sched_getaffinity); #else #define INIT_SCHED_GETAFFINITY #endif #if SANITIZER_INTERCEPT_SCHED_GETPARAM INTERCEPTOR(int, sched_getparam, int pid, void *param) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sched_getparam, pid, param); int res = REAL(sched_getparam)(pid, param); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, param, struct_sched_param_sz); return res; } #define INIT_SCHED_GETPARAM COMMON_INTERCEPT_FUNCTION(sched_getparam); #else #define INIT_SCHED_GETPARAM #endif #if SANITIZER_INTERCEPT_STRERROR INTERCEPTOR(char *, strerror, int errnum) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strerror, errnum); char *res = REAL(strerror)(errnum); if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1); return res; } #define INIT_STRERROR COMMON_INTERCEPT_FUNCTION(strerror); #else #define INIT_STRERROR #endif #if SANITIZER_INTERCEPT_STRERROR_R // There are 2 versions of strerror_r: // * POSIX version returns 0 on success, negative error code on failure, // writes message to buf. // * GNU version returns message pointer, which points to either buf or some // static storage. #if ((_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && !_GNU_SOURCE) || \ SANITIZER_MAC || SANITIZER_ANDROID // POSIX version. Spec is not clear on whether buf is NULL-terminated. // At least on OSX, buf contents are valid even when the call fails. INTERCEPTOR(int, strerror_r, int errnum, char *buf, SIZE_T buflen) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strerror_r, errnum, buf, buflen); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(strerror_r)(errnum, buf, buflen); SIZE_T sz = internal_strnlen(buf, buflen); if (sz < buflen) ++sz; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, sz); return res; } #else // GNU version. INTERCEPTOR(char *, strerror_r, int errnum, char *buf, SIZE_T buflen) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strerror_r, errnum, buf, buflen); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. char *res = REAL(strerror_r)(errnum, buf, buflen); if (res == buf) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); else COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1); return res; } #endif //(_POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600) && !_GNU_SOURCE || //SANITIZER_MAC #define INIT_STRERROR_R COMMON_INTERCEPT_FUNCTION(strerror_r); #else #define INIT_STRERROR_R #endif #if SANITIZER_INTERCEPT_XPG_STRERROR_R INTERCEPTOR(int, __xpg_strerror_r, int errnum, char *buf, SIZE_T buflen) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __xpg_strerror_r, errnum, buf, buflen); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(__xpg_strerror_r)(errnum, buf, buflen); // This version always returns a null-terminated string. if (buf && buflen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, REAL(strlen)(buf) + 1); return res; } #define INIT_XPG_STRERROR_R COMMON_INTERCEPT_FUNCTION(__xpg_strerror_r); #else #define INIT_XPG_STRERROR_R #endif #if SANITIZER_INTERCEPT_SCANDIR typedef int (*scandir_filter_f)(const struct __sanitizer_dirent *); typedef int (*scandir_compar_f)(const struct __sanitizer_dirent **, const struct __sanitizer_dirent **); static THREADLOCAL scandir_filter_f scandir_filter; static THREADLOCAL scandir_compar_f scandir_compar; static int wrapped_scandir_filter(const struct __sanitizer_dirent *dir) { COMMON_INTERCEPTOR_UNPOISON_PARAM(1); COMMON_INTERCEPTOR_INITIALIZE_RANGE(dir, dir->d_reclen); return scandir_filter(dir); } static int wrapped_scandir_compar(const struct __sanitizer_dirent **a, const struct __sanitizer_dirent **b) { COMMON_INTERCEPTOR_UNPOISON_PARAM(2); COMMON_INTERCEPTOR_INITIALIZE_RANGE(a, sizeof(*a)); COMMON_INTERCEPTOR_INITIALIZE_RANGE(*a, (*a)->d_reclen); COMMON_INTERCEPTOR_INITIALIZE_RANGE(b, sizeof(*b)); COMMON_INTERCEPTOR_INITIALIZE_RANGE(*b, (*b)->d_reclen); return scandir_compar(a, b); } INTERCEPTOR(int, scandir, char *dirp, __sanitizer_dirent ***namelist, scandir_filter_f filter, scandir_compar_f compar) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, scandir, dirp, namelist, filter, compar); if (dirp) COMMON_INTERCEPTOR_READ_RANGE(ctx, dirp, REAL(strlen)(dirp) + 1); scandir_filter = filter; scandir_compar = compar; // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(scandir)(dirp, namelist, filter ? wrapped_scandir_filter : nullptr, compar ? wrapped_scandir_compar : nullptr); scandir_filter = nullptr; scandir_compar = nullptr; if (namelist && res > 0) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, namelist, sizeof(*namelist)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *namelist, sizeof(**namelist) * res); for (int i = 0; i < res; ++i) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (*namelist)[i], (*namelist)[i]->d_reclen); } return res; } #define INIT_SCANDIR COMMON_INTERCEPT_FUNCTION(scandir); #else #define INIT_SCANDIR #endif #if SANITIZER_INTERCEPT_SCANDIR64 typedef int (*scandir64_filter_f)(const struct __sanitizer_dirent64 *); typedef int (*scandir64_compar_f)(const struct __sanitizer_dirent64 **, const struct __sanitizer_dirent64 **); static THREADLOCAL scandir64_filter_f scandir64_filter; static THREADLOCAL scandir64_compar_f scandir64_compar; static int wrapped_scandir64_filter(const struct __sanitizer_dirent64 *dir) { COMMON_INTERCEPTOR_UNPOISON_PARAM(1); COMMON_INTERCEPTOR_INITIALIZE_RANGE(dir, dir->d_reclen); return scandir64_filter(dir); } static int wrapped_scandir64_compar(const struct __sanitizer_dirent64 **a, const struct __sanitizer_dirent64 **b) { COMMON_INTERCEPTOR_UNPOISON_PARAM(2); COMMON_INTERCEPTOR_INITIALIZE_RANGE(a, sizeof(*a)); COMMON_INTERCEPTOR_INITIALIZE_RANGE(*a, (*a)->d_reclen); COMMON_INTERCEPTOR_INITIALIZE_RANGE(b, sizeof(*b)); COMMON_INTERCEPTOR_INITIALIZE_RANGE(*b, (*b)->d_reclen); return scandir64_compar(a, b); } INTERCEPTOR(int, scandir64, char *dirp, __sanitizer_dirent64 ***namelist, scandir64_filter_f filter, scandir64_compar_f compar) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, scandir64, dirp, namelist, filter, compar); if (dirp) COMMON_INTERCEPTOR_READ_RANGE(ctx, dirp, REAL(strlen)(dirp) + 1); scandir64_filter = filter; scandir64_compar = compar; // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(scandir64)(dirp, namelist, filter ? wrapped_scandir64_filter : nullptr, compar ? wrapped_scandir64_compar : nullptr); scandir64_filter = nullptr; scandir64_compar = nullptr; if (namelist && res > 0) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, namelist, sizeof(*namelist)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *namelist, sizeof(**namelist) * res); for (int i = 0; i < res; ++i) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (*namelist)[i], (*namelist)[i]->d_reclen); } return res; } #define INIT_SCANDIR64 COMMON_INTERCEPT_FUNCTION(scandir64); #else #define INIT_SCANDIR64 #endif #if SANITIZER_INTERCEPT_GETGROUPS INTERCEPTOR(int, getgroups, int size, u32 *lst) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getgroups, size, lst); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getgroups)(size, lst); if (res >= 0 && lst && size > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lst, res * sizeof(*lst)); return res; } #define INIT_GETGROUPS COMMON_INTERCEPT_FUNCTION(getgroups); #else #define INIT_GETGROUPS #endif #if SANITIZER_INTERCEPT_POLL static void read_pollfd(void *ctx, __sanitizer_pollfd *fds, __sanitizer_nfds_t nfds) { for (unsigned i = 0; i < nfds; ++i) { COMMON_INTERCEPTOR_READ_RANGE(ctx, &fds[i].fd, sizeof(fds[i].fd)); COMMON_INTERCEPTOR_READ_RANGE(ctx, &fds[i].events, sizeof(fds[i].events)); } } static void write_pollfd(void *ctx, __sanitizer_pollfd *fds, __sanitizer_nfds_t nfds) { for (unsigned i = 0; i < nfds; ++i) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &fds[i].revents, sizeof(fds[i].revents)); } INTERCEPTOR(int, poll, __sanitizer_pollfd *fds, __sanitizer_nfds_t nfds, int timeout) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, poll, fds, nfds, timeout); if (fds && nfds) read_pollfd(ctx, fds, nfds); int res = COMMON_INTERCEPTOR_BLOCK_REAL(poll)(fds, nfds, timeout); if (fds && nfds) write_pollfd(ctx, fds, nfds); return res; } #define INIT_POLL COMMON_INTERCEPT_FUNCTION(poll); #else #define INIT_POLL #endif #if SANITIZER_INTERCEPT_PPOLL INTERCEPTOR(int, ppoll, __sanitizer_pollfd *fds, __sanitizer_nfds_t nfds, void *timeout_ts, __sanitizer_sigset_t *sigmask) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ppoll, fds, nfds, timeout_ts, sigmask); if (fds && nfds) read_pollfd(ctx, fds, nfds); if (timeout_ts) COMMON_INTERCEPTOR_READ_RANGE(ctx, timeout_ts, struct_timespec_sz); // FIXME: read sigmask when all of sigemptyset, etc are intercepted. int res = COMMON_INTERCEPTOR_BLOCK_REAL(ppoll)(fds, nfds, timeout_ts, sigmask); if (fds && nfds) write_pollfd(ctx, fds, nfds); return res; } #define INIT_PPOLL COMMON_INTERCEPT_FUNCTION(ppoll); #else #define INIT_PPOLL #endif #if SANITIZER_INTERCEPT_WORDEXP INTERCEPTOR(int, wordexp, char *s, __sanitizer_wordexp_t *p, int flags) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, wordexp, s, p, flags); if (s) COMMON_INTERCEPTOR_READ_RANGE(ctx, s, REAL(strlen)(s) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(wordexp)(s, p, flags); if (!res && p) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p)); if (p->we_wordc) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->we_wordv, sizeof(*p->we_wordv) * p->we_wordc); for (uptr i = 0; i < p->we_wordc; ++i) { char *w = p->we_wordv[i]; if (w) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, w, REAL(strlen)(w) + 1); } } return res; } #define INIT_WORDEXP COMMON_INTERCEPT_FUNCTION(wordexp); #else #define INIT_WORDEXP #endif #if SANITIZER_INTERCEPT_SIGWAIT INTERCEPTOR(int, sigwait, __sanitizer_sigset_t *set, int *sig) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sigwait, set, sig); // FIXME: read sigset_t when all of sigemptyset, etc are intercepted // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(sigwait)(set, sig); if (!res && sig) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sig, sizeof(*sig)); return res; } #define INIT_SIGWAIT COMMON_INTERCEPT_FUNCTION(sigwait); #else #define INIT_SIGWAIT #endif #if SANITIZER_INTERCEPT_SIGWAITINFO INTERCEPTOR(int, sigwaitinfo, __sanitizer_sigset_t *set, void *info) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sigwaitinfo, set, info); // FIXME: read sigset_t when all of sigemptyset, etc are intercepted // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(sigwaitinfo)(set, info); if (res > 0 && info) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, siginfo_t_sz); return res; } #define INIT_SIGWAITINFO COMMON_INTERCEPT_FUNCTION(sigwaitinfo); #else #define INIT_SIGWAITINFO #endif #if SANITIZER_INTERCEPT_SIGTIMEDWAIT INTERCEPTOR(int, sigtimedwait, __sanitizer_sigset_t *set, void *info, void *timeout) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sigtimedwait, set, info, timeout); if (timeout) COMMON_INTERCEPTOR_READ_RANGE(ctx, timeout, struct_timespec_sz); // FIXME: read sigset_t when all of sigemptyset, etc are intercepted // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(sigtimedwait)(set, info, timeout); if (res > 0 && info) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, siginfo_t_sz); return res; } #define INIT_SIGTIMEDWAIT COMMON_INTERCEPT_FUNCTION(sigtimedwait); #else #define INIT_SIGTIMEDWAIT #endif #if SANITIZER_INTERCEPT_SIGSETOPS INTERCEPTOR(int, sigemptyset, __sanitizer_sigset_t *set) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sigemptyset, set); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(sigemptyset)(set); if (!res && set) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, set, sizeof(*set)); return res; } INTERCEPTOR(int, sigfillset, __sanitizer_sigset_t *set) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sigfillset, set); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(sigfillset)(set); if (!res && set) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, set, sizeof(*set)); return res; } #define INIT_SIGSETOPS \ COMMON_INTERCEPT_FUNCTION(sigemptyset); \ COMMON_INTERCEPT_FUNCTION(sigfillset); #else #define INIT_SIGSETOPS #endif #if SANITIZER_INTERCEPT_SIGPENDING INTERCEPTOR(int, sigpending, __sanitizer_sigset_t *set) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sigpending, set); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(sigpending)(set); if (!res && set) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, set, sizeof(*set)); return res; } #define INIT_SIGPENDING COMMON_INTERCEPT_FUNCTION(sigpending); #else #define INIT_SIGPENDING #endif #if SANITIZER_INTERCEPT_SIGPROCMASK INTERCEPTOR(int, sigprocmask, int how, __sanitizer_sigset_t *set, __sanitizer_sigset_t *oldset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sigprocmask, how, set, oldset); // FIXME: read sigset_t when all of sigemptyset, etc are intercepted // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(sigprocmask)(how, set, oldset); if (!res && oldset) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldset, sizeof(*oldset)); return res; } #define INIT_SIGPROCMASK COMMON_INTERCEPT_FUNCTION(sigprocmask); #else #define INIT_SIGPROCMASK #endif #if SANITIZER_INTERCEPT_BACKTRACE INTERCEPTOR(int, backtrace, void **buffer, int size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, backtrace, buffer, size); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(backtrace)(buffer, size); if (res && buffer) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buffer, res * sizeof(*buffer)); return res; } INTERCEPTOR(char **, backtrace_symbols, void **buffer, int size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, backtrace_symbols, buffer, size); if (buffer && size) COMMON_INTERCEPTOR_READ_RANGE(ctx, buffer, size * sizeof(*buffer)); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. char **res = REAL(backtrace_symbols)(buffer, size); if (res && size) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, size * sizeof(*res)); for (int i = 0; i < size; ++i) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res[i], REAL(strlen(res[i])) + 1); } return res; } #define INIT_BACKTRACE \ COMMON_INTERCEPT_FUNCTION(backtrace); \ COMMON_INTERCEPT_FUNCTION(backtrace_symbols); #else #define INIT_BACKTRACE #endif #if SANITIZER_INTERCEPT__EXIT INTERCEPTOR(void, _exit, int status) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, _exit, status); COMMON_INTERCEPTOR_USER_CALLBACK_START(); int status1 = COMMON_INTERCEPTOR_ON_EXIT(ctx); COMMON_INTERCEPTOR_USER_CALLBACK_END(); if (status == 0) status = status1; REAL(_exit)(status); } #define INIT__EXIT COMMON_INTERCEPT_FUNCTION(_exit); #else #define INIT__EXIT #endif #if SANITIZER_INTERCEPT_PHTREAD_MUTEX INTERCEPTOR(int, pthread_mutex_lock, void *m) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pthread_mutex_lock, m); COMMON_INTERCEPTOR_MUTEX_PRE_LOCK(ctx, m); int res = REAL(pthread_mutex_lock)(m); if (res == errno_EOWNERDEAD) COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m); if (res == 0 || res == errno_EOWNERDEAD) COMMON_INTERCEPTOR_MUTEX_POST_LOCK(ctx, m); if (res == errno_EINVAL) COMMON_INTERCEPTOR_MUTEX_INVALID(ctx, m); return res; } INTERCEPTOR(int, pthread_mutex_unlock, void *m) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pthread_mutex_unlock, m); COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m); int res = REAL(pthread_mutex_unlock)(m); if (res == errno_EINVAL) COMMON_INTERCEPTOR_MUTEX_INVALID(ctx, m); return res; } #define INIT_PTHREAD_MUTEX_LOCK COMMON_INTERCEPT_FUNCTION(pthread_mutex_lock) #define INIT_PTHREAD_MUTEX_UNLOCK \ COMMON_INTERCEPT_FUNCTION(pthread_mutex_unlock) #else #define INIT_PTHREAD_MUTEX_LOCK #define INIT_PTHREAD_MUTEX_UNLOCK #endif #if SANITIZER_INTERCEPT_GETMNTENT || SANITIZER_INTERCEPT_GETMNTENT_R static void write_mntent(void *ctx, __sanitizer_mntent *mnt) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt, sizeof(*mnt)); if (mnt->mnt_fsname) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt->mnt_fsname, REAL(strlen)(mnt->mnt_fsname) + 1); if (mnt->mnt_dir) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt->mnt_dir, REAL(strlen)(mnt->mnt_dir) + 1); if (mnt->mnt_type) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt->mnt_type, REAL(strlen)(mnt->mnt_type) + 1); if (mnt->mnt_opts) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt->mnt_opts, REAL(strlen)(mnt->mnt_opts) + 1); } #endif #if SANITIZER_INTERCEPT_GETMNTENT INTERCEPTOR(__sanitizer_mntent *, getmntent, void *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getmntent, fp); __sanitizer_mntent *res = REAL(getmntent)(fp); if (res) write_mntent(ctx, res); return res; } #define INIT_GETMNTENT COMMON_INTERCEPT_FUNCTION(getmntent); #else #define INIT_GETMNTENT #endif #if SANITIZER_INTERCEPT_GETMNTENT_R INTERCEPTOR(__sanitizer_mntent *, getmntent_r, void *fp, __sanitizer_mntent *mntbuf, char *buf, int buflen) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getmntent_r, fp, mntbuf, buf, buflen); __sanitizer_mntent *res = REAL(getmntent_r)(fp, mntbuf, buf, buflen); if (res) write_mntent(ctx, res); return res; } #define INIT_GETMNTENT_R COMMON_INTERCEPT_FUNCTION(getmntent_r); #else #define INIT_GETMNTENT_R #endif #if SANITIZER_INTERCEPT_STATFS INTERCEPTOR(int, statfs, char *path, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, statfs, path, buf); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(statfs)(path, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs_sz); return res; } INTERCEPTOR(int, fstatfs, int fd, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fstatfs, fd, buf); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(fstatfs)(fd, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs_sz); return res; } #define INIT_STATFS \ COMMON_INTERCEPT_FUNCTION(statfs); \ COMMON_INTERCEPT_FUNCTION(fstatfs); #else #define INIT_STATFS #endif #if SANITIZER_INTERCEPT_STATFS64 INTERCEPTOR(int, statfs64, char *path, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, statfs64, path, buf); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(statfs64)(path, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs64_sz); return res; } INTERCEPTOR(int, fstatfs64, int fd, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fstatfs64, fd, buf); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(fstatfs64)(fd, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs64_sz); return res; } #define INIT_STATFS64 \ COMMON_INTERCEPT_FUNCTION(statfs64); \ COMMON_INTERCEPT_FUNCTION(fstatfs64); #else #define INIT_STATFS64 #endif #if SANITIZER_INTERCEPT_STATVFS INTERCEPTOR(int, statvfs, char *path, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, statvfs, path, buf); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(statvfs)(path, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs_sz); return res; } INTERCEPTOR(int, fstatvfs, int fd, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fstatvfs, fd, buf); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(fstatvfs)(fd, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs_sz); return res; } #define INIT_STATVFS \ COMMON_INTERCEPT_FUNCTION(statvfs); \ COMMON_INTERCEPT_FUNCTION(fstatvfs); #else #define INIT_STATVFS #endif #if SANITIZER_INTERCEPT_STATVFS64 INTERCEPTOR(int, statvfs64, char *path, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, statvfs64, path, buf); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(statvfs64)(path, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs64_sz); return res; } INTERCEPTOR(int, fstatvfs64, int fd, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fstatvfs64, fd, buf); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(fstatvfs64)(fd, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs64_sz); return res; } #define INIT_STATVFS64 \ COMMON_INTERCEPT_FUNCTION(statvfs64); \ COMMON_INTERCEPT_FUNCTION(fstatvfs64); #else #define INIT_STATVFS64 #endif #if SANITIZER_INTERCEPT_INITGROUPS INTERCEPTOR(int, initgroups, char *user, u32 group) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, initgroups, user, group); if (user) COMMON_INTERCEPTOR_READ_RANGE(ctx, user, REAL(strlen)(user) + 1); int res = REAL(initgroups)(user, group); return res; } #define INIT_INITGROUPS COMMON_INTERCEPT_FUNCTION(initgroups); #else #define INIT_INITGROUPS #endif #if SANITIZER_INTERCEPT_ETHER_NTOA_ATON INTERCEPTOR(char *, ether_ntoa, __sanitizer_ether_addr *addr) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ether_ntoa, addr); if (addr) COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, sizeof(*addr)); char *res = REAL(ether_ntoa)(addr); if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1); return res; } INTERCEPTOR(__sanitizer_ether_addr *, ether_aton, char *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ether_aton, buf); if (buf) COMMON_INTERCEPTOR_READ_RANGE(ctx, buf, REAL(strlen)(buf) + 1); __sanitizer_ether_addr *res = REAL(ether_aton)(buf); if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, sizeof(*res)); return res; } #define INIT_ETHER_NTOA_ATON \ COMMON_INTERCEPT_FUNCTION(ether_ntoa); \ COMMON_INTERCEPT_FUNCTION(ether_aton); #else #define INIT_ETHER_NTOA_ATON #endif #if SANITIZER_INTERCEPT_ETHER_HOST INTERCEPTOR(int, ether_ntohost, char *hostname, __sanitizer_ether_addr *addr) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ether_ntohost, hostname, addr); if (addr) COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, sizeof(*addr)); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(ether_ntohost)(hostname, addr); if (!res && hostname) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, hostname, REAL(strlen)(hostname) + 1); return res; } INTERCEPTOR(int, ether_hostton, char *hostname, __sanitizer_ether_addr *addr) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ether_hostton, hostname, addr); if (hostname) COMMON_INTERCEPTOR_READ_RANGE(ctx, hostname, REAL(strlen)(hostname) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(ether_hostton)(hostname, addr); if (!res && addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, sizeof(*addr)); return res; } INTERCEPTOR(int, ether_line, char *line, __sanitizer_ether_addr *addr, char *hostname) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ether_line, line, addr, hostname); if (line) COMMON_INTERCEPTOR_READ_RANGE(ctx, line, REAL(strlen)(line) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(ether_line)(line, addr, hostname); if (!res) { if (addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, sizeof(*addr)); if (hostname) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, hostname, REAL(strlen)(hostname) + 1); } return res; } #define INIT_ETHER_HOST \ COMMON_INTERCEPT_FUNCTION(ether_ntohost); \ COMMON_INTERCEPT_FUNCTION(ether_hostton); \ COMMON_INTERCEPT_FUNCTION(ether_line); #else #define INIT_ETHER_HOST #endif #if SANITIZER_INTERCEPT_ETHER_R INTERCEPTOR(char *, ether_ntoa_r, __sanitizer_ether_addr *addr, char *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ether_ntoa_r, addr, buf); if (addr) COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, sizeof(*addr)); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. char *res = REAL(ether_ntoa_r)(addr, buf); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; } INTERCEPTOR(__sanitizer_ether_addr *, ether_aton_r, char *buf, __sanitizer_ether_addr *addr) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ether_aton_r, buf, addr); if (buf) COMMON_INTERCEPTOR_READ_RANGE(ctx, buf, REAL(strlen)(buf) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. __sanitizer_ether_addr *res = REAL(ether_aton_r)(buf, addr); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, sizeof(*res)); return res; } #define INIT_ETHER_R \ COMMON_INTERCEPT_FUNCTION(ether_ntoa_r); \ COMMON_INTERCEPT_FUNCTION(ether_aton_r); #else #define INIT_ETHER_R #endif #if SANITIZER_INTERCEPT_SHMCTL INTERCEPTOR(int, shmctl, int shmid, int cmd, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, shmctl, shmid, cmd, buf); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(shmctl)(shmid, cmd, buf); if (res >= 0) { unsigned sz = 0; if (cmd == shmctl_ipc_stat || cmd == shmctl_shm_stat) sz = sizeof(__sanitizer_shmid_ds); else if (cmd == shmctl_ipc_info) sz = struct_shminfo_sz; else if (cmd == shmctl_shm_info) sz = struct_shm_info_sz; if (sz) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, sz); } return res; } #define INIT_SHMCTL COMMON_INTERCEPT_FUNCTION(shmctl); #else #define INIT_SHMCTL #endif #if SANITIZER_INTERCEPT_RANDOM_R INTERCEPTOR(int, random_r, void *buf, u32 *result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, random_r, buf, result); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(random_r)(buf, result); if (!res && result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); return res; } #define INIT_RANDOM_R COMMON_INTERCEPT_FUNCTION(random_r); #else #define INIT_RANDOM_R #endif // FIXME: under ASan the REAL() call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. #if SANITIZER_INTERCEPT_PTHREAD_ATTR_GET || \ SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSSCHED || \ SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GET || \ SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GET || \ SANITIZER_INTERCEPT_PTHREAD_CONDATTR_GET || \ SANITIZER_INTERCEPT_PTHREAD_BARRIERATTR_GET #define INTERCEPTOR_PTHREAD_OBJECT_ATTR_GET(fn, sz) \ INTERCEPTOR(int, fn, void *attr, void *r) { \ void *ctx; \ COMMON_INTERCEPTOR_ENTER(ctx, fn, attr, r); \ int res = REAL(fn)(attr, r); \ if (!res && r) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, r, sz); \ return res; \ } #define INTERCEPTOR_PTHREAD_ATTR_GET(what, sz) \ INTERCEPTOR_PTHREAD_OBJECT_ATTR_GET(pthread_attr_get##what, sz) #define INTERCEPTOR_PTHREAD_MUTEXATTR_GET(what, sz) \ INTERCEPTOR_PTHREAD_OBJECT_ATTR_GET(pthread_mutexattr_get##what, sz) #define INTERCEPTOR_PTHREAD_RWLOCKATTR_GET(what, sz) \ INTERCEPTOR_PTHREAD_OBJECT_ATTR_GET(pthread_rwlockattr_get##what, sz) #define INTERCEPTOR_PTHREAD_CONDATTR_GET(what, sz) \ INTERCEPTOR_PTHREAD_OBJECT_ATTR_GET(pthread_condattr_get##what, sz) #define INTERCEPTOR_PTHREAD_BARRIERATTR_GET(what, sz) \ INTERCEPTOR_PTHREAD_OBJECT_ATTR_GET(pthread_barrierattr_get##what, sz) #endif #if SANITIZER_INTERCEPT_PTHREAD_ATTR_GET INTERCEPTOR_PTHREAD_ATTR_GET(detachstate, sizeof(int)) INTERCEPTOR_PTHREAD_ATTR_GET(guardsize, sizeof(SIZE_T)) INTERCEPTOR_PTHREAD_ATTR_GET(schedparam, struct_sched_param_sz) INTERCEPTOR_PTHREAD_ATTR_GET(schedpolicy, sizeof(int)) INTERCEPTOR_PTHREAD_ATTR_GET(scope, sizeof(int)) INTERCEPTOR_PTHREAD_ATTR_GET(stacksize, sizeof(SIZE_T)) INTERCEPTOR(int, pthread_attr_getstack, void *attr, void **addr, SIZE_T *size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pthread_attr_getstack, attr, addr, size); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(pthread_attr_getstack)(attr, addr, size); if (!res) { if (addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, sizeof(*addr)); if (size) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, size, sizeof(*size)); } return res; } // We may need to call the real pthread_attr_getstack from the run-time // in sanitizer_common, but we don't want to include the interception headers // there. So, just define this function here. namespace __sanitizer { extern "C" { int real_pthread_attr_getstack(void *attr, void **addr, SIZE_T *size) { return REAL(pthread_attr_getstack)(attr, addr, size); } } // extern "C" } // namespace __sanitizer #define INIT_PTHREAD_ATTR_GET \ COMMON_INTERCEPT_FUNCTION(pthread_attr_getdetachstate); \ COMMON_INTERCEPT_FUNCTION(pthread_attr_getguardsize); \ COMMON_INTERCEPT_FUNCTION(pthread_attr_getschedparam); \ COMMON_INTERCEPT_FUNCTION(pthread_attr_getschedpolicy); \ COMMON_INTERCEPT_FUNCTION(pthread_attr_getscope); \ COMMON_INTERCEPT_FUNCTION(pthread_attr_getstacksize); \ COMMON_INTERCEPT_FUNCTION(pthread_attr_getstack); #else #define INIT_PTHREAD_ATTR_GET #endif #if SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSCHED INTERCEPTOR_PTHREAD_ATTR_GET(inheritsched, sizeof(int)) #define INIT_PTHREAD_ATTR_GETINHERITSCHED \ COMMON_INTERCEPT_FUNCTION(pthread_attr_getinheritsched); #else #define INIT_PTHREAD_ATTR_GETINHERITSCHED #endif #if SANITIZER_INTERCEPT_PTHREAD_ATTR_GETAFFINITY_NP INTERCEPTOR(int, pthread_attr_getaffinity_np, void *attr, SIZE_T cpusetsize, void *cpuset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pthread_attr_getaffinity_np, attr, cpusetsize, cpuset); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(pthread_attr_getaffinity_np)(attr, cpusetsize, cpuset); if (!res && cpusetsize && cpuset) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cpuset, cpusetsize); return res; } #define INIT_PTHREAD_ATTR_GETAFFINITY_NP \ COMMON_INTERCEPT_FUNCTION(pthread_attr_getaffinity_np); #else #define INIT_PTHREAD_ATTR_GETAFFINITY_NP #endif #if SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPSHARED INTERCEPTOR_PTHREAD_MUTEXATTR_GET(pshared, sizeof(int)) #define INIT_PTHREAD_MUTEXATTR_GETPSHARED \ COMMON_INTERCEPT_FUNCTION(pthread_mutexattr_getpshared); #else #define INIT_PTHREAD_MUTEXATTR_GETPSHARED #endif #if SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETTYPE INTERCEPTOR_PTHREAD_MUTEXATTR_GET(type, sizeof(int)) #define INIT_PTHREAD_MUTEXATTR_GETTYPE \ COMMON_INTERCEPT_FUNCTION(pthread_mutexattr_gettype); #else #define INIT_PTHREAD_MUTEXATTR_GETTYPE #endif #if SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPROTOCOL INTERCEPTOR_PTHREAD_MUTEXATTR_GET(protocol, sizeof(int)) #define INIT_PTHREAD_MUTEXATTR_GETPROTOCOL \ COMMON_INTERCEPT_FUNCTION(pthread_mutexattr_getprotocol); #else #define INIT_PTHREAD_MUTEXATTR_GETPROTOCOL #endif #if SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPRIOCEILING INTERCEPTOR_PTHREAD_MUTEXATTR_GET(prioceiling, sizeof(int)) #define INIT_PTHREAD_MUTEXATTR_GETPRIOCEILING \ COMMON_INTERCEPT_FUNCTION(pthread_mutexattr_getprioceiling); #else #define INIT_PTHREAD_MUTEXATTR_GETPRIOCEILING #endif #if SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETROBUST INTERCEPTOR_PTHREAD_MUTEXATTR_GET(robust, sizeof(int)) #define INIT_PTHREAD_MUTEXATTR_GETROBUST \ COMMON_INTERCEPT_FUNCTION(pthread_mutexattr_getrobust); #else #define INIT_PTHREAD_MUTEXATTR_GETROBUST #endif #if SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETROBUST_NP INTERCEPTOR_PTHREAD_MUTEXATTR_GET(robust_np, sizeof(int)) #define INIT_PTHREAD_MUTEXATTR_GETROBUST_NP \ COMMON_INTERCEPT_FUNCTION(pthread_mutexattr_getrobust_np); #else #define INIT_PTHREAD_MUTEXATTR_GETROBUST_NP #endif #if SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GETPSHARED INTERCEPTOR_PTHREAD_RWLOCKATTR_GET(pshared, sizeof(int)) #define INIT_PTHREAD_RWLOCKATTR_GETPSHARED \ COMMON_INTERCEPT_FUNCTION(pthread_rwlockattr_getpshared); #else #define INIT_PTHREAD_RWLOCKATTR_GETPSHARED #endif #if SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GETKIND_NP INTERCEPTOR_PTHREAD_RWLOCKATTR_GET(kind_np, sizeof(int)) #define INIT_PTHREAD_RWLOCKATTR_GETKIND_NP \ COMMON_INTERCEPT_FUNCTION(pthread_rwlockattr_getkind_np); #else #define INIT_PTHREAD_RWLOCKATTR_GETKIND_NP #endif #if SANITIZER_INTERCEPT_PTHREAD_CONDATTR_GETPSHARED INTERCEPTOR_PTHREAD_CONDATTR_GET(pshared, sizeof(int)) #define INIT_PTHREAD_CONDATTR_GETPSHARED \ COMMON_INTERCEPT_FUNCTION(pthread_condattr_getpshared); #else #define INIT_PTHREAD_CONDATTR_GETPSHARED #endif #if SANITIZER_INTERCEPT_PTHREAD_CONDATTR_GETCLOCK INTERCEPTOR_PTHREAD_CONDATTR_GET(clock, sizeof(int)) #define INIT_PTHREAD_CONDATTR_GETCLOCK \ COMMON_INTERCEPT_FUNCTION(pthread_condattr_getclock); #else #define INIT_PTHREAD_CONDATTR_GETCLOCK #endif #if SANITIZER_INTERCEPT_PTHREAD_BARRIERATTR_GETPSHARED INTERCEPTOR_PTHREAD_BARRIERATTR_GET(pshared, sizeof(int)) // !mac !android #define INIT_PTHREAD_BARRIERATTR_GETPSHARED \ COMMON_INTERCEPT_FUNCTION(pthread_barrierattr_getpshared); #else #define INIT_PTHREAD_BARRIERATTR_GETPSHARED #endif #if SANITIZER_INTERCEPT_TMPNAM INTERCEPTOR(char *, tmpnam, char *s) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, tmpnam, s); char *res = REAL(tmpnam)(s); if (res) { if (s) // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. COMMON_INTERCEPTOR_WRITE_RANGE(ctx, s, REAL(strlen)(s) + 1); else COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1); } return res; } #define INIT_TMPNAM COMMON_INTERCEPT_FUNCTION(tmpnam); #else #define INIT_TMPNAM #endif #if SANITIZER_INTERCEPT_TMPNAM_R INTERCEPTOR(char *, tmpnam_r, char *s) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, tmpnam_r, s); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. char *res = REAL(tmpnam_r)(s); if (res && s) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, s, REAL(strlen)(s) + 1); return res; } #define INIT_TMPNAM_R COMMON_INTERCEPT_FUNCTION(tmpnam_r); #else #define INIT_TMPNAM_R #endif #if SANITIZER_INTERCEPT_TTYNAME_R INTERCEPTOR(int, ttyname_r, int fd, char *name, SIZE_T namesize) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ttyname_r, fd, name, namesize); int res = REAL(ttyname_r)(fd, name, namesize); if (res == 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, name, REAL(strlen)(name) + 1); return res; } #define INIT_TTYNAME_R COMMON_INTERCEPT_FUNCTION(ttyname_r); #else #define INIT_TTYNAME_R #endif #if SANITIZER_INTERCEPT_TEMPNAM INTERCEPTOR(char *, tempnam, char *dir, char *pfx) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, tempnam, dir, pfx); if (dir) COMMON_INTERCEPTOR_READ_RANGE(ctx, dir, REAL(strlen)(dir) + 1); if (pfx) COMMON_INTERCEPTOR_READ_RANGE(ctx, pfx, REAL(strlen)(pfx) + 1); char *res = REAL(tempnam)(dir, pfx); if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1); return res; } #define INIT_TEMPNAM COMMON_INTERCEPT_FUNCTION(tempnam); #else #define INIT_TEMPNAM #endif #if SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP INTERCEPTOR(int, pthread_setname_np, uptr thread, const char *name) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pthread_setname_np, thread, name); COMMON_INTERCEPTOR_READ_STRING(ctx, name, 0); COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name); return REAL(pthread_setname_np)(thread, name); } #define INIT_PTHREAD_SETNAME_NP COMMON_INTERCEPT_FUNCTION(pthread_setname_np); #else #define INIT_PTHREAD_SETNAME_NP #endif #if SANITIZER_INTERCEPT_SINCOS INTERCEPTOR(void, sincos, double x, double *sin, double *cos) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sincos, x, sin, cos); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. REAL(sincos)(x, sin, cos); if (sin) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sin, sizeof(*sin)); if (cos) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cos, sizeof(*cos)); } INTERCEPTOR(void, sincosf, float x, float *sin, float *cos) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sincosf, x, sin, cos); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. REAL(sincosf)(x, sin, cos); if (sin) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sin, sizeof(*sin)); if (cos) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cos, sizeof(*cos)); } INTERCEPTOR(void, sincosl, long double x, long double *sin, long double *cos) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sincosl, x, sin, cos); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. REAL(sincosl)(x, sin, cos); if (sin) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sin, sizeof(*sin)); if (cos) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cos, sizeof(*cos)); } #define INIT_SINCOS \ COMMON_INTERCEPT_FUNCTION(sincos); \ COMMON_INTERCEPT_FUNCTION(sincosf); \ COMMON_INTERCEPT_FUNCTION_LDBL(sincosl); #else #define INIT_SINCOS #endif #if SANITIZER_INTERCEPT_REMQUO INTERCEPTOR(double, remquo, double x, double y, int *quo) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, remquo, x, y, quo); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. double res = REAL(remquo)(x, y, quo); if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo)); return res; } INTERCEPTOR(float, remquof, float x, float y, int *quo) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, remquof, x, y, quo); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. float res = REAL(remquof)(x, y, quo); if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo)); return res; } INTERCEPTOR(long double, remquol, long double x, long double y, int *quo) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, remquol, x, y, quo); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. long double res = REAL(remquol)(x, y, quo); if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo)); return res; } #define INIT_REMQUO \ COMMON_INTERCEPT_FUNCTION(remquo); \ COMMON_INTERCEPT_FUNCTION(remquof); \ COMMON_INTERCEPT_FUNCTION_LDBL(remquol); #else #define INIT_REMQUO #endif #if SANITIZER_INTERCEPT_LGAMMA extern int signgam; INTERCEPTOR(double, lgamma, double x) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, lgamma, x); double res = REAL(lgamma)(x); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &signgam, sizeof(signgam)); return res; } INTERCEPTOR(float, lgammaf, float x) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, lgammaf, x); float res = REAL(lgammaf)(x); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &signgam, sizeof(signgam)); return res; } INTERCEPTOR(long double, lgammal, long double x) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, lgammal, x); long double res = REAL(lgammal)(x); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &signgam, sizeof(signgam)); return res; } #define INIT_LGAMMA \ COMMON_INTERCEPT_FUNCTION(lgamma); \ COMMON_INTERCEPT_FUNCTION(lgammaf); \ COMMON_INTERCEPT_FUNCTION_LDBL(lgammal); #else #define INIT_LGAMMA #endif #if SANITIZER_INTERCEPT_LGAMMA_R INTERCEPTOR(double, lgamma_r, double x, int *signp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, lgamma_r, x, signp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. double res = REAL(lgamma_r)(x, signp); if (signp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, signp, sizeof(*signp)); return res; } INTERCEPTOR(float, lgammaf_r, float x, int *signp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, lgammaf_r, x, signp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. float res = REAL(lgammaf_r)(x, signp); if (signp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, signp, sizeof(*signp)); return res; } #define INIT_LGAMMA_R \ COMMON_INTERCEPT_FUNCTION(lgamma_r); \ COMMON_INTERCEPT_FUNCTION(lgammaf_r); #else #define INIT_LGAMMA_R #endif #if SANITIZER_INTERCEPT_LGAMMAL_R INTERCEPTOR(long double, lgammal_r, long double x, int *signp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, lgammal_r, x, signp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. long double res = REAL(lgammal_r)(x, signp); if (signp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, signp, sizeof(*signp)); return res; } #define INIT_LGAMMAL_R COMMON_INTERCEPT_FUNCTION_LDBL(lgammal_r); #else #define INIT_LGAMMAL_R #endif #if SANITIZER_INTERCEPT_DRAND48_R INTERCEPTOR(int, drand48_r, void *buffer, double *result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, drand48_r, buffer, result); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(drand48_r)(buffer, result); if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); return res; } INTERCEPTOR(int, lrand48_r, void *buffer, long *result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, lrand48_r, buffer, result); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(lrand48_r)(buffer, result); if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); return res; } #define INIT_DRAND48_R \ COMMON_INTERCEPT_FUNCTION(drand48_r); \ COMMON_INTERCEPT_FUNCTION(lrand48_r); #else #define INIT_DRAND48_R #endif #if SANITIZER_INTERCEPT_RAND_R INTERCEPTOR(int, rand_r, unsigned *seedp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, rand_r, seedp); COMMON_INTERCEPTOR_READ_RANGE(ctx, seedp, sizeof(*seedp)); return REAL(rand_r)(seedp); } #define INIT_RAND_R COMMON_INTERCEPT_FUNCTION(rand_r); #else #define INIT_RAND_R #endif #if SANITIZER_INTERCEPT_GETLINE INTERCEPTOR(SSIZE_T, getline, char **lineptr, SIZE_T *n, void *stream) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getline, lineptr, n, stream); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(getline)(lineptr, n, stream); if (res > 0) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lineptr, sizeof(*lineptr)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, n, sizeof(*n)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *lineptr, res + 1); } return res; } // FIXME: under ASan the call below may write to freed memory and corrupt its // metadata. See // https://github.com/google/sanitizers/issues/321. #define GETDELIM_INTERCEPTOR_IMPL(vname) \ { \ void *ctx; \ COMMON_INTERCEPTOR_ENTER(ctx, vname, lineptr, n, delim, stream); \ SSIZE_T res = REAL(vname)(lineptr, n, delim, stream); \ if (res > 0) { \ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lineptr, sizeof(*lineptr)); \ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, n, sizeof(*n)); \ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *lineptr, res + 1); \ } \ return res; \ } INTERCEPTOR(SSIZE_T, __getdelim, char **lineptr, SIZE_T *n, int delim, void *stream) GETDELIM_INTERCEPTOR_IMPL(__getdelim) // There's no __getdelim() on FreeBSD so we supply the getdelim() interceptor // with its own body. INTERCEPTOR(SSIZE_T, getdelim, char **lineptr, SIZE_T *n, int delim, void *stream) GETDELIM_INTERCEPTOR_IMPL(getdelim) #define INIT_GETLINE \ COMMON_INTERCEPT_FUNCTION(getline); \ COMMON_INTERCEPT_FUNCTION(__getdelim); \ COMMON_INTERCEPT_FUNCTION(getdelim); #else #define INIT_GETLINE #endif #if SANITIZER_INTERCEPT_ICONV INTERCEPTOR(SIZE_T, iconv, void *cd, char **inbuf, SIZE_T *inbytesleft, char **outbuf, SIZE_T *outbytesleft) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, iconv, cd, inbuf, inbytesleft, outbuf, outbytesleft); if (inbytesleft) COMMON_INTERCEPTOR_READ_RANGE(ctx, inbytesleft, sizeof(*inbytesleft)); if (inbuf && inbytesleft) COMMON_INTERCEPTOR_READ_RANGE(ctx, *inbuf, *inbytesleft); if (outbytesleft) COMMON_INTERCEPTOR_READ_RANGE(ctx, outbytesleft, sizeof(*outbytesleft)); void *outbuf_orig = outbuf ? *outbuf : nullptr; // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SIZE_T res = REAL(iconv)(cd, inbuf, inbytesleft, outbuf, outbytesleft); if (outbuf && *outbuf > outbuf_orig) { SIZE_T sz = (char *)*outbuf - (char *)outbuf_orig; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, outbuf_orig, sz); } return res; } #define INIT_ICONV COMMON_INTERCEPT_FUNCTION(iconv); #else #define INIT_ICONV #endif #if SANITIZER_INTERCEPT_TIMES INTERCEPTOR(__sanitizer_clock_t, times, void *tms) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, times, tms); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. __sanitizer_clock_t res = REAL(times)(tms); if (res != (__sanitizer_clock_t)-1 && tms) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tms, struct_tms_sz); return res; } #define INIT_TIMES COMMON_INTERCEPT_FUNCTION(times); #else #define INIT_TIMES #endif #if SANITIZER_INTERCEPT_TLS_GET_ADDR #if !SANITIZER_S390 #define INIT_TLS_GET_ADDR COMMON_INTERCEPT_FUNCTION(__tls_get_addr) // If you see any crashes around this functions, there are 2 known issues with // it: 1. __tls_get_addr can be called with mis-aligned stack due to: // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58066 // 2. It can be called recursively if sanitizer code uses __tls_get_addr // to access thread local variables (it should not happen normally, // because sanitizers use initial-exec tls model). INTERCEPTOR(void *, __tls_get_addr, void *arg) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __tls_get_addr, arg); void *res = REAL(__tls_get_addr)(arg); uptr tls_begin, tls_end; COMMON_INTERCEPTOR_GET_TLS_RANGE(&tls_begin, &tls_end); DTLS::DTV *dtv = DTLS_on_tls_get_addr(arg, res, tls_begin, tls_end); if (dtv) { // New DTLS block has been allocated. COMMON_INTERCEPTOR_INITIALIZE_RANGE((void *)dtv->beg, dtv->size); } return res; } #if SANITIZER_PPC // On PowerPC, we also need to intercept __tls_get_addr_opt, which has // mostly the same semantics as __tls_get_addr, but its presence enables // some optimizations in linker (which are safe to ignore here). extern "C" __attribute__((alias("__interceptor___tls_get_addr"), visibility("default"))) void *__tls_get_addr_opt(void *arg); #endif #else // SANITIZER_S390 // On s390, we have to intercept two functions here: // - __tls_get_addr_internal, which is a glibc-internal function that is like // the usual __tls_get_addr, but returns a TP-relative offset instead of // a proper pointer. It is used by dlsym for TLS symbols. // - __tls_get_offset, which is like the above, but also takes a GOT-relative // descriptor offset as an argument instead of a pointer. GOT address // is passed in r12, so it's necessary to write it in assembly. This is // the function used by the compiler. extern "C" uptr __tls_get_offset_wrapper(void *arg, uptr (*fn)(void *arg)); #define INIT_TLS_GET_ADDR COMMON_INTERCEPT_FUNCTION(__tls_get_offset) DEFINE_REAL(uptr, __tls_get_offset, void *arg) extern "C" uptr __tls_get_offset(void *arg); extern "C" uptr __interceptor___tls_get_offset(void *arg); INTERCEPTOR(uptr, __tls_get_addr_internal, void *arg) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __tls_get_addr_internal, arg); uptr res = __tls_get_offset_wrapper(arg, REAL(__tls_get_offset)); uptr tp = reinterpret_cast(__builtin_thread_pointer()); void *ptr = reinterpret_cast(res + tp); uptr tls_begin, tls_end; COMMON_INTERCEPTOR_GET_TLS_RANGE(&tls_begin, &tls_end); DTLS::DTV *dtv = DTLS_on_tls_get_addr(arg, ptr, tls_begin, tls_end); if (dtv) { // New DTLS block has been allocated. COMMON_INTERCEPTOR_INITIALIZE_RANGE((void *)dtv->beg, dtv->size); } return res; } // We need a hidden symbol aliasing the above, so that we can jump // directly to it from the assembly below. extern "C" __attribute__((alias("__interceptor___tls_get_addr_internal"), visibility("hidden"))) uptr __tls_get_addr_hidden(void *arg); // Now carefully intercept __tls_get_offset. asm( ".text\n" // The __intercept_ version has to exist, so that gen_dynamic_list.py // exports our symbol. ".weak __tls_get_offset\n" ".type __tls_get_offset, @function\n" "__tls_get_offset:\n" ".global __interceptor___tls_get_offset\n" ".type __interceptor___tls_get_offset, @function\n" "__interceptor___tls_get_offset:\n" #ifdef __s390x__ "la %r2, 0(%r2,%r12)\n" "jg __tls_get_addr_hidden\n" #else "basr %r3,0\n" "0: la %r2,0(%r2,%r12)\n" "l %r4,1f-0b(%r3)\n" "b 0(%r4,%r3)\n" "1: .long __tls_get_addr_hidden - 0b\n" #endif ".size __interceptor___tls_get_offset, .-__interceptor___tls_get_offset\n" // Assembly wrapper to call REAL(__tls_get_offset)(arg) ".type __tls_get_offset_wrapper, @function\n" "__tls_get_offset_wrapper:\n" #ifdef __s390x__ "sgr %r2,%r12\n" #else "sr %r2,%r12\n" #endif "br %r3\n" ".size __tls_get_offset_wrapper, .-__tls_get_offset_wrapper\n" ); #endif // SANITIZER_S390 #else #define INIT_TLS_GET_ADDR #endif #if SANITIZER_INTERCEPT_LISTXATTR INTERCEPTOR(SSIZE_T, listxattr, const char *path, char *list, SIZE_T size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, listxattr, path, list, size); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(listxattr)(path, list, size); // Here and below, size == 0 is a special case where nothing is written to the // buffer, and res contains the desired buffer size. if (size && res > 0 && list) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, list, res); return res; } INTERCEPTOR(SSIZE_T, llistxattr, const char *path, char *list, SIZE_T size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, llistxattr, path, list, size); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(llistxattr)(path, list, size); if (size && res > 0 && list) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, list, res); return res; } INTERCEPTOR(SSIZE_T, flistxattr, int fd, char *list, SIZE_T size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, flistxattr, fd, list, size); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(flistxattr)(fd, list, size); if (size && res > 0 && list) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, list, res); return res; } #define INIT_LISTXATTR \ COMMON_INTERCEPT_FUNCTION(listxattr); \ COMMON_INTERCEPT_FUNCTION(llistxattr); \ COMMON_INTERCEPT_FUNCTION(flistxattr); #else #define INIT_LISTXATTR #endif #if SANITIZER_INTERCEPT_GETXATTR INTERCEPTOR(SSIZE_T, getxattr, const char *path, const char *name, char *value, SIZE_T size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getxattr, path, name, value, size); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); if (name) COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(getxattr)(path, name, value, size); if (size && res > 0 && value) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, value, res); return res; } INTERCEPTOR(SSIZE_T, lgetxattr, const char *path, const char *name, char *value, SIZE_T size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, lgetxattr, path, name, value, size); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); if (name) COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(lgetxattr)(path, name, value, size); if (size && res > 0 && value) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, value, res); return res; } INTERCEPTOR(SSIZE_T, fgetxattr, int fd, const char *name, char *value, SIZE_T size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fgetxattr, fd, name, value, size); if (name) COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. SSIZE_T res = REAL(fgetxattr)(fd, name, value, size); if (size && res > 0 && value) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, value, res); return res; } #define INIT_GETXATTR \ COMMON_INTERCEPT_FUNCTION(getxattr); \ COMMON_INTERCEPT_FUNCTION(lgetxattr); \ COMMON_INTERCEPT_FUNCTION(fgetxattr); #else #define INIT_GETXATTR #endif #if SANITIZER_INTERCEPT_GETRESID INTERCEPTOR(int, getresuid, void *ruid, void *euid, void *suid) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getresuid, ruid, euid, suid); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getresuid)(ruid, euid, suid); if (res >= 0) { if (ruid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ruid, uid_t_sz); if (euid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, euid, uid_t_sz); if (suid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, suid, uid_t_sz); } return res; } INTERCEPTOR(int, getresgid, void *rgid, void *egid, void *sgid) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getresgid, rgid, egid, sgid); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getresgid)(rgid, egid, sgid); if (res >= 0) { if (rgid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rgid, gid_t_sz); if (egid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, egid, gid_t_sz); if (sgid) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sgid, gid_t_sz); } return res; } #define INIT_GETRESID \ COMMON_INTERCEPT_FUNCTION(getresuid); \ COMMON_INTERCEPT_FUNCTION(getresgid); #else #define INIT_GETRESID #endif #if SANITIZER_INTERCEPT_GETIFADDRS // As long as getifaddrs()/freeifaddrs() use calloc()/free(), we don't need to // intercept freeifaddrs(). If that ceases to be the case, we might need to // intercept it to poison the memory again. INTERCEPTOR(int, getifaddrs, __sanitizer_ifaddrs **ifap) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getifaddrs, ifap); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(getifaddrs)(ifap); if (res == 0 && ifap) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ifap, sizeof(void *)); __sanitizer_ifaddrs *p = *ifap; while (p) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(__sanitizer_ifaddrs)); if (p->ifa_name) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ifa_name, REAL(strlen)(p->ifa_name) + 1); if (p->ifa_addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ifa_addr, struct_sockaddr_sz); if (p->ifa_netmask) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ifa_netmask, struct_sockaddr_sz); // On Linux this is a union, but the other member also points to a // struct sockaddr, so the following is sufficient. if (p->ifa_dstaddr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p->ifa_dstaddr, struct_sockaddr_sz); // FIXME(smatveev): Unpoison p->ifa_data as well. p = p->ifa_next; } } return res; } #define INIT_GETIFADDRS \ COMMON_INTERCEPT_FUNCTION(getifaddrs); #else #define INIT_GETIFADDRS #endif #if SANITIZER_INTERCEPT_IF_INDEXTONAME INTERCEPTOR(char *, if_indextoname, unsigned int ifindex, char* ifname) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, if_indextoname, ifindex, ifname); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. char *res = REAL(if_indextoname)(ifindex, ifname); if (res && ifname) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ifname, REAL(strlen)(ifname) + 1); return res; } INTERCEPTOR(unsigned int, if_nametoindex, const char* ifname) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, if_nametoindex, ifname); if (ifname) COMMON_INTERCEPTOR_READ_RANGE(ctx, ifname, REAL(strlen)(ifname) + 1); return REAL(if_nametoindex)(ifname); } #define INIT_IF_INDEXTONAME \ COMMON_INTERCEPT_FUNCTION(if_indextoname); \ COMMON_INTERCEPT_FUNCTION(if_nametoindex); #else #define INIT_IF_INDEXTONAME #endif #if SANITIZER_INTERCEPT_CAPGET INTERCEPTOR(int, capget, void *hdrp, void *datap) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, capget, hdrp, datap); if (hdrp) COMMON_INTERCEPTOR_READ_RANGE(ctx, hdrp, __user_cap_header_struct_sz); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(capget)(hdrp, datap); if (res == 0 && datap) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, datap, __user_cap_data_struct_sz); // We can also return -1 and write to hdrp->version if the version passed in // hdrp->version is unsupported. But that's not a trivial condition to check, // and anyway COMMON_INTERCEPTOR_READ_RANGE protects us to some extent. return res; } INTERCEPTOR(int, capset, void *hdrp, const void *datap) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, capset, hdrp, datap); if (hdrp) COMMON_INTERCEPTOR_READ_RANGE(ctx, hdrp, __user_cap_header_struct_sz); if (datap) COMMON_INTERCEPTOR_READ_RANGE(ctx, datap, __user_cap_data_struct_sz); return REAL(capset)(hdrp, datap); } #define INIT_CAPGET \ COMMON_INTERCEPT_FUNCTION(capget); \ COMMON_INTERCEPT_FUNCTION(capset); #else #define INIT_CAPGET #endif #if SANITIZER_INTERCEPT_AEABI_MEM INTERCEPTOR(void *, __aeabi_memmove, void *to, const void *from, uptr size) { void *ctx; COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, to, from, size); } INTERCEPTOR(void *, __aeabi_memmove4, void *to, const void *from, uptr size) { void *ctx; COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, to, from, size); } INTERCEPTOR(void *, __aeabi_memmove8, void *to, const void *from, uptr size) { void *ctx; COMMON_INTERCEPTOR_MEMMOVE_IMPL(ctx, to, from, size); } INTERCEPTOR(void *, __aeabi_memcpy, void *to, const void *from, uptr size) { void *ctx; COMMON_INTERCEPTOR_MEMCPY_IMPL(ctx, to, from, size); } INTERCEPTOR(void *, __aeabi_memcpy4, void *to, const void *from, uptr size) { void *ctx; COMMON_INTERCEPTOR_MEMCPY_IMPL(ctx, to, from, size); } INTERCEPTOR(void *, __aeabi_memcpy8, void *to, const void *from, uptr size) { void *ctx; COMMON_INTERCEPTOR_MEMCPY_IMPL(ctx, to, from, size); } // Note the argument order. INTERCEPTOR(void *, __aeabi_memset, void *block, uptr size, int c) { void *ctx; COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, c, size); } INTERCEPTOR(void *, __aeabi_memset4, void *block, uptr size, int c) { void *ctx; COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, c, size); } INTERCEPTOR(void *, __aeabi_memset8, void *block, uptr size, int c) { void *ctx; COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, c, size); } INTERCEPTOR(void *, __aeabi_memclr, void *block, uptr size) { void *ctx; COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, 0, size); } INTERCEPTOR(void *, __aeabi_memclr4, void *block, uptr size) { void *ctx; COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, 0, size); } INTERCEPTOR(void *, __aeabi_memclr8, void *block, uptr size) { void *ctx; COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, 0, size); } #define INIT_AEABI_MEM \ COMMON_INTERCEPT_FUNCTION(__aeabi_memmove); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memmove4); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memmove8); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memcpy); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memcpy4); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memcpy8); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memset); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memset4); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memset8); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memclr); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memclr4); \ COMMON_INTERCEPT_FUNCTION(__aeabi_memclr8); #else #define INIT_AEABI_MEM #endif // SANITIZER_INTERCEPT_AEABI_MEM #if SANITIZER_INTERCEPT___BZERO INTERCEPTOR(void *, __bzero, void *block, uptr size) { void *ctx; COMMON_INTERCEPTOR_MEMSET_IMPL(ctx, block, 0, size); } #define INIT___BZERO COMMON_INTERCEPT_FUNCTION(__bzero); #else #define INIT___BZERO #endif // SANITIZER_INTERCEPT___BZERO #if SANITIZER_INTERCEPT_FTIME INTERCEPTOR(int, ftime, __sanitizer_timeb *tp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ftime, tp); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(ftime)(tp); if (tp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tp, sizeof(*tp)); return res; } #define INIT_FTIME COMMON_INTERCEPT_FUNCTION(ftime); #else #define INIT_FTIME #endif // SANITIZER_INTERCEPT_FTIME #if SANITIZER_INTERCEPT_XDR INTERCEPTOR(void, xdrmem_create, __sanitizer_XDR *xdrs, uptr addr, unsigned size, int op) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, xdrmem_create, xdrs, addr, size, op); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. REAL(xdrmem_create)(xdrs, addr, size, op); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, xdrs, sizeof(*xdrs)); if (op == __sanitizer_XDR_ENCODE) { // It's not obvious how much data individual xdr_ routines write. // Simply unpoison the entire target buffer in advance. COMMON_INTERCEPTOR_WRITE_RANGE(ctx, (void *)addr, size); } } INTERCEPTOR(void, xdrstdio_create, __sanitizer_XDR *xdrs, void *file, int op) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, xdrstdio_create, xdrs, file, op); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. REAL(xdrstdio_create)(xdrs, file, op); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, xdrs, sizeof(*xdrs)); } // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. #define XDR_INTERCEPTOR(F, T) \ INTERCEPTOR(int, F, __sanitizer_XDR *xdrs, T *p) { \ void *ctx; \ COMMON_INTERCEPTOR_ENTER(ctx, F, xdrs, p); \ if (p && xdrs->x_op == __sanitizer_XDR_ENCODE) \ COMMON_INTERCEPTOR_READ_RANGE(ctx, p, sizeof(*p)); \ int res = REAL(F)(xdrs, p); \ if (res && p && xdrs->x_op == __sanitizer_XDR_DECODE) \ COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p)); \ return res; \ } XDR_INTERCEPTOR(xdr_short, short) XDR_INTERCEPTOR(xdr_u_short, unsigned short) XDR_INTERCEPTOR(xdr_int, int) XDR_INTERCEPTOR(xdr_u_int, unsigned) XDR_INTERCEPTOR(xdr_long, long) XDR_INTERCEPTOR(xdr_u_long, unsigned long) XDR_INTERCEPTOR(xdr_hyper, long long) XDR_INTERCEPTOR(xdr_u_hyper, unsigned long long) XDR_INTERCEPTOR(xdr_longlong_t, long long) XDR_INTERCEPTOR(xdr_u_longlong_t, unsigned long long) XDR_INTERCEPTOR(xdr_int8_t, u8) XDR_INTERCEPTOR(xdr_uint8_t, u8) XDR_INTERCEPTOR(xdr_int16_t, u16) XDR_INTERCEPTOR(xdr_uint16_t, u16) XDR_INTERCEPTOR(xdr_int32_t, u32) XDR_INTERCEPTOR(xdr_uint32_t, u32) XDR_INTERCEPTOR(xdr_int64_t, u64) XDR_INTERCEPTOR(xdr_uint64_t, u64) XDR_INTERCEPTOR(xdr_quad_t, long long) XDR_INTERCEPTOR(xdr_u_quad_t, unsigned long long) XDR_INTERCEPTOR(xdr_bool, bool) XDR_INTERCEPTOR(xdr_enum, int) XDR_INTERCEPTOR(xdr_char, char) XDR_INTERCEPTOR(xdr_u_char, unsigned char) XDR_INTERCEPTOR(xdr_float, float) XDR_INTERCEPTOR(xdr_double, double) // FIXME: intercept xdr_array, opaque, union, vector, reference, pointer, // wrapstring, sizeof INTERCEPTOR(int, xdr_bytes, __sanitizer_XDR *xdrs, char **p, unsigned *sizep, unsigned maxsize) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, xdr_bytes, xdrs, p, sizep, maxsize); if (p && sizep && xdrs->x_op == __sanitizer_XDR_ENCODE) { COMMON_INTERCEPTOR_READ_RANGE(ctx, p, sizeof(*p)); COMMON_INTERCEPTOR_READ_RANGE(ctx, sizep, sizeof(*sizep)); COMMON_INTERCEPTOR_READ_RANGE(ctx, *p, *sizep); } // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(xdr_bytes)(xdrs, p, sizep, maxsize); if (p && sizep && xdrs->x_op == __sanitizer_XDR_DECODE) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sizep, sizeof(*sizep)); if (res && *p && *sizep) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *p, *sizep); } return res; } INTERCEPTOR(int, xdr_string, __sanitizer_XDR *xdrs, char **p, unsigned maxsize) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, xdr_string, xdrs, p, maxsize); if (p && xdrs->x_op == __sanitizer_XDR_ENCODE) { COMMON_INTERCEPTOR_READ_RANGE(ctx, p, sizeof(*p)); COMMON_INTERCEPTOR_READ_RANGE(ctx, *p, REAL(strlen)(*p) + 1); } // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. int res = REAL(xdr_string)(xdrs, p, maxsize); if (p && xdrs->x_op == __sanitizer_XDR_DECODE) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, p, sizeof(*p)); if (res && *p) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *p, REAL(strlen)(*p) + 1); } return res; } #define INIT_XDR \ COMMON_INTERCEPT_FUNCTION(xdrmem_create); \ COMMON_INTERCEPT_FUNCTION(xdrstdio_create); \ COMMON_INTERCEPT_FUNCTION(xdr_short); \ COMMON_INTERCEPT_FUNCTION(xdr_u_short); \ COMMON_INTERCEPT_FUNCTION(xdr_int); \ COMMON_INTERCEPT_FUNCTION(xdr_u_int); \ COMMON_INTERCEPT_FUNCTION(xdr_long); \ COMMON_INTERCEPT_FUNCTION(xdr_u_long); \ COMMON_INTERCEPT_FUNCTION(xdr_hyper); \ COMMON_INTERCEPT_FUNCTION(xdr_u_hyper); \ COMMON_INTERCEPT_FUNCTION(xdr_longlong_t); \ COMMON_INTERCEPT_FUNCTION(xdr_u_longlong_t); \ COMMON_INTERCEPT_FUNCTION(xdr_int8_t); \ COMMON_INTERCEPT_FUNCTION(xdr_uint8_t); \ COMMON_INTERCEPT_FUNCTION(xdr_int16_t); \ COMMON_INTERCEPT_FUNCTION(xdr_uint16_t); \ COMMON_INTERCEPT_FUNCTION(xdr_int32_t); \ COMMON_INTERCEPT_FUNCTION(xdr_uint32_t); \ COMMON_INTERCEPT_FUNCTION(xdr_int64_t); \ COMMON_INTERCEPT_FUNCTION(xdr_uint64_t); \ COMMON_INTERCEPT_FUNCTION(xdr_quad_t); \ COMMON_INTERCEPT_FUNCTION(xdr_u_quad_t); \ COMMON_INTERCEPT_FUNCTION(xdr_bool); \ COMMON_INTERCEPT_FUNCTION(xdr_enum); \ COMMON_INTERCEPT_FUNCTION(xdr_char); \ COMMON_INTERCEPT_FUNCTION(xdr_u_char); \ COMMON_INTERCEPT_FUNCTION(xdr_float); \ COMMON_INTERCEPT_FUNCTION(xdr_double); \ COMMON_INTERCEPT_FUNCTION(xdr_bytes); \ COMMON_INTERCEPT_FUNCTION(xdr_string); #else #define INIT_XDR #endif // SANITIZER_INTERCEPT_XDR #if SANITIZER_INTERCEPT_TSEARCH INTERCEPTOR(void *, tsearch, void *key, void **rootp, int (*compar)(const void *, const void *)) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, tsearch, key, rootp, compar); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. void *res = REAL(tsearch)(key, rootp, compar); if (res && *(void **)res == key) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, sizeof(void *)); return res; } #define INIT_TSEARCH COMMON_INTERCEPT_FUNCTION(tsearch); #else #define INIT_TSEARCH #endif #if SANITIZER_INTERCEPT_LIBIO_INTERNALS || SANITIZER_INTERCEPT_FOPEN || \ SANITIZER_INTERCEPT_OPEN_MEMSTREAM void unpoison_file(__sanitizer_FILE *fp) { #if SANITIZER_HAS_STRUCT_FILE COMMON_INTERCEPTOR_INITIALIZE_RANGE(fp, sizeof(*fp)); if (fp->_IO_read_base && fp->_IO_read_base < fp->_IO_read_end) COMMON_INTERCEPTOR_INITIALIZE_RANGE(fp->_IO_read_base, fp->_IO_read_end - fp->_IO_read_base); #endif // SANITIZER_HAS_STRUCT_FILE } #endif #if SANITIZER_INTERCEPT_LIBIO_INTERNALS // These guys are called when a .c source is built with -O2. INTERCEPTOR(int, __uflow, __sanitizer_FILE *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __uflow, fp); int res = REAL(__uflow)(fp); unpoison_file(fp); return res; } INTERCEPTOR(int, __underflow, __sanitizer_FILE *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __underflow, fp); int res = REAL(__underflow)(fp); unpoison_file(fp); return res; } INTERCEPTOR(int, __overflow, __sanitizer_FILE *fp, int ch) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __overflow, fp, ch); int res = REAL(__overflow)(fp, ch); unpoison_file(fp); return res; } INTERCEPTOR(int, __wuflow, __sanitizer_FILE *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __wuflow, fp); int res = REAL(__wuflow)(fp); unpoison_file(fp); return res; } INTERCEPTOR(int, __wunderflow, __sanitizer_FILE *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __wunderflow, fp); int res = REAL(__wunderflow)(fp); unpoison_file(fp); return res; } INTERCEPTOR(int, __woverflow, __sanitizer_FILE *fp, int ch) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __woverflow, fp, ch); int res = REAL(__woverflow)(fp, ch); unpoison_file(fp); return res; } #define INIT_LIBIO_INTERNALS \ COMMON_INTERCEPT_FUNCTION(__uflow); \ COMMON_INTERCEPT_FUNCTION(__underflow); \ COMMON_INTERCEPT_FUNCTION(__overflow); \ COMMON_INTERCEPT_FUNCTION(__wuflow); \ COMMON_INTERCEPT_FUNCTION(__wunderflow); \ COMMON_INTERCEPT_FUNCTION(__woverflow); #else #define INIT_LIBIO_INTERNALS #endif #if SANITIZER_INTERCEPT_FOPEN INTERCEPTOR(__sanitizer_FILE *, fopen, const char *path, const char *mode) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fopen, path, mode); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); COMMON_INTERCEPTOR_READ_RANGE(ctx, mode, REAL(strlen)(mode) + 1); __sanitizer_FILE *res = REAL(fopen)(path, mode); COMMON_INTERCEPTOR_FILE_OPEN(ctx, res, path); if (res) unpoison_file(res); return res; } INTERCEPTOR(__sanitizer_FILE *, fdopen, int fd, const char *mode) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fdopen, fd, mode); COMMON_INTERCEPTOR_READ_RANGE(ctx, mode, REAL(strlen)(mode) + 1); __sanitizer_FILE *res = REAL(fdopen)(fd, mode); if (res) unpoison_file(res); return res; } INTERCEPTOR(__sanitizer_FILE *, freopen, const char *path, const char *mode, __sanitizer_FILE *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, freopen, path, mode, fp); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); COMMON_INTERCEPTOR_READ_RANGE(ctx, mode, REAL(strlen)(mode) + 1); COMMON_INTERCEPTOR_FILE_CLOSE(ctx, fp); __sanitizer_FILE *res = REAL(freopen)(path, mode, fp); COMMON_INTERCEPTOR_FILE_OPEN(ctx, res, path); if (res) unpoison_file(res); return res; } #define INIT_FOPEN \ COMMON_INTERCEPT_FUNCTION(fopen); \ COMMON_INTERCEPT_FUNCTION(fdopen); \ COMMON_INTERCEPT_FUNCTION(freopen); #else #define INIT_FOPEN #endif #if SANITIZER_INTERCEPT_FOPEN64 INTERCEPTOR(__sanitizer_FILE *, fopen64, const char *path, const char *mode) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fopen64, path, mode); COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); COMMON_INTERCEPTOR_READ_RANGE(ctx, mode, REAL(strlen)(mode) + 1); __sanitizer_FILE *res = REAL(fopen64)(path, mode); COMMON_INTERCEPTOR_FILE_OPEN(ctx, res, path); if (res) unpoison_file(res); return res; } INTERCEPTOR(__sanitizer_FILE *, freopen64, const char *path, const char *mode, __sanitizer_FILE *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, freopen64, path, mode, fp); if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); COMMON_INTERCEPTOR_READ_RANGE(ctx, mode, REAL(strlen)(mode) + 1); COMMON_INTERCEPTOR_FILE_CLOSE(ctx, fp); __sanitizer_FILE *res = REAL(freopen64)(path, mode, fp); COMMON_INTERCEPTOR_FILE_OPEN(ctx, res, path); if (res) unpoison_file(res); return res; } #define INIT_FOPEN64 \ COMMON_INTERCEPT_FUNCTION(fopen64); \ COMMON_INTERCEPT_FUNCTION(freopen64); #else #define INIT_FOPEN64 #endif #if SANITIZER_INTERCEPT_OPEN_MEMSTREAM INTERCEPTOR(__sanitizer_FILE *, open_memstream, char **ptr, SIZE_T *sizeloc) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, open_memstream, ptr, sizeloc); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. __sanitizer_FILE *res = REAL(open_memstream)(ptr, sizeloc); if (res) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, sizeof(*ptr)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sizeloc, sizeof(*sizeloc)); unpoison_file(res); FileMetadata file = {ptr, sizeloc}; SetInterceptorMetadata(res, file); } return res; } INTERCEPTOR(__sanitizer_FILE *, open_wmemstream, wchar_t **ptr, SIZE_T *sizeloc) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, open_wmemstream, ptr, sizeloc); __sanitizer_FILE *res = REAL(open_wmemstream)(ptr, sizeloc); if (res) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, sizeof(*ptr)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sizeloc, sizeof(*sizeloc)); unpoison_file(res); FileMetadata file = {(char **)ptr, sizeloc}; SetInterceptorMetadata(res, file); } return res; } INTERCEPTOR(__sanitizer_FILE *, fmemopen, void *buf, SIZE_T size, const char *mode) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fmemopen, buf, size, mode); // FIXME: under ASan the call below may write to freed memory and corrupt // its metadata. See // https://github.com/google/sanitizers/issues/321. __sanitizer_FILE *res = REAL(fmemopen)(buf, size, mode); if (res) unpoison_file(res); return res; } #define INIT_OPEN_MEMSTREAM \ COMMON_INTERCEPT_FUNCTION(open_memstream); \ COMMON_INTERCEPT_FUNCTION(open_wmemstream); \ COMMON_INTERCEPT_FUNCTION(fmemopen); #else #define INIT_OPEN_MEMSTREAM #endif #if SANITIZER_INTERCEPT_OBSTACK static void initialize_obstack(__sanitizer_obstack *obstack) { COMMON_INTERCEPTOR_INITIALIZE_RANGE(obstack, sizeof(*obstack)); if (obstack->chunk) COMMON_INTERCEPTOR_INITIALIZE_RANGE(obstack->chunk, sizeof(*obstack->chunk)); } INTERCEPTOR(int, _obstack_begin_1, __sanitizer_obstack *obstack, int sz, int align, void *(*alloc_fn)(uptr arg, uptr sz), void (*free_fn)(uptr arg, void *p)) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, _obstack_begin_1, obstack, sz, align, alloc_fn, free_fn); int res = REAL(_obstack_begin_1)(obstack, sz, align, alloc_fn, free_fn); if (res) initialize_obstack(obstack); return res; } INTERCEPTOR(int, _obstack_begin, __sanitizer_obstack *obstack, int sz, int align, void *(*alloc_fn)(uptr sz), void (*free_fn)(void *p)) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, _obstack_begin, obstack, sz, align, alloc_fn, free_fn); int res = REAL(_obstack_begin)(obstack, sz, align, alloc_fn, free_fn); if (res) initialize_obstack(obstack); return res; } INTERCEPTOR(void, _obstack_newchunk, __sanitizer_obstack *obstack, int length) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, _obstack_newchunk, obstack, length); REAL(_obstack_newchunk)(obstack, length); if (obstack->chunk) COMMON_INTERCEPTOR_INITIALIZE_RANGE( obstack->chunk, obstack->next_free - (char *)obstack->chunk); } #define INIT_OBSTACK \ COMMON_INTERCEPT_FUNCTION(_obstack_begin_1); \ COMMON_INTERCEPT_FUNCTION(_obstack_begin); \ COMMON_INTERCEPT_FUNCTION(_obstack_newchunk); #else #define INIT_OBSTACK #endif #if SANITIZER_INTERCEPT_FFLUSH INTERCEPTOR(int, fflush, __sanitizer_FILE *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fflush, fp); int res = REAL(fflush)(fp); // FIXME: handle fp == NULL if (fp) { const FileMetadata *m = GetInterceptorMetadata(fp); if (m) COMMON_INTERCEPTOR_INITIALIZE_RANGE(*m->addr, *m->size); } return res; } #define INIT_FFLUSH COMMON_INTERCEPT_FUNCTION(fflush); #else #define INIT_FFLUSH #endif #if SANITIZER_INTERCEPT_FCLOSE INTERCEPTOR(int, fclose, __sanitizer_FILE *fp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fclose, fp); COMMON_INTERCEPTOR_FILE_CLOSE(ctx, fp); const FileMetadata *m = GetInterceptorMetadata(fp); int res = REAL(fclose)(fp); if (m) { COMMON_INTERCEPTOR_INITIALIZE_RANGE(*m->addr, *m->size); DeleteInterceptorMetadata(fp); } return res; } #define INIT_FCLOSE COMMON_INTERCEPT_FUNCTION(fclose); #else #define INIT_FCLOSE #endif #if SANITIZER_INTERCEPT_DLOPEN_DLCLOSE INTERCEPTOR(void*, dlopen, const char *filename, int flag) { void *ctx; COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, dlopen, filename, flag); if (filename) COMMON_INTERCEPTOR_READ_STRING(ctx, filename, 0); COMMON_INTERCEPTOR_ON_DLOPEN(filename, flag); void *res = REAL(dlopen)(filename, flag); COMMON_INTERCEPTOR_LIBRARY_LOADED(filename, res); return res; } INTERCEPTOR(int, dlclose, void *handle) { void *ctx; COMMON_INTERCEPTOR_ENTER_NOIGNORE(ctx, dlclose, handle); int res = REAL(dlclose)(handle); COMMON_INTERCEPTOR_LIBRARY_UNLOADED(); return res; } #define INIT_DLOPEN_DLCLOSE \ COMMON_INTERCEPT_FUNCTION(dlopen); \ COMMON_INTERCEPT_FUNCTION(dlclose); #else #define INIT_DLOPEN_DLCLOSE #endif #if SANITIZER_INTERCEPT_GETPASS INTERCEPTOR(char *, getpass, const char *prompt) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpass, prompt); if (prompt) COMMON_INTERCEPTOR_READ_RANGE(ctx, prompt, REAL(strlen)(prompt)+1); char *res = REAL(getpass)(prompt); if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res)+1); return res; } #define INIT_GETPASS COMMON_INTERCEPT_FUNCTION(getpass); #else #define INIT_GETPASS #endif #if SANITIZER_INTERCEPT_TIMERFD INTERCEPTOR(int, timerfd_settime, int fd, int flags, void *new_value, void *old_value) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, timerfd_settime, fd, flags, new_value, old_value); COMMON_INTERCEPTOR_READ_RANGE(ctx, new_value, struct_itimerspec_sz); int res = REAL(timerfd_settime)(fd, flags, new_value, old_value); if (res != -1 && old_value) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, old_value, struct_itimerspec_sz); return res; } INTERCEPTOR(int, timerfd_gettime, int fd, void *curr_value) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, timerfd_gettime, fd, curr_value); int res = REAL(timerfd_gettime)(fd, curr_value); if (res != -1 && curr_value) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, curr_value, struct_itimerspec_sz); return res; } #define INIT_TIMERFD \ COMMON_INTERCEPT_FUNCTION(timerfd_settime); \ COMMON_INTERCEPT_FUNCTION(timerfd_gettime); #else #define INIT_TIMERFD #endif #if SANITIZER_INTERCEPT_MLOCKX // Linux kernel has a bug that leads to kernel deadlock if a process // maps TBs of memory and then calls mlock(). static void MlockIsUnsupported() { static atomic_uint8_t printed; if (atomic_exchange(&printed, 1, memory_order_relaxed)) return; VPrintf(1, "%s ignores mlock/mlockall/munlock/munlockall\n", SanitizerToolName); } INTERCEPTOR(int, mlock, const void *addr, uptr len) { MlockIsUnsupported(); return 0; } INTERCEPTOR(int, munlock, const void *addr, uptr len) { MlockIsUnsupported(); return 0; } INTERCEPTOR(int, mlockall, int flags) { MlockIsUnsupported(); return 0; } INTERCEPTOR(int, munlockall, void) { MlockIsUnsupported(); return 0; } #define INIT_MLOCKX \ COMMON_INTERCEPT_FUNCTION(mlock); \ COMMON_INTERCEPT_FUNCTION(munlock); \ COMMON_INTERCEPT_FUNCTION(mlockall); \ COMMON_INTERCEPT_FUNCTION(munlockall); #else #define INIT_MLOCKX #endif // SANITIZER_INTERCEPT_MLOCKX #if SANITIZER_INTERCEPT_FOPENCOOKIE struct WrappedCookie { void *real_cookie; __sanitizer_cookie_io_functions_t real_io_funcs; }; static uptr wrapped_read(void *cookie, char *buf, uptr size) { COMMON_INTERCEPTOR_UNPOISON_PARAM(3); WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie; __sanitizer_cookie_io_read real_read = wrapped_cookie->real_io_funcs.read; return real_read ? real_read(wrapped_cookie->real_cookie, buf, size) : 0; } static uptr wrapped_write(void *cookie, const char *buf, uptr size) { COMMON_INTERCEPTOR_UNPOISON_PARAM(3); WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie; __sanitizer_cookie_io_write real_write = wrapped_cookie->real_io_funcs.write; return real_write ? real_write(wrapped_cookie->real_cookie, buf, size) : size; } static int wrapped_seek(void *cookie, u64 *offset, int whence) { COMMON_INTERCEPTOR_UNPOISON_PARAM(3); COMMON_INTERCEPTOR_INITIALIZE_RANGE(offset, sizeof(*offset)); WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie; __sanitizer_cookie_io_seek real_seek = wrapped_cookie->real_io_funcs.seek; return real_seek ? real_seek(wrapped_cookie->real_cookie, offset, whence) : -1; } static int wrapped_close(void *cookie) { COMMON_INTERCEPTOR_UNPOISON_PARAM(1); WrappedCookie *wrapped_cookie = (WrappedCookie *)cookie; __sanitizer_cookie_io_close real_close = wrapped_cookie->real_io_funcs.close; int res = real_close ? real_close(wrapped_cookie->real_cookie) : 0; InternalFree(wrapped_cookie); return res; } INTERCEPTOR(__sanitizer_FILE *, fopencookie, void *cookie, const char *mode, __sanitizer_cookie_io_functions_t io_funcs) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, fopencookie, cookie, mode, io_funcs); WrappedCookie *wrapped_cookie = (WrappedCookie *)InternalAlloc(sizeof(WrappedCookie)); wrapped_cookie->real_cookie = cookie; wrapped_cookie->real_io_funcs = io_funcs; __sanitizer_FILE *res = REAL(fopencookie)(wrapped_cookie, mode, {wrapped_read, wrapped_write, wrapped_seek, wrapped_close}); return res; } #define INIT_FOPENCOOKIE COMMON_INTERCEPT_FUNCTION(fopencookie); #else #define INIT_FOPENCOOKIE #endif // SANITIZER_INTERCEPT_FOPENCOOKIE #if SANITIZER_INTERCEPT_SEM INTERCEPTOR(int, sem_init, __sanitizer_sem_t *s, int pshared, unsigned value) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sem_init, s, pshared, value); // Workaround a bug in glibc's "old" semaphore implementation by // zero-initializing the sem_t contents. This has to be done here because // interceptors bind to the lowest symbols version by default, hitting the // buggy code path while the non-sanitized build of the same code works fine. REAL(memset)(s, 0, sizeof(*s)); int res = REAL(sem_init)(s, pshared, value); return res; } INTERCEPTOR(int, sem_destroy, __sanitizer_sem_t *s) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sem_destroy, s); int res = REAL(sem_destroy)(s); return res; } INTERCEPTOR(int, sem_wait, __sanitizer_sem_t *s) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sem_wait, s); int res = COMMON_INTERCEPTOR_BLOCK_REAL(sem_wait)(s); if (res == 0) { COMMON_INTERCEPTOR_ACQUIRE(ctx, (uptr)s); } return res; } INTERCEPTOR(int, sem_trywait, __sanitizer_sem_t *s) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sem_trywait, s); int res = COMMON_INTERCEPTOR_BLOCK_REAL(sem_trywait)(s); if (res == 0) { COMMON_INTERCEPTOR_ACQUIRE(ctx, (uptr)s); } return res; } INTERCEPTOR(int, sem_timedwait, __sanitizer_sem_t *s, void *abstime) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sem_timedwait, s, abstime); COMMON_INTERCEPTOR_READ_RANGE(ctx, abstime, struct_timespec_sz); int res = COMMON_INTERCEPTOR_BLOCK_REAL(sem_timedwait)(s, abstime); if (res == 0) { COMMON_INTERCEPTOR_ACQUIRE(ctx, (uptr)s); } return res; } INTERCEPTOR(int, sem_post, __sanitizer_sem_t *s) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sem_post, s); COMMON_INTERCEPTOR_RELEASE(ctx, (uptr)s); int res = REAL(sem_post)(s); return res; } INTERCEPTOR(int, sem_getvalue, __sanitizer_sem_t *s, int *sval) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sem_getvalue, s, sval); int res = REAL(sem_getvalue)(s, sval); if (res == 0) { COMMON_INTERCEPTOR_ACQUIRE(ctx, (uptr)s); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sval, sizeof(*sval)); } return res; } #define INIT_SEM \ COMMON_INTERCEPT_FUNCTION(sem_init); \ COMMON_INTERCEPT_FUNCTION(sem_destroy); \ COMMON_INTERCEPT_FUNCTION(sem_wait); \ COMMON_INTERCEPT_FUNCTION(sem_trywait); \ COMMON_INTERCEPT_FUNCTION(sem_timedwait); \ COMMON_INTERCEPT_FUNCTION(sem_post); \ COMMON_INTERCEPT_FUNCTION(sem_getvalue); #else #define INIT_SEM #endif // SANITIZER_INTERCEPT_SEM #if SANITIZER_INTERCEPT_PTHREAD_SETCANCEL INTERCEPTOR(int, pthread_setcancelstate, int state, int *oldstate) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pthread_setcancelstate, state, oldstate); int res = REAL(pthread_setcancelstate)(state, oldstate); if (res == 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldstate, sizeof(*oldstate)); return res; } INTERCEPTOR(int, pthread_setcanceltype, int type, int *oldtype) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pthread_setcanceltype, type, oldtype); int res = REAL(pthread_setcanceltype)(type, oldtype); if (res == 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldtype, sizeof(*oldtype)); return res; } #define INIT_PTHREAD_SETCANCEL \ COMMON_INTERCEPT_FUNCTION(pthread_setcancelstate); \ COMMON_INTERCEPT_FUNCTION(pthread_setcanceltype); #else #define INIT_PTHREAD_SETCANCEL #endif #if SANITIZER_INTERCEPT_MINCORE INTERCEPTOR(int, mincore, void *addr, uptr length, unsigned char *vec) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, mincore, addr, length, vec); int res = REAL(mincore)(addr, length, vec); if (res == 0) { uptr page_size = GetPageSizeCached(); uptr vec_size = ((length + page_size - 1) & (~(page_size - 1))) / page_size; COMMON_INTERCEPTOR_WRITE_RANGE(ctx, vec, vec_size); } return res; } #define INIT_MINCORE COMMON_INTERCEPT_FUNCTION(mincore); #else #define INIT_MINCORE #endif #if SANITIZER_INTERCEPT_PROCESS_VM_READV INTERCEPTOR(SSIZE_T, process_vm_readv, int pid, __sanitizer_iovec *local_iov, uptr liovcnt, __sanitizer_iovec *remote_iov, uptr riovcnt, uptr flags) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, process_vm_readv, pid, local_iov, liovcnt, remote_iov, riovcnt, flags); SSIZE_T res = REAL(process_vm_readv)(pid, local_iov, liovcnt, remote_iov, riovcnt, flags); if (res > 0) write_iovec(ctx, local_iov, liovcnt, res); return res; } INTERCEPTOR(SSIZE_T, process_vm_writev, int pid, __sanitizer_iovec *local_iov, uptr liovcnt, __sanitizer_iovec *remote_iov, uptr riovcnt, uptr flags) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, process_vm_writev, pid, local_iov, liovcnt, remote_iov, riovcnt, flags); SSIZE_T res = REAL(process_vm_writev)(pid, local_iov, liovcnt, remote_iov, riovcnt, flags); if (res > 0) read_iovec(ctx, local_iov, liovcnt, res); return res; } #define INIT_PROCESS_VM_READV \ COMMON_INTERCEPT_FUNCTION(process_vm_readv); \ COMMON_INTERCEPT_FUNCTION(process_vm_writev); #else #define INIT_PROCESS_VM_READV #endif #if SANITIZER_INTERCEPT_CTERMID INTERCEPTOR(char *, ctermid, char *s) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ctermid, s); char *res = REAL(ctermid)(s); if (res) { COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1); } return res; } #define INIT_CTERMID COMMON_INTERCEPT_FUNCTION(ctermid); #else #define INIT_CTERMID #endif #if SANITIZER_INTERCEPT_CTERMID_R INTERCEPTOR(char *, ctermid_r, char *s) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, ctermid_r, s); char *res = REAL(ctermid_r)(s); if (res) { COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, REAL(strlen)(res) + 1); } return res; } #define INIT_CTERMID_R COMMON_INTERCEPT_FUNCTION(ctermid_r); #else #define INIT_CTERMID_R #endif #if SANITIZER_INTERCEPT_RECV_RECVFROM INTERCEPTOR(SSIZE_T, recv, int fd, void *buf, SIZE_T len, int flags) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, recv, fd, buf, len, flags); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); SSIZE_T res = REAL(recv)(fd, buf, len, flags); if (res > 0) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, Min((SIZE_T)res, len)); } if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } INTERCEPTOR(SSIZE_T, recvfrom, int fd, void *buf, SIZE_T len, int flags, void *srcaddr, int *addrlen) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, recvfrom, fd, buf, len, flags, srcaddr, addrlen); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); SIZE_T srcaddr_sz; if (srcaddr) srcaddr_sz = *addrlen; (void)srcaddr_sz; // prevent "set but not used" warning SSIZE_T res = REAL(recvfrom)(fd, buf, len, flags, srcaddr, addrlen); if (res > 0) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, Min((SIZE_T)res, len)); if (srcaddr) COMMON_INTERCEPTOR_INITIALIZE_RANGE(srcaddr, Min((SIZE_T)*addrlen, srcaddr_sz)); } return res; } #define INIT_RECV_RECVFROM \ COMMON_INTERCEPT_FUNCTION(recv); \ COMMON_INTERCEPT_FUNCTION(recvfrom); #else #define INIT_RECV_RECVFROM #endif #if SANITIZER_INTERCEPT_SEND_SENDTO INTERCEPTOR(SSIZE_T, send, int fd, void *buf, SIZE_T len, int flags) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, send, fd, buf, len, flags); if (fd >= 0) { COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); } SSIZE_T res = REAL(send)(fd, buf, len, flags); if (common_flags()->intercept_send && res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, buf, Min((SIZE_T)res, len)); return res; } INTERCEPTOR(SSIZE_T, sendto, int fd, void *buf, SIZE_T len, int flags, void *dstaddr, int addrlen) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sendto, fd, buf, len, flags, dstaddr, addrlen); if (fd >= 0) { COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); } // Can't check dstaddr as it may have uninitialized padding at the end. SSIZE_T res = REAL(sendto)(fd, buf, len, flags, dstaddr, addrlen); if (common_flags()->intercept_send && res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, buf, Min((SIZE_T)res, len)); return res; } #define INIT_SEND_SENDTO \ COMMON_INTERCEPT_FUNCTION(send); \ COMMON_INTERCEPT_FUNCTION(sendto); #else #define INIT_SEND_SENDTO #endif #if SANITIZER_INTERCEPT_EVENTFD_READ_WRITE INTERCEPTOR(int, eventfd_read, int fd, u64 *value) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, eventfd_read, fd, value); COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); int res = REAL(eventfd_read)(fd, value); if (res == 0) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, value, sizeof(*value)); if (fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); } return res; } INTERCEPTOR(int, eventfd_write, int fd, u64 value) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, eventfd_write, fd, value); if (fd >= 0) { COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); } int res = REAL(eventfd_write)(fd, value); return res; } #define INIT_EVENTFD_READ_WRITE \ COMMON_INTERCEPT_FUNCTION(eventfd_read); \ COMMON_INTERCEPT_FUNCTION(eventfd_write) #else #define INIT_EVENTFD_READ_WRITE #endif #if SANITIZER_INTERCEPT_STAT INTERCEPTOR(int, stat, const char *path, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, stat, path, buf); if (common_flags()->intercept_stat) COMMON_INTERCEPTOR_READ_STRING(ctx, path, 0); int res = REAL(stat)(path, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, __sanitizer::struct_stat_sz); return res; } #define INIT_STAT COMMON_INTERCEPT_FUNCTION(stat) #else #define INIT_STAT #endif #if SANITIZER_INTERCEPT___XSTAT INTERCEPTOR(int, __xstat, int version, const char *path, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __xstat, version, path, buf); if (common_flags()->intercept_stat) COMMON_INTERCEPTOR_READ_STRING(ctx, path, 0); int res = REAL(__xstat)(version, path, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, __sanitizer::struct_stat_sz); return res; } #define INIT___XSTAT COMMON_INTERCEPT_FUNCTION(__xstat) #else #define INIT___XSTAT #endif #if SANITIZER_INTERCEPT___XSTAT64 INTERCEPTOR(int, __xstat64, int version, const char *path, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __xstat64, version, path, buf); if (common_flags()->intercept_stat) COMMON_INTERCEPTOR_READ_STRING(ctx, path, 0); int res = REAL(__xstat64)(version, path, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, __sanitizer::struct_stat64_sz); return res; } #define INIT___XSTAT64 COMMON_INTERCEPT_FUNCTION(__xstat64) #else #define INIT___XSTAT64 #endif #if SANITIZER_INTERCEPT___LXSTAT INTERCEPTOR(int, __lxstat, int version, const char *path, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __lxstat, version, path, buf); if (common_flags()->intercept_stat) COMMON_INTERCEPTOR_READ_STRING(ctx, path, 0); int res = REAL(__lxstat)(version, path, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, __sanitizer::struct_stat_sz); return res; } #define INIT___LXSTAT COMMON_INTERCEPT_FUNCTION(__lxstat) #else #define INIT___LXSTAT #endif #if SANITIZER_INTERCEPT___LXSTAT64 INTERCEPTOR(int, __lxstat64, int version, const char *path, void *buf) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, __lxstat64, version, path, buf); if (common_flags()->intercept_stat) COMMON_INTERCEPTOR_READ_STRING(ctx, path, 0); int res = REAL(__lxstat64)(version, path, buf); if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, __sanitizer::struct_stat64_sz); return res; } #define INIT___LXSTAT64 COMMON_INTERCEPT_FUNCTION(__lxstat64) #else #define INIT___LXSTAT64 #endif // FIXME: add other *stat interceptor #if SANITIZER_INTERCEPT_UTMP INTERCEPTOR(void *, getutent, int dummy) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getutent, dummy); void *res = REAL(getutent)(dummy); if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, __sanitizer::struct_utmp_sz); return res; } INTERCEPTOR(void *, getutid, void *ut) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getutid, ut); void *res = REAL(getutid)(ut); if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, __sanitizer::struct_utmp_sz); return res; } INTERCEPTOR(void *, getutline, void *ut) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getutline, ut); void *res = REAL(getutline)(ut); if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, __sanitizer::struct_utmp_sz); return res; } #define INIT_UTMP \ COMMON_INTERCEPT_FUNCTION(getutent); \ COMMON_INTERCEPT_FUNCTION(getutid); \ COMMON_INTERCEPT_FUNCTION(getutline); #else #define INIT_UTMP #endif #if SANITIZER_INTERCEPT_UTMPX INTERCEPTOR(void *, getutxent, int dummy) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getutxent, dummy); void *res = REAL(getutxent)(dummy); if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, __sanitizer::struct_utmpx_sz); return res; } INTERCEPTOR(void *, getutxid, void *ut) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getutxid, ut); void *res = REAL(getutxid)(ut); if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, __sanitizer::struct_utmpx_sz); return res; } INTERCEPTOR(void *, getutxline, void *ut) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getutxline, ut); void *res = REAL(getutxline)(ut); if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(res, __sanitizer::struct_utmpx_sz); return res; } #define INIT_UTMPX \ COMMON_INTERCEPT_FUNCTION(getutxent); \ COMMON_INTERCEPT_FUNCTION(getutxid); \ COMMON_INTERCEPT_FUNCTION(getutxline); #else #define INIT_UTMPX #endif #if SANITIZER_INTERCEPT_GETLOADAVG INTERCEPTOR(int, getloadavg, double *loadavg, int nelem) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getloadavg, loadavg, nelem); int res = REAL(getloadavg)(loadavg, nelem); if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, loadavg, res * sizeof(*loadavg)); return res; } #define INIT_GETLOADAVG \ COMMON_INTERCEPT_FUNCTION(getloadavg); #else #define INIT_GETLOADAVG #endif #if SANITIZER_INTERCEPT_MCHECK_MPROBE INTERCEPTOR(int, mcheck, void (*abortfunc)(int mstatus)) { return 0; } INTERCEPTOR(int, mcheck_pedantic, void (*abortfunc)(int mstatus)) { return 0; } INTERCEPTOR(int, mprobe, void *ptr) { return 0; } #endif +INTERCEPTOR(SIZE_T, wcslen, const wchar_t *s) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, wcslen, s); + SIZE_T res = REAL(wcslen)(s); + COMMON_INTERCEPTOR_READ_RANGE(ctx, s, sizeof(wchar_t) * (res + 1)); + return res; +} + +INTERCEPTOR(SIZE_T, wcsnlen, const wchar_t *s, SIZE_T n) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, wcsnlen, s, n); + SIZE_T res = REAL(wcsnlen)(s, n); + COMMON_INTERCEPTOR_READ_RANGE(ctx, s, sizeof(wchar_t) * Min(res + 1, n)); + return res; +} +#define INIT_WCSLEN \ + COMMON_INTERCEPT_FUNCTION(wcslen); \ + COMMON_INTERCEPT_FUNCTION(wcsnlen); + +#if SANITIZER_INTERCEPT_WCSCAT +INTERCEPTOR(wchar_t *, wcscat, wchar_t *dst, const wchar_t *src) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, wcscat, dst, src); + SIZE_T src_size = REAL(wcslen)(src); + SIZE_T dst_size = REAL(wcslen)(dst); + COMMON_INTERCEPTOR_READ_RANGE(ctx, src, (src_size + 1) * sizeof(wchar_t)); + COMMON_INTERCEPTOR_READ_RANGE(ctx, dst, (dst_size + 1) * sizeof(wchar_t)); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst + dst_size, + (src_size + 1) * sizeof(wchar_t)); + return REAL(wcscat)(dst, src); // NOLINT +} + +INTERCEPTOR(wchar_t *, wcsncat, wchar_t *dst, const wchar_t *src, SIZE_T n) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, wcsncat, dst, src, n); + SIZE_T src_size = REAL(wcsnlen)(src, n); + SIZE_T dst_size = REAL(wcslen)(dst); + COMMON_INTERCEPTOR_READ_RANGE(ctx, src, + Min(src_size + 1, n) * sizeof(wchar_t)); + COMMON_INTERCEPTOR_READ_RANGE(ctx, dst, (dst_size + 1) * sizeof(wchar_t)); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, dst + dst_size, + (src_size + 1) * sizeof(wchar_t)); + return REAL(wcsncat)(dst, src, n); // NOLINT +} +#define INIT_WCSCAT \ + COMMON_INTERCEPT_FUNCTION(wcscat); \ + COMMON_INTERCEPT_FUNCTION(wcsncat); +#else +#define INIT_WCSCAT +#endif + static void InitializeCommonInterceptors() { static u64 metadata_mem[sizeof(MetadataHashMap) / sizeof(u64) + 1]; interceptor_metadata_map = new((void *)&metadata_mem) MetadataHashMap(); INIT_TEXTDOMAIN; INIT_STRLEN; INIT_STRNLEN; INIT_STRNDUP; INIT___STRNDUP; INIT_STRCMP; INIT_STRNCMP; INIT_STRCASECMP; INIT_STRNCASECMP; INIT_STRSTR; INIT_STRCASESTR; INIT_STRCHR; INIT_STRCHRNUL; INIT_STRRCHR; INIT_STRSPN; INIT_STRTOK; INIT_STRPBRK; INIT_MEMSET; INIT_MEMMOVE; INIT_MEMCPY; INIT_MEMCHR; INIT_MEMCMP; INIT_MEMRCHR; INIT_MEMMEM; INIT_READ; INIT_FREAD; INIT_PREAD; INIT_PREAD64; INIT_READV; INIT_PREADV; INIT_PREADV64; INIT_WRITE; INIT_FWRITE; INIT_PWRITE; INIT_PWRITE64; INIT_WRITEV; INIT_PWRITEV; INIT_PWRITEV64; INIT_PRCTL; INIT_LOCALTIME_AND_FRIENDS; INIT_STRPTIME; INIT_SCANF; INIT_ISOC99_SCANF; INIT_PRINTF; INIT_PRINTF_L; INIT_ISOC99_PRINTF; INIT_FREXP; INIT_FREXPF_FREXPL; INIT_GETPWNAM_AND_FRIENDS; INIT_GETPWNAM_R_AND_FRIENDS; INIT_GETPWENT; INIT_FGETPWENT; INIT_GETPWENT_R; INIT_SETPWENT; INIT_CLOCK_GETTIME; INIT_GETITIMER; INIT_TIME; INIT_GLOB; INIT_WAIT; INIT_WAIT4; INIT_INET; INIT_PTHREAD_GETSCHEDPARAM; INIT_GETADDRINFO; INIT_GETNAMEINFO; INIT_GETSOCKNAME; INIT_GETHOSTBYNAME; INIT_GETHOSTBYNAME_R; INIT_GETHOSTBYNAME2_R; INIT_GETHOSTBYADDR_R; INIT_GETHOSTENT_R; INIT_GETSOCKOPT; INIT_ACCEPT; INIT_ACCEPT4; INIT_MODF; INIT_RECVMSG; INIT_SENDMSG; INIT_GETPEERNAME; INIT_IOCTL; INIT_INET_ATON; INIT_SYSINFO; INIT_READDIR; INIT_READDIR64; INIT_PTRACE; INIT_SETLOCALE; INIT_GETCWD; INIT_GET_CURRENT_DIR_NAME; INIT_STRTOIMAX; INIT_MBSTOWCS; INIT_MBSNRTOWCS; INIT_WCSTOMBS; INIT_WCSNRTOMBS; INIT_WCRTOMB; INIT_TCGETATTR; INIT_REALPATH; INIT_CANONICALIZE_FILE_NAME; INIT_CONFSTR; INIT_SCHED_GETAFFINITY; INIT_SCHED_GETPARAM; INIT_STRERROR; INIT_STRERROR_R; INIT_XPG_STRERROR_R; INIT_SCANDIR; INIT_SCANDIR64; INIT_GETGROUPS; INIT_POLL; INIT_PPOLL; INIT_WORDEXP; INIT_SIGWAIT; INIT_SIGWAITINFO; INIT_SIGTIMEDWAIT; INIT_SIGSETOPS; INIT_SIGPENDING; INIT_SIGPROCMASK; INIT_BACKTRACE; INIT__EXIT; INIT_PTHREAD_MUTEX_LOCK; INIT_PTHREAD_MUTEX_UNLOCK; INIT_GETMNTENT; INIT_GETMNTENT_R; INIT_STATFS; INIT_STATFS64; INIT_STATVFS; INIT_STATVFS64; INIT_INITGROUPS; INIT_ETHER_NTOA_ATON; INIT_ETHER_HOST; INIT_ETHER_R; INIT_SHMCTL; INIT_RANDOM_R; INIT_PTHREAD_ATTR_GET; INIT_PTHREAD_ATTR_GETINHERITSCHED; INIT_PTHREAD_ATTR_GETAFFINITY_NP; INIT_PTHREAD_MUTEXATTR_GETPSHARED; INIT_PTHREAD_MUTEXATTR_GETTYPE; INIT_PTHREAD_MUTEXATTR_GETPROTOCOL; INIT_PTHREAD_MUTEXATTR_GETPRIOCEILING; INIT_PTHREAD_MUTEXATTR_GETROBUST; INIT_PTHREAD_MUTEXATTR_GETROBUST_NP; INIT_PTHREAD_RWLOCKATTR_GETPSHARED; INIT_PTHREAD_RWLOCKATTR_GETKIND_NP; INIT_PTHREAD_CONDATTR_GETPSHARED; INIT_PTHREAD_CONDATTR_GETCLOCK; INIT_PTHREAD_BARRIERATTR_GETPSHARED; INIT_TMPNAM; INIT_TMPNAM_R; INIT_TTYNAME_R; INIT_TEMPNAM; INIT_PTHREAD_SETNAME_NP; INIT_SINCOS; INIT_REMQUO; INIT_LGAMMA; INIT_LGAMMA_R; INIT_LGAMMAL_R; INIT_DRAND48_R; INIT_RAND_R; INIT_GETLINE; INIT_ICONV; INIT_TIMES; INIT_TLS_GET_ADDR; INIT_LISTXATTR; INIT_GETXATTR; INIT_GETRESID; INIT_GETIFADDRS; INIT_IF_INDEXTONAME; INIT_CAPGET; INIT_AEABI_MEM; INIT___BZERO; INIT_FTIME; INIT_XDR; INIT_TSEARCH; INIT_LIBIO_INTERNALS; INIT_FOPEN; INIT_FOPEN64; INIT_OPEN_MEMSTREAM; INIT_OBSTACK; INIT_FFLUSH; INIT_FCLOSE; INIT_DLOPEN_DLCLOSE; INIT_GETPASS; INIT_TIMERFD; INIT_MLOCKX; INIT_FOPENCOOKIE; INIT_SEM; INIT_PTHREAD_SETCANCEL; INIT_MINCORE; INIT_PROCESS_VM_READV; INIT_CTERMID; INIT_CTERMID_R; INIT_RECV_RECVFROM; INIT_SEND_SENDTO; INIT_STAT; INIT_EVENTFD_READ_WRITE; INIT___XSTAT; INIT___XSTAT64; INIT___LXSTAT; INIT___LXSTAT64; // FIXME: add other *stat interceptors. INIT_UTMP; INIT_UTMPX; INIT_GETLOADAVG; + INIT_WCSLEN; + INIT_WCSCAT; } Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_platform_interceptors.h =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_platform_interceptors.h (revision 320536) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_platform_interceptors.h (revision 320537) @@ -1,358 +1,359 @@ //===-- sanitizer_platform_interceptors.h -----------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file defines macro telling whether sanitizer tools can/should intercept // given library functions on a given platform. // //===----------------------------------------------------------------------===// #ifndef SANITIZER_PLATFORM_INTERCEPTORS_H #define SANITIZER_PLATFORM_INTERCEPTORS_H #include "sanitizer_internal_defs.h" #if !SANITIZER_WINDOWS # define SI_WINDOWS 0 # define SI_NOT_WINDOWS 1 # include "sanitizer_platform_limits_posix.h" #else # define SI_WINDOWS 1 # define SI_NOT_WINDOWS 0 #endif #if SANITIZER_POSIX # define SI_POSIX 1 #else # define SI_POSIX 0 #endif #if SANITIZER_LINUX && !SANITIZER_ANDROID # define SI_LINUX_NOT_ANDROID 1 #else # define SI_LINUX_NOT_ANDROID 0 #endif #if SANITIZER_ANDROID # define SI_ANDROID 1 #else # define SI_ANDROID 0 #endif #if SANITIZER_FREEBSD # define SI_FREEBSD 1 #else # define SI_FREEBSD 0 #endif #if SANITIZER_LINUX # define SI_LINUX 1 #else # define SI_LINUX 0 #endif #if SANITIZER_MAC # define SI_MAC 1 # define SI_NOT_MAC 0 #else # define SI_MAC 0 # define SI_NOT_MAC 1 #endif #if SANITIZER_IOS # define SI_IOS 1 #else # define SI_IOS 0 #endif #if !SANITIZER_WINDOWS && !SANITIZER_MAC # define SI_UNIX_NOT_MAC 1 #else # define SI_UNIX_NOT_MAC 0 #endif #if SANITIZER_LINUX && !SANITIZER_FREEBSD # define SI_LINUX_NOT_FREEBSD 1 # else # define SI_LINUX_NOT_FREEBSD 0 #endif #define SANITIZER_INTERCEPT_STRLEN 1 #define SANITIZER_INTERCEPT_STRNLEN SI_NOT_MAC #define SANITIZER_INTERCEPT_STRCMP 1 #define SANITIZER_INTERCEPT_STRSTR 1 #define SANITIZER_INTERCEPT_STRCASESTR SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_STRTOK 1 #define SANITIZER_INTERCEPT_STRCHR 1 #define SANITIZER_INTERCEPT_STRCHRNUL SI_UNIX_NOT_MAC #define SANITIZER_INTERCEPT_STRRCHR 1 #define SANITIZER_INTERCEPT_STRSPN 1 #define SANITIZER_INTERCEPT_STRPBRK 1 #define SANITIZER_INTERCEPT_TEXTDOMAIN SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_STRCASECMP SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_MEMSET 1 #define SANITIZER_INTERCEPT_MEMMOVE 1 #define SANITIZER_INTERCEPT_MEMCPY 1 #define SANITIZER_INTERCEPT_MEMCMP 1 #define SANITIZER_INTERCEPT_STRNDUP SI_POSIX #define SANITIZER_INTERCEPT___STRNDUP SI_LINUX_NOT_FREEBSD #if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && \ __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ < 1070 # define SI_MAC_DEPLOYMENT_BELOW_10_7 1 #else # define SI_MAC_DEPLOYMENT_BELOW_10_7 0 #endif // memmem on Darwin doesn't exist on 10.6 // FIXME: enable memmem on Windows. #define SANITIZER_INTERCEPT_MEMMEM \ SI_NOT_WINDOWS && !SI_MAC_DEPLOYMENT_BELOW_10_7 #define SANITIZER_INTERCEPT_MEMCHR 1 #define SANITIZER_INTERCEPT_MEMRCHR SI_FREEBSD || SI_LINUX #define SANITIZER_INTERCEPT_READ SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PREAD SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_WRITE SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PWRITE SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_FREAD SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_FWRITE SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PREAD64 SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PWRITE64 SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_READV SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_WRITEV SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PREADV SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PWRITEV SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PREADV64 SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PWRITEV64 SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PRCTL SI_LINUX #define SANITIZER_INTERCEPT_LOCALTIME_AND_FRIENDS SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_STRPTIME SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_SCANF SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_ISOC99_SCANF SI_LINUX_NOT_ANDROID #ifndef SANITIZER_INTERCEPT_PRINTF # define SANITIZER_INTERCEPT_PRINTF SI_NOT_WINDOWS # define SANITIZER_INTERCEPT_PRINTF_L SI_FREEBSD # define SANITIZER_INTERCEPT_ISOC99_PRINTF SI_LINUX_NOT_ANDROID #endif #define SANITIZER_INTERCEPT_FREXP 1 #define SANITIZER_INTERCEPT_FREXPF_FREXPL SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS \ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_GETPWENT \ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_FGETPWENT SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_GETPWENT_R SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_SETPWENT SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_CLOCK_GETTIME SI_FREEBSD || SI_LINUX #define SANITIZER_INTERCEPT_GETITIMER SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_TIME SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GLOB SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_WAIT SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_INET SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PTHREAD_GETSCHEDPARAM SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETADDRINFO SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETNAMEINFO SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETSOCKNAME SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETHOSTBYNAME SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETHOSTBYNAME_R SI_FREEBSD || SI_LINUX #define SANITIZER_INTERCEPT_GETHOSTBYNAME2_R SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_GETHOSTBYADDR_R SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_GETHOSTENT_R SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_GETSOCKOPT SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_ACCEPT SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_ACCEPT4 SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_MODF SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_RECVMSG SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_SENDMSG SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETPEERNAME SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_IOCTL SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_INET_ATON SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_SYSINFO SI_LINUX #define SANITIZER_INTERCEPT_READDIR SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_READDIR64 SI_LINUX_NOT_ANDROID #if SI_LINUX_NOT_ANDROID && \ (defined(__i386) || defined(__x86_64) || defined(__mips64) || \ defined(__powerpc64__) || defined(__aarch64__) || defined(__arm__) || \ defined(__s390__)) #define SANITIZER_INTERCEPT_PTRACE 1 #else #define SANITIZER_INTERCEPT_PTRACE 0 #endif #define SANITIZER_INTERCEPT_SETLOCALE SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GETCWD SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_GET_CURRENT_DIR_NAME SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_STRTOIMAX SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_MBSTOWCS SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_MBSNRTOWCS SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_WCSTOMBS SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_WCSNRTOMBS \ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_WCRTOMB \ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_TCGETATTR SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_REALPATH SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_CANONICALIZE_FILE_NAME SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_CONFSTR \ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_SCHED_GETAFFINITY SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_SCHED_GETPARAM SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_STRERROR SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_STRERROR_R SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_XPG_STRERROR_R SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_SCANDIR \ SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_SCANDIR64 SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_GETGROUPS SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_POLL SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PPOLL SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_WORDEXP \ SI_FREEBSD || (SI_MAC && !SI_IOS) || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_SIGWAIT SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_SIGWAITINFO SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_SIGTIMEDWAIT SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_SIGSETOPS \ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_SIGPENDING SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_SIGPROCMASK SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_BACKTRACE SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_GETMNTENT SI_LINUX #define SANITIZER_INTERCEPT_GETMNTENT_R SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_STATFS SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_STATFS64 \ (SI_MAC && !SI_IOS) || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_STATVFS SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_STATVFS64 SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_INITGROUPS SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_ETHER_NTOA_ATON SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_ETHER_HOST \ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_ETHER_R SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_SHMCTL \ ((SI_FREEBSD || SI_LINUX_NOT_ANDROID) && SANITIZER_WORDSIZE == 64) #define SANITIZER_INTERCEPT_RANDOM_R SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PTHREAD_ATTR_GET SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSCHED \ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETAFFINITY_NP SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPSHARED SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETTYPE SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPROTOCOL \ SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETPRIOCEILING \ SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETROBUST SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PTHREAD_MUTEXATTR_GETROBUST_NP SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GETPSHARED SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PTHREAD_RWLOCKATTR_GETKIND_NP SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PTHREAD_CONDATTR_GETPSHARED SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PTHREAD_CONDATTR_GETCLOCK SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_PTHREAD_BARRIERATTR_GETPSHARED SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_TMPNAM SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_TMPNAM_R SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_TTYNAME_R SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_TEMPNAM SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_SINCOS SI_LINUX #define SANITIZER_INTERCEPT_REMQUO SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_LGAMMA SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_LGAMMA_R SI_FREEBSD || SI_LINUX #define SANITIZER_INTERCEPT_LGAMMAL_R SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_DRAND48_R SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_RAND_R \ SI_FREEBSD || SI_MAC || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_ICONV SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_TIMES SI_NOT_WINDOWS // FIXME: getline seems to be available on OSX 10.7 #define SANITIZER_INTERCEPT_GETLINE SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT__EXIT SI_LINUX || SI_FREEBSD || SI_MAC #define SANITIZER_INTERCEPT_PHTREAD_MUTEX SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP \ SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_TLS_GET_ADDR \ SI_FREEBSD || SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_LISTXATTR SI_LINUX #define SANITIZER_INTERCEPT_GETXATTR SI_LINUX #define SANITIZER_INTERCEPT_GETRESID SI_LINUX #define SANITIZER_INTERCEPT_GETIFADDRS \ SI_FREEBSD || SI_LINUX_NOT_ANDROID || SI_MAC #define SANITIZER_INTERCEPT_IF_INDEXTONAME \ SI_FREEBSD || SI_LINUX_NOT_ANDROID || SI_MAC #define SANITIZER_INTERCEPT_CAPGET SI_LINUX_NOT_ANDROID #if SI_LINUX && defined(__arm__) #define SANITIZER_INTERCEPT_AEABI_MEM 1 #else #define SANITIZER_INTERCEPT_AEABI_MEM 0 #endif #define SANITIZER_INTERCEPT___BZERO SI_MAC #define SANITIZER_INTERCEPT_FTIME !SI_FREEBSD && SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_XDR SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_TSEARCH SI_LINUX_NOT_ANDROID || SI_MAC #define SANITIZER_INTERCEPT_LIBIO_INTERNALS SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_FOPEN SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_FOPEN64 SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_OPEN_MEMSTREAM SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_OBSTACK SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_FFLUSH SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_FCLOSE SI_NOT_WINDOWS #ifndef SANITIZER_INTERCEPT_DLOPEN_DLCLOSE #define SANITIZER_INTERCEPT_DLOPEN_DLCLOSE \ SI_FREEBSD || SI_LINUX_NOT_ANDROID || SI_MAC #endif #define SANITIZER_INTERCEPT_GETPASS SI_LINUX_NOT_ANDROID || SI_MAC #define SANITIZER_INTERCEPT_TIMERFD SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_MLOCKX SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_FOPENCOOKIE SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_SEM SI_LINUX || SI_FREEBSD #define SANITIZER_INTERCEPT_PTHREAD_SETCANCEL SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_MINCORE SI_LINUX #define SANITIZER_INTERCEPT_PROCESS_VM_READV SI_LINUX #define SANITIZER_INTERCEPT_CTERMID SI_LINUX || SI_MAC || SI_FREEBSD #define SANITIZER_INTERCEPT_CTERMID_R SI_MAC || SI_FREEBSD #define SANITIZER_INTERCEPTOR_HOOKS SI_LINUX || SI_MAC || SI_WINDOWS #define SANITIZER_INTERCEPT_RECV_RECVFROM SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_SEND_SENDTO SI_NOT_WINDOWS #define SANITIZER_INTERCEPT_EVENTFD_READ_WRITE SI_LINUX #define SANITIZER_INTERCEPT_STAT (SI_FREEBSD || SI_MAC || SI_ANDROID) #define SANITIZER_INTERCEPT___XSTAT !SANITIZER_INTERCEPT_STAT && SI_NOT_WINDOWS #define SANITIZER_INTERCEPT___XSTAT64 SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT___LXSTAT SANITIZER_INTERCEPT___XSTAT #define SANITIZER_INTERCEPT___LXSTAT64 SI_LINUX_NOT_ANDROID #define SANITIZER_INTERCEPT_UTMP SI_NOT_WINDOWS && !SI_MAC && !SI_FREEBSD #define SANITIZER_INTERCEPT_UTMPX SI_LINUX_NOT_ANDROID || SI_MAC || SI_FREEBSD #define SANITIZER_INTERCEPT_GETLOADAVG \ SI_LINUX_NOT_ANDROID || SI_MAC || SI_FREEBSD #define SANITIZER_INTERCEPT_MALLOPT_AND_MALLINFO (!SI_FREEBSD && !SI_MAC) #define SANITIZER_INTERCEPT_MEMALIGN (!SI_FREEBSD && !SI_MAC) #define SANITIZER_INTERCEPT_PVALLOC (!SI_FREEBSD && !SI_MAC) #define SANITIZER_INTERCEPT_CFREE (!SI_FREEBSD && !SI_MAC) #define SANITIZER_INTERCEPT_ALIGNED_ALLOC (!SI_MAC) #define SANITIZER_INTERCEPT_MALLOC_USABLE_SIZE (!SI_MAC) #define SANITIZER_INTERCEPT_MCHECK_MPROBE SI_LINUX_NOT_ANDROID +#define SANITIZER_INTERCEPT_WCSCAT SI_NOT_WINDOWS #endif // #ifndef SANITIZER_PLATFORM_INTERCEPTORS_H Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_posix.cc =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_posix.cc (revision 320536) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_posix.cc (revision 320537) @@ -1,398 +1,408 @@ //===-- sanitizer_posix.cc ------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is shared between AddressSanitizer and ThreadSanitizer // run-time libraries and implements POSIX-specific functions from // sanitizer_posix.h. //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" #if SANITIZER_POSIX #include "sanitizer_common.h" #include "sanitizer_libc.h" #include "sanitizer_posix.h" #include "sanitizer_procmaps.h" #include "sanitizer_stacktrace.h" #include #include #include #include #if SANITIZER_LINUX #include #endif #if SANITIZER_LINUX && !SANITIZER_ANDROID #include #endif #if SANITIZER_FREEBSD // The MAP_NORESERVE define has been removed in FreeBSD 11.x, and even before // that, it was never implemented. So just define it to zero. #undef MAP_NORESERVE #define MAP_NORESERVE 0 #endif namespace __sanitizer { // ------------- sanitizer_common.h uptr GetMmapGranularity() { return GetPageSize(); } #if SANITIZER_WORDSIZE == 32 // Take care of unusable kernel area in top gigabyte. static uptr GetKernelAreaSize() { #if SANITIZER_LINUX && !SANITIZER_X32 const uptr gbyte = 1UL << 30; // Firstly check if there are writable segments // mapped to top gigabyte (e.g. stack). MemoryMappingLayout proc_maps(/*cache_enabled*/true); uptr end, prot; while (proc_maps.Next(/*start*/nullptr, &end, /*offset*/nullptr, /*filename*/nullptr, /*filename_size*/0, &prot)) { if ((end >= 3 * gbyte) && (prot & MemoryMappingLayout::kProtectionWrite) != 0) return 0; } #if !SANITIZER_ANDROID // Even if nothing is mapped, top Gb may still be accessible // if we are running on 64-bit kernel. // Uname may report misleading results if personality type // is modified (e.g. under schroot) so check this as well. struct utsname uname_info; int pers = personality(0xffffffffUL); if (!(pers & PER_MASK) && uname(&uname_info) == 0 && internal_strstr(uname_info.machine, "64")) return 0; #endif // SANITIZER_ANDROID // Top gigabyte is reserved for kernel. return gbyte; #else return 0; #endif // SANITIZER_LINUX && !SANITIZER_X32 } #endif // SANITIZER_WORDSIZE == 32 uptr GetMaxVirtualAddress() { #if SANITIZER_WORDSIZE == 64 # if defined(__aarch64__) && SANITIZER_IOS && !SANITIZER_IOSSIM // Ideally, we would derive the upper bound from MACH_VM_MAX_ADDRESS. The // upper bound can change depending on the device. return 0x200000000 - 1; # elif defined(__powerpc64__) || defined(__aarch64__) // On PowerPC64 we have two different address space layouts: 44- and 46-bit. // We somehow need to figure out which one we are using now and choose // one of 0x00000fffffffffffUL and 0x00003fffffffffffUL. // Note that with 'ulimit -s unlimited' the stack is moved away from the top // of the address space, so simply checking the stack address is not enough. // This should (does) work for both PowerPC64 Endian modes. // Similarly, aarch64 has multiple address space layouts: 39, 42 and 47-bit. return (1ULL << (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1)) - 1; # elif defined(__mips64) return (1ULL << 40) - 1; // 0x000000ffffffffffUL; # elif defined(__s390x__) return (1ULL << 53) - 1; // 0x001fffffffffffffUL; # else return (1ULL << 47) - 1; // 0x00007fffffffffffUL; # endif #else // SANITIZER_WORDSIZE == 32 # if defined(__s390__) return (1ULL << 31) - 1; // 0x7fffffff; # else uptr res = (1ULL << 32) - 1; // 0xffffffff; if (!common_flags()->full_address_space) res -= GetKernelAreaSize(); CHECK_LT(reinterpret_cast(&res), res); return res; # endif #endif // SANITIZER_WORDSIZE } void *MmapOrDie(uptr size, const char *mem_type, bool raw_report) { size = RoundUpTo(size, GetPageSizeCached()); uptr res = internal_mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); int reserrno; - if (internal_iserror(res, &reserrno)) + if (UNLIKELY(internal_iserror(res, &reserrno))) ReportMmapFailureAndDie(size, mem_type, "allocate", reserrno, raw_report); IncreaseTotalMmap(size); return (void *)res; } void UnmapOrDie(void *addr, uptr size) { if (!addr || !size) return; uptr res = internal_munmap(addr, size); - if (internal_iserror(res)) { + if (UNLIKELY(internal_iserror(res))) { Report("ERROR: %s failed to deallocate 0x%zx (%zd) bytes at address %p\n", SanitizerToolName, size, size, addr); CHECK("unable to unmap" && 0); } DecreaseTotalMmap(size); } void *MmapOrDieOnFatalError(uptr size, const char *mem_type) { size = RoundUpTo(size, GetPageSizeCached()); uptr res = internal_mmap(nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); int reserrno; - if (internal_iserror(res, &reserrno)) { + if (UNLIKELY(internal_iserror(res, &reserrno))) { if (reserrno == ENOMEM) return nullptr; ReportMmapFailureAndDie(size, mem_type, "allocate", reserrno); } IncreaseTotalMmap(size); return (void *)res; } // We want to map a chunk of address space aligned to 'alignment'. // We do it by maping a bit more and then unmaping redundant pieces. // We probably can do it with fewer syscalls in some OS-dependent way. void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment, const char *mem_type) { CHECK(IsPowerOfTwo(size)); CHECK(IsPowerOfTwo(alignment)); uptr map_size = size + alignment; uptr map_res = (uptr)MmapOrDieOnFatalError(map_size, mem_type); - if (!map_res) + if (UNLIKELY(!map_res)) return nullptr; uptr map_end = map_res + map_size; uptr res = map_res; - if (res & (alignment - 1)) // Not aligned. - res = (map_res + alignment) & ~(alignment - 1); - uptr end = res + size; - if (res != map_res) + if (!IsAligned(res, alignment)) { + res = (map_res + alignment - 1) & ~(alignment - 1); UnmapOrDie((void*)map_res, res - map_res); + } + uptr end = res + size; if (end != map_end) UnmapOrDie((void*)end, map_end - end); return (void*)res; } void *MmapNoReserveOrDie(uptr size, const char *mem_type) { uptr PageSize = GetPageSizeCached(); uptr p = internal_mmap(nullptr, RoundUpTo(size, PageSize), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_NORESERVE, -1, 0); int reserrno; - if (internal_iserror(p, &reserrno)) + if (UNLIKELY(internal_iserror(p, &reserrno))) ReportMmapFailureAndDie(size, mem_type, "allocate noreserve", reserrno); IncreaseTotalMmap(size); return (void *)p; } -void *MmapFixedOrDie(uptr fixed_addr, uptr size) { +void *MmapFixedImpl(uptr fixed_addr, uptr size, bool tolerate_enomem) { uptr PageSize = GetPageSizeCached(); uptr p = internal_mmap((void*)(fixed_addr & ~(PageSize - 1)), RoundUpTo(size, PageSize), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON | MAP_FIXED, -1, 0); int reserrno; - if (internal_iserror(p, &reserrno)) { - char mem_type[30]; + if (UNLIKELY(internal_iserror(p, &reserrno))) { + if (tolerate_enomem && reserrno == ENOMEM) + return nullptr; + char mem_type[40]; internal_snprintf(mem_type, sizeof(mem_type), "memory at address 0x%zx", fixed_addr); ReportMmapFailureAndDie(size, mem_type, "allocate", reserrno); } IncreaseTotalMmap(size); return (void *)p; +} + +void *MmapFixedOrDie(uptr fixed_addr, uptr size) { + return MmapFixedImpl(fixed_addr, size, false /*tolerate_enomem*/); +} + +void *MmapFixedOrDieOnFatalError(uptr fixed_addr, uptr size) { + return MmapFixedImpl(fixed_addr, size, true /*tolerate_enomem*/); } bool MprotectNoAccess(uptr addr, uptr size) { return 0 == internal_mprotect((void*)addr, size, PROT_NONE); } bool MprotectReadOnly(uptr addr, uptr size) { return 0 == internal_mprotect((void *)addr, size, PROT_READ); } fd_t OpenFile(const char *filename, FileAccessMode mode, error_t *errno_p) { int flags; switch (mode) { case RdOnly: flags = O_RDONLY; break; case WrOnly: flags = O_WRONLY | O_CREAT; break; case RdWr: flags = O_RDWR | O_CREAT; break; } fd_t res = internal_open(filename, flags, 0660); if (internal_iserror(res, errno_p)) return kInvalidFd; return res; } void CloseFile(fd_t fd) { internal_close(fd); } bool ReadFromFile(fd_t fd, void *buff, uptr buff_size, uptr *bytes_read, error_t *error_p) { uptr res = internal_read(fd, buff, buff_size); if (internal_iserror(res, error_p)) return false; if (bytes_read) *bytes_read = res; return true; } bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written, error_t *error_p) { uptr res = internal_write(fd, buff, buff_size); if (internal_iserror(res, error_p)) return false; if (bytes_written) *bytes_written = res; return true; } bool RenameFile(const char *oldpath, const char *newpath, error_t *error_p) { uptr res = internal_rename(oldpath, newpath); return !internal_iserror(res, error_p); } void *MapFileToMemory(const char *file_name, uptr *buff_size) { fd_t fd = OpenFile(file_name, RdOnly); CHECK(fd != kInvalidFd); uptr fsize = internal_filesize(fd); CHECK_NE(fsize, (uptr)-1); CHECK_GT(fsize, 0); *buff_size = RoundUpTo(fsize, GetPageSizeCached()); uptr map = internal_mmap(nullptr, *buff_size, PROT_READ, MAP_PRIVATE, fd, 0); return internal_iserror(map) ? nullptr : (void *)map; } void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset) { uptr flags = MAP_SHARED; if (addr) flags |= MAP_FIXED; uptr p = internal_mmap(addr, size, PROT_READ | PROT_WRITE, flags, fd, offset); int mmap_errno = 0; if (internal_iserror(p, &mmap_errno)) { Printf("could not map writable file (%d, %lld, %zu): %zd, errno: %d\n", fd, (long long)offset, size, p, mmap_errno); return nullptr; } return (void *)p; } static inline bool IntervalsAreSeparate(uptr start1, uptr end1, uptr start2, uptr end2) { CHECK(start1 <= end1); CHECK(start2 <= end2); return (end1 < start2) || (end2 < start1); } // FIXME: this is thread-unsafe, but should not cause problems most of the time. // When the shadow is mapped only a single thread usually exists (plus maybe // several worker threads on Mac, which aren't expected to map big chunks of // memory). bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) { MemoryMappingLayout proc_maps(/*cache_enabled*/true); uptr start, end; while (proc_maps.Next(&start, &end, /*offset*/nullptr, /*filename*/nullptr, /*filename_size*/0, /*protection*/nullptr)) { if (start == end) continue; // Empty range. CHECK_NE(0, end); if (!IntervalsAreSeparate(start, end - 1, range_start, range_end)) return false; } return true; } void DumpProcessMap() { MemoryMappingLayout proc_maps(/*cache_enabled*/true); uptr start, end; const sptr kBufSize = 4095; char *filename = (char*)MmapOrDie(kBufSize, __func__); Report("Process memory map follows:\n"); while (proc_maps.Next(&start, &end, /* file_offset */nullptr, filename, kBufSize, /* protection */nullptr)) { Printf("\t%p-%p\t%s\n", (void*)start, (void*)end, filename); } Report("End of process memory map.\n"); UnmapOrDie(filename, kBufSize); } const char *GetPwd() { return GetEnv("PWD"); } bool IsPathSeparator(const char c) { return c == '/'; } bool IsAbsolutePath(const char *path) { return path != nullptr && IsPathSeparator(path[0]); } void ReportFile::Write(const char *buffer, uptr length) { SpinMutexLock l(mu); static const char *kWriteError = "ReportFile::Write() can't output requested buffer!\n"; ReopenIfNecessary(); if (length != internal_write(fd, buffer, length)) { internal_write(fd, kWriteError, internal_strlen(kWriteError)); Die(); } } bool GetCodeRangeForFile(const char *module, uptr *start, uptr *end) { uptr s, e, off, prot; InternalScopedString buff(kMaxPathLength); MemoryMappingLayout proc_maps(/*cache_enabled*/false); while (proc_maps.Next(&s, &e, &off, buff.data(), buff.size(), &prot)) { if ((prot & MemoryMappingLayout::kProtectionExecute) != 0 && internal_strcmp(module, buff.data()) == 0) { *start = s; *end = e; return true; } } return false; } SignalContext SignalContext::Create(void *siginfo, void *context) { auto si = (siginfo_t *)siginfo; uptr addr = (uptr)si->si_addr; uptr pc, sp, bp; GetPcSpBp(context, &pc, &sp, &bp); WriteFlag write_flag = GetWriteFlag(context); bool is_memory_access = si->si_signo == SIGSEGV; return SignalContext(context, addr, pc, sp, bp, is_memory_access, write_flag); } const char *DescribeSignalOrException(int signo) { switch (signo) { case SIGFPE: return "FPE"; case SIGILL: return "ILL"; case SIGABRT: return "ABRT"; case SIGSEGV: return "SEGV"; case SIGBUS: return "BUS"; } return "UNKNOWN SIGNAL"; } } // namespace __sanitizer #endif // SANITIZER_POSIX Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_win.cc =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_win.cc (revision 320536) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_win.cc (revision 320537) @@ -1,1017 +1,1029 @@ //===-- sanitizer_win.cc --------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is shared between AddressSanitizer and ThreadSanitizer // run-time libraries and implements windows-specific functions from // sanitizer_libc.h. //===----------------------------------------------------------------------===// #include "sanitizer_platform.h" #if SANITIZER_WINDOWS #define WIN32_LEAN_AND_MEAN #define NOGDI #include #include #include #include #include "sanitizer_common.h" #include "sanitizer_dbghelp.h" #include "sanitizer_libc.h" #include "sanitizer_mutex.h" #include "sanitizer_placement_new.h" #include "sanitizer_procmaps.h" #include "sanitizer_stacktrace.h" #include "sanitizer_symbolizer.h" #include "sanitizer_win_defs.h" // A macro to tell the compiler that this part of the code cannot be reached, // if the compiler supports this feature. Since we're using this in // code that is called when terminating the process, the expansion of the // macro should not terminate the process to avoid infinite recursion. #if defined(__clang__) # define BUILTIN_UNREACHABLE() __builtin_unreachable() #elif defined(__GNUC__) && \ (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5)) # define BUILTIN_UNREACHABLE() __builtin_unreachable() #elif defined(_MSC_VER) # define BUILTIN_UNREACHABLE() __assume(0) #else # define BUILTIN_UNREACHABLE() #endif namespace __sanitizer { #include "sanitizer_syscall_generic.inc" // --------------------- sanitizer_common.h uptr GetPageSize() { SYSTEM_INFO si; GetSystemInfo(&si); return si.dwPageSize; } uptr GetMmapGranularity() { SYSTEM_INFO si; GetSystemInfo(&si); return si.dwAllocationGranularity; } uptr GetMaxVirtualAddress() { SYSTEM_INFO si; GetSystemInfo(&si); return (uptr)si.lpMaximumApplicationAddress; } bool FileExists(const char *filename) { return ::GetFileAttributesA(filename) != INVALID_FILE_ATTRIBUTES; } uptr internal_getpid() { return GetProcessId(GetCurrentProcess()); } // In contrast to POSIX, on Windows GetCurrentThreadId() // returns a system-unique identifier. tid_t GetTid() { return GetCurrentThreadId(); } uptr GetThreadSelf() { return GetTid(); } #if !SANITIZER_GO void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, uptr *stack_bottom) { CHECK(stack_top); CHECK(stack_bottom); MEMORY_BASIC_INFORMATION mbi; CHECK_NE(VirtualQuery(&mbi /* on stack */, &mbi, sizeof(mbi)), 0); // FIXME: is it possible for the stack to not be a single allocation? // Are these values what ASan expects to get (reserved, not committed; // including stack guard page) ? *stack_top = (uptr)mbi.BaseAddress + mbi.RegionSize; *stack_bottom = (uptr)mbi.AllocationBase; } #endif // #if !SANITIZER_GO void *MmapOrDie(uptr size, const char *mem_type, bool raw_report) { void *rv = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (rv == 0) ReportMmapFailureAndDie(size, mem_type, "allocate", GetLastError(), raw_report); return rv; } void UnmapOrDie(void *addr, uptr size) { if (!size || !addr) return; MEMORY_BASIC_INFORMATION mbi; CHECK(VirtualQuery(addr, &mbi, sizeof(mbi))); // MEM_RELEASE can only be used to unmap whole regions previously mapped with // VirtualAlloc. So we first try MEM_RELEASE since it is better, and if that // fails try MEM_DECOMMIT. if (VirtualFree(addr, 0, MEM_RELEASE) == 0) { if (VirtualFree(addr, size, MEM_DECOMMIT) == 0) { Report("ERROR: %s failed to " "deallocate 0x%zx (%zd) bytes at address %p (error code: %d)\n", SanitizerToolName, size, size, addr, GetLastError()); CHECK("unable to unmap" && 0); } } } static void *ReturnNullptrOnOOMOrDie(uptr size, const char *mem_type, const char *mmap_type) { error_t last_error = GetLastError(); if (last_error == ERROR_NOT_ENOUGH_MEMORY) return nullptr; ReportMmapFailureAndDie(size, mem_type, mmap_type, last_error); } void *MmapOrDieOnFatalError(uptr size, const char *mem_type) { void *rv = VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (rv == 0) return ReturnNullptrOnOOMOrDie(size, mem_type, "allocate"); return rv; } // We want to map a chunk of address space aligned to 'alignment'. void *MmapAlignedOrDieOnFatalError(uptr size, uptr alignment, const char *mem_type) { CHECK(IsPowerOfTwo(size)); CHECK(IsPowerOfTwo(alignment)); // Windows will align our allocations to at least 64K. alignment = Max(alignment, GetMmapGranularity()); uptr mapped_addr = (uptr)VirtualAlloc(0, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (!mapped_addr) return ReturnNullptrOnOOMOrDie(size, mem_type, "allocate aligned"); // If we got it right on the first try, return. Otherwise, unmap it and go to // the slow path. if (IsAligned(mapped_addr, alignment)) return (void*)mapped_addr; if (VirtualFree((void *)mapped_addr, 0, MEM_RELEASE) == 0) ReportMmapFailureAndDie(size, mem_type, "deallocate", GetLastError()); // If we didn't get an aligned address, overallocate, find an aligned address, // unmap, and try to allocate at that aligned address. int retries = 0; const int kMaxRetries = 10; for (; retries < kMaxRetries && (mapped_addr == 0 || !IsAligned(mapped_addr, alignment)); retries++) { // Overallocate size + alignment bytes. mapped_addr = (uptr)VirtualAlloc(0, size + alignment, MEM_RESERVE, PAGE_NOACCESS); if (!mapped_addr) return ReturnNullptrOnOOMOrDie(size, mem_type, "allocate aligned"); // Find the aligned address. uptr aligned_addr = RoundUpTo(mapped_addr, alignment); // Free the overallocation. if (VirtualFree((void *)mapped_addr, 0, MEM_RELEASE) == 0) ReportMmapFailureAndDie(size, mem_type, "deallocate", GetLastError()); // Attempt to allocate exactly the number of bytes we need at the aligned // address. This may fail for a number of reasons, in which case we continue // the loop. mapped_addr = (uptr)VirtualAlloc((void *)aligned_addr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); } // Fail if we can't make this work quickly. if (retries == kMaxRetries && mapped_addr == 0) return ReturnNullptrOnOOMOrDie(size, mem_type, "allocate aligned"); return (void *)mapped_addr; } void *MmapFixedNoReserve(uptr fixed_addr, uptr size, const char *name) { // FIXME: is this really "NoReserve"? On Win32 this does not matter much, // but on Win64 it does. (void)name; // unsupported #if !SANITIZER_GO && SANITIZER_WINDOWS64 // On asan/Windows64, use MEM_COMMIT would result in error // 1455:ERROR_COMMITMENT_LIMIT. // Asan uses exception handler to commit page on demand. void *p = VirtualAlloc((LPVOID)fixed_addr, size, MEM_RESERVE, PAGE_READWRITE); #else void *p = VirtualAlloc((LPVOID)fixed_addr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); #endif if (p == 0) Report("ERROR: %s failed to " "allocate %p (%zd) bytes at %p (error code: %d)\n", SanitizerToolName, size, size, fixed_addr, GetLastError()); return p; } // Memory space mapped by 'MmapFixedOrDie' must have been reserved by // 'MmapFixedNoAccess'. void *MmapFixedOrDie(uptr fixed_addr, uptr size) { void *p = VirtualAlloc((LPVOID)fixed_addr, size, MEM_COMMIT, PAGE_READWRITE); if (p == 0) { char mem_type[30]; internal_snprintf(mem_type, sizeof(mem_type), "memory at address 0x%zx", fixed_addr); ReportMmapFailureAndDie(size, mem_type, "allocate", GetLastError()); } return p; } +void *MmapFixedOrDieOnFatalError(uptr fixed_addr, uptr size) { + void *p = VirtualAlloc((LPVOID)fixed_addr, size, + MEM_COMMIT, PAGE_READWRITE); + if (p == 0) { + char mem_type[30]; + internal_snprintf(mem_type, sizeof(mem_type), "memory at address 0x%zx", + fixed_addr); + return ReturnNullptrOnOOMOrDie(size, mem_type, "allocate"); + } + return p; +} + void *MmapNoReserveOrDie(uptr size, const char *mem_type) { // FIXME: make this really NoReserve? return MmapOrDie(size, mem_type); } void *MmapFixedNoAccess(uptr fixed_addr, uptr size, const char *name) { (void)name; // unsupported void *res = VirtualAlloc((LPVOID)fixed_addr, size, MEM_RESERVE, PAGE_NOACCESS); if (res == 0) Report("WARNING: %s failed to " "mprotect %p (%zd) bytes at %p (error code: %d)\n", SanitizerToolName, size, size, fixed_addr, GetLastError()); return res; } void *MmapNoAccess(uptr size) { void *res = VirtualAlloc(nullptr, size, MEM_RESERVE, PAGE_NOACCESS); if (res == 0) Report("WARNING: %s failed to " "mprotect %p (%zd) bytes (error code: %d)\n", SanitizerToolName, size, size, GetLastError()); return res; } bool MprotectNoAccess(uptr addr, uptr size) { DWORD old_protection; return VirtualProtect((LPVOID)addr, size, PAGE_NOACCESS, &old_protection); } void ReleaseMemoryPagesToOS(uptr beg, uptr end) { // This is almost useless on 32-bits. // FIXME: add madvise-analog when we move to 64-bits. } void NoHugePagesInRegion(uptr addr, uptr size) { // FIXME: probably similar to ReleaseMemoryToOS. } void DontDumpShadowMemory(uptr addr, uptr length) { // This is almost useless on 32-bits. // FIXME: add madvise-analog when we move to 64-bits. } uptr FindAvailableMemoryRange(uptr size, uptr alignment, uptr left_padding) { uptr address = 0; while (true) { MEMORY_BASIC_INFORMATION info; if (!::VirtualQuery((void*)address, &info, sizeof(info))) return 0; if (info.State == MEM_FREE) { uptr shadow_address = RoundUpTo((uptr)info.BaseAddress + left_padding, alignment); if (shadow_address + size < (uptr)info.BaseAddress + info.RegionSize) return shadow_address; } // Move to the next region. address = (uptr)info.BaseAddress + info.RegionSize; } return 0; } bool MemoryRangeIsAvailable(uptr range_start, uptr range_end) { MEMORY_BASIC_INFORMATION mbi; CHECK(VirtualQuery((void *)range_start, &mbi, sizeof(mbi))); return mbi.Protect == PAGE_NOACCESS && (uptr)mbi.BaseAddress + mbi.RegionSize >= range_end; } void *MapFileToMemory(const char *file_name, uptr *buff_size) { UNIMPLEMENTED(); } void *MapWritableFileToMemory(void *addr, uptr size, fd_t fd, OFF_T offset) { UNIMPLEMENTED(); } static const int kMaxEnvNameLength = 128; static const DWORD kMaxEnvValueLength = 32767; namespace { struct EnvVariable { char name[kMaxEnvNameLength]; char value[kMaxEnvValueLength]; }; } // namespace static const int kEnvVariables = 5; static EnvVariable env_vars[kEnvVariables]; static int num_env_vars; const char *GetEnv(const char *name) { // Note: this implementation caches the values of the environment variables // and limits their quantity. for (int i = 0; i < num_env_vars; i++) { if (0 == internal_strcmp(name, env_vars[i].name)) return env_vars[i].value; } CHECK_LT(num_env_vars, kEnvVariables); DWORD rv = GetEnvironmentVariableA(name, env_vars[num_env_vars].value, kMaxEnvValueLength); if (rv > 0 && rv < kMaxEnvValueLength) { CHECK_LT(internal_strlen(name), kMaxEnvNameLength); internal_strncpy(env_vars[num_env_vars].name, name, kMaxEnvNameLength); num_env_vars++; return env_vars[num_env_vars - 1].value; } return 0; } const char *GetPwd() { UNIMPLEMENTED(); } u32 GetUid() { UNIMPLEMENTED(); } namespace { struct ModuleInfo { const char *filepath; uptr base_address; uptr end_address; }; #if !SANITIZER_GO int CompareModulesBase(const void *pl, const void *pr) { const ModuleInfo *l = (ModuleInfo *)pl, *r = (ModuleInfo *)pr; if (l->base_address < r->base_address) return -1; return l->base_address > r->base_address; } #endif } // namespace #if !SANITIZER_GO void DumpProcessMap() { Report("Dumping process modules:\n"); ListOfModules modules; modules.init(); uptr num_modules = modules.size(); InternalScopedBuffer module_infos(num_modules); for (size_t i = 0; i < num_modules; ++i) { module_infos[i].filepath = modules[i].full_name(); module_infos[i].base_address = modules[i].ranges().front()->beg; module_infos[i].end_address = modules[i].ranges().back()->end; } qsort(module_infos.data(), num_modules, sizeof(ModuleInfo), CompareModulesBase); for (size_t i = 0; i < num_modules; ++i) { const ModuleInfo &mi = module_infos[i]; if (mi.end_address != 0) { Printf("\t%p-%p %s\n", mi.base_address, mi.end_address, mi.filepath[0] ? mi.filepath : "[no name]"); } else if (mi.filepath[0]) { Printf("\t??\?-??? %s\n", mi.filepath); } else { Printf("\t???\n"); } } } #endif void PrintModuleMap() { } void DisableCoreDumperIfNecessary() { // Do nothing. } void ReExec() { UNIMPLEMENTED(); } void PrepareForSandboxing(__sanitizer_sandbox_arguments *args) { } bool StackSizeIsUnlimited() { UNIMPLEMENTED(); } void SetStackSizeLimitInBytes(uptr limit) { UNIMPLEMENTED(); } bool AddressSpaceIsUnlimited() { UNIMPLEMENTED(); } void SetAddressSpaceUnlimited() { UNIMPLEMENTED(); } bool IsPathSeparator(const char c) { return c == '\\' || c == '/'; } bool IsAbsolutePath(const char *path) { UNIMPLEMENTED(); } void SleepForSeconds(int seconds) { Sleep(seconds * 1000); } void SleepForMillis(int millis) { Sleep(millis); } u64 NanoTime() { return 0; } void Abort() { internal__exit(3); } #if !SANITIZER_GO // Read the file to extract the ImageBase field from the PE header. If ASLR is // disabled and this virtual address is available, the loader will typically // load the image at this address. Therefore, we call it the preferred base. Any // addresses in the DWARF typically assume that the object has been loaded at // this address. static uptr GetPreferredBase(const char *modname) { fd_t fd = OpenFile(modname, RdOnly, nullptr); if (fd == kInvalidFd) return 0; FileCloser closer(fd); // Read just the DOS header. IMAGE_DOS_HEADER dos_header; uptr bytes_read; if (!ReadFromFile(fd, &dos_header, sizeof(dos_header), &bytes_read) || bytes_read != sizeof(dos_header)) return 0; // The file should start with the right signature. if (dos_header.e_magic != IMAGE_DOS_SIGNATURE) return 0; // The layout at e_lfanew is: // "PE\0\0" // IMAGE_FILE_HEADER // IMAGE_OPTIONAL_HEADER // Seek to e_lfanew and read all that data. char buf[4 + sizeof(IMAGE_FILE_HEADER) + sizeof(IMAGE_OPTIONAL_HEADER)]; if (::SetFilePointer(fd, dos_header.e_lfanew, nullptr, FILE_BEGIN) == INVALID_SET_FILE_POINTER) return 0; if (!ReadFromFile(fd, &buf[0], sizeof(buf), &bytes_read) || bytes_read != sizeof(buf)) return 0; // Check for "PE\0\0" before the PE header. char *pe_sig = &buf[0]; if (internal_memcmp(pe_sig, "PE\0\0", 4) != 0) return 0; // Skip over IMAGE_FILE_HEADER. We could do more validation here if we wanted. IMAGE_OPTIONAL_HEADER *pe_header = (IMAGE_OPTIONAL_HEADER *)(pe_sig + 4 + sizeof(IMAGE_FILE_HEADER)); // Check for more magic in the PE header. if (pe_header->Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC) return 0; // Finally, return the ImageBase. return (uptr)pe_header->ImageBase; } void ListOfModules::init() { clear(); HANDLE cur_process = GetCurrentProcess(); // Query the list of modules. Start by assuming there are no more than 256 // modules and retry if that's not sufficient. HMODULE *hmodules = 0; uptr modules_buffer_size = sizeof(HMODULE) * 256; DWORD bytes_required; while (!hmodules) { hmodules = (HMODULE *)MmapOrDie(modules_buffer_size, __FUNCTION__); CHECK(EnumProcessModules(cur_process, hmodules, modules_buffer_size, &bytes_required)); if (bytes_required > modules_buffer_size) { // Either there turned out to be more than 256 hmodules, or new hmodules // could have loaded since the last try. Retry. UnmapOrDie(hmodules, modules_buffer_size); hmodules = 0; modules_buffer_size = bytes_required; } } // |num_modules| is the number of modules actually present, size_t num_modules = bytes_required / sizeof(HMODULE); for (size_t i = 0; i < num_modules; ++i) { HMODULE handle = hmodules[i]; MODULEINFO mi; if (!GetModuleInformation(cur_process, handle, &mi, sizeof(mi))) continue; // Get the UTF-16 path and convert to UTF-8. wchar_t modname_utf16[kMaxPathLength]; int modname_utf16_len = GetModuleFileNameW(handle, modname_utf16, kMaxPathLength); if (modname_utf16_len == 0) modname_utf16[0] = '\0'; char module_name[kMaxPathLength]; int module_name_len = ::WideCharToMultiByte(CP_UTF8, 0, modname_utf16, modname_utf16_len + 1, &module_name[0], kMaxPathLength, NULL, NULL); module_name[module_name_len] = '\0'; uptr base_address = (uptr)mi.lpBaseOfDll; uptr end_address = (uptr)mi.lpBaseOfDll + mi.SizeOfImage; // Adjust the base address of the module so that we get a VA instead of an // RVA when computing the module offset. This helps llvm-symbolizer find the // right DWARF CU. In the common case that the image is loaded at it's // preferred address, we will now print normal virtual addresses. uptr preferred_base = GetPreferredBase(&module_name[0]); uptr adjusted_base = base_address - preferred_base; LoadedModule cur_module; cur_module.set(module_name, adjusted_base); // We add the whole module as one single address range. cur_module.addAddressRange(base_address, end_address, /*executable*/ true, /*writable*/ true); modules_.push_back(cur_module); } UnmapOrDie(hmodules, modules_buffer_size); }; // We can't use atexit() directly at __asan_init time as the CRT is not fully // initialized at this point. Place the functions into a vector and use // atexit() as soon as it is ready for use (i.e. after .CRT$XIC initializers). InternalMmapVectorNoCtor atexit_functions; int Atexit(void (*function)(void)) { atexit_functions.push_back(function); return 0; } static int RunAtexit() { int ret = 0; for (uptr i = 0; i < atexit_functions.size(); ++i) { ret |= atexit(atexit_functions[i]); } return ret; } #pragma section(".CRT$XID", long, read) // NOLINT __declspec(allocate(".CRT$XID")) int (*__run_atexit)() = RunAtexit; #endif // ------------------ sanitizer_libc.h fd_t OpenFile(const char *filename, FileAccessMode mode, error_t *last_error) { // FIXME: Use the wide variants to handle Unicode filenames. fd_t res; if (mode == RdOnly) { res = CreateFileA(filename, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); } else if (mode == WrOnly) { res = CreateFileA(filename, GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); } else { UNIMPLEMENTED(); } CHECK(res != kStdoutFd || kStdoutFd == kInvalidFd); CHECK(res != kStderrFd || kStderrFd == kInvalidFd); if (res == kInvalidFd && last_error) *last_error = GetLastError(); return res; } void CloseFile(fd_t fd) { CloseHandle(fd); } bool ReadFromFile(fd_t fd, void *buff, uptr buff_size, uptr *bytes_read, error_t *error_p) { CHECK(fd != kInvalidFd); // bytes_read can't be passed directly to ReadFile: // uptr is unsigned long long on 64-bit Windows. unsigned long num_read_long; bool success = ::ReadFile(fd, buff, buff_size, &num_read_long, nullptr); if (!success && error_p) *error_p = GetLastError(); if (bytes_read) *bytes_read = num_read_long; return success; } bool SupportsColoredOutput(fd_t fd) { // FIXME: support colored output. return false; } bool WriteToFile(fd_t fd, const void *buff, uptr buff_size, uptr *bytes_written, error_t *error_p) { CHECK(fd != kInvalidFd); // Handle null optional parameters. error_t dummy_error; error_p = error_p ? error_p : &dummy_error; uptr dummy_bytes_written; bytes_written = bytes_written ? bytes_written : &dummy_bytes_written; // Initialize output parameters in case we fail. *error_p = 0; *bytes_written = 0; // Map the conventional Unix fds 1 and 2 to Windows handles. They might be // closed, in which case this will fail. if (fd == kStdoutFd || fd == kStderrFd) { fd = GetStdHandle(fd == kStdoutFd ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE); if (fd == 0) { *error_p = ERROR_INVALID_HANDLE; return false; } } DWORD bytes_written_32; if (!WriteFile(fd, buff, buff_size, &bytes_written_32, 0)) { *error_p = GetLastError(); return false; } else { *bytes_written = bytes_written_32; return true; } } bool RenameFile(const char *oldpath, const char *newpath, error_t *error_p) { UNIMPLEMENTED(); } uptr internal_sched_yield() { Sleep(0); return 0; } void internal__exit(int exitcode) { // ExitProcess runs some finalizers, so use TerminateProcess to avoid that. // The debugger doesn't stop on TerminateProcess like it does on ExitProcess, // so add our own breakpoint here. if (::IsDebuggerPresent()) __debugbreak(); TerminateProcess(GetCurrentProcess(), exitcode); BUILTIN_UNREACHABLE(); } uptr internal_ftruncate(fd_t fd, uptr size) { UNIMPLEMENTED(); } uptr GetRSS() { return 0; } void *internal_start_thread(void (*func)(void *arg), void *arg) { return 0; } void internal_join_thread(void *th) { } // ---------------------- BlockingMutex ---------------- {{{1 const uptr LOCK_UNINITIALIZED = 0; const uptr LOCK_READY = (uptr)-1; BlockingMutex::BlockingMutex(LinkerInitialized li) { // FIXME: see comments in BlockingMutex::Lock() for the details. CHECK(li == LINKER_INITIALIZED || owner_ == LOCK_UNINITIALIZED); CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_)); InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_); owner_ = LOCK_READY; } BlockingMutex::BlockingMutex() { CHECK(sizeof(CRITICAL_SECTION) <= sizeof(opaque_storage_)); InitializeCriticalSection((LPCRITICAL_SECTION)opaque_storage_); owner_ = LOCK_READY; } void BlockingMutex::Lock() { if (owner_ == LOCK_UNINITIALIZED) { // FIXME: hm, global BlockingMutex objects are not initialized?!? // This might be a side effect of the clang+cl+link Frankenbuild... new(this) BlockingMutex((LinkerInitialized)(LINKER_INITIALIZED + 1)); // FIXME: If it turns out the linker doesn't invoke our // constructors, we should probably manually Lock/Unlock all the global // locks while we're starting in one thread to avoid double-init races. } EnterCriticalSection((LPCRITICAL_SECTION)opaque_storage_); CHECK_EQ(owner_, LOCK_READY); owner_ = GetThreadSelf(); } void BlockingMutex::Unlock() { CHECK_EQ(owner_, GetThreadSelf()); owner_ = LOCK_READY; LeaveCriticalSection((LPCRITICAL_SECTION)opaque_storage_); } void BlockingMutex::CheckLocked() { CHECK_EQ(owner_, GetThreadSelf()); } uptr GetTlsSize() { return 0; } void InitTlsSize() { } void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, uptr *tls_addr, uptr *tls_size) { #if SANITIZER_GO *stk_addr = 0; *stk_size = 0; *tls_addr = 0; *tls_size = 0; #else uptr stack_top, stack_bottom; GetThreadStackTopAndBottom(main, &stack_top, &stack_bottom); *stk_addr = stack_bottom; *stk_size = stack_top - stack_bottom; *tls_addr = 0; *tls_size = 0; #endif } #if !SANITIZER_GO void BufferedStackTrace::SlowUnwindStack(uptr pc, u32 max_depth) { CHECK_GE(max_depth, 2); // FIXME: CaptureStackBackTrace might be too slow for us. // FIXME: Compare with StackWalk64. // FIXME: Look at LLVMUnhandledExceptionFilter in Signals.inc size = CaptureStackBackTrace(1, Min(max_depth, kStackTraceMax), (void**)trace, 0); if (size == 0) return; // Skip the RTL frames by searching for the PC in the stacktrace. uptr pc_location = LocatePcInTrace(pc); PopStackFrames(pc_location); } void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context, u32 max_depth) { CONTEXT ctx = *(CONTEXT *)context; STACKFRAME64 stack_frame; memset(&stack_frame, 0, sizeof(stack_frame)); InitializeDbgHelpIfNeeded(); size = 0; #if defined(_WIN64) int machine_type = IMAGE_FILE_MACHINE_AMD64; stack_frame.AddrPC.Offset = ctx.Rip; stack_frame.AddrFrame.Offset = ctx.Rbp; stack_frame.AddrStack.Offset = ctx.Rsp; #else int machine_type = IMAGE_FILE_MACHINE_I386; stack_frame.AddrPC.Offset = ctx.Eip; stack_frame.AddrFrame.Offset = ctx.Ebp; stack_frame.AddrStack.Offset = ctx.Esp; #endif stack_frame.AddrPC.Mode = AddrModeFlat; stack_frame.AddrFrame.Mode = AddrModeFlat; stack_frame.AddrStack.Mode = AddrModeFlat; while (StackWalk64(machine_type, GetCurrentProcess(), GetCurrentThread(), &stack_frame, &ctx, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL) && size < Min(max_depth, kStackTraceMax)) { trace_buffer[size++] = (uptr)stack_frame.AddrPC.Offset; } } #endif // #if !SANITIZER_GO void ReportFile::Write(const char *buffer, uptr length) { SpinMutexLock l(mu); ReopenIfNecessary(); if (!WriteToFile(fd, buffer, length)) { // stderr may be closed, but we may be able to print to the debugger // instead. This is the case when launching a program from Visual Studio, // and the following routine should write to its console. OutputDebugStringA(buffer); } } void SetAlternateSignalStack() { // FIXME: Decide what to do on Windows. } void UnsetAlternateSignalStack() { // FIXME: Decide what to do on Windows. } void InstallDeadlySignalHandlers(SignalHandlerType handler) { (void)handler; // FIXME: Decide what to do on Windows. } HandleSignalMode GetHandleSignalMode(int signum) { // FIXME: Decide what to do on Windows. return kHandleSignalNo; } // Check based on flags if we should handle this exception. bool IsHandledDeadlyException(DWORD exceptionCode) { switch (exceptionCode) { case EXCEPTION_ACCESS_VIOLATION: case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: case EXCEPTION_STACK_OVERFLOW: case EXCEPTION_DATATYPE_MISALIGNMENT: case EXCEPTION_IN_PAGE_ERROR: return common_flags()->handle_segv; case EXCEPTION_ILLEGAL_INSTRUCTION: case EXCEPTION_PRIV_INSTRUCTION: case EXCEPTION_BREAKPOINT: return common_flags()->handle_sigill; case EXCEPTION_FLT_DENORMAL_OPERAND: case EXCEPTION_FLT_DIVIDE_BY_ZERO: case EXCEPTION_FLT_INEXACT_RESULT: case EXCEPTION_FLT_INVALID_OPERATION: case EXCEPTION_FLT_OVERFLOW: case EXCEPTION_FLT_STACK_CHECK: case EXCEPTION_FLT_UNDERFLOW: case EXCEPTION_INT_DIVIDE_BY_ZERO: case EXCEPTION_INT_OVERFLOW: return common_flags()->handle_sigfpe; } return false; } const char *DescribeSignalOrException(int signo) { unsigned code = signo; // Get the string description of the exception if this is a known deadly // exception. switch (code) { case EXCEPTION_ACCESS_VIOLATION: return "access-violation"; case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return "array-bounds-exceeded"; case EXCEPTION_STACK_OVERFLOW: return "stack-overflow"; case EXCEPTION_DATATYPE_MISALIGNMENT: return "datatype-misalignment"; case EXCEPTION_IN_PAGE_ERROR: return "in-page-error"; case EXCEPTION_ILLEGAL_INSTRUCTION: return "illegal-instruction"; case EXCEPTION_PRIV_INSTRUCTION: return "priv-instruction"; case EXCEPTION_BREAKPOINT: return "breakpoint"; case EXCEPTION_FLT_DENORMAL_OPERAND: return "flt-denormal-operand"; case EXCEPTION_FLT_DIVIDE_BY_ZERO: return "flt-divide-by-zero"; case EXCEPTION_FLT_INEXACT_RESULT: return "flt-inexact-result"; case EXCEPTION_FLT_INVALID_OPERATION: return "flt-invalid-operation"; case EXCEPTION_FLT_OVERFLOW: return "flt-overflow"; case EXCEPTION_FLT_STACK_CHECK: return "flt-stack-check"; case EXCEPTION_FLT_UNDERFLOW: return "flt-underflow"; case EXCEPTION_INT_DIVIDE_BY_ZERO: return "int-divide-by-zero"; case EXCEPTION_INT_OVERFLOW: return "int-overflow"; } return "unknown exception"; } bool IsAccessibleMemoryRange(uptr beg, uptr size) { SYSTEM_INFO si; GetNativeSystemInfo(&si); uptr page_size = si.dwPageSize; uptr page_mask = ~(page_size - 1); for (uptr page = beg & page_mask, end = (beg + size - 1) & page_mask; page <= end;) { MEMORY_BASIC_INFORMATION info; if (VirtualQuery((LPCVOID)page, &info, sizeof(info)) != sizeof(info)) return false; if (info.Protect == 0 || info.Protect == PAGE_NOACCESS || info.Protect == PAGE_EXECUTE) return false; if (info.RegionSize == 0) return false; page += info.RegionSize; } return true; } SignalContext SignalContext::Create(void *siginfo, void *context) { EXCEPTION_RECORD *exception_record = (EXCEPTION_RECORD *)siginfo; CONTEXT *context_record = (CONTEXT *)context; uptr pc = (uptr)exception_record->ExceptionAddress; #ifdef _WIN64 uptr bp = (uptr)context_record->Rbp; uptr sp = (uptr)context_record->Rsp; #else uptr bp = (uptr)context_record->Ebp; uptr sp = (uptr)context_record->Esp; #endif uptr access_addr = exception_record->ExceptionInformation[1]; // The contents of this array are documented at // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363082(v=vs.85).aspx // The first element indicates read as 0, write as 1, or execute as 8. The // second element is the faulting address. WriteFlag write_flag = SignalContext::UNKNOWN; switch (exception_record->ExceptionInformation[0]) { case 0: write_flag = SignalContext::READ; break; case 1: write_flag = SignalContext::WRITE; break; case 8: write_flag = SignalContext::UNKNOWN; break; } bool is_memory_access = write_flag != SignalContext::UNKNOWN; return SignalContext(context, access_addr, pc, sp, bp, is_memory_access, write_flag); } void SignalContext::DumpAllRegisters(void *context) { // FIXME: Implement this. } uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) { // FIXME: Actually implement this function. CHECK_GT(buf_len, 0); buf[0] = 0; return 0; } uptr ReadLongProcessName(/*out*/char *buf, uptr buf_len) { return ReadBinaryName(buf, buf_len); } void CheckVMASize() { // Do nothing. } void MaybeReexec() { // No need to re-exec on Windows. } char **GetArgv() { // FIXME: Actually implement this function. return 0; } pid_t StartSubprocess(const char *program, const char *const argv[], fd_t stdin_fd, fd_t stdout_fd, fd_t stderr_fd) { // FIXME: implement on this platform // Should be implemented based on // SymbolizerProcess::StarAtSymbolizerSubprocess // from lib/sanitizer_common/sanitizer_symbolizer_win.cc. return -1; } bool IsProcessRunning(pid_t pid) { // FIXME: implement on this platform. return false; } int WaitForProcess(pid_t pid) { return -1; } // FIXME implement on this platform. void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) { } void CheckNoDeepBind(const char *filename, int flag) { // Do nothing. } // FIXME: implement on this platform. bool GetRandom(void *buffer, uptr length) { UNIMPLEMENTED(); } } // namespace __sanitizer #endif // _WIN32 Index: vendor/compiler-rt/dist/lib/sanitizer_common/tests/sanitizer_allocator_test.cc =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/tests/sanitizer_allocator_test.cc (revision 320536) +++ vendor/compiler-rt/dist/lib/sanitizer_common/tests/sanitizer_allocator_test.cc (revision 320537) @@ -1,1058 +1,1060 @@ //===-- sanitizer_allocator_test.cc ---------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer/AddressSanitizer runtime. // Tests for sanitizer_allocator.h. // //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_allocator.h" #include "sanitizer_common/sanitizer_allocator_internal.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_test_utils.h" #include "sanitizer_pthread_wrappers.h" #include "gtest/gtest.h" #include #include #include #include #include using namespace __sanitizer; // Too slow for debug build #if !SANITIZER_DEBUG #if SANITIZER_CAN_USE_ALLOCATOR64 #if SANITIZER_WINDOWS // On Windows 64-bit there is no easy way to find a large enough fixed address // space that is always available. Thus, a dynamically allocated address space // is used instead (i.e. ~(uptr)0). static const uptr kAllocatorSpace = ~(uptr)0; static const uptr kAllocatorSize = 0x8000000000ULL; // 500G static const u64 kAddressSpaceSize = 1ULL << 47; typedef DefaultSizeClassMap SizeClassMap; #elif SANITIZER_ANDROID && defined(__aarch64__) static const uptr kAllocatorSpace = 0x3000000000ULL; static const uptr kAllocatorSize = 0x2000000000ULL; static const u64 kAddressSpaceSize = 1ULL << 39; typedef VeryCompactSizeClassMap SizeClassMap; #else static const uptr kAllocatorSpace = 0x700000000000ULL; static const uptr kAllocatorSize = 0x010000000000ULL; // 1T. static const u64 kAddressSpaceSize = 1ULL << 47; typedef DefaultSizeClassMap SizeClassMap; #endif struct AP64 { // Allocator Params. Short name for shorter demangled names.. static const uptr kSpaceBeg = kAllocatorSpace; static const uptr kSpaceSize = kAllocatorSize; static const uptr kMetadataSize = 16; typedef ::SizeClassMap SizeClassMap; typedef NoOpMapUnmapCallback MapUnmapCallback; static const uptr kFlags = 0; }; struct AP64Dyn { static const uptr kSpaceBeg = ~(uptr)0; static const uptr kSpaceSize = kAllocatorSize; static const uptr kMetadataSize = 16; typedef ::SizeClassMap SizeClassMap; typedef NoOpMapUnmapCallback MapUnmapCallback; static const uptr kFlags = 0; }; struct AP64Compact { static const uptr kSpaceBeg = ~(uptr)0; static const uptr kSpaceSize = kAllocatorSize; static const uptr kMetadataSize = 16; typedef CompactSizeClassMap SizeClassMap; typedef NoOpMapUnmapCallback MapUnmapCallback; static const uptr kFlags = 0; }; struct AP64VeryCompact { static const uptr kSpaceBeg = ~(uptr)0; static const uptr kSpaceSize = 1ULL << 37; static const uptr kMetadataSize = 16; typedef VeryCompactSizeClassMap SizeClassMap; typedef NoOpMapUnmapCallback MapUnmapCallback; static const uptr kFlags = 0; }; typedef SizeClassAllocator64 Allocator64; typedef SizeClassAllocator64 Allocator64Dynamic; typedef SizeClassAllocator64 Allocator64Compact; typedef SizeClassAllocator64 Allocator64VeryCompact; #elif defined(__mips64) static const u64 kAddressSpaceSize = 1ULL << 40; #elif defined(__aarch64__) static const u64 kAddressSpaceSize = 1ULL << 39; #elif defined(__s390x__) static const u64 kAddressSpaceSize = 1ULL << 53; #elif defined(__s390__) static const u64 kAddressSpaceSize = 1ULL << 31; #else static const u64 kAddressSpaceSize = 1ULL << 32; #endif static const uptr kRegionSizeLog = FIRST_32_SECOND_64(20, 24); static const uptr kFlatByteMapSize = kAddressSpaceSize >> kRegionSizeLog; struct AP32Compact { static const uptr kSpaceBeg = 0; static const u64 kSpaceSize = kAddressSpaceSize; static const uptr kMetadataSize = 16; typedef CompactSizeClassMap SizeClassMap; static const uptr kRegionSizeLog = ::kRegionSizeLog; typedef FlatByteMap ByteMap; typedef NoOpMapUnmapCallback MapUnmapCallback; static const uptr kFlags = 0; }; typedef SizeClassAllocator32 Allocator32Compact; template void TestSizeClassMap() { typedef SizeClassMap SCMap; SCMap::Print(); SCMap::Validate(); } TEST(SanitizerCommon, DefaultSizeClassMap) { TestSizeClassMap(); } TEST(SanitizerCommon, CompactSizeClassMap) { TestSizeClassMap(); } TEST(SanitizerCommon, VeryCompactSizeClassMap) { TestSizeClassMap(); } TEST(SanitizerCommon, InternalSizeClassMap) { TestSizeClassMap(); } template void TestSizeClassAllocator() { Allocator *a = new Allocator; a->Init(kReleaseToOSIntervalNever); SizeClassAllocatorLocalCache cache; memset(&cache, 0, sizeof(cache)); cache.Init(0); static const uptr sizes[] = { 1, 16, 30, 40, 100, 1000, 10000, 50000, 60000, 100000, 120000, 300000, 500000, 1000000, 2000000 }; std::vector allocated; uptr last_total_allocated = 0; for (int i = 0; i < 3; i++) { // Allocate a bunch of chunks. for (uptr s = 0; s < ARRAY_SIZE(sizes); s++) { uptr size = sizes[s]; if (!a->CanAllocate(size, 1)) continue; // printf("s = %ld\n", size); uptr n_iter = std::max((uptr)6, 4000000 / size); // fprintf(stderr, "size: %ld iter: %ld\n", size, n_iter); for (uptr i = 0; i < n_iter; i++) { uptr class_id0 = Allocator::SizeClassMapT::ClassID(size); char *x = (char*)cache.Allocate(a, class_id0); x[0] = 0; x[size - 1] = 0; x[size / 2] = 0; allocated.push_back(x); CHECK_EQ(x, a->GetBlockBegin(x)); CHECK_EQ(x, a->GetBlockBegin(x + size - 1)); CHECK(a->PointerIsMine(x)); CHECK(a->PointerIsMine(x + size - 1)); CHECK(a->PointerIsMine(x + size / 2)); CHECK_GE(a->GetActuallyAllocatedSize(x), size); uptr class_id = a->GetSizeClass(x); CHECK_EQ(class_id, Allocator::SizeClassMapT::ClassID(size)); uptr *metadata = reinterpret_cast(a->GetMetaData(x)); metadata[0] = reinterpret_cast(x) + 1; metadata[1] = 0xABCD; } } // Deallocate all. for (uptr i = 0; i < allocated.size(); i++) { void *x = allocated[i]; uptr *metadata = reinterpret_cast(a->GetMetaData(x)); CHECK_EQ(metadata[0], reinterpret_cast(x) + 1); CHECK_EQ(metadata[1], 0xABCD); cache.Deallocate(a, a->GetSizeClass(x), x); } allocated.clear(); uptr total_allocated = a->TotalMemoryUsed(); if (last_total_allocated == 0) last_total_allocated = total_allocated; CHECK_EQ(last_total_allocated, total_allocated); } // Check that GetBlockBegin never crashes. for (uptr x = 0, step = kAddressSpaceSize / 100000; x < kAddressSpaceSize - step; x += step) if (a->PointerIsMine(reinterpret_cast(x))) Ident(a->GetBlockBegin(reinterpret_cast(x))); a->TestOnlyUnmap(); delete a; } #if SANITIZER_CAN_USE_ALLOCATOR64 // These tests can fail on Windows if memory is somewhat full and lit happens // to run them all at the same time. FIXME: Make them not flaky and reenable. #if !SANITIZER_WINDOWS TEST(SanitizerCommon, SizeClassAllocator64) { TestSizeClassAllocator(); } TEST(SanitizerCommon, SizeClassAllocator64Dynamic) { TestSizeClassAllocator(); } #if !SANITIZER_ANDROID TEST(SanitizerCommon, SizeClassAllocator64Compact) { TestSizeClassAllocator(); } #endif TEST(SanitizerCommon, SizeClassAllocator64VeryCompact) { TestSizeClassAllocator(); } #endif #endif TEST(SanitizerCommon, SizeClassAllocator32Compact) { TestSizeClassAllocator(); } template void SizeClassAllocatorMetadataStress() { Allocator *a = new Allocator; a->Init(kReleaseToOSIntervalNever); SizeClassAllocatorLocalCache cache; memset(&cache, 0, sizeof(cache)); cache.Init(0); const uptr kNumAllocs = 1 << 13; void *allocated[kNumAllocs]; void *meta[kNumAllocs]; for (uptr i = 0; i < kNumAllocs; i++) { void *x = cache.Allocate(a, 1 + i % (Allocator::kNumClasses - 1)); allocated[i] = x; meta[i] = a->GetMetaData(x); } // Get Metadata kNumAllocs^2 times. for (uptr i = 0; i < kNumAllocs * kNumAllocs; i++) { uptr idx = i % kNumAllocs; void *m = a->GetMetaData(allocated[idx]); EXPECT_EQ(m, meta[idx]); } for (uptr i = 0; i < kNumAllocs; i++) { cache.Deallocate(a, 1 + i % (Allocator::kNumClasses - 1), allocated[i]); } a->TestOnlyUnmap(); delete a; } #if SANITIZER_CAN_USE_ALLOCATOR64 // These tests can fail on Windows if memory is somewhat full and lit happens // to run them all at the same time. FIXME: Make them not flaky and reenable. #if !SANITIZER_WINDOWS TEST(SanitizerCommon, SizeClassAllocator64MetadataStress) { SizeClassAllocatorMetadataStress(); } TEST(SanitizerCommon, SizeClassAllocator64DynamicMetadataStress) { SizeClassAllocatorMetadataStress(); } #if !SANITIZER_ANDROID TEST(SanitizerCommon, SizeClassAllocator64CompactMetadataStress) { SizeClassAllocatorMetadataStress(); } #endif #endif #endif // SANITIZER_CAN_USE_ALLOCATOR64 TEST(SanitizerCommon, SizeClassAllocator32CompactMetadataStress) { SizeClassAllocatorMetadataStress(); } template void SizeClassAllocatorGetBlockBeginStress(u64 TotalSize) { Allocator *a = new Allocator; a->Init(kReleaseToOSIntervalNever); SizeClassAllocatorLocalCache cache; memset(&cache, 0, sizeof(cache)); cache.Init(0); uptr max_size_class = Allocator::SizeClassMapT::kLargestClassID; uptr size = Allocator::SizeClassMapT::Size(max_size_class); // Make sure we correctly compute GetBlockBegin() w/o overflow. for (size_t i = 0; i <= TotalSize / size; i++) { void *x = cache.Allocate(a, max_size_class); void *beg = a->GetBlockBegin(x); // if ((i & (i - 1)) == 0) // fprintf(stderr, "[%zd] %p %p\n", i, x, beg); EXPECT_EQ(x, beg); } a->TestOnlyUnmap(); delete a; } #if SANITIZER_CAN_USE_ALLOCATOR64 // These tests can fail on Windows if memory is somewhat full and lit happens // to run them all at the same time. FIXME: Make them not flaky and reenable. #if !SANITIZER_WINDOWS TEST(SanitizerCommon, SizeClassAllocator64GetBlockBegin) { SizeClassAllocatorGetBlockBeginStress( 1ULL << (SANITIZER_ANDROID ? 31 : 33)); } TEST(SanitizerCommon, SizeClassAllocator64DynamicGetBlockBegin) { SizeClassAllocatorGetBlockBeginStress( 1ULL << (SANITIZER_ANDROID ? 31 : 33)); } #if !SANITIZER_ANDROID TEST(SanitizerCommon, SizeClassAllocator64CompactGetBlockBegin) { SizeClassAllocatorGetBlockBeginStress(1ULL << 33); } #endif TEST(SanitizerCommon, SizeClassAllocator64VeryCompactGetBlockBegin) { // Does not have > 4Gb for each class. SizeClassAllocatorGetBlockBeginStress(1ULL << 31); } TEST(SanitizerCommon, SizeClassAllocator32CompactGetBlockBegin) { SizeClassAllocatorGetBlockBeginStress(1ULL << 33); } #endif #endif // SANITIZER_CAN_USE_ALLOCATOR64 struct TestMapUnmapCallback { static int map_count, unmap_count; void OnMap(uptr p, uptr size) const { map_count++; } void OnUnmap(uptr p, uptr size) const { unmap_count++; } }; int TestMapUnmapCallback::map_count; int TestMapUnmapCallback::unmap_count; #if SANITIZER_CAN_USE_ALLOCATOR64 // These tests can fail on Windows if memory is somewhat full and lit happens // to run them all at the same time. FIXME: Make them not flaky and reenable. #if !SANITIZER_WINDOWS struct AP64WithCallback { static const uptr kSpaceBeg = kAllocatorSpace; static const uptr kSpaceSize = kAllocatorSize; static const uptr kMetadataSize = 16; typedef ::SizeClassMap SizeClassMap; typedef TestMapUnmapCallback MapUnmapCallback; static const uptr kFlags = 0; }; TEST(SanitizerCommon, SizeClassAllocator64MapUnmapCallback) { TestMapUnmapCallback::map_count = 0; TestMapUnmapCallback::unmap_count = 0; typedef SizeClassAllocator64 Allocator64WithCallBack; Allocator64WithCallBack *a = new Allocator64WithCallBack; a->Init(kReleaseToOSIntervalNever); EXPECT_EQ(TestMapUnmapCallback::map_count, 1); // Allocator state. SizeClassAllocatorLocalCache cache; memset(&cache, 0, sizeof(cache)); cache.Init(0); AllocatorStats stats; stats.Init(); const size_t kNumChunks = 128; uint32_t chunks[kNumChunks]; a->GetFromAllocator(&stats, 30, chunks, kNumChunks); // State + alloc + metadata + freearray. EXPECT_EQ(TestMapUnmapCallback::map_count, 4); a->TestOnlyUnmap(); EXPECT_EQ(TestMapUnmapCallback::unmap_count, 1); // The whole thing. delete a; } #endif #endif struct AP32WithCallback { static const uptr kSpaceBeg = 0; static const u64 kSpaceSize = kAddressSpaceSize; static const uptr kMetadataSize = 16; typedef CompactSizeClassMap SizeClassMap; static const uptr kRegionSizeLog = ::kRegionSizeLog; typedef FlatByteMap ByteMap; typedef TestMapUnmapCallback MapUnmapCallback; static const uptr kFlags = 0; }; TEST(SanitizerCommon, SizeClassAllocator32MapUnmapCallback) { TestMapUnmapCallback::map_count = 0; TestMapUnmapCallback::unmap_count = 0; typedef SizeClassAllocator32 Allocator32WithCallBack; Allocator32WithCallBack *a = new Allocator32WithCallBack; a->Init(kReleaseToOSIntervalNever); EXPECT_EQ(TestMapUnmapCallback::map_count, 0); SizeClassAllocatorLocalCache cache; memset(&cache, 0, sizeof(cache)); cache.Init(0); AllocatorStats stats; stats.Init(); a->AllocateBatch(&stats, &cache, 32); EXPECT_EQ(TestMapUnmapCallback::map_count, 1); a->TestOnlyUnmap(); EXPECT_EQ(TestMapUnmapCallback::unmap_count, 1); delete a; // fprintf(stderr, "Map: %d Unmap: %d\n", // TestMapUnmapCallback::map_count, // TestMapUnmapCallback::unmap_count); } TEST(SanitizerCommon, LargeMmapAllocatorMapUnmapCallback) { TestMapUnmapCallback::map_count = 0; TestMapUnmapCallback::unmap_count = 0; LargeMmapAllocator a; a.Init(); AllocatorStats stats; stats.Init(); void *x = a.Allocate(&stats, 1 << 20, 1); EXPECT_EQ(TestMapUnmapCallback::map_count, 1); a.Deallocate(&stats, x); EXPECT_EQ(TestMapUnmapCallback::unmap_count, 1); } -template -void FailInAssertionOnOOM() { - Allocator a; +// Don't test OOM conditions on Win64 because it causes other tests on the same +// machine to OOM. +#if SANITIZER_CAN_USE_ALLOCATOR64 && !SANITIZER_WINDOWS64 && !SANITIZER_ANDROID +TEST(SanitizerCommon, SizeClassAllocator64Overflow) { + Allocator64 a; a.Init(kReleaseToOSIntervalNever); - SizeClassAllocatorLocalCache cache; + SizeClassAllocatorLocalCache cache; memset(&cache, 0, sizeof(cache)); cache.Init(0); AllocatorStats stats; stats.Init(); + const size_t kNumChunks = 128; uint32_t chunks[kNumChunks]; + bool allocation_failed = false; for (int i = 0; i < 1000000; i++) { - a.GetFromAllocator(&stats, 52, chunks, kNumChunks); + if (!a.GetFromAllocator(&stats, 52, chunks, kNumChunks)) { + allocation_failed = true; + break; + } } + EXPECT_EQ(allocation_failed, true); a.TestOnlyUnmap(); } - -// Don't test OOM conditions on Win64 because it causes other tests on the same -// machine to OOM. -#if SANITIZER_CAN_USE_ALLOCATOR64 && !SANITIZER_WINDOWS64 && !SANITIZER_ANDROID -TEST(SanitizerCommon, SizeClassAllocator64Overflow) { - EXPECT_DEATH(FailInAssertionOnOOM(), "Out of memory"); -} #endif TEST(SanitizerCommon, LargeMmapAllocator) { LargeMmapAllocator a; a.Init(); AllocatorStats stats; stats.Init(); static const int kNumAllocs = 1000; char *allocated[kNumAllocs]; static const uptr size = 4000; // Allocate some. for (int i = 0; i < kNumAllocs; i++) { allocated[i] = (char *)a.Allocate(&stats, size, 1); CHECK(a.PointerIsMine(allocated[i])); } // Deallocate all. CHECK_GT(a.TotalMemoryUsed(), size * kNumAllocs); for (int i = 0; i < kNumAllocs; i++) { char *p = allocated[i]; CHECK(a.PointerIsMine(p)); a.Deallocate(&stats, p); } // Check that non left. CHECK_EQ(a.TotalMemoryUsed(), 0); // Allocate some more, also add metadata. for (int i = 0; i < kNumAllocs; i++) { char *x = (char *)a.Allocate(&stats, size, 1); CHECK_GE(a.GetActuallyAllocatedSize(x), size); uptr *meta = reinterpret_cast(a.GetMetaData(x)); *meta = i; allocated[i] = x; } for (int i = 0; i < kNumAllocs * kNumAllocs; i++) { char *p = allocated[i % kNumAllocs]; CHECK(a.PointerIsMine(p)); CHECK(a.PointerIsMine(p + 2000)); } CHECK_GT(a.TotalMemoryUsed(), size * kNumAllocs); // Deallocate all in reverse order. for (int i = 0; i < kNumAllocs; i++) { int idx = kNumAllocs - i - 1; char *p = allocated[idx]; uptr *meta = reinterpret_cast(a.GetMetaData(p)); CHECK_EQ(*meta, idx); CHECK(a.PointerIsMine(p)); a.Deallocate(&stats, p); } CHECK_EQ(a.TotalMemoryUsed(), 0); // Test alignments. Test with 512MB alignment on x64 non-Windows machines. // Windows doesn't overcommit, and many machines do not have 51.2GB of swap. uptr max_alignment = (SANITIZER_WORDSIZE == 64 && !SANITIZER_WINDOWS) ? (1 << 28) : (1 << 24); for (uptr alignment = 8; alignment <= max_alignment; alignment *= 2) { const uptr kNumAlignedAllocs = 100; for (uptr i = 0; i < kNumAlignedAllocs; i++) { uptr size = ((i % 10) + 1) * 4096; char *p = allocated[i] = (char *)a.Allocate(&stats, size, alignment); CHECK_EQ(p, a.GetBlockBegin(p)); CHECK_EQ(p, a.GetBlockBegin(p + size - 1)); CHECK_EQ(p, a.GetBlockBegin(p + size / 2)); CHECK_EQ(0, (uptr)allocated[i] % alignment); p[0] = p[size - 1] = 0; } for (uptr i = 0; i < kNumAlignedAllocs; i++) { a.Deallocate(&stats, allocated[i]); } } // Regression test for boundary condition in GetBlockBegin(). uptr page_size = GetPageSizeCached(); char *p = (char *)a.Allocate(&stats, page_size, 1); CHECK_EQ(p, a.GetBlockBegin(p)); CHECK_EQ(p, (char *)a.GetBlockBegin(p + page_size - 1)); CHECK_NE(p, (char *)a.GetBlockBegin(p + page_size)); a.Deallocate(&stats, p); } template void TestCombinedAllocator() { typedef CombinedAllocator Allocator; SetAllocatorMayReturnNull(true); Allocator *a = new Allocator; a->Init(kReleaseToOSIntervalNever); std::mt19937 r; AllocatorCache cache; memset(&cache, 0, sizeof(cache)); a->InitCache(&cache); EXPECT_EQ(a->Allocate(&cache, -1, 1), (void*)0); EXPECT_EQ(a->Allocate(&cache, -1, 1024), (void*)0); EXPECT_EQ(a->Allocate(&cache, (uptr)-1 - 1024, 1), (void*)0); EXPECT_EQ(a->Allocate(&cache, (uptr)-1 - 1024, 1024), (void*)0); EXPECT_EQ(a->Allocate(&cache, (uptr)-1 - 1023, 1024), (void*)0); // Set to false SetAllocatorMayReturnNull(false); EXPECT_DEATH(a->Allocate(&cache, -1, 1), "allocator is terminating the process"); const uptr kNumAllocs = 100000; const uptr kNumIter = 10; for (uptr iter = 0; iter < kNumIter; iter++) { std::vector allocated; for (uptr i = 0; i < kNumAllocs; i++) { uptr size = (i % (1 << 14)) + 1; if ((i % 1024) == 0) size = 1 << (10 + (i % 14)); void *x = a->Allocate(&cache, size, 1); uptr *meta = reinterpret_cast(a->GetMetaData(x)); CHECK_EQ(*meta, 0); *meta = size; allocated.push_back(x); } std::shuffle(allocated.begin(), allocated.end(), r); for (uptr i = 0; i < kNumAllocs; i++) { void *x = allocated[i]; uptr *meta = reinterpret_cast(a->GetMetaData(x)); CHECK_NE(*meta, 0); CHECK(a->PointerIsMine(x)); *meta = 0; a->Deallocate(&cache, x); } allocated.clear(); a->SwallowCache(&cache); } a->DestroyCache(&cache); a->TestOnlyUnmap(); } #if SANITIZER_CAN_USE_ALLOCATOR64 TEST(SanitizerCommon, CombinedAllocator64) { TestCombinedAllocator, SizeClassAllocatorLocalCache > (); } TEST(SanitizerCommon, CombinedAllocator64Dynamic) { TestCombinedAllocator, SizeClassAllocatorLocalCache > (); } #if !SANITIZER_ANDROID TEST(SanitizerCommon, CombinedAllocator64Compact) { TestCombinedAllocator, SizeClassAllocatorLocalCache > (); } #endif TEST(SanitizerCommon, CombinedAllocator64VeryCompact) { TestCombinedAllocator, SizeClassAllocatorLocalCache > (); } #endif TEST(SanitizerCommon, CombinedAllocator32Compact) { TestCombinedAllocator, SizeClassAllocatorLocalCache > (); } template void TestSizeClassAllocatorLocalCache() { AllocatorCache cache; typedef typename AllocatorCache::Allocator Allocator; Allocator *a = new Allocator(); a->Init(kReleaseToOSIntervalNever); memset(&cache, 0, sizeof(cache)); cache.Init(0); const uptr kNumAllocs = 10000; const int kNumIter = 100; uptr saved_total = 0; for (int class_id = 1; class_id <= 5; class_id++) { for (int it = 0; it < kNumIter; it++) { void *allocated[kNumAllocs]; for (uptr i = 0; i < kNumAllocs; i++) { allocated[i] = cache.Allocate(a, class_id); } for (uptr i = 0; i < kNumAllocs; i++) { cache.Deallocate(a, class_id, allocated[i]); } cache.Drain(a); uptr total_allocated = a->TotalMemoryUsed(); if (it) CHECK_EQ(saved_total, total_allocated); saved_total = total_allocated; } } a->TestOnlyUnmap(); delete a; } #if SANITIZER_CAN_USE_ALLOCATOR64 // These tests can fail on Windows if memory is somewhat full and lit happens // to run them all at the same time. FIXME: Make them not flaky and reenable. #if !SANITIZER_WINDOWS TEST(SanitizerCommon, SizeClassAllocator64LocalCache) { TestSizeClassAllocatorLocalCache< SizeClassAllocatorLocalCache >(); } TEST(SanitizerCommon, SizeClassAllocator64DynamicLocalCache) { TestSizeClassAllocatorLocalCache< SizeClassAllocatorLocalCache >(); } #if !SANITIZER_ANDROID TEST(SanitizerCommon, SizeClassAllocator64CompactLocalCache) { TestSizeClassAllocatorLocalCache< SizeClassAllocatorLocalCache >(); } #endif TEST(SanitizerCommon, SizeClassAllocator64VeryCompactLocalCache) { TestSizeClassAllocatorLocalCache< SizeClassAllocatorLocalCache >(); } #endif #endif TEST(SanitizerCommon, SizeClassAllocator32CompactLocalCache) { TestSizeClassAllocatorLocalCache< SizeClassAllocatorLocalCache >(); } #if SANITIZER_CAN_USE_ALLOCATOR64 typedef SizeClassAllocatorLocalCache AllocatorCache; static AllocatorCache static_allocator_cache; void *AllocatorLeakTestWorker(void *arg) { typedef AllocatorCache::Allocator Allocator; Allocator *a = (Allocator*)(arg); static_allocator_cache.Allocate(a, 10); static_allocator_cache.Drain(a); return 0; } TEST(SanitizerCommon, AllocatorLeakTest) { typedef AllocatorCache::Allocator Allocator; Allocator a; a.Init(kReleaseToOSIntervalNever); uptr total_used_memory = 0; for (int i = 0; i < 100; i++) { pthread_t t; PTHREAD_CREATE(&t, 0, AllocatorLeakTestWorker, &a); PTHREAD_JOIN(t, 0); if (i == 0) total_used_memory = a.TotalMemoryUsed(); EXPECT_EQ(a.TotalMemoryUsed(), total_used_memory); } a.TestOnlyUnmap(); } // Struct which is allocated to pass info to new threads. The new thread frees // it. struct NewThreadParams { AllocatorCache *thread_cache; AllocatorCache::Allocator *allocator; uptr class_id; }; // Called in a new thread. Just frees its argument. static void *DeallocNewThreadWorker(void *arg) { NewThreadParams *params = reinterpret_cast(arg); params->thread_cache->Deallocate(params->allocator, params->class_id, params); return NULL; } // The allocator cache is supposed to be POD and zero initialized. We should be // able to call Deallocate on a zeroed cache, and it will self-initialize. TEST(Allocator, AllocatorCacheDeallocNewThread) { AllocatorCache::Allocator allocator; allocator.Init(kReleaseToOSIntervalNever); AllocatorCache main_cache; AllocatorCache child_cache; memset(&main_cache, 0, sizeof(main_cache)); memset(&child_cache, 0, sizeof(child_cache)); uptr class_id = DefaultSizeClassMap::ClassID(sizeof(NewThreadParams)); NewThreadParams *params = reinterpret_cast( main_cache.Allocate(&allocator, class_id)); params->thread_cache = &child_cache; params->allocator = &allocator; params->class_id = class_id; pthread_t t; PTHREAD_CREATE(&t, 0, DeallocNewThreadWorker, params); PTHREAD_JOIN(t, 0); allocator.TestOnlyUnmap(); } #endif TEST(Allocator, Basic) { char *p = (char*)InternalAlloc(10); EXPECT_NE(p, (char*)0); char *p2 = (char*)InternalAlloc(20); EXPECT_NE(p2, (char*)0); EXPECT_NE(p2, p); InternalFree(p); InternalFree(p2); } TEST(Allocator, Stress) { const int kCount = 1000; char *ptrs[kCount]; unsigned rnd = 42; for (int i = 0; i < kCount; i++) { uptr sz = my_rand_r(&rnd) % 1000; char *p = (char*)InternalAlloc(sz); EXPECT_NE(p, (char*)0); ptrs[i] = p; } for (int i = 0; i < kCount; i++) { InternalFree(ptrs[i]); } } TEST(Allocator, LargeAlloc) { void *p = InternalAlloc(10 << 20); InternalFree(p); } TEST(Allocator, ScopedBuffer) { const int kSize = 512; { InternalScopedBuffer int_buf(kSize); EXPECT_EQ(sizeof(int) * kSize, int_buf.size()); // NOLINT } InternalScopedBuffer char_buf(kSize); EXPECT_EQ(sizeof(char) * kSize, char_buf.size()); // NOLINT internal_memset(char_buf.data(), 'c', kSize); for (int i = 0; i < kSize; i++) { EXPECT_EQ('c', char_buf[i]); } } void IterationTestCallback(uptr chunk, void *arg) { reinterpret_cast *>(arg)->insert(chunk); } template void TestSizeClassAllocatorIteration() { Allocator *a = new Allocator; a->Init(kReleaseToOSIntervalNever); SizeClassAllocatorLocalCache cache; memset(&cache, 0, sizeof(cache)); cache.Init(0); static const uptr sizes[] = {1, 16, 30, 40, 100, 1000, 10000, 50000, 60000, 100000, 120000, 300000, 500000, 1000000, 2000000}; std::vector allocated; // Allocate a bunch of chunks. for (uptr s = 0; s < ARRAY_SIZE(sizes); s++) { uptr size = sizes[s]; if (!a->CanAllocate(size, 1)) continue; // printf("s = %ld\n", size); uptr n_iter = std::max((uptr)6, 80000 / size); // fprintf(stderr, "size: %ld iter: %ld\n", size, n_iter); for (uptr j = 0; j < n_iter; j++) { uptr class_id0 = Allocator::SizeClassMapT::ClassID(size); void *x = cache.Allocate(a, class_id0); allocated.push_back(x); } } std::set reported_chunks; a->ForceLock(); a->ForEachChunk(IterationTestCallback, &reported_chunks); a->ForceUnlock(); for (uptr i = 0; i < allocated.size(); i++) { // Don't use EXPECT_NE. Reporting the first mismatch is enough. ASSERT_NE(reported_chunks.find(reinterpret_cast(allocated[i])), reported_chunks.end()); } a->TestOnlyUnmap(); delete a; } #if SANITIZER_CAN_USE_ALLOCATOR64 // These tests can fail on Windows if memory is somewhat full and lit happens // to run them all at the same time. FIXME: Make them not flaky and reenable. #if !SANITIZER_WINDOWS TEST(SanitizerCommon, SizeClassAllocator64Iteration) { TestSizeClassAllocatorIteration(); } TEST(SanitizerCommon, SizeClassAllocator64DynamicIteration) { TestSizeClassAllocatorIteration(); } #endif #endif TEST(SanitizerCommon, SizeClassAllocator32Iteration) { TestSizeClassAllocatorIteration(); } TEST(SanitizerCommon, LargeMmapAllocatorIteration) { LargeMmapAllocator a; a.Init(); AllocatorStats stats; stats.Init(); static const uptr kNumAllocs = 1000; char *allocated[kNumAllocs]; static const uptr size = 40; // Allocate some. for (uptr i = 0; i < kNumAllocs; i++) allocated[i] = (char *)a.Allocate(&stats, size, 1); std::set reported_chunks; a.ForceLock(); a.ForEachChunk(IterationTestCallback, &reported_chunks); a.ForceUnlock(); for (uptr i = 0; i < kNumAllocs; i++) { // Don't use EXPECT_NE. Reporting the first mismatch is enough. ASSERT_NE(reported_chunks.find(reinterpret_cast(allocated[i])), reported_chunks.end()); } for (uptr i = 0; i < kNumAllocs; i++) a.Deallocate(&stats, allocated[i]); } TEST(SanitizerCommon, LargeMmapAllocatorBlockBegin) { LargeMmapAllocator a; a.Init(); AllocatorStats stats; stats.Init(); static const uptr kNumAllocs = 1024; static const uptr kNumExpectedFalseLookups = 10000000; char *allocated[kNumAllocs]; static const uptr size = 4096; // Allocate some. for (uptr i = 0; i < kNumAllocs; i++) { allocated[i] = (char *)a.Allocate(&stats, size, 1); } a.ForceLock(); for (uptr i = 0; i < kNumAllocs * kNumAllocs; i++) { // if ((i & (i - 1)) == 0) fprintf(stderr, "[%zd]\n", i); char *p1 = allocated[i % kNumAllocs]; EXPECT_EQ(p1, a.GetBlockBeginFastLocked(p1)); EXPECT_EQ(p1, a.GetBlockBeginFastLocked(p1 + size / 2)); EXPECT_EQ(p1, a.GetBlockBeginFastLocked(p1 + size - 1)); EXPECT_EQ(p1, a.GetBlockBeginFastLocked(p1 - 100)); } for (uptr i = 0; i < kNumExpectedFalseLookups; i++) { void *p = reinterpret_cast(i % 1024); EXPECT_EQ((void *)0, a.GetBlockBeginFastLocked(p)); p = reinterpret_cast(~0L - (i % 1024)); EXPECT_EQ((void *)0, a.GetBlockBeginFastLocked(p)); } a.ForceUnlock(); for (uptr i = 0; i < kNumAllocs; i++) a.Deallocate(&stats, allocated[i]); } // Don't test OOM conditions on Win64 because it causes other tests on the same // machine to OOM. #if SANITIZER_CAN_USE_ALLOCATOR64 && !SANITIZER_WINDOWS64 && !SANITIZER_ANDROID typedef SizeClassMap<3, 4, 8, 63, 128, 16> SpecialSizeClassMap; struct AP64_SpecialSizeClassMap { static const uptr kSpaceBeg = kAllocatorSpace; static const uptr kSpaceSize = kAllocatorSize; static const uptr kMetadataSize = 0; typedef SpecialSizeClassMap SizeClassMap; typedef NoOpMapUnmapCallback MapUnmapCallback; static const uptr kFlags = 0; }; // Regression test for out-of-memory condition in PopulateFreeList(). TEST(SanitizerCommon, SizeClassAllocator64PopulateFreeListOOM) { // In a world where regions are small and chunks are huge... typedef SizeClassAllocator64 SpecialAllocator64; const uptr kRegionSize = kAllocatorSize / SpecialSizeClassMap::kNumClassesRounded; SpecialAllocator64 *a = new SpecialAllocator64; a->Init(kReleaseToOSIntervalNever); SizeClassAllocatorLocalCache cache; memset(&cache, 0, sizeof(cache)); cache.Init(0); // ...one man is on a mission to overflow a region with a series of // successive allocations. const uptr kClassID = 107; const uptr kAllocationSize = SpecialSizeClassMap::Size(kClassID); ASSERT_LT(2 * kAllocationSize, kRegionSize); ASSERT_GT(3 * kAllocationSize, kRegionSize); - cache.Allocate(a, kClassID); - EXPECT_DEATH(cache.Allocate(a, kClassID) && cache.Allocate(a, kClassID), - "The process has exhausted"); + EXPECT_NE(cache.Allocate(a, kClassID), nullptr); + EXPECT_NE(cache.Allocate(a, kClassID), nullptr); + EXPECT_EQ(cache.Allocate(a, kClassID), nullptr); const uptr Class2 = 100; const uptr Size2 = SpecialSizeClassMap::Size(Class2); ASSERT_EQ(Size2 * 8, kRegionSize); char *p[7]; for (int i = 0; i < 7; i++) { p[i] = (char*)cache.Allocate(a, Class2); + EXPECT_NE(p[i], nullptr); fprintf(stderr, "p[%d] %p s = %lx\n", i, (void*)p[i], Size2); p[i][Size2 - 1] = 42; if (i) ASSERT_LT(p[i - 1], p[i]); } - EXPECT_DEATH(cache.Allocate(a, Class2), "The process has exhausted"); + EXPECT_EQ(cache.Allocate(a, Class2), nullptr); cache.Deallocate(a, Class2, p[0]); cache.Drain(a); ASSERT_EQ(p[6][Size2 - 1], 42); a->TestOnlyUnmap(); delete a; } #endif TEST(SanitizerCommon, TwoLevelByteMap) { const u64 kSize1 = 1 << 6, kSize2 = 1 << 12; const u64 n = kSize1 * kSize2; TwoLevelByteMap m; m.TestOnlyInit(); for (u64 i = 0; i < n; i += 7) { m.set(i, (i % 100) + 1); } for (u64 j = 0; j < n; j++) { if (j % 7) EXPECT_EQ(m[j], 0); else EXPECT_EQ(m[j], (j % 100) + 1); } m.TestOnlyUnmap(); } typedef TwoLevelByteMap<1 << 12, 1 << 13, TestMapUnmapCallback> TestByteMap; struct TestByteMapParam { TestByteMap *m; size_t shard; size_t num_shards; }; void *TwoLevelByteMapUserThread(void *param) { TestByteMapParam *p = (TestByteMapParam*)param; for (size_t i = p->shard; i < p->m->size(); i += p->num_shards) { size_t val = (i % 100) + 1; p->m->set(i, val); EXPECT_EQ((*p->m)[i], val); } return 0; } TEST(SanitizerCommon, ThreadedTwoLevelByteMap) { TestByteMap m; m.TestOnlyInit(); TestMapUnmapCallback::map_count = 0; TestMapUnmapCallback::unmap_count = 0; static const int kNumThreads = 4; pthread_t t[kNumThreads]; TestByteMapParam p[kNumThreads]; for (int i = 0; i < kNumThreads; i++) { p[i].m = &m; p[i].shard = i; p[i].num_shards = kNumThreads; PTHREAD_CREATE(&t[i], 0, TwoLevelByteMapUserThread, &p[i]); } for (int i = 0; i < kNumThreads; i++) { PTHREAD_JOIN(t[i], 0); } EXPECT_EQ((uptr)TestMapUnmapCallback::map_count, m.size1()); EXPECT_EQ((uptr)TestMapUnmapCallback::unmap_count, 0UL); m.TestOnlyUnmap(); EXPECT_EQ((uptr)TestMapUnmapCallback::map_count, m.size1()); EXPECT_EQ((uptr)TestMapUnmapCallback::unmap_count, m.size1()); } #endif // #if !SANITIZER_DEBUG Index: vendor/compiler-rt/dist/lib/scudo/scudo_allocator.cpp =================================================================== --- vendor/compiler-rt/dist/lib/scudo/scudo_allocator.cpp (revision 320536) +++ vendor/compiler-rt/dist/lib/scudo/scudo_allocator.cpp (revision 320537) @@ -1,721 +1,723 @@ //===-- scudo_allocator.cpp -------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// Scudo Hardened Allocator implementation. /// It uses the sanitizer_common allocator as a base and aims at mitigating /// heap corruption vulnerabilities. It provides a checksum-guarded chunk /// header, a delayed free list, and additional sanity checks. /// //===----------------------------------------------------------------------===// #include "scudo_allocator.h" #include "scudo_crc32.h" #include "scudo_tls.h" #include "scudo_utils.h" #include "sanitizer_common/sanitizer_allocator_interface.h" #include "sanitizer_common/sanitizer_quarantine.h" -#include -#include +#include #include namespace __scudo { // Global static cookie, initialized at start-up. static uptr Cookie; // We default to software CRC32 if the alternatives are not supported, either // at compilation or at runtime. static atomic_uint8_t HashAlgorithm = { CRC32Software }; INLINE u32 computeCRC32(uptr Crc, uptr Value, uptr *Array, uptr ArraySize) { // If the hardware CRC32 feature is defined here, it was enabled everywhere, // as opposed to only for scudo_crc32.cpp. This means that other hardware // specific instructions were likely emitted at other places, and as a // result there is no reason to not use it here. #if defined(__SSE4_2__) || defined(__ARM_FEATURE_CRC32) Crc = CRC32_INTRINSIC(Crc, Value); for (uptr i = 0; i < ArraySize; i++) Crc = CRC32_INTRINSIC(Crc, Array[i]); return Crc; #else if (atomic_load_relaxed(&HashAlgorithm) == CRC32Hardware) { Crc = computeHardwareCRC32(Crc, Value); for (uptr i = 0; i < ArraySize; i++) Crc = computeHardwareCRC32(Crc, Array[i]); return Crc; } Crc = computeSoftwareCRC32(Crc, Value); for (uptr i = 0; i < ArraySize; i++) Crc = computeSoftwareCRC32(Crc, Array[i]); return Crc; #endif // defined(__SSE4_2__) || defined(__ARM_FEATURE_CRC32) } static ScudoBackendAllocator &getBackendAllocator(); struct ScudoChunk : UnpackedHeader { // We can't use the offset member of the chunk itself, as we would double // fetch it without any warranty that it wouldn't have been tampered. To // prevent this, we work with a local copy of the header. void *getAllocBeg(UnpackedHeader *Header) { return reinterpret_cast( reinterpret_cast(this) - (Header->Offset << MinAlignmentLog)); } // Returns the usable size for a chunk, meaning the amount of bytes from the // beginning of the user data to the end of the backend allocated chunk. uptr getUsableSize(UnpackedHeader *Header) { uptr Size = getBackendAllocator().GetActuallyAllocatedSize(getAllocBeg(Header), Header->FromPrimary); if (Size == 0) return 0; return Size - AlignedChunkHeaderSize - (Header->Offset << MinAlignmentLog); } // Compute the checksum of the Chunk pointer and its ChunkHeader. u16 computeChecksum(UnpackedHeader *Header) const { UnpackedHeader ZeroChecksumHeader = *Header; ZeroChecksumHeader.Checksum = 0; uptr HeaderHolder[sizeof(UnpackedHeader) / sizeof(uptr)]; memcpy(&HeaderHolder, &ZeroChecksumHeader, sizeof(HeaderHolder)); u32 Crc = computeCRC32(Cookie, reinterpret_cast(this), HeaderHolder, ARRAY_SIZE(HeaderHolder)); return static_cast(Crc); } // Checks the validity of a chunk by verifying its checksum. It doesn't // incur termination in the event of an invalid chunk. bool isValid() { UnpackedHeader NewUnpackedHeader; const AtomicPackedHeader *AtomicHeader = reinterpret_cast(this); PackedHeader NewPackedHeader = atomic_load_relaxed(AtomicHeader); NewUnpackedHeader = bit_cast(NewPackedHeader); return (NewUnpackedHeader.Checksum == computeChecksum(&NewUnpackedHeader)); } // Nulls out a chunk header. When returning the chunk to the backend, there // is no need to store a valid ChunkAvailable header, as this would be // computationally expensive. Zeroing out serves the same purpose by making // the header invalid. In the extremely rare event where 0 would be a valid // checksum for the chunk, the state of the chunk is ChunkAvailable anyway. COMPILER_CHECK(ChunkAvailable == 0); void eraseHeader() { PackedHeader NullPackedHeader = 0; AtomicPackedHeader *AtomicHeader = reinterpret_cast(this); atomic_store_relaxed(AtomicHeader, NullPackedHeader); } // Loads and unpacks the header, verifying the checksum in the process. void loadHeader(UnpackedHeader *NewUnpackedHeader) const { const AtomicPackedHeader *AtomicHeader = reinterpret_cast(this); PackedHeader NewPackedHeader = atomic_load_relaxed(AtomicHeader); *NewUnpackedHeader = bit_cast(NewPackedHeader); if (UNLIKELY(NewUnpackedHeader->Checksum != computeChecksum(NewUnpackedHeader))) { dieWithMessage("ERROR: corrupted chunk header at address %p\n", this); } } // Packs and stores the header, computing the checksum in the process. void storeHeader(UnpackedHeader *NewUnpackedHeader) { NewUnpackedHeader->Checksum = computeChecksum(NewUnpackedHeader); PackedHeader NewPackedHeader = bit_cast(*NewUnpackedHeader); AtomicPackedHeader *AtomicHeader = reinterpret_cast(this); atomic_store_relaxed(AtomicHeader, NewPackedHeader); } // Packs and stores the header, computing the checksum in the process. We // compare the current header with the expected provided one to ensure that // we are not being raced by a corruption occurring in another thread. void compareExchangeHeader(UnpackedHeader *NewUnpackedHeader, UnpackedHeader *OldUnpackedHeader) { NewUnpackedHeader->Checksum = computeChecksum(NewUnpackedHeader); PackedHeader NewPackedHeader = bit_cast(*NewUnpackedHeader); PackedHeader OldPackedHeader = bit_cast(*OldUnpackedHeader); AtomicPackedHeader *AtomicHeader = reinterpret_cast(this); if (UNLIKELY(!atomic_compare_exchange_strong(AtomicHeader, &OldPackedHeader, NewPackedHeader, memory_order_relaxed))) { dieWithMessage("ERROR: race on chunk header at address %p\n", this); } } }; ScudoChunk *getScudoChunk(uptr UserBeg) { return reinterpret_cast(UserBeg - AlignedChunkHeaderSize); } struct AllocatorOptions { u32 QuarantineSizeMb; u32 ThreadLocalQuarantineSizeKb; bool MayReturnNull; s32 ReleaseToOSIntervalMs; bool DeallocationTypeMismatch; bool DeleteSizeMismatch; bool ZeroContents; void setFrom(const Flags *f, const CommonFlags *cf); void copyTo(Flags *f, CommonFlags *cf) const; }; void AllocatorOptions::setFrom(const Flags *f, const CommonFlags *cf) { MayReturnNull = cf->allocator_may_return_null; ReleaseToOSIntervalMs = cf->allocator_release_to_os_interval_ms; QuarantineSizeMb = f->QuarantineSizeMb; ThreadLocalQuarantineSizeKb = f->ThreadLocalQuarantineSizeKb; DeallocationTypeMismatch = f->DeallocationTypeMismatch; DeleteSizeMismatch = f->DeleteSizeMismatch; ZeroContents = f->ZeroContents; } void AllocatorOptions::copyTo(Flags *f, CommonFlags *cf) const { cf->allocator_may_return_null = MayReturnNull; cf->allocator_release_to_os_interval_ms = ReleaseToOSIntervalMs; f->QuarantineSizeMb = QuarantineSizeMb; f->ThreadLocalQuarantineSizeKb = ThreadLocalQuarantineSizeKb; f->DeallocationTypeMismatch = DeallocationTypeMismatch; f->DeleteSizeMismatch = DeleteSizeMismatch; f->ZeroContents = ZeroContents; } static void initScudoInternal(const AllocatorOptions &Options); static bool ScudoInitIsRunning = false; void initScudo() { SanitizerToolName = "Scudo"; CHECK(!ScudoInitIsRunning && "Scudo init calls itself!"); ScudoInitIsRunning = true; // Check if hardware CRC32 is supported in the binary and by the platform, if // so, opt for the CRC32 hardware version of the checksum. if (computeHardwareCRC32 && testCPUFeature(CRC32CPUFeature)) atomic_store_relaxed(&HashAlgorithm, CRC32Hardware); initFlags(); AllocatorOptions Options; Options.setFrom(getFlags(), common_flags()); initScudoInternal(Options); // TODO(kostyak): determine if MaybeStartBackgroudThread could be of some use. ScudoInitIsRunning = false; } struct QuarantineCallback { explicit QuarantineCallback(AllocatorCache *Cache) : Cache_(Cache) {} // Chunk recycling function, returns a quarantined chunk to the backend, // first making sure it hasn't been tampered with. void Recycle(ScudoChunk *Chunk) { UnpackedHeader Header; Chunk->loadHeader(&Header); if (UNLIKELY(Header.State != ChunkQuarantine)) { dieWithMessage("ERROR: invalid chunk state when recycling address %p\n", Chunk); } Chunk->eraseHeader(); void *Ptr = Chunk->getAllocBeg(&Header); getBackendAllocator().Deallocate(Cache_, Ptr, Header.FromPrimary); } // Internal quarantine allocation and deallocation functions. We first check // that the batches are indeed serviced by the Primary. // TODO(kostyak): figure out the best way to protect the batches. COMPILER_CHECK(sizeof(QuarantineBatch) < SizeClassMap::kMaxSize); void *Allocate(uptr Size) { return getBackendAllocator().Allocate(Cache_, Size, MinAlignment, true); } void Deallocate(void *Ptr) { getBackendAllocator().Deallocate(Cache_, Ptr, true); } AllocatorCache *Cache_; }; typedef Quarantine ScudoQuarantine; typedef ScudoQuarantine::Cache ScudoQuarantineCache; COMPILER_CHECK(sizeof(ScudoQuarantineCache) <= sizeof(ScudoThreadContext::QuarantineCachePlaceHolder)); AllocatorCache *getAllocatorCache(ScudoThreadContext *ThreadContext) { return &ThreadContext->Cache; } ScudoQuarantineCache *getQuarantineCache(ScudoThreadContext *ThreadContext) { return reinterpret_cast< ScudoQuarantineCache *>(ThreadContext->QuarantineCachePlaceHolder); } Xorshift128Plus *getPrng(ScudoThreadContext *ThreadContext) { return &ThreadContext->Prng; } struct ScudoAllocator { static const uptr MaxAllowedMallocSize = FIRST_32_SECOND_64(2UL << 30, 1ULL << 40); typedef ReturnNullOrDieOnFailure FailureHandler; ScudoBackendAllocator BackendAllocator; ScudoQuarantine AllocatorQuarantine; // The fallback caches are used when the thread local caches have been // 'detroyed' on thread tear-down. They are protected by a Mutex as they can // be accessed by different threads. StaticSpinMutex FallbackMutex; AllocatorCache FallbackAllocatorCache; ScudoQuarantineCache FallbackQuarantineCache; Xorshift128Plus FallbackPrng; bool DeallocationTypeMismatch; bool ZeroContents; bool DeleteSizeMismatch; explicit ScudoAllocator(LinkerInitialized) : AllocatorQuarantine(LINKER_INITIALIZED), FallbackQuarantineCache(LINKER_INITIALIZED) {} void init(const AllocatorOptions &Options) { // Verify that the header offset field can hold the maximum offset. In the // case of the Secondary allocator, it takes care of alignment and the // offset will always be 0. In the case of the Primary, the worst case // scenario happens in the last size class, when the backend allocation // would already be aligned on the requested alignment, which would happen // to be the maximum alignment that would fit in that size class. As a // result, the maximum offset will be at most the maximum alignment for the // last size class minus the header size, in multiples of MinAlignment. UnpackedHeader Header = {}; uptr MaxPrimaryAlignment = 1 << MostSignificantSetBitIndex( SizeClassMap::kMaxSize - MinAlignment); uptr MaxOffset = (MaxPrimaryAlignment - AlignedChunkHeaderSize) >> MinAlignmentLog; Header.Offset = MaxOffset; if (Header.Offset != MaxOffset) { dieWithMessage("ERROR: the maximum possible offset doesn't fit in the " "header\n"); } // Verify that we can fit the maximum size or amount of unused bytes in the // header. Given that the Secondary fits the allocation to a page, the worst // case scenario happens in the Primary. It will depend on the second to // last and last class sizes, as well as the dynamic base for the Primary. // The following is an over-approximation that works for our needs. uptr MaxSizeOrUnusedBytes = SizeClassMap::kMaxSize - 1; Header.SizeOrUnusedBytes = MaxSizeOrUnusedBytes; if (Header.SizeOrUnusedBytes != MaxSizeOrUnusedBytes) { dieWithMessage("ERROR: the maximum possible unused bytes doesn't fit in " "the header\n"); } DeallocationTypeMismatch = Options.DeallocationTypeMismatch; DeleteSizeMismatch = Options.DeleteSizeMismatch; ZeroContents = Options.ZeroContents; SetAllocatorMayReturnNull(Options.MayReturnNull); BackendAllocator.Init(Options.ReleaseToOSIntervalMs); AllocatorQuarantine.Init( static_cast(Options.QuarantineSizeMb) << 20, static_cast(Options.ThreadLocalQuarantineSizeKb) << 10); BackendAllocator.InitCache(&FallbackAllocatorCache); FallbackPrng.initFromURandom(); Cookie = FallbackPrng.getNext(); } // Helper function that checks for a valid Scudo chunk. nullptr isn't. bool isValidPointer(const void *UserPtr) { initThreadMaybe(); - if (!UserPtr) + if (UNLIKELY(!UserPtr)) return false; uptr UserBeg = reinterpret_cast(UserPtr); if (!IsAligned(UserBeg, MinAlignment)) return false; return getScudoChunk(UserBeg)->isValid(); } // Allocates a chunk. void *allocate(uptr Size, uptr Alignment, AllocType Type, bool ForceZeroContents = false) { initThreadMaybe(); - if (UNLIKELY(!IsPowerOfTwo(Alignment))) { - dieWithMessage("ERROR: alignment is not a power of 2\n"); - } - if (Alignment > MaxAlignment) + if (UNLIKELY(Alignment > MaxAlignment)) return FailureHandler::OnBadRequest(); - if (Alignment < MinAlignment) + if (UNLIKELY(Alignment < MinAlignment)) Alignment = MinAlignment; - if (Size >= MaxAllowedMallocSize) + if (UNLIKELY(Size >= MaxAllowedMallocSize)) return FailureHandler::OnBadRequest(); - if (Size == 0) + if (UNLIKELY(Size == 0)) Size = 1; uptr NeededSize = RoundUpTo(Size, MinAlignment) + AlignedChunkHeaderSize; uptr AlignedSize = (Alignment > MinAlignment) ? NeededSize + (Alignment - AlignedChunkHeaderSize) : NeededSize; - if (AlignedSize >= MaxAllowedMallocSize) + if (UNLIKELY(AlignedSize >= MaxAllowedMallocSize)) return FailureHandler::OnBadRequest(); // Primary and Secondary backed allocations have a different treatment. We // deal with alignment requirements of Primary serviced allocations here, // but the Secondary will take care of its own alignment needs. bool FromPrimary = PrimaryAllocator::CanAllocate(AlignedSize, MinAlignment); void *Ptr; uptr Salt; uptr AllocationSize = FromPrimary ? AlignedSize : NeededSize; uptr AllocationAlignment = FromPrimary ? MinAlignment : Alignment; ScudoThreadContext *ThreadContext = getThreadContextAndLock(); if (LIKELY(ThreadContext)) { Salt = getPrng(ThreadContext)->getNext(); Ptr = BackendAllocator.Allocate(getAllocatorCache(ThreadContext), AllocationSize, AllocationAlignment, FromPrimary); ThreadContext->unlock(); } else { SpinMutexLock l(&FallbackMutex); Salt = FallbackPrng.getNext(); Ptr = BackendAllocator.Allocate(&FallbackAllocatorCache, AllocationSize, AllocationAlignment, FromPrimary); } - if (!Ptr) + if (UNLIKELY(!Ptr)) return FailureHandler::OnOOM(); // If requested, we will zero out the entire contents of the returned chunk. if ((ForceZeroContents || ZeroContents) && FromPrimary) memset(Ptr, 0, BackendAllocator.GetActuallyAllocatedSize(Ptr, FromPrimary)); UnpackedHeader Header = {}; uptr AllocBeg = reinterpret_cast(Ptr); uptr UserBeg = AllocBeg + AlignedChunkHeaderSize; - if (!IsAligned(UserBeg, Alignment)) { + if (UNLIKELY(!IsAligned(UserBeg, Alignment))) { // Since the Secondary takes care of alignment, a non-aligned pointer // means it is from the Primary. It is also the only case where the offset // field of the header would be non-zero. CHECK(FromPrimary); UserBeg = RoundUpTo(UserBeg, Alignment); uptr Offset = UserBeg - AlignedChunkHeaderSize - AllocBeg; Header.Offset = Offset >> MinAlignmentLog; } CHECK_LE(UserBeg + Size, AllocBeg + AllocationSize); Header.State = ChunkAllocated; Header.AllocType = Type; if (FromPrimary) { Header.FromPrimary = FromPrimary; Header.SizeOrUnusedBytes = Size; } else { // The secondary fits the allocations to a page, so the amount of unused // bytes is the difference between the end of the user allocation and the // next page boundary. uptr PageSize = GetPageSizeCached(); uptr TrailingBytes = (UserBeg + Size) & (PageSize - 1); if (TrailingBytes) Header.SizeOrUnusedBytes = PageSize - TrailingBytes; } Header.Salt = static_cast(Salt); getScudoChunk(UserBeg)->storeHeader(&Header); void *UserPtr = reinterpret_cast(UserBeg); // if (&__sanitizer_malloc_hook) __sanitizer_malloc_hook(UserPtr, Size); return UserPtr; } // Place a chunk in the quarantine. In the event of a zero-sized quarantine, // we directly deallocate the chunk, otherwise the flow would lead to the // chunk being loaded (and checked) twice, and stored (and checksummed) once, // with no additional security value. void quarantineOrDeallocateChunk(ScudoChunk *Chunk, UnpackedHeader *Header, uptr Size) { bool FromPrimary = Header->FromPrimary; bool BypassQuarantine = (AllocatorQuarantine.GetCacheSize() == 0); if (BypassQuarantine) { Chunk->eraseHeader(); void *Ptr = Chunk->getAllocBeg(Header); ScudoThreadContext *ThreadContext = getThreadContextAndLock(); if (LIKELY(ThreadContext)) { getBackendAllocator().Deallocate(getAllocatorCache(ThreadContext), Ptr, FromPrimary); ThreadContext->unlock(); } else { SpinMutexLock Lock(&FallbackMutex); getBackendAllocator().Deallocate(&FallbackAllocatorCache, Ptr, FromPrimary); } } else { UnpackedHeader NewHeader = *Header; NewHeader.State = ChunkQuarantine; Chunk->compareExchangeHeader(&NewHeader, Header); ScudoThreadContext *ThreadContext = getThreadContextAndLock(); if (LIKELY(ThreadContext)) { AllocatorQuarantine.Put(getQuarantineCache(ThreadContext), QuarantineCallback( getAllocatorCache(ThreadContext)), Chunk, Size); ThreadContext->unlock(); } else { SpinMutexLock l(&FallbackMutex); AllocatorQuarantine.Put(&FallbackQuarantineCache, QuarantineCallback(&FallbackAllocatorCache), Chunk, Size); } } } // Deallocates a Chunk, which means adding it to the delayed free list (or // Quarantine). void deallocate(void *UserPtr, uptr DeleteSize, AllocType Type) { initThreadMaybe(); // if (&__sanitizer_free_hook) __sanitizer_free_hook(UserPtr); - if (!UserPtr) + if (UNLIKELY(!UserPtr)) return; uptr UserBeg = reinterpret_cast(UserPtr); if (UNLIKELY(!IsAligned(UserBeg, MinAlignment))) { dieWithMessage("ERROR: attempted to deallocate a chunk not properly " "aligned at address %p\n", UserPtr); } ScudoChunk *Chunk = getScudoChunk(UserBeg); UnpackedHeader OldHeader; Chunk->loadHeader(&OldHeader); if (UNLIKELY(OldHeader.State != ChunkAllocated)) { dieWithMessage("ERROR: invalid chunk state when deallocating address " "%p\n", UserPtr); } if (DeallocationTypeMismatch) { // The deallocation type has to match the allocation one. if (OldHeader.AllocType != Type) { // With the exception of memalign'd Chunks, that can be still be free'd. if (OldHeader.AllocType != FromMemalign || Type != FromMalloc) { dieWithMessage("ERROR: allocation type mismatch on address %p\n", UserPtr); } } } uptr Size = OldHeader.FromPrimary ? OldHeader.SizeOrUnusedBytes : Chunk->getUsableSize(&OldHeader) - OldHeader.SizeOrUnusedBytes; if (DeleteSizeMismatch) { if (DeleteSize && DeleteSize != Size) { dieWithMessage("ERROR: invalid sized delete on chunk at address %p\n", UserPtr); } } // If a small memory amount was allocated with a larger alignment, we want // to take that into account. Otherwise the Quarantine would be filled with // tiny chunks, taking a lot of VA memory. This is an approximation of the // usable size, that allows us to not call GetActuallyAllocatedSize. uptr LiableSize = Size + (OldHeader.Offset << MinAlignment); quarantineOrDeallocateChunk(Chunk, &OldHeader, LiableSize); } // Reallocates a chunk. We can save on a new allocation if the new requested // size still fits in the chunk. void *reallocate(void *OldPtr, uptr NewSize) { initThreadMaybe(); uptr UserBeg = reinterpret_cast(OldPtr); if (UNLIKELY(!IsAligned(UserBeg, MinAlignment))) { dieWithMessage("ERROR: attempted to reallocate a chunk not properly " "aligned at address %p\n", OldPtr); } ScudoChunk *Chunk = getScudoChunk(UserBeg); UnpackedHeader OldHeader; Chunk->loadHeader(&OldHeader); if (UNLIKELY(OldHeader.State != ChunkAllocated)) { dieWithMessage("ERROR: invalid chunk state when reallocating address " "%p\n", OldPtr); } if (UNLIKELY(OldHeader.AllocType != FromMalloc)) { dieWithMessage("ERROR: invalid chunk type when reallocating address %p\n", OldPtr); } uptr UsableSize = Chunk->getUsableSize(&OldHeader); // The new size still fits in the current chunk, and the size difference // is reasonable. if (NewSize <= UsableSize && (UsableSize - NewSize) < (SizeClassMap::kMaxSize / 2)) { UnpackedHeader NewHeader = OldHeader; NewHeader.SizeOrUnusedBytes = OldHeader.FromPrimary ? NewSize : UsableSize - NewSize; Chunk->compareExchangeHeader(&NewHeader, &OldHeader); return OldPtr; } // Otherwise, we have to allocate a new chunk and copy the contents of the // old one. void *NewPtr = allocate(NewSize, MinAlignment, FromMalloc); if (NewPtr) { uptr OldSize = OldHeader.FromPrimary ? OldHeader.SizeOrUnusedBytes : UsableSize - OldHeader.SizeOrUnusedBytes; memcpy(NewPtr, OldPtr, Min(NewSize, OldSize)); quarantineOrDeallocateChunk(Chunk, &OldHeader, UsableSize); } return NewPtr; } // Helper function that returns the actual usable size of a chunk. uptr getUsableSize(const void *Ptr) { initThreadMaybe(); - if (!Ptr) + if (UNLIKELY(!Ptr)) return 0; uptr UserBeg = reinterpret_cast(Ptr); ScudoChunk *Chunk = getScudoChunk(UserBeg); UnpackedHeader Header; Chunk->loadHeader(&Header); // Getting the usable size of a chunk only makes sense if it's allocated. if (UNLIKELY(Header.State != ChunkAllocated)) { dieWithMessage("ERROR: invalid chunk state when sizing address %p\n", Ptr); } return Chunk->getUsableSize(&Header); } void *calloc(uptr NMemB, uptr Size) { initThreadMaybe(); - uptr Total = NMemB * Size; - if (Size != 0 && Total / Size != NMemB) // Overflow check + if (CheckForCallocOverflow(NMemB, Size)) return FailureHandler::OnBadRequest(); - return allocate(Total, MinAlignment, FromMalloc, true); + return allocate(NMemB * Size, MinAlignment, FromMalloc, true); } void commitBack(ScudoThreadContext *ThreadContext) { AllocatorCache *Cache = getAllocatorCache(ThreadContext); AllocatorQuarantine.Drain(getQuarantineCache(ThreadContext), QuarantineCallback(Cache)); BackendAllocator.DestroyCache(Cache); } uptr getStats(AllocatorStat StatType) { initThreadMaybe(); uptr stats[AllocatorStatCount]; BackendAllocator.GetStats(stats); return stats[StatType]; } }; static ScudoAllocator Instance(LINKER_INITIALIZED); static ScudoBackendAllocator &getBackendAllocator() { return Instance.BackendAllocator; } static void initScudoInternal(const AllocatorOptions &Options) { Instance.init(Options); } void ScudoThreadContext::init() { getBackendAllocator().InitCache(&Cache); Prng.initFromURandom(); memset(QuarantineCachePlaceHolder, 0, sizeof(QuarantineCachePlaceHolder)); } void ScudoThreadContext::commitBack() { Instance.commitBack(this); } void *scudoMalloc(uptr Size, AllocType Type) { return Instance.allocate(Size, MinAlignment, Type); } void scudoFree(void *Ptr, AllocType Type) { Instance.deallocate(Ptr, 0, Type); } void scudoSizedFree(void *Ptr, uptr Size, AllocType Type) { Instance.deallocate(Ptr, Size, Type); } void *scudoRealloc(void *Ptr, uptr Size) { if (!Ptr) return Instance.allocate(Size, MinAlignment, FromMalloc); if (Size == 0) { Instance.deallocate(Ptr, 0, FromMalloc); return nullptr; } return Instance.reallocate(Ptr, Size); } void *scudoCalloc(uptr NMemB, uptr Size) { return Instance.calloc(NMemB, Size); } void *scudoValloc(uptr Size) { return Instance.allocate(Size, GetPageSizeCached(), FromMemalign); } -void *scudoMemalign(uptr Alignment, uptr Size) { - return Instance.allocate(Size, Alignment, FromMemalign); -} - void *scudoPvalloc(uptr Size) { uptr PageSize = GetPageSizeCached(); Size = RoundUpTo(Size, PageSize); if (Size == 0) { // pvalloc(0) should allocate one page. Size = PageSize; } return Instance.allocate(Size, PageSize, FromMemalign); } +void *scudoMemalign(uptr Alignment, uptr Size) { + if (UNLIKELY(!IsPowerOfTwo(Alignment))) + return ScudoAllocator::FailureHandler::OnBadRequest(); + return Instance.allocate(Size, Alignment, FromMemalign); +} + int scudoPosixMemalign(void **MemPtr, uptr Alignment, uptr Size) { + if (UNLIKELY(!IsPowerOfTwo(Alignment) || (Alignment % sizeof(void *)) != 0)) { + *MemPtr = ScudoAllocator::FailureHandler::OnBadRequest(); + return EINVAL; + } *MemPtr = Instance.allocate(Size, Alignment, FromMemalign); + if (!*MemPtr) + return ENOMEM; return 0; } void *scudoAlignedAlloc(uptr Alignment, uptr Size) { - // size must be a multiple of the alignment. To avoid a division, we first - // make sure that alignment is a power of 2. - CHECK(IsPowerOfTwo(Alignment)); - CHECK_EQ((Size & (Alignment - 1)), 0); + // Alignment must be a power of 2, Size must be a multiple of Alignment. + if (UNLIKELY(!IsPowerOfTwo(Alignment) || (Size & (Alignment - 1)) != 0)) + return ScudoAllocator::FailureHandler::OnBadRequest(); return Instance.allocate(Size, Alignment, FromMalloc); } uptr scudoMallocUsableSize(void *Ptr) { return Instance.getUsableSize(Ptr); } } // namespace __scudo using namespace __scudo; // MallocExtension helper functions uptr __sanitizer_get_current_allocated_bytes() { return Instance.getStats(AllocatorStatAllocated); } uptr __sanitizer_get_heap_size() { return Instance.getStats(AllocatorStatMapped); } uptr __sanitizer_get_free_bytes() { return 1; } uptr __sanitizer_get_unmapped_bytes() { return 1; } uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; } int __sanitizer_get_ownership(const void *Ptr) { return Instance.isValidPointer(Ptr); } uptr __sanitizer_get_allocated_size(const void *Ptr) { return Instance.getUsableSize(Ptr); } Index: vendor/compiler-rt/dist/lib/scudo/scudo_allocator.h =================================================================== --- vendor/compiler-rt/dist/lib/scudo/scudo_allocator.h (revision 320536) +++ vendor/compiler-rt/dist/lib/scudo/scudo_allocator.h (revision 320537) @@ -1,143 +1,148 @@ //===-- scudo_allocator.h ---------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// Header for scudo_allocator.cpp. /// //===----------------------------------------------------------------------===// #ifndef SCUDO_ALLOCATOR_H_ #define SCUDO_ALLOCATOR_H_ #include "scudo_flags.h" #include "sanitizer_common/sanitizer_allocator.h" #if !SANITIZER_LINUX # error "The Scudo hardened allocator is currently only supported on Linux." #endif namespace __scudo { enum AllocType : u8 { FromMalloc = 0, // Memory block came from malloc, realloc, calloc, etc. FromNew = 1, // Memory block came from operator new. FromNewArray = 2, // Memory block came from operator new []. FromMemalign = 3, // Memory block came from memalign, posix_memalign, etc. }; enum ChunkState : u8 { ChunkAvailable = 0, ChunkAllocated = 1, ChunkQuarantine = 2 }; // Our header requires 64 bits of storage. Having the offset saves us from // using functions such as GetBlockBegin, that is fairly costly. Our first // implementation used the MetaData as well, which offers the advantage of // being stored away from the chunk itself, but accessing it was costly as // well. The header will be atomically loaded and stored. typedef u64 PackedHeader; struct UnpackedHeader { u64 Checksum : 16; u64 SizeOrUnusedBytes : 19; // Size for Primary backed allocations, amount of // unused bytes in the chunk for Secondary ones. u64 FromPrimary : 1; u64 State : 2; // available, allocated, or quarantined u64 AllocType : 2; // malloc, new, new[], or memalign u64 Offset : 16; // Offset from the beginning of the backend // allocation to the beginning of the chunk // itself, in multiples of MinAlignment. See // comment about its maximum value and in init(). u64 Salt : 8; }; typedef atomic_uint64_t AtomicPackedHeader; COMPILER_CHECK(sizeof(UnpackedHeader) == sizeof(PackedHeader)); // Minimum alignment of 8 bytes for 32-bit, 16 for 64-bit const uptr MinAlignmentLog = FIRST_32_SECOND_64(3, 4); const uptr MaxAlignmentLog = 24; // 16 MB const uptr MinAlignment = 1 << MinAlignmentLog; const uptr MaxAlignment = 1 << MaxAlignmentLog; const uptr ChunkHeaderSize = sizeof(PackedHeader); const uptr AlignedChunkHeaderSize = (ChunkHeaderSize + MinAlignment - 1) & ~(MinAlignment - 1); #if SANITIZER_CAN_USE_ALLOCATOR64 const uptr AllocatorSpace = ~0ULL; # if defined(__aarch64__) && SANITIZER_ANDROID const uptr AllocatorSize = 0x4000000000ULL; // 256G. # elif defined(__aarch64__) const uptr AllocatorSize = 0x10000000000ULL; // 1T. # else const uptr AllocatorSize = 0x40000000000ULL; // 4T. # endif typedef DefaultSizeClassMap SizeClassMap; struct AP64 { static const uptr kSpaceBeg = AllocatorSpace; static const uptr kSpaceSize = AllocatorSize; static const uptr kMetadataSize = 0; typedef __scudo::SizeClassMap SizeClassMap; typedef NoOpMapUnmapCallback MapUnmapCallback; static const uptr kFlags = SizeClassAllocator64FlagMasks::kRandomShuffleChunks; }; typedef SizeClassAllocator64 PrimaryAllocator; #else // Currently, the 32-bit Sanitizer allocator has not yet benefited from all the // security improvements brought to the 64-bit one. This makes the 32-bit // version of Scudo slightly less toughened. static const uptr RegionSizeLog = 20; static const uptr NumRegions = SANITIZER_MMAP_RANGE_SIZE >> RegionSizeLog; # if SANITIZER_WORDSIZE == 32 typedef FlatByteMap ByteMap; # elif SANITIZER_WORDSIZE == 64 typedef TwoLevelByteMap<(NumRegions >> 12), 1 << 12> ByteMap; # endif // SANITIZER_WORDSIZE typedef DefaultSizeClassMap SizeClassMap; struct AP32 { static const uptr kSpaceBeg = 0; static const u64 kSpaceSize = SANITIZER_MMAP_RANGE_SIZE; static const uptr kMetadataSize = 0; typedef __scudo::SizeClassMap SizeClassMap; static const uptr kRegionSizeLog = RegionSizeLog; typedef __scudo::ByteMap ByteMap; typedef NoOpMapUnmapCallback MapUnmapCallback; static const uptr kFlags = SizeClassAllocator32FlagMasks::kRandomShuffleChunks; }; typedef SizeClassAllocator32 PrimaryAllocator; #endif // SANITIZER_CAN_USE_ALLOCATOR64 +// __sanitizer::RoundUp has a CHECK that is extraneous for us. Use our own. +INLINE uptr RoundUpTo(uptr Size, uptr Boundary) { + return (Size + Boundary - 1) & ~(Boundary - 1); +} + #include "scudo_allocator_secondary.h" #include "scudo_allocator_combined.h" typedef SizeClassAllocatorLocalCache AllocatorCache; typedef ScudoLargeMmapAllocator SecondaryAllocator; typedef ScudoCombinedAllocator ScudoBackendAllocator; void initScudo(); void *scudoMalloc(uptr Size, AllocType Type); void scudoFree(void *Ptr, AllocType Type); void scudoSizedFree(void *Ptr, uptr Size, AllocType Type); void *scudoRealloc(void *Ptr, uptr Size); void *scudoCalloc(uptr NMemB, uptr Size); void *scudoMemalign(uptr Alignment, uptr Size); void *scudoValloc(uptr Size); void *scudoPvalloc(uptr Size); int scudoPosixMemalign(void **MemPtr, uptr Alignment, uptr Size); void *scudoAlignedAlloc(uptr Alignment, uptr Size); uptr scudoMallocUsableSize(void *Ptr); } // namespace __scudo #endif // SCUDO_ALLOCATOR_H_ Index: vendor/compiler-rt/dist/lib/scudo/scudo_allocator_combined.h =================================================================== --- vendor/compiler-rt/dist/lib/scudo/scudo_allocator_combined.h (revision 320536) +++ vendor/compiler-rt/dist/lib/scudo/scudo_allocator_combined.h (revision 320537) @@ -1,70 +1,70 @@ //===-- scudo_allocator_combined.h ------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// Scudo Combined Allocator, dispatches allocation & deallocation requests to /// the Primary or the Secondary backend allocators. /// //===----------------------------------------------------------------------===// #ifndef SCUDO_ALLOCATOR_COMBINED_H_ #define SCUDO_ALLOCATOR_COMBINED_H_ #ifndef SCUDO_ALLOCATOR_H_ #error "This file must be included inside scudo_allocator.h." #endif template class ScudoCombinedAllocator { public: void Init(s32 ReleaseToOSIntervalMs) { Primary.Init(ReleaseToOSIntervalMs); Secondary.Init(); Stats.Init(); } void *Allocate(AllocatorCache *Cache, uptr Size, uptr Alignment, bool FromPrimary) { if (FromPrimary) return Cache->Allocate(&Primary, Primary.ClassID(Size)); return Secondary.Allocate(&Stats, Size, Alignment); } void Deallocate(AllocatorCache *Cache, void *Ptr, bool FromPrimary) { if (FromPrimary) Cache->Deallocate(&Primary, Primary.GetSizeClass(Ptr), Ptr); else Secondary.Deallocate(&Stats, Ptr); } uptr GetActuallyAllocatedSize(void *Ptr, bool FromPrimary) { if (FromPrimary) - return Primary.GetActuallyAllocatedSize(Ptr); + return PrimaryAllocator::ClassIdToSize(Primary.GetSizeClass(Ptr)); return Secondary.GetActuallyAllocatedSize(Ptr); } void InitCache(AllocatorCache *Cache) { Cache->Init(&Stats); } void DestroyCache(AllocatorCache *Cache) { Cache->Destroy(&Primary, &Stats); } void GetStats(AllocatorStatCounters StatType) const { Stats.Get(StatType); } private: PrimaryAllocator Primary; SecondaryAllocator Secondary; AllocatorGlobalStats Stats; }; #endif // SCUDO_ALLOCATOR_COMBINED_H_ Index: vendor/compiler-rt/dist/lib/scudo/scudo_new_delete.cpp =================================================================== --- vendor/compiler-rt/dist/lib/scudo/scudo_new_delete.cpp (revision 320536) +++ vendor/compiler-rt/dist/lib/scudo/scudo_new_delete.cpp (revision 320537) @@ -1,69 +1,74 @@ //===-- scudo_new_delete.cpp ------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// Interceptors for operators new and delete. /// //===----------------------------------------------------------------------===// #include "scudo_allocator.h" #include "interception/interception.h" #include using namespace __scudo; #define CXX_OPERATOR_ATTRIBUTE INTERCEPTOR_ATTRIBUTE // Fake std::nothrow_t to avoid including . namespace std { struct nothrow_t {}; } // namespace std +// TODO(alekseys): throw std::bad_alloc instead of dying on OOM. CXX_OPERATOR_ATTRIBUTE void *operator new(size_t size) { - return scudoMalloc(size, FromNew); + void *res = scudoMalloc(size, FromNew); + if (UNLIKELY(!res)) DieOnFailure::OnOOM(); + return res; } CXX_OPERATOR_ATTRIBUTE void *operator new[](size_t size) { - return scudoMalloc(size, FromNewArray); + void *res = scudoMalloc(size, FromNewArray); + if (UNLIKELY(!res)) DieOnFailure::OnOOM(); + return res; } CXX_OPERATOR_ATTRIBUTE void *operator new(size_t size, std::nothrow_t const&) { return scudoMalloc(size, FromNew); } CXX_OPERATOR_ATTRIBUTE void *operator new[](size_t size, std::nothrow_t const&) { return scudoMalloc(size, FromNewArray); } CXX_OPERATOR_ATTRIBUTE void operator delete(void *ptr) NOEXCEPT { return scudoFree(ptr, FromNew); } CXX_OPERATOR_ATTRIBUTE void operator delete[](void *ptr) NOEXCEPT { return scudoFree(ptr, FromNewArray); } CXX_OPERATOR_ATTRIBUTE void operator delete(void *ptr, std::nothrow_t const&) NOEXCEPT { return scudoFree(ptr, FromNew); } CXX_OPERATOR_ATTRIBUTE void operator delete[](void *ptr, std::nothrow_t const&) NOEXCEPT { return scudoFree(ptr, FromNewArray); } CXX_OPERATOR_ATTRIBUTE void operator delete(void *ptr, size_t size) NOEXCEPT { scudoSizedFree(ptr, size, FromNew); } CXX_OPERATOR_ATTRIBUTE void operator delete[](void *ptr, size_t size) NOEXCEPT { scudoSizedFree(ptr, size, FromNewArray); } Index: vendor/compiler-rt/dist/lib/scudo/scudo_tls_android.cpp =================================================================== --- vendor/compiler-rt/dist/lib/scudo/scudo_tls_android.cpp (revision 320536) +++ vendor/compiler-rt/dist/lib/scudo/scudo_tls_android.cpp (revision 320537) @@ -1,95 +1,95 @@ //===-- scudo_tls_android.cpp -----------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// Scudo thread local structure implementation for Android. /// //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_platform.h" #if SANITIZER_LINUX && SANITIZER_ANDROID #include "scudo_tls.h" #include namespace __scudo { static pthread_once_t GlobalInitialized = PTHREAD_ONCE_INIT; static pthread_key_t PThreadKey; static atomic_uint32_t ThreadContextCurrentIndex; static ScudoThreadContext *ThreadContexts; static uptr NumberOfContexts; // sysconf(_SC_NPROCESSORS_{CONF,ONLN}) cannot be used as they allocate memory. static uptr getNumberOfCPUs() { cpu_set_t CPUs; CHECK_EQ(sched_getaffinity(0, sizeof(cpu_set_t), &CPUs), 0); return CPU_COUNT(&CPUs); } static void initOnce() { // Hack: TLS_SLOT_TSAN was introduced in N. To be able to use it on M for // testing, we create an unused key. Since the key_data array follows the tls // array, it basically gives us the extra entry we need. // TODO(kostyak): remove and restrict to N and above. CHECK_EQ(pthread_key_create(&PThreadKey, NULL), 0); initScudo(); NumberOfContexts = getNumberOfCPUs(); ThreadContexts = reinterpret_cast( MmapOrDie(sizeof(ScudoThreadContext) * NumberOfContexts, __func__)); - for (int i = 0; i < NumberOfContexts; i++) + for (uptr i = 0; i < NumberOfContexts; i++) ThreadContexts[i].init(); } void initThread() { pthread_once(&GlobalInitialized, initOnce); // Initial context assignment is done in a plain round-robin fashion. u32 Index = atomic_fetch_add(&ThreadContextCurrentIndex, 1, memory_order_relaxed); ScudoThreadContext *ThreadContext = &ThreadContexts[Index % NumberOfContexts]; *get_android_tls_ptr() = reinterpret_cast(ThreadContext); } ScudoThreadContext *getThreadContextAndLockSlow() { ScudoThreadContext *ThreadContext; // Go through all the contexts and find the first unlocked one. for (u32 i = 0; i < NumberOfContexts; i++) { ThreadContext = &ThreadContexts[i]; if (ThreadContext->tryLock()) { *get_android_tls_ptr() = reinterpret_cast(ThreadContext); return ThreadContext; } } // No luck, find the one with the lowest precedence, and slow lock it. u64 Precedence = UINT64_MAX; for (u32 i = 0; i < NumberOfContexts; i++) { u64 SlowLockPrecedence = ThreadContexts[i].getSlowLockPrecedence(); if (SlowLockPrecedence && SlowLockPrecedence < Precedence) { ThreadContext = &ThreadContexts[i]; Precedence = SlowLockPrecedence; } } if (LIKELY(Precedence != UINT64_MAX)) { ThreadContext->lock(); *get_android_tls_ptr() = reinterpret_cast(ThreadContext); return ThreadContext; } // Last resort (can this happen?), stick with the current one. ThreadContext = reinterpret_cast(*get_android_tls_ptr()); ThreadContext->lock(); return ThreadContext; } } // namespace __scudo #endif // SANITIZER_LINUX && SANITIZER_ANDROID Index: vendor/compiler-rt/dist/lib/tsan/rtl/tsan_mman.cc =================================================================== --- vendor/compiler-rt/dist/lib/tsan/rtl/tsan_mman.cc (revision 320536) +++ vendor/compiler-rt/dist/lib/tsan/rtl/tsan_mman.cc (revision 320537) @@ -1,301 +1,301 @@ //===-- tsan_mman.cc ------------------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer (TSan), a race detector. // //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_allocator_interface.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_placement_new.h" #include "tsan_mman.h" #include "tsan_rtl.h" #include "tsan_report.h" #include "tsan_flags.h" // May be overriden by front-end. SANITIZER_WEAK_DEFAULT_IMPL void __sanitizer_malloc_hook(void *ptr, uptr size) { (void)ptr; (void)size; } SANITIZER_WEAK_DEFAULT_IMPL void __sanitizer_free_hook(void *ptr) { (void)ptr; } namespace __tsan { struct MapUnmapCallback { void OnMap(uptr p, uptr size) const { } void OnUnmap(uptr p, uptr size) const { // We are about to unmap a chunk of user memory. // Mark the corresponding shadow memory as not needed. DontNeedShadowFor(p, size); // Mark the corresponding meta shadow memory as not needed. // Note the block does not contain any meta info at this point // (this happens after free). const uptr kMetaRatio = kMetaShadowCell / kMetaShadowSize; const uptr kPageSize = GetPageSizeCached() * kMetaRatio; // Block came from LargeMmapAllocator, so must be large. // We rely on this in the calculations below. CHECK_GE(size, 2 * kPageSize); uptr diff = RoundUp(p, kPageSize) - p; if (diff != 0) { p += diff; size -= diff; } diff = p + size - RoundDown(p + size, kPageSize); if (diff != 0) size -= diff; uptr p_meta = (uptr)MemToMeta(p); ReleaseMemoryPagesToOS(p_meta, p_meta + size / kMetaRatio); } }; static char allocator_placeholder[sizeof(Allocator)] ALIGNED(64); Allocator *allocator() { return reinterpret_cast(&allocator_placeholder); } struct GlobalProc { Mutex mtx; Processor *proc; GlobalProc() : mtx(MutexTypeGlobalProc, StatMtxGlobalProc) , proc(ProcCreate()) { } }; static char global_proc_placeholder[sizeof(GlobalProc)] ALIGNED(64); GlobalProc *global_proc() { return reinterpret_cast(&global_proc_placeholder); } ScopedGlobalProcessor::ScopedGlobalProcessor() { GlobalProc *gp = global_proc(); ThreadState *thr = cur_thread(); if (thr->proc()) return; // If we don't have a proc, use the global one. // There are currently only two known case where this path is triggered: // __interceptor_free // __nptl_deallocate_tsd // start_thread // clone // and: // ResetRange // __interceptor_munmap // __deallocate_stack // start_thread // clone // Ideally, we destroy thread state (and unwire proc) when a thread actually // exits (i.e. when we join/wait it). Then we would not need the global proc gp->mtx.Lock(); ProcWire(gp->proc, thr); } ScopedGlobalProcessor::~ScopedGlobalProcessor() { GlobalProc *gp = global_proc(); ThreadState *thr = cur_thread(); if (thr->proc() != gp->proc) return; ProcUnwire(gp->proc, thr); gp->mtx.Unlock(); } void InitializeAllocator() { SetAllocatorMayReturnNull(common_flags()->allocator_may_return_null); allocator()->Init(common_flags()->allocator_release_to_os_interval_ms); } void InitializeAllocatorLate() { new(global_proc()) GlobalProc(); } void AllocatorProcStart(Processor *proc) { allocator()->InitCache(&proc->alloc_cache); internal_allocator()->InitCache(&proc->internal_alloc_cache); } void AllocatorProcFinish(Processor *proc) { allocator()->DestroyCache(&proc->alloc_cache); internal_allocator()->DestroyCache(&proc->internal_alloc_cache); } void AllocatorPrintStats() { allocator()->PrintStats(); } static void SignalUnsafeCall(ThreadState *thr, uptr pc) { if (atomic_load_relaxed(&thr->in_signal_handler) == 0 || !flags()->report_signal_unsafe) return; VarSizeStackTrace stack; ObtainCurrentStack(thr, pc, &stack); if (IsFiredSuppression(ctx, ReportTypeSignalUnsafe, stack)) return; ThreadRegistryLock l(ctx->thread_registry); ScopedReport rep(ReportTypeSignalUnsafe); rep.AddStack(stack, true); OutputReport(thr, rep); } void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align, bool signal) { if ((sz >= (1ull << 40)) || (align >= (1ull << 40))) return Allocator::FailureHandler::OnBadRequest(); void *p = allocator()->Allocate(&thr->proc()->alloc_cache, sz, align); if (p == 0) return 0; if (ctx && ctx->initialized) OnUserAlloc(thr, pc, (uptr)p, sz, true); if (signal) SignalUnsafeCall(thr, pc); return p; } void *user_calloc(ThreadState *thr, uptr pc, uptr size, uptr n) { - if (CallocShouldReturnNullDueToOverflow(size, n)) + if (CheckForCallocOverflow(size, n)) return Allocator::FailureHandler::OnBadRequest(); void *p = user_alloc(thr, pc, n * size); if (p) internal_memset(p, 0, n * size); return p; } void user_free(ThreadState *thr, uptr pc, void *p, bool signal) { ScopedGlobalProcessor sgp; if (ctx && ctx->initialized) OnUserFree(thr, pc, (uptr)p, true); allocator()->Deallocate(&thr->proc()->alloc_cache, p); if (signal) SignalUnsafeCall(thr, pc); } void OnUserAlloc(ThreadState *thr, uptr pc, uptr p, uptr sz, bool write) { DPrintf("#%d: alloc(%zu) = %p\n", thr->tid, sz, p); ctx->metamap.AllocBlock(thr, pc, p, sz); if (write && thr->ignore_reads_and_writes == 0) MemoryRangeImitateWrite(thr, pc, (uptr)p, sz); else MemoryResetRange(thr, pc, (uptr)p, sz); } void OnUserFree(ThreadState *thr, uptr pc, uptr p, bool write) { CHECK_NE(p, (void*)0); uptr sz = ctx->metamap.FreeBlock(thr->proc(), p); DPrintf("#%d: free(%p, %zu)\n", thr->tid, p, sz); if (write && thr->ignore_reads_and_writes == 0) MemoryRangeFreed(thr, pc, (uptr)p, sz); } void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz) { // FIXME: Handle "shrinking" more efficiently, // it seems that some software actually does this. void *p2 = user_alloc(thr, pc, sz); if (p2 == 0) return 0; if (p) { uptr oldsz = user_alloc_usable_size(p); internal_memcpy(p2, p, min(oldsz, sz)); user_free(thr, pc, p); } return p2; } uptr user_alloc_usable_size(const void *p) { if (p == 0) return 0; MBlock *b = ctx->metamap.GetBlock((uptr)p); if (!b) return 0; // Not a valid pointer. if (b->siz == 0) return 1; // Zero-sized allocations are actually 1 byte. return b->siz; } void invoke_malloc_hook(void *ptr, uptr size) { ThreadState *thr = cur_thread(); if (ctx == 0 || !ctx->initialized || thr->ignore_interceptors) return; __sanitizer_malloc_hook(ptr, size); RunMallocHooks(ptr, size); } void invoke_free_hook(void *ptr) { ThreadState *thr = cur_thread(); if (ctx == 0 || !ctx->initialized || thr->ignore_interceptors) return; __sanitizer_free_hook(ptr); RunFreeHooks(ptr); } void *internal_alloc(MBlockType typ, uptr sz) { ThreadState *thr = cur_thread(); if (thr->nomalloc) { thr->nomalloc = 0; // CHECK calls internal_malloc(). CHECK(0); } return InternalAlloc(sz, &thr->proc()->internal_alloc_cache); } void internal_free(void *p) { ThreadState *thr = cur_thread(); if (thr->nomalloc) { thr->nomalloc = 0; // CHECK calls internal_malloc(). CHECK(0); } InternalFree(p, &thr->proc()->internal_alloc_cache); } } // namespace __tsan using namespace __tsan; extern "C" { uptr __sanitizer_get_current_allocated_bytes() { uptr stats[AllocatorStatCount]; allocator()->GetStats(stats); return stats[AllocatorStatAllocated]; } uptr __sanitizer_get_heap_size() { uptr stats[AllocatorStatCount]; allocator()->GetStats(stats); return stats[AllocatorStatMapped]; } uptr __sanitizer_get_free_bytes() { return 1; } uptr __sanitizer_get_unmapped_bytes() { return 1; } uptr __sanitizer_get_estimated_allocated_size(uptr size) { return size; } int __sanitizer_get_ownership(const void *p) { return allocator()->GetBlockBegin(p) != 0; } uptr __sanitizer_get_allocated_size(const void *p) { return user_alloc_usable_size(p); } void __tsan_on_thread_idle() { ThreadState *thr = cur_thread(); allocator()->SwallowCache(&thr->proc()->alloc_cache); internal_allocator()->SwallowCache(&thr->proc()->internal_alloc_cache); ctx->metamap.OnProcIdle(thr->proc()); } } // extern "C" Index: vendor/compiler-rt/dist/lib/tsan/rtl/tsan_new_delete.cc =================================================================== --- vendor/compiler-rt/dist/lib/tsan/rtl/tsan_new_delete.cc (revision 320536) +++ vendor/compiler-rt/dist/lib/tsan/rtl/tsan_new_delete.cc (revision 320537) @@ -1,92 +1,95 @@ //===-- tsan_new_delete.cc ----------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file is a part of ThreadSanitizer (TSan), a race detector. // // Interceptors for operators new and delete. //===----------------------------------------------------------------------===// #include "interception/interception.h" +#include "sanitizer_common/sanitizer_allocator.h" #include "sanitizer_common/sanitizer_internal_defs.h" #include "tsan_interceptors.h" using namespace __tsan; // NOLINT namespace std { struct nothrow_t {}; } // namespace std DECLARE_REAL(void *, malloc, uptr size) DECLARE_REAL(void, free, void *ptr) -#define OPERATOR_NEW_BODY(mangled_name) \ +// TODO(alekseys): throw std::bad_alloc instead of dying on OOM. +#define OPERATOR_NEW_BODY(mangled_name, nothrow) \ if (cur_thread()->in_symbolizer) \ return InternalAlloc(size); \ void *p = 0; \ { \ SCOPED_INTERCEPTOR_RAW(mangled_name, size); \ p = user_alloc(thr, pc, size); \ + if (!nothrow && UNLIKELY(!p)) DieOnFailure::OnOOM(); \ } \ invoke_malloc_hook(p, size); \ return p; SANITIZER_INTERFACE_ATTRIBUTE void *operator new(__sanitizer::uptr size); void *operator new(__sanitizer::uptr size) { - OPERATOR_NEW_BODY(_Znwm); + OPERATOR_NEW_BODY(_Znwm, false /*nothrow*/); } SANITIZER_INTERFACE_ATTRIBUTE void *operator new[](__sanitizer::uptr size); void *operator new[](__sanitizer::uptr size) { - OPERATOR_NEW_BODY(_Znam); + OPERATOR_NEW_BODY(_Znam, false /*nothrow*/); } SANITIZER_INTERFACE_ATTRIBUTE void *operator new(__sanitizer::uptr size, std::nothrow_t const&); void *operator new(__sanitizer::uptr size, std::nothrow_t const&) { - OPERATOR_NEW_BODY(_ZnwmRKSt9nothrow_t); + OPERATOR_NEW_BODY(_ZnwmRKSt9nothrow_t, true /*nothrow*/); } SANITIZER_INTERFACE_ATTRIBUTE void *operator new[](__sanitizer::uptr size, std::nothrow_t const&); void *operator new[](__sanitizer::uptr size, std::nothrow_t const&) { - OPERATOR_NEW_BODY(_ZnamRKSt9nothrow_t); + OPERATOR_NEW_BODY(_ZnamRKSt9nothrow_t, true /*nothrow*/); } #define OPERATOR_DELETE_BODY(mangled_name) \ if (ptr == 0) return; \ if (cur_thread()->in_symbolizer) \ return InternalFree(ptr); \ invoke_free_hook(ptr); \ SCOPED_INTERCEPTOR_RAW(mangled_name, ptr); \ user_free(thr, pc, ptr); SANITIZER_INTERFACE_ATTRIBUTE void operator delete(void *ptr) NOEXCEPT; void operator delete(void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY(_ZdlPv); } SANITIZER_INTERFACE_ATTRIBUTE void operator delete[](void *ptr) NOEXCEPT; void operator delete[](void *ptr) NOEXCEPT { OPERATOR_DELETE_BODY(_ZdaPv); } SANITIZER_INTERFACE_ATTRIBUTE void operator delete(void *ptr, std::nothrow_t const&); void operator delete(void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY(_ZdlPvRKSt9nothrow_t); } SANITIZER_INTERFACE_ATTRIBUTE void operator delete[](void *ptr, std::nothrow_t const&); void operator delete[](void *ptr, std::nothrow_t const&) { OPERATOR_DELETE_BODY(_ZdaPvRKSt9nothrow_t); } Index: vendor/compiler-rt/dist/lib/tsan/tests/CMakeLists.txt =================================================================== --- vendor/compiler-rt/dist/lib/tsan/tests/CMakeLists.txt (revision 320536) +++ vendor/compiler-rt/dist/lib/tsan/tests/CMakeLists.txt (revision 320537) @@ -1,99 +1,100 @@ include_directories(../rtl) add_custom_target(TsanUnitTests) set_target_properties(TsanUnitTests PROPERTIES FOLDER "TSan unittests") set(TSAN_UNITTEST_CFLAGS ${TSAN_CFLAGS} ${COMPILER_RT_UNITTEST_CFLAGS} ${COMPILER_RT_GTEST_CFLAGS} + -I${COMPILER_RT_SOURCE_DIR}/include -I${COMPILER_RT_SOURCE_DIR}/lib -I${COMPILER_RT_SOURCE_DIR}/lib/tsan/rtl -DGTEST_HAS_RTTI=0) if(APPLE) list(APPEND TSAN_UNITTEST_CFLAGS ${DARWIN_osx_CFLAGS}) endif() set(TSAN_RTL_HEADERS) foreach (header ${TSAN_HEADERS}) list(APPEND TSAN_RTL_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/../${header}) endforeach() # tsan_compile(obj_list, source, arch, {headers}) macro(tsan_compile obj_list source arch) get_filename_component(basename ${source} NAME) set(output_obj "${basename}.${arch}.o") get_target_flags_for_arch(${arch} TARGET_CFLAGS) set(COMPILE_DEPS ${TSAN_RTL_HEADERS} ${ARGN}) if(NOT COMPILER_RT_STANDALONE_BUILD) list(APPEND COMPILE_DEPS gtest tsan) endif() clang_compile(${output_obj} ${source} CFLAGS ${TSAN_UNITTEST_CFLAGS} ${TARGET_CFLAGS} DEPS ${COMPILE_DEPS}) list(APPEND ${obj_list} ${output_obj}) endmacro() macro(add_tsan_unittest testname) set(TSAN_TEST_ARCH ${TSAN_SUPPORTED_ARCH}) if(APPLE) darwin_filter_host_archs(TSAN_SUPPORTED_ARCH TSAN_TEST_ARCH) endif() if(UNIX) foreach(arch ${TSAN_TEST_ARCH}) cmake_parse_arguments(TEST "" "" "SOURCES;HEADERS" ${ARGN}) set(TEST_OBJECTS) foreach(SOURCE ${TEST_SOURCES} ${COMPILER_RT_GTEST_SOURCE}) tsan_compile(TEST_OBJECTS ${SOURCE} ${arch} ${TEST_HEADERS}) endforeach() get_target_flags_for_arch(${arch} TARGET_LINK_FLAGS) set(TEST_DEPS ${TEST_OBJECTS}) if(NOT COMPILER_RT_STANDALONE_BUILD) list(APPEND TEST_DEPS tsan) endif() if(NOT APPLE) # FIXME: Looks like we should link TSan with just-built runtime, # and not rely on -fsanitize=thread, as these tests are essentially # unit tests. add_compiler_rt_test(TsanUnitTests ${testname} OBJECTS ${TEST_OBJECTS} DEPS ${TEST_DEPS} LINK_FLAGS ${TARGET_LINK_FLAGS} -fsanitize=thread -lstdc++ -lm) else() set(TSAN_TEST_RUNTIME_OBJECTS $ $ $ $ $) set(TSAN_TEST_RUNTIME RTTsanTest.${testname}.${arch}) add_library(${TSAN_TEST_RUNTIME} STATIC ${TSAN_TEST_RUNTIME_OBJECTS}) set_target_properties(${TSAN_TEST_RUNTIME} PROPERTIES ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) list(APPEND TEST_OBJECTS lib${TSAN_TEST_RUNTIME}.a) list(APPEND TEST_DEPS ${TSAN_TEST_RUNTIME}) add_weak_symbols("ubsan" WEAK_SYMBOL_LINK_FLAGS) add_weak_symbols("sanitizer_common" WEAK_SYMBOL_LINK_FLAGS) # Intentionally do *not* link with `-fsanitize=thread`. We already link # against a static version of the runtime, and we don't want the dynamic # one. add_compiler_rt_test(TsanUnitTests "${testname}-${arch}-Test" OBJECTS ${TEST_OBJECTS} DEPS ${TEST_DEPS} LINK_FLAGS ${TARGET_LINK_FLAGS} ${DARWIN_osx_LINK_FLAGS} ${WEAK_SYMBOL_LINK_FLAGS} -lc++) endif() endforeach() endif() endmacro() if(COMPILER_RT_CAN_EXECUTE_TESTS) add_subdirectory(rtl) add_subdirectory(unit) endif() Index: vendor/compiler-rt/dist/lib/xray/xray_always_instrument.txt =================================================================== --- vendor/compiler-rt/dist/lib/xray/xray_always_instrument.txt (nonexistent) +++ vendor/compiler-rt/dist/lib/xray/xray_always_instrument.txt (revision 320537) @@ -0,0 +1,6 @@ +# List of function matchers common to C/C++ applications that make sense to +# always instrument. You can use this as an argument to +# -fxray-always-instrument= along with your project-specific lists. + +# Always instrument the main function. +fun:main Property changes on: vendor/compiler-rt/dist/lib/xray/xray_always_instrument.txt ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/compiler-rt/dist/lib/xray/xray_never_instrument.txt =================================================================== --- vendor/compiler-rt/dist/lib/xray/xray_never_instrument.txt (nonexistent) +++ vendor/compiler-rt/dist/lib/xray/xray_never_instrument.txt (revision 320537) @@ -0,0 +1,6 @@ +# List of function matchers common to C/C++ applications that make sense to +# never instrument. You can use this as an argument to +# -fxray-never-instrument= along with your project-specific lists. + +# Never instrument any function whose symbol starts with __xray. +fun:__xray* Property changes on: vendor/compiler-rt/dist/lib/xray/xray_never_instrument.txt ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/compiler-rt/dist/test/CMakeLists.txt =================================================================== --- vendor/compiler-rt/dist/test/CMakeLists.txt (revision 320536) +++ vendor/compiler-rt/dist/test/CMakeLists.txt (revision 320537) @@ -1,105 +1,90 @@ # Needed for lit support include(AddLLVM) configure_lit_site_cfg( ${CMAKE_CURRENT_SOURCE_DIR}/lit.common.configured.in ${CMAKE_CURRENT_BINARY_DIR}/lit.common.configured) # BlocksRuntime (and most of builtins) testsuites are not yet ported to lit. # add_subdirectory(BlocksRuntime) set(SANITIZER_COMMON_LIT_TEST_DEPS) if(COMPILER_RT_STANDALONE_BUILD) add_executable(FileCheck IMPORTED GLOBAL) set_property(TARGET FileCheck PROPERTY IMPORTED_LOCATION ${LLVM_TOOLS_BINARY_DIR}/FileCheck) list(APPEND SANITIZER_COMMON_LIT_TEST_DEPS FileCheck) endif() # When ANDROID, we build tests with the host compiler (i.e. CMAKE_C_COMPILER), # and run tests with tools from the host toolchain. if(NOT ANDROID) if(NOT COMPILER_RT_STANDALONE_BUILD) # Use LLVM utils and Clang from the same build tree. list(APPEND SANITIZER_COMMON_LIT_TEST_DEPS clang clang-headers FileCheck count not llvm-config llvm-nm llvm-objdump llvm-readobj llvm-symbolizer compiler-rt-headers sancov) if (COMPILER_RT_HAS_PROFILE) list(APPEND SANITIZER_COMMON_LIT_TEST_DEPS profile) endif() if (WIN32) list(APPEND SANITIZER_COMMON_LIT_TEST_DEPS KillTheDoctor) endif() endif() if(CMAKE_HOST_UNIX) list(APPEND SANITIZER_COMMON_LIT_TEST_DEPS SanitizerLintCheck) endif() endif() +function(compiler_rt_test_runtime runtime) + string(TOUPPER ${runtime} runtime_uppercase) + if(COMPILER_RT_HAS_${runtime_uppercase}) + add_subdirectory(${runtime}) + foreach(directory ${ARGN}) + add_subdirectory(${directory}) + endforeach() + endif() +endfunction() + # Run sanitizer tests only if we're sure that clang would produce # working binaries. if(COMPILER_RT_CAN_EXECUTE_TESTS) if(COMPILER_RT_BUILD_BUILTINS) add_subdirectory(builtins) endif() if(COMPILER_RT_BUILD_SANITIZERS) - if(COMPILER_RT_HAS_ASAN) - add_subdirectory(asan) - endif() - if(COMPILER_RT_HAS_DFSAN) - add_subdirectory(dfsan) - endif() - if (COMPILER_RT_HAS_INTERCEPTION) - add_subdirectory(interception) - endif() - if(COMPILER_RT_HAS_LSAN) - add_subdirectory(lsan) - endif() - if(COMPILER_RT_HAS_MSAN) - add_subdirectory(msan) - endif() - if(COMPILER_RT_HAS_PROFILE) - add_subdirectory(profile) - endif() - if(COMPILER_RT_HAS_SANITIZER_COMMON) - add_subdirectory(sanitizer_common) - endif() - if(COMPILER_RT_HAS_TSAN) - add_subdirectory(tsan) - endif() - if(COMPILER_RT_HAS_UBSAN) - add_subdirectory(ubsan) - endif() + compiler_rt_test_runtime(interception) + + compiler_rt_test_runtime(lsan) # CFI tests require diagnostic mode, which is implemented in UBSan. - if(COMPILER_RT_HAS_UBSAN) - add_subdirectory(cfi) - endif() - if(COMPILER_RT_HAS_SAFESTACK) - add_subdirectory(safestack) - endif() - if(COMPILER_RT_HAS_ESAN) - add_subdirectory(esan) - endif() - if(COMPILER_RT_HAS_SCUDO) - add_subdirectory(scudo) - endif() + compiler_rt_test_runtime(ubsan cfi) + compiler_rt_test_runtime(sanitizer_common) + + foreach(sanitizer ${COMPILER_RT_SANITIZERS_TO_BUILD}) + # cfi testing is gated on ubsan + if(NOT ${sanitizer} STREQUAL cfi) + compiler_rt_test_runtime(${sanitizer}) + endif() + endforeach() + + compiler_rt_test_runtime(profile) endif() - if(COMPILER_RT_BUILD_XRAY AND COMPILER_RT_HAS_XRAY) - add_subdirectory(xray) + if(COMPILER_RT_BUILD_XRAY) + compiler_rt_test_runtime(xray) endif() endif() if(COMPILER_RT_STANDALONE_BUILD) # Now that we've traversed all the directories and know all the lit testsuites, # introduce a rule to run to run all of them. get_property(LLVM_LIT_TESTSUITES GLOBAL PROPERTY LLVM_LIT_TESTSUITES) get_property(LLVM_LIT_DEPENDS GLOBAL PROPERTY LLVM_LIT_DEPENDS) add_lit_target(check-compiler-rt "Running all regression tests" ${LLVM_LIT_TESTSUITES} DEPENDS ${LLVM_LIT_DEPENDS}) if(NOT TARGET check-all) add_custom_target(check-all) endif() add_custom_target(compiler-rt-test-depends DEPENDS ${LLVM_LIT_DEPENDS}) add_dependencies(check-all check-compiler-rt) endif() Index: vendor/compiler-rt/dist/test/asan/TestCases/Darwin/asan_gen_prefixes.cc =================================================================== --- vendor/compiler-rt/dist/test/asan/TestCases/Darwin/asan_gen_prefixes.cc (revision 320536) +++ vendor/compiler-rt/dist/test/asan/TestCases/Darwin/asan_gen_prefixes.cc (revision 320537) @@ -1,14 +1,16 @@ // Make sure __asan_gen_* strings have the correct prefixes on Darwin // ("L" in __TEXT,__cstring, "l" in __TEXT,__const // RUN: %clang_asan %s -S -o %t.s // RUN: cat %t.s | FileCheck %s || exit 1 +// UNSUPPORTED: ios + int x, y, z; int main() { return 0; } // CHECK: .section{{.*}}__TEXT,__const // CHECK: l___asan_gen_ // CHECK: .section{{.*}}__TEXT,__cstring,cstring_literals // CHECK: L___asan_gen_ // CHECK: L___asan_gen_ // CHECK: L___asan_gen_ Index: vendor/compiler-rt/dist/test/asan/TestCases/Darwin/nil-return-struct.mm =================================================================== --- vendor/compiler-rt/dist/test/asan/TestCases/Darwin/nil-return-struct.mm (nonexistent) +++ vendor/compiler-rt/dist/test/asan/TestCases/Darwin/nil-return-struct.mm (revision 320537) @@ -0,0 +1,31 @@ +// RUN: %clang_asan %s -o %t -framework Foundation +// RUN: %run %t 2>&1 | FileCheck %s + +#import + +struct MyStruct { + long a, b, c, d; +}; + +@interface MyClass: NSObject +- (MyStruct)methodWhichReturnsARect; +@end +@implementation MyClass +- (MyStruct)methodWhichReturnsARect { + MyStruct s; + s.a = 10; + s.b = 20; + s.c = 30; + s.d = 40; + return s; +} +@end + +int main() { + MyClass *myNil = nil; // intentionally nil + [myNil methodWhichReturnsARect]; + fprintf(stderr, "Hello world"); +} + +// CHECK-NOT: AddressSanitizer: stack-use-after-scope +// CHECK: Hello world Index: vendor/compiler-rt/dist/test/asan/TestCases/Linux/allocator_oom_test.cc =================================================================== --- vendor/compiler-rt/dist/test/asan/TestCases/Linux/allocator_oom_test.cc (revision 320536) +++ vendor/compiler-rt/dist/test/asan/TestCases/Linux/allocator_oom_test.cc (revision 320537) @@ -1,85 +1,86 @@ // Test the behavior of malloc/calloc/realloc when the allocation causes OOM // in the secondary allocator. // By default (allocator_may_return_null=0) the process should crash. // With allocator_may_return_null=1 the allocator should return 0. // Set the limit to 20.5T on 64 bits to account for ASan shadow memory, // allocator buffers etc. so that the test allocation of ~1T will trigger OOM. // Limit this test to Linux since we're relying on allocator internal // limits (shadow memory size, allocation limits etc.) // RUN: %clangxx_asan -O0 %s -o %t // RUN: ulimit -v 22024290304 // RUN: not %run %t malloc 2>&1 \ // RUN: | FileCheck %s --check-prefixes=CHECK-MALLOC,CHECK-CRASH // RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t malloc 2>&1 \ // RUN: | FileCheck %s --check-prefixes=CHECK-MALLOC,CHECK-CRASH // RUN: %env_asan_opts=allocator_may_return_null=1 %run %t malloc 2>&1 \ // RUN: | FileCheck %s --check-prefixes=CHECK-MALLOC,CHECK-NULL // RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t calloc 2>&1 \ // RUN: | FileCheck %s --check-prefixes=CHECK-CALLOC,CHECK-CRASH // RUN: %env_asan_opts=allocator_may_return_null=1 %run %t calloc 2>&1 \ // RUN: | FileCheck %s --check-prefixes=CHECK-CALLOC,CHECK-NULL // RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t realloc 2>&1 \ // RUN: | FileCheck %s --check-prefixes=CHECK-REALLOC,CHECK-CRASH // RUN: %env_asan_opts=allocator_may_return_null=1 %run %t realloc 2>&1 \ // RUN: | FileCheck %s --check-prefixes=CHECK-REALLOC,CHECK-NULL // RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t realloc-after-malloc 2>&1 \ // RUN: | FileCheck %s --check-prefixes=CHECK-MALLOC-REALLOC,CHECK-CRASH // RUN: %env_asan_opts=allocator_may_return_null=1 %run %t realloc-after-malloc 2>&1 \ // RUN: | FileCheck %s --check-prefixes=CHECK-MALLOC-REALLOC,CHECK-NULL // ASan shadow memory on s390 is too large for this test. -// UNSUPPORTED: s390 +// TODO(alekseys): Android lit do not run ulimit on device. +// UNSUPPORTED: s390,android #include #include #include #include int main(int argc, char **argv) { assert(argc == 2); const char *action = argv[1]; fprintf(stderr, "%s:\n", action); // Allocate just a bit less than max allocation size enforced by ASan's // allocator (currently 1T and 3G). const size_t size = #if __LP64__ (1ULL << 40) - (1ULL << 30); #else (3ULL << 30) - (1ULL << 20); #endif void *x = 0; if (!strcmp(action, "malloc")) { x = malloc(size); } else if (!strcmp(action, "calloc")) { x = calloc(size / 4, 4); } else if (!strcmp(action, "realloc")) { x = realloc(0, size); } else if (!strcmp(action, "realloc-after-malloc")) { char *t = (char*)malloc(100); *t = 42; x = realloc(t, size); assert(*t == 42); free(t); } else { assert(0); } // The NULL pointer is printed differently on different systems, while (long)0 // is always the same. fprintf(stderr, "x: %lx\n", (long)x); free(x); return x != 0; } // CHECK-MALLOC: malloc: // CHECK-CALLOC: calloc: // CHECK-REALLOC: realloc: // CHECK-MALLOC-REALLOC: realloc-after-malloc: // CHECK-CRASH: AddressSanitizer's allocator is terminating the process // CHECK-NULL: x: 0 Index: vendor/compiler-rt/dist/test/asan/TestCases/Linux/asan_preload_test-3.cc =================================================================== --- vendor/compiler-rt/dist/test/asan/TestCases/Linux/asan_preload_test-3.cc (revision 320536) +++ vendor/compiler-rt/dist/test/asan/TestCases/Linux/asan_preload_test-3.cc (revision 320537) @@ -1,33 +1,36 @@ // Regression test for PR33206 // // RUN: %clang -DDYN=1 -DMALLOC=1 -fPIC -shared %s -o %t-dso1.so // RUN: %clang -DDYN=1 -DMALLOC=1 -fPIC -shared %s -o %t-dso2.so %t-dso1.so // RUN: %clang %s -o %t-1 %t-dso2.so // RUN: env LD_PRELOAD=%shared_libasan %run %t-1 2>&1 | FileCheck %s // RUN: %clang -DDYN=1 -DREALLOC=1 -fPIC -shared %s -o %t-dso3.so // RUN: %clang -DDYN=1 -DREALLOC=1 -fPIC -shared %s -o %t-dso4.so %t-dso3.so // RUN: %clang %s -o %t-2 %t-dso4.so // RUN: env LD_PRELOAD=%shared_libasan %run %t-2 2>&1 | FileCheck %s // REQUIRES: asan-dynamic-runtime +// FIXME: Test regressed while android bot was disabled. Needs investigation. +// UNSUPPORTED: android + #include #include #ifdef DYN __attribute__((constructor)) void foo() { void *p; #ifdef MALLOC p = malloc(1 << 20); #endif #ifdef REALLOC p = realloc (0, 1 << 20); #endif free(p); } #else int main() { // CHECK: Success printf("Success\n"); return 0; } #endif Index: vendor/compiler-rt/dist/test/asan/TestCases/Linux/init_fini_sections.cc =================================================================== --- vendor/compiler-rt/dist/test/asan/TestCases/Linux/init_fini_sections.cc (revision 320536) +++ vendor/compiler-rt/dist/test/asan/TestCases/Linux/init_fini_sections.cc (revision 320537) @@ -1,24 +1,30 @@ // RUN: %clangxx_asan %s -o %t && %run %t | FileCheck %s #include +int c = 0; + static void foo() { - printf("foo\n"); + ++c; } +static void fini() { + printf("fini\n"); +} + int main() { + printf("c=%d\n", c); return 0; } __attribute__((section(".preinit_array"))) void (*call_foo)(void) = &foo; __attribute__((section(".init_array"))) void (*call_foo_2)(void) = &foo; __attribute__((section(".fini_array"))) -void (*call_foo_3)(void) = &foo; +void (*call_foo_3)(void) = &fini; -// CHECK: foo -// CHECK: foo -// CHECK: foo +// CHECK: c=2 +// CHECK: fini Index: vendor/compiler-rt/dist/test/asan/TestCases/Linux/textdomain.c =================================================================== --- vendor/compiler-rt/dist/test/asan/TestCases/Linux/textdomain.c (revision 320536) +++ vendor/compiler-rt/dist/test/asan/TestCases/Linux/textdomain.c (revision 320537) @@ -1,10 +1,13 @@ // RUN: %clang_asan -O0 -g %s -o %t // RUN: %env_asan_opts=strict_string_checks=1 %run %t +// Android NDK does not have libintl.h +// UNSUPPORTED: android + #include #include int main() { textdomain(NULL); return 0; } Index: vendor/compiler-rt/dist/test/asan/TestCases/Posix/asan-sigbus.cpp =================================================================== --- vendor/compiler-rt/dist/test/asan/TestCases/Posix/asan-sigbus.cpp (revision 320536) +++ vendor/compiler-rt/dist/test/asan/TestCases/Posix/asan-sigbus.cpp (revision 320537) @@ -1,42 +1,42 @@ // Check handle_bus flag // Defaults to true // RUN: %clangxx_asan -std=c++11 %s -o %t -// RUN: not %run %t %T/file 2>&1 | FileCheck %s -check-prefix=CHECK-BUS -// RUN: %env_asan_opts=handle_sigbus=0 not --crash %run %t %T/file 2>&1 | FileCheck %s +// RUN: not %run %t 2>&1 | FileCheck %s -check-prefix=CHECK-BUS +// RUN: %env_asan_opts=handle_sigbus=0 not --crash %run %t 2>&1 | FileCheck %s // UNSUPPORTED: ios #include #include #include #include #include #include +#include char array[4096]; int main(int argc, char **argv) { - assert(argc > 1); - int fd = open(argv[1], O_RDWR | O_CREAT, 0700); + int fd = open((std::string(argv[0]) + ".m").c_str(), O_RDWR | O_CREAT, 0700); if (fd < 0) { perror("open"); exit(1); } assert(write(fd, array, sizeof(array)) == sizeof(array)); // Write some zeroes to the file, then mmap it while it has a 4KiB size char *addr = (char *)mmap(nullptr, sizeof(array), PROT_READ, MAP_FILE | MAP_SHARED, fd, 0); if (addr == MAP_FAILED) { perror("mmap"); exit(1); } // Truncate the file so our memory isn't valid any more assert(ftruncate(fd, 0) == 0); // Try to access the memory return addr[42]; // CHECK-NOT: DEADLYSIGNAL // CHECK-BUS: DEADLYSIGNAL // CHECK-BUS: ERROR: AddressSanitizer: BUS } Index: vendor/compiler-rt/dist/test/asan/TestCases/allocator_returns_null.cc =================================================================== --- vendor/compiler-rt/dist/test/asan/TestCases/allocator_returns_null.cc (revision 320536) +++ vendor/compiler-rt/dist/test/asan/TestCases/allocator_returns_null.cc (revision 320537) @@ -1,86 +1,121 @@ -// Test the behavior of malloc/calloc/realloc when the allocation size is huge. +// Test the behavior of malloc/calloc/realloc/new when the allocation size is +// more than ASan allocator's max allowed one. // By default (allocator_may_return_null=0) the process should crash. -// With allocator_may_return_null=1 the allocator should return 0. +// With allocator_may_return_null=1 the allocator should return 0, except the +// operator new(), which should crash anyway (operator new(std::nothrow) should +// return nullptr, indeed). // // RUN: %clangxx_asan -O0 %s -o %t // RUN: not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH -// RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH -// RUN: %env_asan_opts=allocator_may_return_null=1 %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mNULL -// RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cCRASH -// RUN: %env_asan_opts=allocator_may_return_null=1 %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cNULL -// RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coCRASH -// RUN: %env_asan_opts=allocator_may_return_null=1 %run %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coNULL -// RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rCRASH -// RUN: %env_asan_opts=allocator_may_return_null=1 %run %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rNULL -// RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrCRASH -// RUN: %env_asan_opts=allocator_may_return_null=1 %run %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrNULL +// RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t malloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: %env_asan_opts=allocator_may_return_null=1 %run %t malloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-mNULL +// RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t calloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-cCRASH +// RUN: %env_asan_opts=allocator_may_return_null=1 %run %t calloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-cNULL +// RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t calloc-overflow 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-coCRASH +// RUN: %env_asan_opts=allocator_may_return_null=1 %run %t calloc-overflow 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-coNULL +// RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t realloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-rCRASH +// RUN: %env_asan_opts=allocator_may_return_null=1 %run %t realloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-rNULL +// RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t realloc-after-malloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-mrCRASH +// RUN: %env_asan_opts=allocator_may_return_null=1 %run %t realloc-after-malloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-mrNULL +// RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t new 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-nCRASH +// RUN: %env_asan_opts=allocator_may_return_null=1 not %run %t new 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-nCRASH +// RUN: %env_asan_opts=allocator_may_return_null=0 not %run %t new-nothrow 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-nnCRASH +// RUN: %env_asan_opts=allocator_may_return_null=1 %run %t new-nothrow 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-nnNULL -#include -#include +#include #include #include -#include +#include #include +#include + int main(int argc, char **argv) { // Disable stderr buffering. Needed on Windows. setvbuf(stderr, NULL, _IONBF, 0); - volatile size_t size = std::numeric_limits::max() - 10000; assert(argc == 2); - void *x = 0; - if (!strcmp(argv[1], "malloc")) { - fprintf(stderr, "malloc:\n"); - x = malloc(size); - } - if (!strcmp(argv[1], "calloc")) { - fprintf(stderr, "calloc:\n"); - x = calloc(size / 4, 4); - } + const char *action = argv[1]; + fprintf(stderr, "%s:\n", action); - if (!strcmp(argv[1], "calloc-overflow")) { - fprintf(stderr, "calloc-overflow:\n"); + static const size_t kMaxAllowedMallocSizePlusOne = +#if __LP64__ || defined(_WIN64) + (1ULL << 40) + 1; +#else + (3UL << 30) + 1; +#endif + + void *x = 0; + if (!strcmp(action, "malloc")) { + x = malloc(kMaxAllowedMallocSizePlusOne); + } else if (!strcmp(action, "calloc")) { + x = calloc((kMaxAllowedMallocSizePlusOne / 4) + 1, 4); + } else if (!strcmp(action, "calloc-overflow")) { volatile size_t kMaxSizeT = std::numeric_limits::max(); size_t kArraySize = 4096; volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10; x = calloc(kArraySize, kArraySize2); - } - - if (!strcmp(argv[1], "realloc")) { - fprintf(stderr, "realloc:\n"); - x = realloc(0, size); - } - if (!strcmp(argv[1], "realloc-after-malloc")) { - fprintf(stderr, "realloc-after-malloc:\n"); + } else if (!strcmp(action, "realloc")) { + x = realloc(0, kMaxAllowedMallocSizePlusOne); + } else if (!strcmp(action, "realloc-after-malloc")) { char *t = (char*)malloc(100); *t = 42; - x = realloc(t, size); + x = realloc(t, kMaxAllowedMallocSizePlusOne); assert(*t == 42); free(t); + } else if (!strcmp(action, "new")) { + x = operator new(kMaxAllowedMallocSizePlusOne); + } else if (!strcmp(action, "new-nothrow")) { + x = operator new(kMaxAllowedMallocSizePlusOne, std::nothrow); + } else { + assert(0); } + // The NULL pointer is printed differently on different systems, while (long)0 // is always the same. fprintf(stderr, "x: %lx\n", (long)x); free(x); + return x != 0; } + // CHECK-mCRASH: malloc: // CHECK-mCRASH: AddressSanitizer's allocator is terminating the process // CHECK-cCRASH: calloc: // CHECK-cCRASH: AddressSanitizer's allocator is terminating the process // CHECK-coCRASH: calloc-overflow: // CHECK-coCRASH: AddressSanitizer's allocator is terminating the process // CHECK-rCRASH: realloc: // CHECK-rCRASH: AddressSanitizer's allocator is terminating the process // CHECK-mrCRASH: realloc-after-malloc: // CHECK-mrCRASH: AddressSanitizer's allocator is terminating the process +// CHECK-nCRASH: new: +// CHECK-nCRASH: AddressSanitizer's allocator is terminating the process +// CHECK-nnCRASH: new-nothrow: +// CHECK-nnCRASH: AddressSanitizer's allocator is terminating the process // CHECK-mNULL: malloc: // CHECK-mNULL: x: 0 // CHECK-cNULL: calloc: // CHECK-cNULL: x: 0 // CHECK-coNULL: calloc-overflow: // CHECK-coNULL: x: 0 // CHECK-rNULL: realloc: // CHECK-rNULL: x: 0 // CHECK-mrNULL: realloc-after-malloc: // CHECK-mrNULL: x: 0 +// CHECK-nnNULL: new-nothrow: +// CHECK-nnNULL: x: 0 Index: vendor/compiler-rt/dist/test/asan/TestCases/malloc-no-intercept.c =================================================================== --- vendor/compiler-rt/dist/test/asan/TestCases/malloc-no-intercept.c (revision 320536) +++ vendor/compiler-rt/dist/test/asan/TestCases/malloc-no-intercept.c (revision 320537) @@ -1,24 +1,27 @@ // Test that on non-glibc platforms, a number of malloc-related functions are // not intercepted. // RUN: not %clang_asan -Dtestfunc=mallinfo %s -o %t // RUN: not %clang_asan -Dtestfunc=mallopt %s -o %t // RUN: not %clang_asan -Dtestfunc=memalign %s -o %t // RUN: not %clang_asan -Dtestfunc=pvalloc %s -o %t // RUN: not %clang_asan -Dtestfunc=cfree %s -o %t +// Conflicts with BIONIC declarations. +// UNSUPPORTED: android + #include // For glibc, cause link failures by referencing a nonexistent function. #ifdef __GLIBC__ #undef testfunc #define testfunc nonexistent_function #endif void testfunc(void); int main(void) { testfunc(); return 0; } Index: vendor/compiler-rt/dist/test/asan/TestCases/throw_call_test.cc =================================================================== --- vendor/compiler-rt/dist/test/asan/TestCases/throw_call_test.cc (revision 320536) +++ vendor/compiler-rt/dist/test/asan/TestCases/throw_call_test.cc (revision 320537) @@ -1,52 +1,49 @@ // RUN: %clangxx_asan %s -o %t && %run %t // http://code.google.com/p/address-sanitizer/issues/detail?id=147 (not fixed). // BROKEN: %clangxx_asan %s -o %t -static-libstdc++ && %run %t -// -// Android builds with static libstdc++ by default. -// XFAIL: android #include static volatile int zero = 0; inline void pretend_to_do_something(void *x) { __asm__ __volatile__("" : : "r" (x) : "memory"); } __attribute__((noinline, no_sanitize_address)) void ReallyThrow() { fprintf(stderr, "ReallyThrow\n"); if (zero == 0) throw 42; } __attribute__((noinline)) void Throw() { int a, b, c, d, e, f, g, h; pretend_to_do_something(&a); pretend_to_do_something(&b); pretend_to_do_something(&c); pretend_to_do_something(&d); pretend_to_do_something(&e); pretend_to_do_something(&f); pretend_to_do_something(&g); pretend_to_do_something(&h); fprintf(stderr, "Throw stack = %p\n", &a); ReallyThrow(); } __attribute__((noinline)) void CheckStack() { int ar[100]; pretend_to_do_something(ar); fprintf(stderr, "CheckStack stack = %p, %p\n", ar, ar + 100); for (int i = 0; i < 100; i++) ar[i] = i; } int main(int argc, char** argv) { try { Throw(); } catch(int a) { fprintf(stderr, "a = %d\n", a); } CheckStack(); } Index: vendor/compiler-rt/dist/test/asan/android_commands/android_common.py =================================================================== --- vendor/compiler-rt/dist/test/asan/android_commands/android_common.py (revision 320536) +++ vendor/compiler-rt/dist/test/asan/android_commands/android_common.py (revision 320537) @@ -1,44 +1,41 @@ import os, subprocess, tempfile import time ANDROID_TMPDIR = '/data/local/tmp/Output' ADB = os.environ.get('ADB', 'adb') verbose = False if os.environ.get('ANDROID_RUN_VERBOSE') == '1': verbose = True def adb(args, attempts = 1): if verbose: print args tmpname = tempfile.mktemp() out = open(tmpname, 'w') ret = 255 while attempts > 0 and ret != 0: attempts -= 1 ret = subprocess.call([ADB] + args, stdout=out, stderr=subprocess.STDOUT) if attempts != 0: ret = 5 if ret != 0: print "adb command failed", args print tmpname out.close() out = open(tmpname, 'r') print out.read() out.close() os.unlink(tmpname) return ret def pull_from_device(path): tmp = tempfile.mktemp() adb(['pull', path, tmp], 5) text = open(tmp, 'r').read() os.unlink(tmp) return text def push_to_device(path): - # Workaround for https://code.google.com/p/android/issues/detail?id=65857 dst_path = os.path.join(ANDROID_TMPDIR, os.path.basename(path)) - tmp_path = dst_path + '.push' - adb(['push', path, tmp_path], 5) - adb(['shell', 'cp "%s" "%s" 2>&1' % (tmp_path, dst_path)], 5) + adb(['push', path, dst_path], 5) Index: vendor/compiler-rt/dist/test/builtins/Unit/fp_test.h =================================================================== --- vendor/compiler-rt/dist/test/builtins/Unit/fp_test.h (revision 320536) +++ vendor/compiler-rt/dist/test/builtins/Unit/fp_test.h (revision 320537) @@ -1,279 +1,279 @@ //===--------------------------- fp_test.h - ------------------------------===// // // The LLVM Compiler Infrastructure // // This file is dual licensed under the MIT and the University of Illinois Open // Source Licenses. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file defines shared functions for the test. // //===----------------------------------------------------------------------===// #include #include #include #include enum EXPECTED_RESULT { LESS_0, LESS_EQUAL_0, EQUAL_0, GREATER_0, GREATER_EQUAL_0, NEQUAL_0 }; static inline uint16_t fromRep16(uint16_t x) { return x; } static inline float fromRep32(uint32_t x) { float ret; memcpy(&ret, &x, 4); return ret; } static inline double fromRep64(uint64_t x) { double ret; memcpy(&ret, &x, 8); return ret; } #if __LDBL_MANT_DIG__ == 113 static inline long double fromRep128(uint64_t hi, uint64_t lo) { __uint128_t x = ((__uint128_t)hi << 64) + lo; long double ret; memcpy(&ret, &x, 16); return ret; } #endif static inline uint16_t toRep16(uint16_t x) { return x; } static inline uint32_t toRep32(float x) { uint32_t ret; memcpy(&ret, &x, 4); return ret; } static inline uint64_t toRep64(double x) { uint64_t ret; memcpy(&ret, &x, 8); return ret; } #if __LDBL_MANT_DIG__ == 113 static inline __uint128_t toRep128(long double x) { __uint128_t ret; memcpy(&ret, &x, 16); return ret; } #endif static inline int compareResultH(uint16_t result, uint16_t expected) { uint16_t rep = toRep16(result); if (rep == expected){ return 0; } - // test other posible NaN representation(signal NaN) + // test other possible NaN representation(signal NaN) else if (expected == 0x7e00U){ if ((rep & 0x7c00U) == 0x7c00U && (rep & 0x3ffU) > 0){ return 0; } } return 1; } static inline int compareResultF(float result, uint32_t expected) { uint32_t rep = toRep32(result); if (rep == expected){ return 0; } - // test other posible NaN representation(signal NaN) + // test other possible NaN representation(signal NaN) else if (expected == 0x7fc00000U){ if ((rep & 0x7f800000U) == 0x7f800000U && (rep & 0x7fffffU) > 0){ return 0; } } return 1; } static inline int compareResultD(double result, uint64_t expected) { uint64_t rep = toRep64(result); if (rep == expected){ return 0; } - // test other posible NaN representation(signal NaN) + // test other possible NaN representation(signal NaN) else if (expected == 0x7ff8000000000000UL){ if ((rep & 0x7ff0000000000000UL) == 0x7ff0000000000000UL && (rep & 0xfffffffffffffUL) > 0){ return 0; } } return 1; } #if __LDBL_MANT_DIG__ == 113 // return 0 if equal // use two 64-bit integers intead of one 128-bit integer // because 128-bit integer constant can't be assigned directly static inline int compareResultLD(long double result, uint64_t expectedHi, uint64_t expectedLo) { __uint128_t rep = toRep128(result); uint64_t hi = rep >> 64; uint64_t lo = rep; if (hi == expectedHi && lo == expectedLo){ return 0; } - // test other posible NaN representation(signal NaN) + // test other possible NaN representation(signal NaN) else if (expectedHi == 0x7fff800000000000UL && expectedLo == 0x0UL){ if ((hi & 0x7fff000000000000UL) == 0x7fff000000000000UL && ((hi & 0xffffffffffffUL) > 0 || lo > 0)){ return 0; } } return 1; } #endif static inline int compareResultCMP(int result, enum EXPECTED_RESULT expected) { switch(expected){ case LESS_0: if (result < 0) return 0; break; case LESS_EQUAL_0: if (result <= 0) return 0; break; case EQUAL_0: if (result == 0) return 0; break; case NEQUAL_0: if (result != 0) return 0; break; case GREATER_EQUAL_0: if (result >= 0) return 0; break; case GREATER_0: if (result > 0) return 0; break; default: return 1; } return 1; } static inline char *expectedStr(enum EXPECTED_RESULT expected) { switch(expected){ case LESS_0: return "<0"; case LESS_EQUAL_0: return "<=0"; case EQUAL_0: return "=0"; case NEQUAL_0: return "!=0"; case GREATER_EQUAL_0: return ">=0"; case GREATER_0: return ">0"; default: return ""; } return ""; } static inline uint16_t makeQNaN16() { return fromRep16(0x7e00U); } static inline float makeQNaN32() { return fromRep32(0x7fc00000U); } static inline double makeQNaN64() { return fromRep64(0x7ff8000000000000UL); } #if __LDBL_MANT_DIG__ == 113 static inline long double makeQNaN128() { return fromRep128(0x7fff800000000000UL, 0x0UL); } #endif static inline uint16_t makeNaN16(uint16_t rand) { return fromRep16(0x7c00U | (rand & 0x7fffU)); } static inline float makeNaN32(uint32_t rand) { return fromRep32(0x7f800000U | (rand & 0x7fffffU)); } static inline double makeNaN64(uint64_t rand) { return fromRep64(0x7ff0000000000000UL | (rand & 0xfffffffffffffUL)); } #if __LDBL_MANT_DIG__ == 113 static inline long double makeNaN128(uint64_t rand) { return fromRep128(0x7fff000000000000UL | (rand & 0xffffffffffffUL), 0x0UL); } #endif static inline uint16_t makeInf16() { return fromRep16(0x7c00U); } static inline float makeInf32() { return fromRep32(0x7f800000U); } static inline double makeInf64() { return fromRep64(0x7ff0000000000000UL); } #if __LDBL_MANT_DIG__ == 113 static inline long double makeInf128() { return fromRep128(0x7fff000000000000UL, 0x0UL); } #endif Index: vendor/compiler-rt/dist/test/cfi/sibling.cpp =================================================================== --- vendor/compiler-rt/dist/test/cfi/sibling.cpp (revision 320536) +++ vendor/compiler-rt/dist/test/cfi/sibling.cpp (revision 320537) @@ -1,54 +1,54 @@ // XFAIL: * // RUN: %clangxx_cfi -o %t1 %s // RUN: %expect_crash %t1 2>&1 | FileCheck --check-prefix=CFI %s // RUN: %clangxx_cfi -DB32 -o %t2 %s // RUN: %expect_crash %t2 2>&1 | FileCheck --check-prefix=CFI %s // RUN: %clangxx_cfi -DB64 -o %t3 %s // RUN: %expect_crash %t3 2>&1 | FileCheck --check-prefix=CFI %s // RUN: %clangxx_cfi -DBM -o %t4 %s // RUN: %expect_crash %t4 2>&1 | FileCheck --check-prefix=CFI %s // RUN: %clangxx -o %t5 %s // RUN: %t5 2>&1 | FileCheck --check-prefix=NCFI %s -// Tests that the CFI enforcement distinguishes betwen non-overriding siblings. +// Tests that the CFI enforcement distinguishes between non-overriding siblings. // XFAILed as not implemented yet. #include #include "utils.h" struct A { virtual void f(); }; void A::f() {} struct B : A { virtual void f(); }; void B::f() {} struct C : A { }; int main() { create_derivers(); B *b = new B; break_optimization(b); // CFI: 1 // NCFI: 1 fprintf(stderr, "1\n"); ((C *)b)->f(); // UB here // CFI-NOT: 2 // NCFI: 2 fprintf(stderr, "2\n"); } Index: vendor/compiler-rt/dist/test/lsan/TestCases/allocator_returns_null.cc =================================================================== --- vendor/compiler-rt/dist/test/lsan/TestCases/allocator_returns_null.cc (nonexistent) +++ vendor/compiler-rt/dist/test/lsan/TestCases/allocator_returns_null.cc (revision 320537) @@ -0,0 +1,123 @@ +// Test the behavior of malloc/calloc/realloc/new when the allocation size is +// more than LSan allocator's max allowed one. +// By default (allocator_may_return_null=0) the process should crash. +// With allocator_may_return_null=1 the allocator should return 0, except the +// operator new(), which should crash anyway (operator new(std::nothrow) should +// return nullptr, indeed). +// +// RUN: %clangxx_lsan -O0 %s -o %t +// RUN: not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: %env_lsan_opts=allocator_may_return_null=0 not %run %t malloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: %env_lsan_opts=allocator_may_return_null=1 %run %t malloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-mNULL +// RUN: %env_lsan_opts=allocator_may_return_null=0 not %run %t calloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-cCRASH +// RUN: %env_lsan_opts=allocator_may_return_null=1 %run %t calloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-cNULL +// RUN: %env_lsan_opts=allocator_may_return_null=0 not %run %t calloc-overflow 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-coCRASH +// RUN: %env_lsan_opts=allocator_may_return_null=1 %run %t calloc-overflow 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-coNULL +// RUN: %env_lsan_opts=allocator_may_return_null=0 not %run %t realloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-rCRASH +// RUN: %env_lsan_opts=allocator_may_return_null=1 %run %t realloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-rNULL +// RUN: %env_lsan_opts=allocator_may_return_null=0 not %run %t realloc-after-malloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-mrCRASH +// RUN: %env_lsan_opts=allocator_may_return_null=1 %run %t realloc-after-malloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-mrNULL +// RUN: %env_lsan_opts=allocator_may_return_null=0 not %run %t new 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-nCRASH +// RUN: %env_lsan_opts=allocator_may_return_null=1 not %run %t new 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-nCRASH +// RUN: %env_lsan_opts=allocator_may_return_null=0 not %run %t new-nothrow 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-nnCRASH +// RUN: %env_lsan_opts=allocator_may_return_null=1 %run %t new-nothrow 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-nnNULL + +#include +#include +#include +#include +#include +#include + +int main(int argc, char **argv) { + // Disable stderr buffering. Needed on Windows. + setvbuf(stderr, NULL, _IONBF, 0); + + assert(argc == 2); + const char *action = argv[1]; + fprintf(stderr, "%s:\n", action); + + // Use max of ASan and LSan allocator limits to cover both "lsan" and + // "lsan + asan" configs. + static const size_t kMaxAllowedMallocSizePlusOne = +#if __LP64__ || defined(_WIN64) + (1ULL << 40) + 1; +#else + (3UL << 30) + 1; +#endif + + void *x = 0; + if (!strcmp(action, "malloc")) { + x = malloc(kMaxAllowedMallocSizePlusOne); + } else if (!strcmp(action, "calloc")) { + x = calloc((kMaxAllowedMallocSizePlusOne / 4) + 1, 4); + } else if (!strcmp(action, "calloc-overflow")) { + volatile size_t kMaxSizeT = std::numeric_limits::max(); + size_t kArraySize = 4096; + volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10; + x = calloc(kArraySize, kArraySize2); + } else if (!strcmp(action, "realloc")) { + x = realloc(0, kMaxAllowedMallocSizePlusOne); + } else if (!strcmp(action, "realloc-after-malloc")) { + char *t = (char*)malloc(100); + *t = 42; + x = realloc(t, kMaxAllowedMallocSizePlusOne); + assert(*t == 42); + free(t); + } else if (!strcmp(action, "new")) { + x = operator new(kMaxAllowedMallocSizePlusOne); + } else if (!strcmp(action, "new-nothrow")) { + x = operator new(kMaxAllowedMallocSizePlusOne, std::nothrow); + } else { + assert(0); + } + + // The NULL pointer is printed differently on different systems, while (long)0 + // is always the same. + fprintf(stderr, "x: %zu\n", (size_t)x); + free(x); + + return x != 0; +} + +// CHECK-mCRASH: malloc: +// CHECK-mCRASH: Sanitizer's allocator is terminating the process +// CHECK-cCRASH: calloc: +// CHECK-cCRASH: Sanitizer's allocator is terminating the process +// CHECK-coCRASH: calloc-overflow: +// CHECK-coCRASH: Sanitizer's allocator is terminating the process +// CHECK-rCRASH: realloc: +// CHECK-rCRASH: Sanitizer's allocator is terminating the process +// CHECK-mrCRASH: realloc-after-malloc: +// CHECK-mrCRASH: Sanitizer's allocator is terminating the process +// CHECK-nCRASH: new: +// CHECK-nCRASH: Sanitizer's allocator is terminating the process +// CHECK-nnCRASH: new-nothrow: +// CHECK-nnCRASH: Sanitizer's allocator is terminating the process + +// CHECK-mNULL: malloc: +// CHECK-mNULL: x: 0 +// CHECK-cNULL: calloc: +// CHECK-cNULL: x: 0 +// CHECK-coNULL: calloc-overflow: +// CHECK-coNULL: x: 0 +// CHECK-rNULL: realloc: +// CHECK-rNULL: x: 0 +// CHECK-mrNULL: realloc-after-malloc: +// CHECK-mrNULL: x: 0 +// CHECK-nnNULL: new-nothrow: +// CHECK-nnNULL: x: 0 Property changes on: vendor/compiler-rt/dist/test/lsan/TestCases/allocator_returns_null.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/compiler-rt/dist/test/msan/allocator_returns_null.cc =================================================================== --- vendor/compiler-rt/dist/test/msan/allocator_returns_null.cc (revision 320536) +++ vendor/compiler-rt/dist/test/msan/allocator_returns_null.cc (revision 320537) @@ -1,81 +1,122 @@ -// Test the behavior of malloc/calloc/realloc when the allocation size is huge. +// Test the behavior of malloc/calloc/realloc/new when the allocation size is +// more than MSan allocator's max allowed one. // By default (allocator_may_return_null=0) the process should crash. -// With allocator_may_return_null=1 the allocator should return 0. +// With allocator_may_return_null=1 the allocator should return 0, except the +// operator new(), which should crash anyway (operator new(std::nothrow) should +// return nullptr, indeed). // // RUN: %clangxx_msan -O0 %s -o %t // RUN: not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH -// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH -// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mNULL -// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cCRASH -// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cNULL -// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coCRASH -// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coNULL -// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rCRASH -// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rNULL -// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrCRASH -// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrNULL +// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t malloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %t malloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-mNULL +// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t calloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-cCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %t calloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-cNULL +// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t calloc-overflow 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-coCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %t calloc-overflow 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-coNULL +// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t realloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-rCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %t realloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-rNULL +// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t realloc-after-malloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-mrCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %t realloc-after-malloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-mrNULL +// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t new 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-nCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=1 not %run %t new 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-nCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=0 not %run %t new-nothrow 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-nnCRASH +// RUN: MSAN_OPTIONS=allocator_may_return_null=1 %run %t new-nothrow 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-nnNULL -#include -#include + +#include #include #include -#include +#include #include +#include + int main(int argc, char **argv) { - volatile size_t size = std::numeric_limits::max() - 10000; + // Disable stderr buffering. Needed on Windows. + setvbuf(stderr, NULL, _IONBF, 0); + assert(argc == 2); - char *x = 0; - if (!strcmp(argv[1], "malloc")) { - fprintf(stderr, "malloc:\n"); - x = (char*)malloc(size); - } - if (!strcmp(argv[1], "calloc")) { - fprintf(stderr, "calloc:\n"); - x = (char*)calloc(size / 4, 4); - } + const char *action = argv[1]; + fprintf(stderr, "%s:\n", action); - if (!strcmp(argv[1], "calloc-overflow")) { - fprintf(stderr, "calloc-overflow:\n"); + static const size_t kMaxAllowedMallocSizePlusOne = +#if __LP64__ || defined(_WIN64) + (8UL << 30) + 1; +#else + (2UL << 30) + 1; +#endif + + void *x = 0; + if (!strcmp(action, "malloc")) { + x = malloc(kMaxAllowedMallocSizePlusOne); + } else if (!strcmp(action, "calloc")) { + x = calloc((kMaxAllowedMallocSizePlusOne / 4) + 1, 4); + } else if (!strcmp(action, "calloc-overflow")) { volatile size_t kMaxSizeT = std::numeric_limits::max(); size_t kArraySize = 4096; volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10; - x = (char*)calloc(kArraySize, kArraySize2); - } - - if (!strcmp(argv[1], "realloc")) { - fprintf(stderr, "realloc:\n"); - x = (char*)realloc(0, size); - } - if (!strcmp(argv[1], "realloc-after-malloc")) { - fprintf(stderr, "realloc-after-malloc:\n"); + x = calloc(kArraySize, kArraySize2); + } else if (!strcmp(action, "realloc")) { + x = realloc(0, kMaxAllowedMallocSizePlusOne); + } else if (!strcmp(action, "realloc-after-malloc")) { char *t = (char*)malloc(100); *t = 42; - x = (char*)realloc(t, size); + x = realloc(t, kMaxAllowedMallocSizePlusOne); assert(*t == 42); + free(t); + } else if (!strcmp(action, "new")) { + x = operator new(kMaxAllowedMallocSizePlusOne); + } else if (!strcmp(action, "new-nothrow")) { + x = operator new(kMaxAllowedMallocSizePlusOne, std::nothrow); + } else { + assert(0); } + // The NULL pointer is printed differently on different systems, while (long)0 // is always the same. fprintf(stderr, "x: %lx\n", (long)x); + free(x); + return x != 0; } + // CHECK-mCRASH: malloc: // CHECK-mCRASH: MemorySanitizer's allocator is terminating the process // CHECK-cCRASH: calloc: // CHECK-cCRASH: MemorySanitizer's allocator is terminating the process // CHECK-coCRASH: calloc-overflow: // CHECK-coCRASH: MemorySanitizer's allocator is terminating the process // CHECK-rCRASH: realloc: // CHECK-rCRASH: MemorySanitizer's allocator is terminating the process // CHECK-mrCRASH: realloc-after-malloc: // CHECK-mrCRASH: MemorySanitizer's allocator is terminating the process +// CHECK-nCRASH: new: +// CHECK-nCRASH: MemorySanitizer's allocator is terminating the process +// CHECK-nnCRASH: new-nothrow: +// CHECK-nnCRASH: MemorySanitizer's allocator is terminating the process // CHECK-mNULL: malloc: // CHECK-mNULL: x: 0 // CHECK-cNULL: calloc: // CHECK-cNULL: x: 0 // CHECK-coNULL: calloc-overflow: // CHECK-coNULL: x: 0 // CHECK-rNULL: realloc: // CHECK-rNULL: x: 0 // CHECK-mrNULL: realloc-after-malloc: // CHECK-mrNULL: x: 0 +// CHECK-nnNULL: new-nothrow: +// CHECK-nnNULL: x: 0 Index: vendor/compiler-rt/dist/test/profile/instrprof-override-filename.c =================================================================== --- vendor/compiler-rt/dist/test/profile/instrprof-override-filename.c (revision 320536) +++ vendor/compiler-rt/dist/test/profile/instrprof-override-filename.c (revision 320537) @@ -1,14 +1,24 @@ -// RUN: %clang_profgen=%t.profraw -o %t -O3 %s -// RUN: %run %t %t.profraw -// RUN: llvm-profdata merge -o %t.profdata %t.profraw -// RUN: %clang_profuse=%t.profdata -o - -S -emit-llvm %s | FileCheck %s +// RUN: rm -rf %t.dir && mkdir -p %t.dir +// RUN: cd %t.dir +// +// RUN: %clang_profgen=P_RAW -o %t -O3 %s +// RUN: %run %t P_RAW +// RUN: llvm-profdata merge -o %t.profdata P_RAW +// RUN: %clang_profuse=%t.profdata -o - -S -emit-llvm %s | FileCheck %s --check-prefix=FE +// +// RUN: %clang_pgogen=I_RAW -o %t.2 %s +// RUN: %run %t.2 I_RAW +// RUN: llvm-profdata merge -o %t2.profdata I_RAW +// RUN: %clang_profuse=%t2.profdata -o - -S -emit-llvm %s | FileCheck %s --check-prefix=IR void bar() {} int main(int argc, const char *argv[]) { - // CHECK: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof ![[PD1:[0-9]+]] + // FE: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof ![[PD1:[0-9]+]] + // IR: br i1 %{{.*}}, label %{{.*}}, label %{{.*}}, !prof ![[PD1:[0-9]+]] if (argc < 2) return 1; bar(); return 0; } -// CHECK: ![[PD1]] = !{!"branch_weights", i32 1, i32 2} +// FE: ![[PD1]] = !{!"branch_weights", i32 1, i32 2} +// IR: ![[PD1]] = !{!"branch_weights", i32 0, i32 1} Index: vendor/compiler-rt/dist/test/scudo/memalign.cpp =================================================================== --- vendor/compiler-rt/dist/test/scudo/memalign.cpp (revision 320536) +++ vendor/compiler-rt/dist/test/scudo/memalign.cpp (revision 320537) @@ -1,66 +1,80 @@ // RUN: %clang_scudo %s -o %t -// RUN: %run %t valid 2>&1 -// RUN: not %run %t invalid 2>&1 | FileCheck %s +// RUN: %run %t valid 2>&1 +// RUN: %run %t invalid 2>&1 // Tests that the various aligned allocation functions work as intended. Also // tests for the condition where the alignment is not a power of 2. #include +#include #include #include #include // Reduce the size of the quarantine, or the test can run out of aligned memory // on 32-bit for the larger alignments. extern "C" const char *__scudo_default_options() { return "QuarantineSizeMb=1"; } // Sometimes the headers may not have this... extern "C" void *aligned_alloc (size_t alignment, size_t size); int main(int argc, char **argv) { void *p = nullptr; size_t alignment = 1U << 12; size_t size = 1U << 12; + int err; assert(argc == 2); if (!strcmp(argv[1], "valid")) { posix_memalign(&p, alignment, size); assert(p); free(p); p = aligned_alloc(alignment, size); assert(p); free(p); // Tests various combinations of alignment and sizes for (int i = (sizeof(void *) == 4) ? 3 : 4; i < 19; i++) { alignment = 1U << i; for (int j = 1; j < 33; j++) { size = 0x800 * j; for (int k = 0; k < 3; k++) { p = memalign(alignment, size - (2 * sizeof(void *) * k)); assert(p); free(p); } } } // For larger alignment, reduce the number of allocations to avoid running // out of potential addresses (on 32-bit). for (int i = 19; i <= 24; i++) { for (int k = 0; k < 3; k++) { p = memalign(alignment, 0x1000 - (2 * sizeof(void *) * k)); assert(p); free(p); } } } if (!strcmp(argv[1], "invalid")) { + // Alignment is not a power of 2. p = memalign(alignment - 1, size); - free(p); + assert(!p); + // Size is not a multiple of alignment. + p = aligned_alloc(alignment, size >> 1); + assert(!p); + p = (void *)0x42UL; + // Alignment is not a power of 2. + err = posix_memalign(&p, 3, size); + assert(!p); + assert(err == EINVAL); + p = (void *)0x42UL; + // Alignment is a power of 2, but not a multiple of size(void *). + err = posix_memalign(&p, 2, size); + assert(!p); + assert(err == EINVAL); } return 0; } - -// CHECK: ERROR: alignment is not a power of 2 Index: vendor/compiler-rt/dist/test/scudo/sizes.cpp =================================================================== --- vendor/compiler-rt/dist/test/scudo/sizes.cpp (revision 320536) +++ vendor/compiler-rt/dist/test/scudo/sizes.cpp (revision 320537) @@ -1,54 +1,73 @@ -// RUN: %clang_scudo %s -o %t +// RUN: %clang_scudo %s -lstdc++ -o %t // RUN: SCUDO_OPTIONS=allocator_may_return_null=0 not %run %t malloc 2>&1 | FileCheck %s // RUN: SCUDO_OPTIONS=allocator_may_return_null=1 %run %t malloc 2>&1 // RUN: SCUDO_OPTIONS=allocator_may_return_null=0 not %run %t calloc 2>&1 | FileCheck %s // RUN: SCUDO_OPTIONS=allocator_may_return_null=1 %run %t calloc 2>&1 +// RUN: SCUDO_OPTIONS=allocator_may_return_null=0 not %run %t new 2>&1 | FileCheck %s +// RUN: SCUDO_OPTIONS=allocator_may_return_null=1 not %run %t new 2>&1 | FileCheck %s +// RUN: SCUDO_OPTIONS=allocator_may_return_null=0 not %run %t new-nothrow 2>&1 | FileCheck %s +// RUN: SCUDO_OPTIONS=allocator_may_return_null=1 %run %t new-nothrow 2>&1 // RUN: %run %t usable 2>&1 // Tests for various edge cases related to sizes, notably the maximum size the // allocator can allocate. Tests that an integer overflow in the parameters of // calloc is caught. #include #include #include #include #include +#include -int main(int argc, char **argv) -{ +int main(int argc, char **argv) { assert(argc == 2); - if (!strcmp(argv[1], "malloc")) { - // Currently the maximum size the allocator can allocate is 1ULL<<40 bytes. - size_t size = std::numeric_limits::max(); - void *p = malloc(size); + const char *action = argv[1]; + fprintf(stderr, "%s:\n", action); + +#if __LP64__ || defined(_WIN64) + static const size_t kMaxAllowedMallocSize = 1ULL << 40; + static const size_t kChunkHeaderSize = 16; +#else + static const size_t kMaxAllowedMallocSize = 2UL << 30; + static const size_t kChunkHeaderSize = 8; +#endif + + if (!strcmp(action, "malloc")) { + void *p = malloc(kMaxAllowedMallocSize); assert(!p); - size = (1ULL << 40) - 16; - p = malloc(size); + p = malloc(kMaxAllowedMallocSize - kChunkHeaderSize); assert(!p); - } - if (!strcmp(argv[1], "calloc")) { + } else if (!strcmp(action, "calloc")) { // Trigger an overflow in calloc. size_t size = std::numeric_limits::max(); void *p = calloc((size / 0x1000) + 1, 0x1000); assert(!p); - } - if (!strcmp(argv[1], "usable")) { + } else if (!strcmp(action, "new")) { + void *p = operator new(kMaxAllowedMallocSize); + assert(!p); + } else if (!strcmp(action, "new-nothrow")) { + void *p = operator new(kMaxAllowedMallocSize, std::nothrow); + assert(!p); + } else if (!strcmp(action, "usable")) { // Playing with the actual usable size of a chunk. void *p = malloc(1007); assert(p); size_t size = malloc_usable_size(p); assert(size >= 1007); memset(p, 'A', size); p = realloc(p, 2014); assert(p); size = malloc_usable_size(p); assert(size >= 2014); memset(p, 'B', size); free(p); + } else { + assert(0); } + return 0; } // CHECK: allocator is terminating the process Index: vendor/compiler-rt/dist/test/tsan/allocator_returns_null.cc =================================================================== --- vendor/compiler-rt/dist/test/tsan/allocator_returns_null.cc (revision 320536) +++ vendor/compiler-rt/dist/test/tsan/allocator_returns_null.cc (revision 320537) @@ -1,64 +1,114 @@ -// Test the behavior of malloc/calloc/realloc when the allocation size is huge. +// Test the behavior of malloc/calloc/realloc/new when the allocation size is +// more than TSan allocator's max allowed one. // By default (allocator_may_return_null=0) the process should crash. -// With allocator_may_return_null=1 the allocator should return 0. +// With allocator_may_return_null=1 the allocator should return 0, except the +// operator new(), which should crash anyway (operator new(std::nothrow) should +// return nullptr, indeed). // // RUN: %clangxx_tsan -O0 %s -o %t // RUN: not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH -// RUN: %env_tsan_opts=allocator_may_return_null=0 not %run %t malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mCRASH -// RUN: %env_tsan_opts=allocator_may_return_null=0 not %run %t calloc 2>&1 | FileCheck %s --check-prefix=CHECK-cCRASH -// RUN: %env_tsan_opts=allocator_may_return_null=0 not %run %t calloc-overflow 2>&1 | FileCheck %s --check-prefix=CHECK-coCRASH -// RUN: %env_tsan_opts=allocator_may_return_null=0 not %run %t realloc 2>&1 | FileCheck %s --check-prefix=CHECK-rCRASH -// RUN: %env_tsan_opts=allocator_may_return_null=0 not %run %t realloc-after-malloc 2>&1 | FileCheck %s --check-prefix=CHECK-mrCRASH +// RUN: %env_tsan_opts=allocator_may_return_null=0 not %run %t malloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-mCRASH +// RUN: %env_tsan_opts=allocator_may_return_null=1 %run %t malloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-mNULL +// RUN: %env_tsan_opts=allocator_may_return_null=0 not %run %t calloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-cCRASH +// RUN: %env_tsan_opts=allocator_may_return_null=1 %run %t calloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-cNULL +// RUN: %env_tsan_opts=allocator_may_return_null=0 not %run %t calloc-overflow 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-coCRASH +// RUN: %env_tsan_opts=allocator_may_return_null=1 %run %t calloc-overflow 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-coNULL +// RUN: %env_tsan_opts=allocator_may_return_null=0 not %run %t realloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-rCRASH +// RUN: %env_tsan_opts=allocator_may_return_null=1 %run %t realloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-rNULL +// RUN: %env_tsan_opts=allocator_may_return_null=0 not %run %t realloc-after-malloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-mrCRASH +// RUN: %env_tsan_opts=allocator_may_return_null=1 %run %t realloc-after-malloc 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-mrNULL +// RUN: %env_tsan_opts=allocator_may_return_null=0 not %run %t new 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-nCRASH +// RUN: %env_tsan_opts=allocator_may_return_null=1 not %run %t new 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-nCRASH +// RUN: %env_tsan_opts=allocator_may_return_null=0 not %run %t new-nothrow 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-nnCRASH +// RUN: %env_tsan_opts=allocator_may_return_null=1 %run %t new-nothrow 2>&1 \ +// RUN: | FileCheck %s --check-prefix=CHECK-nnNULL -#include -#include +#include #include #include -#include +#include #include +#include + int main(int argc, char **argv) { - volatile size_t size = std::numeric_limits::max() - 10000; + // Disable stderr buffering. Needed on Windows. + setvbuf(stderr, NULL, _IONBF, 0); + assert(argc == 2); - char *x = 0; - if (!strcmp(argv[1], "malloc")) { - fprintf(stderr, "malloc:\n"); - x = (char*)malloc(size); - } - if (!strcmp(argv[1], "calloc")) { - fprintf(stderr, "calloc:\n"); - x = (char*)calloc(size / 4, 4); - } + const char *action = argv[1]; + fprintf(stderr, "%s:\n", action); - if (!strcmp(argv[1], "calloc-overflow")) { - fprintf(stderr, "calloc-overflow:\n"); + static const size_t kMaxAllowedMallocSizePlusOne = (1ULL << 40) + 1; + + void *x = 0; + if (!strcmp(action, "malloc")) { + x = malloc(kMaxAllowedMallocSizePlusOne); + } else if (!strcmp(action, "calloc")) { + x = calloc((kMaxAllowedMallocSizePlusOne / 4) + 1, 4); + } else if (!strcmp(action, "calloc-overflow")) { volatile size_t kMaxSizeT = std::numeric_limits::max(); size_t kArraySize = 4096; volatile size_t kArraySize2 = kMaxSizeT / kArraySize + 10; - x = (char*)calloc(kArraySize, kArraySize2); - } - - if (!strcmp(argv[1], "realloc")) { - fprintf(stderr, "realloc:\n"); - x = (char*)realloc(0, size); - } - if (!strcmp(argv[1], "realloc-after-malloc")) { - fprintf(stderr, "realloc-after-malloc:\n"); + x = calloc(kArraySize, kArraySize2); + } else if (!strcmp(action, "realloc")) { + x = realloc(0, kMaxAllowedMallocSizePlusOne); + } else if (!strcmp(action, "realloc-after-malloc")) { char *t = (char*)malloc(100); *t = 42; - x = (char*)realloc(t, size); + x = realloc(t, kMaxAllowedMallocSizePlusOne); assert(*t == 42); + } else if (!strcmp(action, "new")) { + x = operator new(kMaxAllowedMallocSizePlusOne); + } else if (!strcmp(action, "new-nothrow")) { + x = operator new(kMaxAllowedMallocSizePlusOne, std::nothrow); + } else { + assert(0); } - fprintf(stderr, "x: %p\n", x); + + // The NULL pointer is printed differently on different systems, while (long)0 + // is always the same. + fprintf(stderr, "x: %lx\n", (long)x); + free(x); return x != 0; } + // CHECK-mCRASH: malloc: // CHECK-mCRASH: ThreadSanitizer's allocator is terminating the process // CHECK-cCRASH: calloc: // CHECK-cCRASH: ThreadSanitizer's allocator is terminating the process // CHECK-coCRASH: calloc-overflow: // CHECK-coCRASH: ThreadSanitizer's allocator is terminating the process // CHECK-rCRASH: realloc: // CHECK-rCRASH: ThreadSanitizer's allocator is terminating the process // CHECK-mrCRASH: realloc-after-malloc: // CHECK-mrCRASH: ThreadSanitizer's allocator is terminating the process +// CHECK-nCRASH: new: +// CHECK-nCRASH: ThreadSanitizer's allocator is terminating the process +// CHECK-nnCRASH: new-nothrow: +// CHECK-nnCRASH: ThreadSanitizer's allocator is terminating the process +// CHECK-mNULL: malloc: +// CHECK-mNULL: x: 0 +// CHECK-cNULL: calloc: +// CHECK-cNULL: x: 0 +// CHECK-coNULL: calloc-overflow: +// CHECK-coNULL: x: 0 +// CHECK-rNULL: realloc: +// CHECK-rNULL: x: 0 +// CHECK-mrNULL: realloc-after-malloc: +// CHECK-mrNULL: x: 0 +// CHECK-nnNULL: new-nothrow: +// CHECK-nnNULL: x: 0 Index: vendor/compiler-rt/dist/test/xray/TestCases/Linux/always-never-instrument.cc =================================================================== --- vendor/compiler-rt/dist/test/xray/TestCases/Linux/always-never-instrument.cc (nonexistent) +++ vendor/compiler-rt/dist/test/xray/TestCases/Linux/always-never-instrument.cc (revision 320537) @@ -0,0 +1,23 @@ +// Test that the always/never instrument lists apply. +// RUN: echo "fun:main" > %tmp-always.txt +// RUN: echo "fun:__xray*" > %tmp-never.txt +// RUN: %clangxx_xray \ +// RUN: -fxray-never-instrument=%tmp-never.txt \ +// RUN: -fxray-always-instrument=%tmp-always.txt \ +// RUN: %s -o %t +// RUN: %llvm_xray extract -symbolize %t | \ +// RUN: FileCheck %s --check-prefix NOINSTR +// RUN: %llvm_xray extract -symbolize %t | \ +// RUN: FileCheck %s --check-prefix ALWAYSINSTR +// REQUIRES: x86_64-linux +// REQUIRES: built-in-llvm-tree + +// NOINSTR-NOT: {{.*__xray_NeverInstrumented.*}} +int __xray_NeverInstrumented() { + return 0; +} + +// ALWAYSINSTR: {{.*function-name:.*main.*}} +int main(int argc, char *argv[]) { + return __xray_NeverInstrumented(); +} Property changes on: vendor/compiler-rt/dist/test/xray/TestCases/Linux/always-never-instrument.cc ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property