Index: vendor/compiler-rt/dist/include/sanitizer/coverage_interface.h =================================================================== --- vendor/compiler-rt/dist/include/sanitizer/coverage_interface.h (revision 319464) +++ vendor/compiler-rt/dist/include/sanitizer/coverage_interface.h (revision 319465) @@ -1,42 +1,39 @@ //===-- sanitizer/coverage_interface.h --------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Public interface for sanitizer coverage. //===----------------------------------------------------------------------===// #ifndef SANITIZER_COVERAG_INTERFACE_H #define SANITIZER_COVERAG_INTERFACE_H #include #ifdef __cplusplus extern "C" { #endif // Initialize coverage. void __sanitizer_cov_init(); // Record and dump coverage info. void __sanitizer_cov_dump(); // Dump collected coverage info. Sorts pcs by module into individual // .sancov files. void __sanitizer_dump_coverage(const uintptr_t *pcs, uintptr_t len); // Open .sancov.packed in the coverage directory and return the file // descriptor. Returns -1 on failure, or if coverage dumping is disabled. // This is intended for use by sandboxing code. intptr_t __sanitizer_maybe_open_cov_file(const char *name); - // Get the number of unique covered blocks (or edges). - // This can be useful for coverage-directed in-process fuzzers. - uintptr_t __sanitizer_get_total_unique_coverage(); #ifdef __cplusplus } // extern "C" #endif #endif // SANITIZER_COVERAG_INTERFACE_H Index: vendor/compiler-rt/dist/lib/asan/asan_allocator.cc =================================================================== --- vendor/compiler-rt/dist/lib/asan/asan_allocator.cc (revision 319464) +++ vendor/compiler-rt/dist/lib/asan/asan_allocator.cc (revision 319465) @@ -1,996 +1,994 @@ //===-- 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, false); } 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; // ------------------- 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) { allocator.Init(options.may_return_null, options.release_to_os_interval_ms); SharedInitCode(options); } 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) { allocator.SetMayReturnNull(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 = allocator.MayReturnNull(); 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(); 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 allocator.ReturnNullOrDieOnBadRequest(); } AsanThread *t = GetCurrentThread(); void *allocated; bool check_rss_limit = true; if (t) { AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage()); allocated = allocator.Allocate(cache, needed_size, 8, false, check_rss_limit); } else { SpinMutexLock l(&fallback_mutex); AllocatorCache *cache = &fallback_allocator_cache; allocated = allocator.Allocate(cache, needed_size, 8, false, check_rss_limit); } if (!allocated) return allocator.ReturnNullOrDieOnOOM(); 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) { 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); } 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)) return allocator.ReturnNullOrDieOnBadRequest(); 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() { +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 exceeded) { instance.allocator.SetRssLimitIsExceeded(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_allocator.h =================================================================== --- vendor/compiler-rt/dist/lib/asan/asan_allocator.h (revision 319464) +++ vendor/compiler-rt/dist/lib/asan/asan_allocator.h (revision 319465) @@ -1,217 +1,219 @@ //===-- asan_allocator.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 a part of AddressSanitizer, an address sanity checker. // // ASan-private header for asan_allocator.cc. //===----------------------------------------------------------------------===// #ifndef ASAN_ALLOCATOR_H #define ASAN_ALLOCATOR_H #include "asan_flags.h" #include "asan_internal.h" #include "asan_interceptors.h" #include "sanitizer_common/sanitizer_allocator.h" #include "sanitizer_common/sanitizer_list.h" namespace __asan { enum AllocType { FROM_MALLOC = 1, // Memory block came from malloc, calloc, realloc, etc. FROM_NEW = 2, // Memory block came from operator new. FROM_NEW_BR = 3 // Memory block came from operator new [ ] }; struct AsanChunk; struct AllocatorOptions { u32 quarantine_size_mb; u32 thread_local_quarantine_size_kb; u16 min_redzone; u16 max_redzone; u8 may_return_null; u8 alloc_dealloc_mismatch; s32 release_to_os_interval_ms; void SetFrom(const Flags *f, const CommonFlags *cf); void CopyTo(Flags *f, CommonFlags *cf); }; void InitializeAllocator(const AllocatorOptions &options); void ReInitializeAllocator(const AllocatorOptions &options); void GetAllocatorOptions(AllocatorOptions *options); class AsanChunkView { public: explicit AsanChunkView(AsanChunk *chunk) : chunk_(chunk) {} bool IsValid() const; // Checks if AsanChunkView points to a valid // allocated or quarantined chunk. bool IsAllocated() const; // Checks if the memory is currently allocated. bool IsQuarantined() const; // Checks if the memory is currently quarantined. uptr Beg() const; // First byte of user memory. uptr End() const; // Last byte of user memory. uptr UsedSize() const; // Size requested by the user. uptr AllocTid() const; uptr FreeTid() const; bool Eq(const AsanChunkView &c) const { return chunk_ == c.chunk_; } u32 GetAllocStackId() const; u32 GetFreeStackId() const; StackTrace GetAllocStack() const; StackTrace GetFreeStack() const; AllocType GetAllocType() const; bool AddrIsInside(uptr addr, uptr access_size, sptr *offset) const { if (addr >= Beg() && (addr + access_size) <= End()) { *offset = addr - Beg(); return true; } return false; } bool AddrIsAtLeft(uptr addr, uptr access_size, sptr *offset) const { (void)access_size; if (addr < Beg()) { *offset = Beg() - addr; return true; } return false; } bool AddrIsAtRight(uptr addr, uptr access_size, sptr *offset) const { if (addr + access_size > End()) { *offset = addr - End(); return true; } return false; } private: AsanChunk *const chunk_; }; AsanChunkView FindHeapChunkByAddress(uptr address); AsanChunkView FindHeapChunkByAllocBeg(uptr address); // List of AsanChunks with total size. class AsanChunkFifoList: public IntrusiveList { public: explicit AsanChunkFifoList(LinkerInitialized) { } AsanChunkFifoList() { clear(); } void Push(AsanChunk *n); void PushList(AsanChunkFifoList *q); AsanChunk *Pop(); uptr size() { return size_; } void clear() { IntrusiveList::clear(); size_ = 0; } private: uptr size_; }; struct AsanMapUnmapCallback { void OnMap(uptr p, uptr size) const; void OnUnmap(uptr p, uptr size) const; }; #if SANITIZER_CAN_USE_ALLOCATOR64 # if defined(__powerpc64__) const uptr kAllocatorSpace = 0xa0000000000ULL; const uptr kAllocatorSize = 0x20000000000ULL; // 2T. typedef DefaultSizeClassMap SizeClassMap; # elif defined(__aarch64__) && SANITIZER_ANDROID const uptr kAllocatorSpace = 0x3000000000ULL; const uptr kAllocatorSize = 0x2000000000ULL; // 128G. typedef VeryCompactSizeClassMap SizeClassMap; # elif defined(__aarch64__) // AArch64/SANITIZER_CAN_USER_ALLOCATOR64 is only for 42-bit VMA // so no need to different values for different VMA. const uptr kAllocatorSpace = 0x10000000000ULL; const uptr kAllocatorSize = 0x10000000000ULL; // 3T. typedef DefaultSizeClassMap SizeClassMap; # elif SANITIZER_WINDOWS const uptr kAllocatorSpace = ~(uptr)0; const uptr kAllocatorSize = 0x8000000000ULL; // 500G typedef DefaultSizeClassMap SizeClassMap; # else const uptr kAllocatorSpace = 0x600000000000ULL; const uptr kAllocatorSize = 0x40000000000ULL; // 4T. typedef DefaultSizeClassMap SizeClassMap; # endif struct AP64 { // Allocator64 parameters. Deliberately using a short name. static const uptr kSpaceBeg = kAllocatorSpace; static const uptr kSpaceSize = kAllocatorSize; static const uptr kMetadataSize = 0; typedef __asan::SizeClassMap SizeClassMap; typedef AsanMapUnmapCallback MapUnmapCallback; static const uptr kFlags = 0; }; typedef SizeClassAllocator64 PrimaryAllocator; #else // Fallback to SizeClassAllocator32. static const uptr kRegionSizeLog = 20; static const uptr kNumRegions = SANITIZER_MMAP_RANGE_SIZE >> kRegionSizeLog; # if SANITIZER_WORDSIZE == 32 typedef FlatByteMap ByteMap; # elif SANITIZER_WORDSIZE == 64 typedef TwoLevelByteMap<(kNumRegions >> 12), 1 << 12> ByteMap; # endif typedef CompactSizeClassMap SizeClassMap; struct AP32 { static const uptr kSpaceBeg = 0; static const u64 kSpaceSize = SANITIZER_MMAP_RANGE_SIZE; static const uptr kMetadataSize = 16; typedef __asan::SizeClassMap SizeClassMap; static const uptr kRegionSizeLog = __asan::kRegionSizeLog; typedef __asan::ByteMap ByteMap; typedef AsanMapUnmapCallback MapUnmapCallback; static const uptr kFlags = 0; }; typedef SizeClassAllocator32 PrimaryAllocator; #endif // SANITIZER_CAN_USE_ALLOCATOR64 static const uptr kNumberOfSizeClasses = SizeClassMap::kNumClasses; typedef SizeClassAllocatorLocalCache AllocatorCache; typedef LargeMmapAllocator SecondaryAllocator; typedef CombinedAllocator AsanAllocator; struct AsanThreadLocalMallocStorage { uptr quarantine_cache[16]; AllocatorCache allocator_cache; void CommitBack(); private: // These objects are allocated via mmap() and are zero-initialized. AsanThreadLocalMallocStorage() {} }; void *asan_memalign(uptr alignment, uptr size, BufferedStackTrace *stack, AllocType alloc_type); void asan_free(void *ptr, BufferedStackTrace *stack, AllocType alloc_type); void asan_sized_free(void *ptr, uptr size, BufferedStackTrace *stack, AllocType alloc_type); void *asan_malloc(uptr size, BufferedStackTrace *stack); void *asan_calloc(uptr nmemb, uptr size, BufferedStackTrace *stack); void *asan_realloc(void *p, uptr size, BufferedStackTrace *stack); void *asan_valloc(uptr size, BufferedStackTrace *stack); void *asan_pvalloc(uptr size, BufferedStackTrace *stack); int asan_posix_memalign(void **memptr, uptr alignment, uptr size, BufferedStackTrace *stack); uptr asan_malloc_usable_size(const void *ptr, uptr pc, uptr bp); uptr asan_mz_size(const void *ptr); void asan_mz_force_lock(); void asan_mz_force_unlock(); void PrintInternalAllocatorStats(); void AsanSoftRssLimitExceededCallback(bool exceeded); +AsanAllocator &get_allocator(); + } // namespace __asan #endif // ASAN_ALLOCATOR_H Index: vendor/compiler-rt/dist/lib/asan/asan_flags.cc =================================================================== --- vendor/compiler-rt/dist/lib/asan/asan_flags.cc (revision 319464) +++ vendor/compiler-rt/dist/lib/asan/asan_flags.cc (revision 319465) @@ -1,203 +1,207 @@ //===-- asan_flags.cc -------------------------------------------*- 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 a part of AddressSanitizer, an address sanity checker. // // ASan flag parsing logic. //===----------------------------------------------------------------------===// #include "asan_activation.h" #include "asan_flags.h" #include "asan_interface_internal.h" #include "asan_stack.h" #include "lsan/lsan_common.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_flag_parser.h" #include "ubsan/ubsan_flags.h" #include "ubsan/ubsan_platform.h" namespace __asan { Flags asan_flags_dont_use_directly; // use via flags(). static const char *MaybeCallAsanDefaultOptions() { return (&__asan_default_options) ? __asan_default_options() : ""; } static const char *MaybeUseAsanDefaultOptionsCompileDefinition() { #ifdef ASAN_DEFAULT_OPTIONS // Stringize the macro value. # define ASAN_STRINGIZE(x) #x # define ASAN_STRINGIZE_OPTIONS(options) ASAN_STRINGIZE(options) return ASAN_STRINGIZE_OPTIONS(ASAN_DEFAULT_OPTIONS); #else return ""; #endif } void Flags::SetDefaults() { #define ASAN_FLAG(Type, Name, DefaultValue, Description) Name = DefaultValue; #include "asan_flags.inc" #undef ASAN_FLAG } static void RegisterAsanFlags(FlagParser *parser, Flags *f) { #define ASAN_FLAG(Type, Name, DefaultValue, Description) \ RegisterFlag(parser, #Name, Description, &f->Name); #include "asan_flags.inc" #undef ASAN_FLAG } void InitializeFlags() { // Set the default values and prepare for parsing ASan and common flags. SetCommonFlagsDefaults(); { CommonFlags cf; cf.CopyFrom(*common_flags()); cf.detect_leaks = cf.detect_leaks && CAN_SANITIZE_LEAKS; cf.external_symbolizer_path = GetEnv("ASAN_SYMBOLIZER_PATH"); cf.malloc_context_size = kDefaultMallocContextSize; cf.intercept_tls_get_addr = true; cf.exitcode = 1; OverrideCommonFlags(cf); } Flags *f = flags(); f->SetDefaults(); FlagParser asan_parser; RegisterAsanFlags(&asan_parser, f); RegisterCommonFlags(&asan_parser); // Set the default values and prepare for parsing LSan and UBSan flags // (which can also overwrite common flags). #if CAN_SANITIZE_LEAKS __lsan::Flags *lf = __lsan::flags(); lf->SetDefaults(); FlagParser lsan_parser; __lsan::RegisterLsanFlags(&lsan_parser, lf); RegisterCommonFlags(&lsan_parser); #endif #if CAN_SANITIZE_UB __ubsan::Flags *uf = __ubsan::flags(); uf->SetDefaults(); FlagParser ubsan_parser; __ubsan::RegisterUbsanFlags(&ubsan_parser, uf); RegisterCommonFlags(&ubsan_parser); #endif if (SANITIZER_MAC) { // Support macOS MallocScribble and MallocPreScribble: // if (GetEnv("MallocScribble")) { f->max_free_fill_size = 0x1000; } if (GetEnv("MallocPreScribble")) { f->malloc_fill_byte = 0xaa; } } // Override from ASan compile definition. const char *asan_compile_def = MaybeUseAsanDefaultOptionsCompileDefinition(); asan_parser.ParseString(asan_compile_def); // Override from user-specified string. const char *asan_default_options = MaybeCallAsanDefaultOptions(); asan_parser.ParseString(asan_default_options); #if CAN_SANITIZE_UB const char *ubsan_default_options = __ubsan::MaybeCallUbsanDefaultOptions(); ubsan_parser.ParseString(ubsan_default_options); #endif // Override from command line. asan_parser.ParseString(GetEnv("ASAN_OPTIONS")); #if CAN_SANITIZE_LEAKS lsan_parser.ParseString(GetEnv("LSAN_OPTIONS")); #endif #if CAN_SANITIZE_UB ubsan_parser.ParseString(GetEnv("UBSAN_OPTIONS")); #endif InitializeCommonFlags(); // TODO(eugenis): dump all flags at verbosity>=2? if (Verbosity()) ReportUnrecognizedFlags(); if (common_flags()->help) { // TODO(samsonov): print all of the flags (ASan, LSan, common). asan_parser.PrintFlagDescriptions(); } // Flag validation: if (!CAN_SANITIZE_LEAKS && common_flags()->detect_leaks) { Report("%s: detect_leaks is not supported on this platform.\n", SanitizerToolName); Die(); } // Make "strict_init_order" imply "check_initialization_order". // TODO(samsonov): Use a single runtime flag for an init-order checker. if (f->strict_init_order) { f->check_initialization_order = true; } CHECK_LE((uptr)common_flags()->malloc_context_size, kStackTraceMax); CHECK_LE(f->min_uar_stack_size_log, f->max_uar_stack_size_log); CHECK_GE(f->redzone, 16); CHECK_GE(f->max_redzone, f->redzone); CHECK_LE(f->max_redzone, 2048); CHECK(IsPowerOfTwo(f->redzone)); CHECK(IsPowerOfTwo(f->max_redzone)); // quarantine_size is deprecated but we still honor it. // quarantine_size can not be used together with quarantine_size_mb. if (f->quarantine_size >= 0 && f->quarantine_size_mb >= 0) { Report("%s: please use either 'quarantine_size' (deprecated) or " "quarantine_size_mb, but not both\n", SanitizerToolName); Die(); } if (f->quarantine_size >= 0) f->quarantine_size_mb = f->quarantine_size >> 20; if (f->quarantine_size_mb < 0) { const int kDefaultQuarantineSizeMb = (ASAN_LOW_MEMORY) ? 1UL << 4 : 1UL << 8; f->quarantine_size_mb = kDefaultQuarantineSizeMb; } if (f->thread_local_quarantine_size_kb < 0) { const u32 kDefaultThreadLocalQuarantineSizeKb = // It is not advised to go lower than 64Kb, otherwise quarantine batches // pushed from thread local quarantine to global one will create too // much overhead. One quarantine batch size is 8Kb and it holds up to // 1021 chunk, which amounts to 1/8 memory overhead per batch when // thread local quarantine is set to 64Kb. (ASAN_LOW_MEMORY) ? 1 << 6 : FIRST_32_SECOND_64(1 << 8, 1 << 10); f->thread_local_quarantine_size_kb = kDefaultThreadLocalQuarantineSizeKb; } if (f->thread_local_quarantine_size_kb == 0 && f->quarantine_size_mb > 0) { Report("%s: thread_local_quarantine_size_kb can be set to 0 only when " "quarantine_size_mb is set to 0\n", SanitizerToolName); Die(); } if (!f->replace_str && common_flags()->intercept_strlen) { Report("WARNING: strlen interceptor is enabled even though replace_str=0. " "Use intercept_strlen=0 to disable it."); } if (!f->replace_str && common_flags()->intercept_strchr) { Report("WARNING: strchr* interceptors are enabled even though " "replace_str=0. Use intercept_strchr=0 to disable them."); } + if (!f->replace_str && common_flags()->intercept_strndup) { + Report("WARNING: strndup* interceptors are enabled even though " + "replace_str=0. Use intercept_strndup=0 to disable them."); + } } } // namespace __asan SANITIZER_INTERFACE_WEAK_DEF(const char*, __asan_default_options, void) { return ""; } Index: vendor/compiler-rt/dist/lib/asan/asan_interceptors.cc =================================================================== --- vendor/compiler-rt/dist/lib/asan/asan_interceptors.cc (revision 319464) +++ vendor/compiler-rt/dist/lib/asan/asan_interceptors.cc (revision 319465) @@ -1,798 +1,815 @@ //===-- 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_stackdepot.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) \ CoverageUpdateMapping() #define COMMON_INTERCEPTOR_LIBRARY_UNLOADED() CoverageUpdateMapping() #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 +static void BeforeFork() { + if (SANITIZER_LINUX) { + get_allocator().ForceLock(); + StackDepotLockAll(); + } +} + +static void AfterFork() { + if (SANITIZER_LINUX) { + StackDepotUnlockAll(); + get_allocator().ForceUnlock(); + } +} + INTERCEPTOR(int, fork, void) { ENSURE_ASAN_INITED(); + BeforeFork(); if (common_flags()->coverage) CovBeforeFork(); int pid = REAL(fork)(); if (common_flags()->coverage) CovAfterFork(pid); + AfterFork(); 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/tests/asan_str_test.cc =================================================================== --- vendor/compiler-rt/dist/lib/asan/tests/asan_str_test.cc (revision 319464) +++ vendor/compiler-rt/dist/lib/asan/tests/asan_str_test.cc (revision 319465) @@ -1,606 +1,633 @@ //=-- asan_str_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 AddressSanitizer, an address sanity checker. // //===----------------------------------------------------------------------===// #include "asan_test_utils.h" #if defined(__APPLE__) #include // For MAC_OS_X_VERSION_* #endif // Used for string functions tests static char global_string[] = "global"; static size_t global_string_length = 6; const char kStackReadUnderflow[] = #if !GTEST_USES_SIMPLE_RE ASAN_PCRE_DOTALL "READ.*" #endif "underflows this variable"; const char kStackReadOverflow[] = #if !GTEST_USES_SIMPLE_RE ASAN_PCRE_DOTALL "READ.*" #endif "overflows this variable"; namespace { enum class OOBKind { Heap, Stack, Global, }; string LeftOOBReadMessage(OOBKind oob_kind, int oob_distance) { return oob_kind == OOBKind::Stack ? kStackReadUnderflow : ::LeftOOBReadMessage(oob_distance); } string RightOOBReadMessage(OOBKind oob_kind, int oob_distance) { return oob_kind == OOBKind::Stack ? kStackReadOverflow : ::RightOOBReadMessage(oob_distance); } } // namespace // Input to a test is a zero-terminated string str with given length // Accesses to the bytes to the left and to the right of str // are presumed to produce OOB errors void StrLenOOBTestTemplate(char *str, size_t length, OOBKind oob_kind) { // Normal strlen calls EXPECT_EQ(strlen(str), length); if (length > 0) { EXPECT_EQ(length - 1, strlen(str + 1)); EXPECT_EQ(0U, strlen(str + length)); } // Arg of strlen is not malloced, OOB access if (oob_kind != OOBKind::Global) { // We don't insert RedZones to the left of global variables EXPECT_DEATH(Ident(strlen(str - 1)), LeftOOBReadMessage(oob_kind, 1)); EXPECT_DEATH(Ident(strlen(str - 5)), LeftOOBReadMessage(oob_kind, 5)); } EXPECT_DEATH(Ident(strlen(str + length + 1)), RightOOBReadMessage(oob_kind, 0)); // Overwrite terminator str[length] = 'a'; // String is not zero-terminated, strlen will lead to OOB access EXPECT_DEATH(Ident(strlen(str)), RightOOBReadMessage(oob_kind, 0)); EXPECT_DEATH(Ident(strlen(str + length)), RightOOBReadMessage(oob_kind, 0)); // Restore terminator str[length] = 0; } TEST(AddressSanitizer, StrLenOOBTest) { // Check heap-allocated string size_t length = Ident(10); char *heap_string = Ident((char*)malloc(length + 1)); char stack_string[10 + 1]; break_optimization(&stack_string); for (size_t i = 0; i < length; i++) { heap_string[i] = 'a'; stack_string[i] = 'b'; } heap_string[length] = 0; stack_string[length] = 0; StrLenOOBTestTemplate(heap_string, length, OOBKind::Heap); StrLenOOBTestTemplate(stack_string, length, OOBKind::Stack); StrLenOOBTestTemplate(global_string, global_string_length, OOBKind::Global); free(heap_string); } TEST(AddressSanitizer, WcsLenTest) { EXPECT_EQ(0U, wcslen(Ident(L""))); size_t hello_len = 13; size_t hello_size = (hello_len + 1) * sizeof(wchar_t); EXPECT_EQ(hello_len, wcslen(Ident(L"Hello, World!"))); wchar_t *heap_string = Ident((wchar_t*)malloc(hello_size)); memcpy(heap_string, L"Hello, World!", hello_size); EXPECT_EQ(hello_len, Ident(wcslen(heap_string))); EXPECT_DEATH(Ident(wcslen(heap_string + 14)), RightOOBReadMessage(0)); free(heap_string); } #if SANITIZER_TEST_HAS_STRNLEN TEST(AddressSanitizer, StrNLenOOBTest) { size_t size = Ident(123); char *str = MallocAndMemsetString(size); // Normal strnlen calls. Ident(strnlen(str - 1, 0)); Ident(strnlen(str, size)); Ident(strnlen(str + size - 1, 1)); str[size - 1] = '\0'; Ident(strnlen(str, 2 * size)); // Argument points to not allocated memory. EXPECT_DEATH(Ident(strnlen(str - 1, 1)), LeftOOBReadMessage(1)); EXPECT_DEATH(Ident(strnlen(str + size, 1)), RightOOBReadMessage(0)); // Overwrite the terminating '\0' and hit unallocated memory. str[size - 1] = 'z'; EXPECT_DEATH(Ident(strnlen(str, size + 1)), RightOOBReadMessage(0)); free(str); } #endif // SANITIZER_TEST_HAS_STRNLEN // This test fails with the WinASan dynamic runtime because we fail to intercept // strdup. #if defined(_MSC_VER) && defined(_DLL) #define MAYBE_StrDupOOBTest DISABLED_StrDupOOBTest #else #define MAYBE_StrDupOOBTest StrDupOOBTest #endif TEST(AddressSanitizer, MAYBE_StrDupOOBTest) { size_t size = Ident(42); char *str = MallocAndMemsetString(size); char *new_str; // Normal strdup calls. str[size - 1] = '\0'; new_str = strdup(str); free(new_str); new_str = strdup(str + size - 1); free(new_str); // Argument points to not allocated memory. EXPECT_DEATH(Ident(strdup(str - 1)), LeftOOBReadMessage(1)); EXPECT_DEATH(Ident(strdup(str + size)), RightOOBReadMessage(0)); // Overwrite the terminating '\0' and hit unallocated memory. str[size - 1] = 'z'; EXPECT_DEATH(Ident(strdup(str)), RightOOBReadMessage(0)); free(str); } +#if SANITIZER_TEST_HAS_STRNDUP +TEST(AddressSanitizer, MAYBE_StrNDupOOBTest) { + size_t size = Ident(42); + char *str = MallocAndMemsetString(size); + char *new_str; + // Normal strndup calls. + str[size - 1] = '\0'; + new_str = strndup(str, size - 13); + free(new_str); + new_str = strndup(str + size - 1, 13); + free(new_str); + // Argument points to not allocated memory. + EXPECT_DEATH(Ident(strndup(str - 1, 13)), LeftOOBReadMessage(1)); + EXPECT_DEATH(Ident(strndup(str + size, 13)), RightOOBReadMessage(0)); + // Overwrite the terminating '\0' and hit unallocated memory. + str[size - 1] = 'z'; + EXPECT_DEATH(Ident(strndup(str, size + 13)), RightOOBReadMessage(0)); + // Check handling of non 0 terminated strings. + Ident(new_str = strndup(str + size - 1, 0)); + free(new_str); + Ident(new_str = strndup(str + size - 1, 1)); + free(new_str); + EXPECT_DEATH(Ident(strndup(str + size - 1, 2)), RightOOBReadMessage(0)); + free(str); +} +#endif // SANITIZER_TEST_HAS_STRNDUP + TEST(AddressSanitizer, StrCpyOOBTest) { size_t to_size = Ident(30); size_t from_size = Ident(6); // less than to_size char *to = Ident((char*)malloc(to_size)); char *from = Ident((char*)malloc(from_size)); // Normal strcpy calls. strcpy(from, "hello"); strcpy(to, from); strcpy(to + to_size - from_size, from); // Length of "from" is too small. EXPECT_DEATH(Ident(strcpy(from, "hello2")), RightOOBWriteMessage(0)); // "to" or "from" points to not allocated memory. EXPECT_DEATH(Ident(strcpy(to - 1, from)), LeftOOBWriteMessage(1)); EXPECT_DEATH(Ident(strcpy(to, from - 1)), LeftOOBReadMessage(1)); EXPECT_DEATH(Ident(strcpy(to, from + from_size)), RightOOBReadMessage(0)); EXPECT_DEATH(Ident(strcpy(to + to_size, from)), RightOOBWriteMessage(0)); // Overwrite the terminating '\0' character and hit unallocated memory. from[from_size - 1] = '!'; EXPECT_DEATH(Ident(strcpy(to, from)), RightOOBReadMessage(0)); free(to); free(from); } TEST(AddressSanitizer, StrNCpyOOBTest) { size_t to_size = Ident(20); size_t from_size = Ident(6); // less than to_size char *to = Ident((char*)malloc(to_size)); // From is a zero-terminated string "hello\0" of length 6 char *from = Ident((char*)malloc(from_size)); strcpy(from, "hello"); // copy 0 bytes strncpy(to, from, 0); strncpy(to - 1, from - 1, 0); // normal strncpy calls strncpy(to, from, from_size); strncpy(to, from, to_size); strncpy(to, from + from_size - 1, to_size); strncpy(to + to_size - 1, from, 1); // One of {to, from} points to not allocated memory EXPECT_DEATH(Ident(strncpy(to, from - 1, from_size)), LeftOOBReadMessage(1)); EXPECT_DEATH(Ident(strncpy(to - 1, from, from_size)), LeftOOBWriteMessage(1)); EXPECT_DEATH(Ident(strncpy(to, from + from_size, 1)), RightOOBReadMessage(0)); EXPECT_DEATH(Ident(strncpy(to + to_size, from, 1)), RightOOBWriteMessage(0)); // Length of "to" is too small EXPECT_DEATH(Ident(strncpy(to + to_size - from_size + 1, from, from_size)), RightOOBWriteMessage(0)); EXPECT_DEATH(Ident(strncpy(to + 1, from, to_size)), RightOOBWriteMessage(0)); // Overwrite terminator in from from[from_size - 1] = '!'; // normal strncpy call strncpy(to, from, from_size); // Length of "from" is too small EXPECT_DEATH(Ident(strncpy(to, from, to_size)), RightOOBReadMessage(0)); free(to); free(from); } // Users may have different definitions of "strchr" and "index", so provide // function pointer typedefs and overload RunStrChrTest implementation. // We can't use macro for RunStrChrTest body here, as this macro would // confuse EXPECT_DEATH gtest macro. typedef char*(*PointerToStrChr1)(const char*, int); typedef char*(*PointerToStrChr2)(char*, int); template static void RunStrChrTestImpl(StrChrFn *StrChr) { size_t size = Ident(100); char *str = MallocAndMemsetString(size); str[10] = 'q'; str[11] = '\0'; EXPECT_EQ(str, StrChr(str, 'z')); EXPECT_EQ(str + 10, StrChr(str, 'q')); EXPECT_EQ(NULL, StrChr(str, 'a')); // StrChr argument points to not allocated memory. EXPECT_DEATH(Ident(StrChr(str - 1, 'z')), LeftOOBReadMessage(1)); EXPECT_DEATH(Ident(StrChr(str + size, 'z')), RightOOBReadMessage(0)); // Overwrite the terminator and hit not allocated memory. str[11] = 'z'; EXPECT_DEATH(Ident(StrChr(str, 'a')), RightOOBReadMessage(0)); free(str); } // Prefer to use the standard signature if both are available. UNUSED static void RunStrChrTest(PointerToStrChr1 StrChr, ...) { RunStrChrTestImpl(StrChr); } UNUSED static void RunStrChrTest(PointerToStrChr2 StrChr, int) { RunStrChrTestImpl(StrChr); } TEST(AddressSanitizer, StrChrAndIndexOOBTest) { RunStrChrTest(&strchr, 0); // No index() on Windows and on Android L. #if !defined(_WIN32) && !defined(__ANDROID__) RunStrChrTest(&index, 0); #endif } TEST(AddressSanitizer, StrCmpAndFriendsLogicTest) { // strcmp EXPECT_EQ(0, strcmp("", "")); EXPECT_EQ(0, strcmp("abcd", "abcd")); EXPECT_GT(0, strcmp("ab", "ac")); EXPECT_GT(0, strcmp("abc", "abcd")); EXPECT_LT(0, strcmp("acc", "abc")); EXPECT_LT(0, strcmp("abcd", "abc")); // strncmp EXPECT_EQ(0, strncmp("a", "b", 0)); EXPECT_EQ(0, strncmp("abcd", "abcd", 10)); EXPECT_EQ(0, strncmp("abcd", "abcef", 3)); EXPECT_GT(0, strncmp("abcde", "abcfa", 4)); EXPECT_GT(0, strncmp("a", "b", 5)); EXPECT_GT(0, strncmp("bc", "bcde", 4)); EXPECT_LT(0, strncmp("xyz", "xyy", 10)); EXPECT_LT(0, strncmp("baa", "aaa", 1)); EXPECT_LT(0, strncmp("zyx", "", 2)); #if !defined(_WIN32) // no str[n]casecmp on Windows. // strcasecmp EXPECT_EQ(0, strcasecmp("", "")); EXPECT_EQ(0, strcasecmp("zzz", "zzz")); EXPECT_EQ(0, strcasecmp("abCD", "ABcd")); EXPECT_GT(0, strcasecmp("aB", "Ac")); EXPECT_GT(0, strcasecmp("ABC", "ABCd")); EXPECT_LT(0, strcasecmp("acc", "abc")); EXPECT_LT(0, strcasecmp("ABCd", "abc")); // strncasecmp EXPECT_EQ(0, strncasecmp("a", "b", 0)); EXPECT_EQ(0, strncasecmp("abCD", "ABcd", 10)); EXPECT_EQ(0, strncasecmp("abCd", "ABcef", 3)); EXPECT_GT(0, strncasecmp("abcde", "ABCfa", 4)); EXPECT_GT(0, strncasecmp("a", "B", 5)); EXPECT_GT(0, strncasecmp("bc", "BCde", 4)); EXPECT_LT(0, strncasecmp("xyz", "xyy", 10)); EXPECT_LT(0, strncasecmp("Baa", "aaa", 1)); EXPECT_LT(0, strncasecmp("zyx", "", 2)); #endif // memcmp EXPECT_EQ(0, memcmp("a", "b", 0)); EXPECT_EQ(0, memcmp("ab\0c", "ab\0c", 4)); EXPECT_GT(0, memcmp("\0ab", "\0ac", 3)); EXPECT_GT(0, memcmp("abb\0", "abba", 4)); EXPECT_LT(0, memcmp("ab\0cd", "ab\0c\0", 5)); EXPECT_LT(0, memcmp("zza", "zyx", 3)); } typedef int(*PointerToStrCmp)(const char*, const char*); void RunStrCmpTest(PointerToStrCmp StrCmp) { size_t size = Ident(100); int fill = 'o'; char *s1 = MallocAndMemsetString(size, fill); char *s2 = MallocAndMemsetString(size, fill); s1[size - 1] = '\0'; s2[size - 1] = '\0'; // Normal StrCmp calls Ident(StrCmp(s1, s2)); Ident(StrCmp(s1, s2 + size - 1)); Ident(StrCmp(s1 + size - 1, s2 + size - 1)); // One of arguments points to not allocated memory. EXPECT_DEATH(Ident(StrCmp)(s1 - 1, s2), LeftOOBReadMessage(1)); EXPECT_DEATH(Ident(StrCmp)(s1, s2 - 1), LeftOOBReadMessage(1)); EXPECT_DEATH(Ident(StrCmp)(s1 + size, s2), RightOOBReadMessage(0)); EXPECT_DEATH(Ident(StrCmp)(s1, s2 + size), RightOOBReadMessage(0)); // Hit unallocated memory and die. s1[size - 1] = fill; EXPECT_DEATH(Ident(StrCmp)(s1, s1), RightOOBReadMessage(0)); EXPECT_DEATH(Ident(StrCmp)(s1 + size - 1, s2), RightOOBReadMessage(0)); free(s1); free(s2); } TEST(AddressSanitizer, StrCmpOOBTest) { RunStrCmpTest(&strcmp); } #if !defined(_WIN32) // no str[n]casecmp on Windows. TEST(AddressSanitizer, StrCaseCmpOOBTest) { RunStrCmpTest(&strcasecmp); } #endif typedef int(*PointerToStrNCmp)(const char*, const char*, size_t); void RunStrNCmpTest(PointerToStrNCmp StrNCmp) { size_t size = Ident(100); char *s1 = MallocAndMemsetString(size); char *s2 = MallocAndMemsetString(size); s1[size - 1] = '\0'; s2[size - 1] = '\0'; // Normal StrNCmp calls Ident(StrNCmp(s1, s2, size + 2)); s1[size - 1] = 'z'; s2[size - 1] = 'x'; Ident(StrNCmp(s1 + size - 2, s2 + size - 2, size)); s2[size - 1] = 'z'; Ident(StrNCmp(s1 - 1, s2 - 1, 0)); Ident(StrNCmp(s1 + size - 1, s2 + size - 1, 1)); // One of arguments points to not allocated memory. EXPECT_DEATH(Ident(StrNCmp)(s1 - 1, s2, 1), LeftOOBReadMessage(1)); EXPECT_DEATH(Ident(StrNCmp)(s1, s2 - 1, 1), LeftOOBReadMessage(1)); EXPECT_DEATH(Ident(StrNCmp)(s1 + size, s2, 1), RightOOBReadMessage(0)); EXPECT_DEATH(Ident(StrNCmp)(s1, s2 + size, 1), RightOOBReadMessage(0)); // Hit unallocated memory and die. EXPECT_DEATH(Ident(StrNCmp)(s1 + 1, s2 + 1, size), RightOOBReadMessage(0)); EXPECT_DEATH(Ident(StrNCmp)(s1 + size - 1, s2, 2), RightOOBReadMessage(0)); free(s1); free(s2); } TEST(AddressSanitizer, StrNCmpOOBTest) { RunStrNCmpTest(&strncmp); } #if !defined(_WIN32) // no str[n]casecmp on Windows. TEST(AddressSanitizer, StrNCaseCmpOOBTest) { RunStrNCmpTest(&strncasecmp); } #endif TEST(AddressSanitizer, StrCatOOBTest) { // strcat() reads strlen(to) bytes from |to| before concatenating. size_t to_size = Ident(100); char *to = MallocAndMemsetString(to_size); to[0] = '\0'; size_t from_size = Ident(20); char *from = MallocAndMemsetString(from_size); from[from_size - 1] = '\0'; // Normal strcat calls. strcat(to, from); strcat(to, from); strcat(to + from_size, from + from_size - 2); // Passing an invalid pointer is an error even when concatenating an empty // string. EXPECT_DEATH(strcat(to - 1, from + from_size - 1), LeftOOBAccessMessage(1)); // One of arguments points to not allocated memory. EXPECT_DEATH(strcat(to - 1, from), LeftOOBAccessMessage(1)); EXPECT_DEATH(strcat(to, from - 1), LeftOOBReadMessage(1)); EXPECT_DEATH(strcat(to, from + from_size), RightOOBReadMessage(0)); // "from" is not zero-terminated. from[from_size - 1] = 'z'; EXPECT_DEATH(strcat(to, from), RightOOBReadMessage(0)); from[from_size - 1] = '\0'; // "to" is too short to fit "from". memset(to, 'z', to_size); to[to_size - from_size + 1] = '\0'; EXPECT_DEATH(strcat(to, from), RightOOBWriteMessage(0)); // length of "to" is just enough. strcat(to, from + 1); free(to); free(from); } TEST(AddressSanitizer, StrNCatOOBTest) { // strncat() reads strlen(to) bytes from |to| before concatenating. size_t to_size = Ident(100); char *to = MallocAndMemsetString(to_size); to[0] = '\0'; size_t from_size = Ident(20); char *from = MallocAndMemsetString(from_size); // Normal strncat calls. strncat(to, from, 0); strncat(to, from, from_size); from[from_size - 1] = '\0'; strncat(to, from, 2 * from_size); // Catenating empty string with an invalid string is still an error. EXPECT_DEATH(strncat(to - 1, from, 0), LeftOOBAccessMessage(1)); strncat(to, from + from_size - 1, 10); // One of arguments points to not allocated memory. EXPECT_DEATH(strncat(to - 1, from, 2), LeftOOBAccessMessage(1)); EXPECT_DEATH(strncat(to, from - 1, 2), LeftOOBReadMessage(1)); EXPECT_DEATH(strncat(to, from + from_size, 2), RightOOBReadMessage(0)); memset(from, 'z', from_size); memset(to, 'z', to_size); to[0] = '\0'; // "from" is too short. EXPECT_DEATH(strncat(to, from, from_size + 1), RightOOBReadMessage(0)); // "to" is too short to fit "from". to[0] = 'z'; to[to_size - from_size + 1] = '\0'; EXPECT_DEATH(strncat(to, from, from_size - 1), RightOOBWriteMessage(0)); // "to" is just enough. strncat(to, from, from_size - 2); free(to); free(from); } static string OverlapErrorMessage(const string &func) { return func + "-param-overlap"; } TEST(AddressSanitizer, StrArgsOverlapTest) { size_t size = Ident(100); char *str = Ident((char*)malloc(size)); // Do not check memcpy() on OS X 10.7 and later, where it actually aliases // memmove(). #if !defined(__APPLE__) || !defined(MAC_OS_X_VERSION_10_7) || \ (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7) // Check "memcpy". Use Ident() to avoid inlining. #if PLATFORM_HAS_DIFFERENT_MEMCPY_AND_MEMMOVE memset(str, 'z', size); Ident(memcpy)(str + 1, str + 11, 10); Ident(memcpy)(str, str, 0); EXPECT_DEATH(Ident(memcpy)(str, str + 14, 15), OverlapErrorMessage("memcpy")); EXPECT_DEATH(Ident(memcpy)(str + 14, str, 15), OverlapErrorMessage("memcpy")); #endif #endif // We do not treat memcpy with to==from as a bug. // See http://llvm.org/bugs/show_bug.cgi?id=11763. // EXPECT_DEATH(Ident(memcpy)(str + 20, str + 20, 1), // OverlapErrorMessage("memcpy")); // Check "strcpy". memset(str, 'z', size); str[9] = '\0'; strcpy(str + 10, str); EXPECT_DEATH(strcpy(str + 9, str), OverlapErrorMessage("strcpy")); EXPECT_DEATH(strcpy(str, str + 4), OverlapErrorMessage("strcpy")); strcpy(str, str + 5); // Check "strncpy". memset(str, 'z', size); strncpy(str, str + 10, 10); EXPECT_DEATH(strncpy(str, str + 9, 10), OverlapErrorMessage("strncpy")); EXPECT_DEATH(strncpy(str + 9, str, 10), OverlapErrorMessage("strncpy")); str[10] = '\0'; strncpy(str + 11, str, 20); EXPECT_DEATH(strncpy(str + 10, str, 20), OverlapErrorMessage("strncpy")); // Check "strcat". memset(str, 'z', size); str[10] = '\0'; str[20] = '\0'; strcat(str, str + 10); EXPECT_DEATH(strcat(str, str + 11), OverlapErrorMessage("strcat")); str[10] = '\0'; strcat(str + 11, str); EXPECT_DEATH(strcat(str, str + 9), OverlapErrorMessage("strcat")); EXPECT_DEATH(strcat(str + 9, str), OverlapErrorMessage("strcat")); EXPECT_DEATH(strcat(str + 10, str), OverlapErrorMessage("strcat")); // Check "strncat". memset(str, 'z', size); str[10] = '\0'; strncat(str, str + 10, 10); // from is empty EXPECT_DEATH(strncat(str, str + 11, 10), OverlapErrorMessage("strncat")); str[10] = '\0'; str[20] = '\0'; strncat(str + 5, str, 5); str[10] = '\0'; EXPECT_DEATH(strncat(str + 5, str, 6), OverlapErrorMessage("strncat")); EXPECT_DEATH(strncat(str, str + 9, 10), OverlapErrorMessage("strncat")); free(str); } typedef void(*PointerToCallAtoi)(const char*); void RunAtoiOOBTest(PointerToCallAtoi Atoi) { char *array = MallocAndMemsetString(10, '1'); // Invalid pointer to the string. EXPECT_DEATH(Atoi(array + 11), RightOOBReadMessage(1)); EXPECT_DEATH(Atoi(array - 1), LeftOOBReadMessage(1)); // Die if a buffer doesn't have terminating NULL. EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0)); // Make last symbol a terminating NULL array[9] = '\0'; Atoi(array); // Sometimes we need to detect overflow if no digits are found. memset(array, ' ', 10); EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0)); array[9] = '-'; EXPECT_DEATH(Atoi(array), RightOOBReadMessage(0)); EXPECT_DEATH(Atoi(array + 9), RightOOBReadMessage(0)); free(array); } #if !defined(_WIN32) // FIXME: Fix and enable on Windows. void CallAtoi(const char *nptr) { Ident(atoi(nptr)); } void CallAtol(const char *nptr) { Ident(atol(nptr)); } void CallAtoll(const char *nptr) { Ident(atoll(nptr)); } TEST(AddressSanitizer, AtoiAndFriendsOOBTest) { RunAtoiOOBTest(&CallAtoi); RunAtoiOOBTest(&CallAtol); RunAtoiOOBTest(&CallAtoll); } #endif typedef void(*PointerToCallStrtol)(const char*, char**, int); void RunStrtolOOBTest(PointerToCallStrtol Strtol) { char *array = MallocAndMemsetString(3); array[0] = '1'; array[1] = '2'; array[2] = '3'; // Invalid pointer to the string. EXPECT_DEATH(Strtol(array + 3, NULL, 0), RightOOBReadMessage(0)); EXPECT_DEATH(Strtol(array - 1, NULL, 0), LeftOOBReadMessage(1)); // Buffer overflow if there is no terminating null (depends on base). EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0)); array[2] = 'z'; EXPECT_DEATH(Strtol(array, NULL, 36), RightOOBReadMessage(0)); // Add terminating zero to get rid of overflow. array[2] = '\0'; Strtol(array, NULL, 36); // Sometimes we need to detect overflow if no digits are found. array[0] = array[1] = array[2] = ' '; EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0)); array[2] = '+'; EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0)); array[2] = '-'; EXPECT_DEATH(Strtol(array, NULL, 0), RightOOBReadMessage(0)); free(array); } #if !defined(_WIN32) // FIXME: Fix and enable on Windows. void CallStrtol(const char *nptr, char **endptr, int base) { Ident(strtol(nptr, endptr, base)); } void CallStrtoll(const char *nptr, char **endptr, int base) { Ident(strtoll(nptr, endptr, base)); } TEST(AddressSanitizer, StrtollOOBTest) { RunStrtolOOBTest(&CallStrtoll); } TEST(AddressSanitizer, StrtolOOBTest) { RunStrtolOOBTest(&CallStrtol); } #endif Index: vendor/compiler-rt/dist/lib/dfsan/done_abilist.txt =================================================================== --- vendor/compiler-rt/dist/lib/dfsan/done_abilist.txt (revision 319464) +++ vendor/compiler-rt/dist/lib/dfsan/done_abilist.txt (revision 319465) @@ -1,300 +1,298 @@ fun:main=uninstrumented fun:main=discard ############################################################################### # DFSan interface functions ############################################################################### fun:dfsan_union=uninstrumented fun:dfsan_union=discard fun:dfsan_create_label=uninstrumented fun:dfsan_create_label=discard fun:dfsan_set_label=uninstrumented fun:dfsan_set_label=discard fun:dfsan_add_label=uninstrumented fun:dfsan_add_label=discard fun:dfsan_get_label=uninstrumented fun:dfsan_get_label=custom fun:dfsan_read_label=uninstrumented fun:dfsan_read_label=discard fun:dfsan_get_label_count=uninstrumented fun:dfsan_get_label_count=discard fun:dfsan_get_label_info=uninstrumented fun:dfsan_get_label_info=discard fun:dfsan_has_label=uninstrumented fun:dfsan_has_label=discard fun:dfsan_has_label_with_desc=uninstrumented fun:dfsan_has_label_with_desc=discard fun:dfsan_set_write_callback=uninstrumented fun:dfsan_set_write_callback=custom ############################################################################### # glibc ############################################################################### fun:malloc=discard fun:realloc=discard fun:free=discard # Functions that return a value that depends on the input, but the output might # not be necessarily data-dependent on the input. fun:isalpha=functional fun:isdigit=functional fun:isprint=functional fun:isxdigit=functional fun:isalnum=functional fun:ispunct=functional fun:isspace=functional fun:tolower=functional fun:toupper=functional # Functions that return a value that is data-dependent on the input. fun:btowc=functional fun:exp=functional fun:exp2=functional fun:fabs=functional fun:finite=functional fun:floor=functional fun:fmod=functional fun:isinf=functional fun:isnan=functional fun:log=functional fun:modf=functional fun:pow=functional fun:round=functional fun:sqrt=functional fun:wctob=functional # Functions that produce an output that does not depend on the input (shadow is # zeroed automatically). fun:__assert_fail=discard fun:__ctype_b_loc=discard fun:__cxa_atexit=discard fun:__errno_location=discard fun:__newlocale=discard fun:__sbrk=discard fun:__sigsetjmp=discard fun:__uselocale=discard fun:__wctype_l=discard fun:access=discard fun:alarm=discard fun:atexit=discard fun:bind=discard fun:chdir=discard fun:close=discard fun:closedir=discard fun:connect=discard fun:dladdr=discard fun:dlclose=discard fun:fclose=discard fun:feof=discard fun:ferror=discard fun:fflush=discard fun:fileno=discard fun:fopen=discard fun:fprintf=discard fun:fputc=discard fun:fputc=discard fun:fputs=discard fun:fputs=discard fun:fseek=discard fun:ftell=discard fun:fwrite=discard fun:getenv=discard fun:getuid=discard fun:geteuid=discard fun:getpagesize=discard fun:getpid=discard fun:kill=discard fun:listen=discard fun:lseek=discard fun:mkdir=discard fun:mmap=discard fun:munmap=discard fun:open=discard fun:pipe=discard fun:posix_fadvise=discard fun:posix_memalign=discard fun:prctl=discard fun:printf=discard fun:pthread_sigmask=discard fun:putc=discard fun:putchar=discard fun:puts=discard fun:rand=discard fun:random=discard fun:remove=discard fun:sched_getcpu=discard fun:sched_get_priority_max=discard fun:sched_setaffinity=discard fun:sched_yield=discard fun:sem_destroy=discard fun:sem_init=discard fun:sem_post=discard fun:sem_wait=discard fun:send=discard fun:sendmsg=discard fun:sendto=discard fun:setsockopt=discard fun:shutdown=discard fun:sleep=discard fun:socket=discard fun:strerror=discard fun:strspn=discard fun:strcspn=discard fun:symlink=discard fun:syscall=discard fun:unlink=discard fun:uselocale=discard # Functions that produce output does not depend on the input (need to zero the # shadow manually). fun:calloc=custom fun:clock_gettime=custom fun:dlopen=custom fun:fgets=custom fun:fstat=custom fun:getcwd=custom fun:get_current_dir_name=custom fun:gethostname=custom fun:getrlimit=custom fun:getrusage=custom fun:nanosleep=custom fun:pread=custom fun:read=custom fun:socketpair=custom fun:stat=custom fun:time=custom # Functions that produce an output that depend on the input (propagate the # shadow manually). fun:ctime_r=custom fun:inet_pton=custom fun:localtime_r=custom fun:memcpy=custom fun:memset=custom fun:strcpy=custom fun:strdup=custom fun:strncpy=custom fun:strtod=custom fun:strtol=custom fun:strtoll=custom fun:strtoul=custom fun:strtoull=custom # Functions that produce an output that is computed from the input, but is not # necessarily data dependent. fun:memchr=custom fun:memcmp=custom fun:strcasecmp=custom fun:strchr=custom fun:strcmp=custom fun:strlen=custom fun:strncasecmp=custom fun:strncmp=custom fun:strrchr=custom fun:strstr=custom # Functions which take action based on global state, such as running a callback # set by a sepperate function. fun:write=custom # Functions that take a callback (wrap the callback manually). fun:dl_iterate_phdr=custom fun:getpwuid_r=custom fun:poll=custom fun:sched_getaffinity=custom fun:select=custom fun:sigemptyset=custom fun:sigaction=custom fun:gettimeofday=custom # sprintf-like fun:sprintf=custom fun:snprintf=custom # TODO: custom fun:asprintf=discard fun:qsort=discard ############################################################################### # pthread ############################################################################### fun:pthread_equal=discard fun:pthread_getspecific=discard fun:pthread_key_create=discard fun:pthread_key_delete=discard fun:pthread_mutex_destroy=discard fun:pthread_mutex_init=discard fun:pthread_mutex_lock=discard fun:pthread_mutex_trylock=discard fun:pthread_mutex_unlock=discard fun:pthread_mutexattr_destroy=discard fun:pthread_mutexattr_init=discard fun:pthread_mutexattr_settype=discard fun:pthread_once=discard fun:pthread_self=discard fun:pthread_setspecific=discard # Functions that take a callback (wrap the callback manually). fun:pthread_create=custom ############################################################################### # libffi/libgo ############################################################################### # Functions that are written in asm or are called from asm. fun:ffi_call_unix64=uninstrumented fun:ffi_call_unix64=discard fun:ffi_closure_unix64_inner=uninstrumented fun:ffi_closure_unix64_inner=discard fun:ffi_closure_unix64=uninstrumented fun:ffi_closure_unix64=discard fun:__go_get_closure=uninstrumented fun:__go_get_closure=discard fun:__go_makefunc_can_recover=uninstrumented fun:__go_makefunc_can_recover=discard fun:__go_makefunc_returning=uninstrumented fun:__go_makefunc_returning=discard fun:reflect.MakeFuncStubGo=uninstrumented fun:reflect.MakeFuncStubGo=discard fun:reflect.makeFuncStub=uninstrumented fun:reflect.makeFuncStub=discard ############################################################################### # lib/Fuzzer ############################################################################### # Replaces __sanitizer_cov_trace_cmp with __dfsw___sanitizer_cov_trace_cmp fun:__sanitizer_cov_trace_cmp=custom fun:__sanitizer_cov_trace_cmp=uninstrumented fun:__sanitizer_cov_trace_cmp1=custom fun:__sanitizer_cov_trace_cmp1=uninstrumented fun:__sanitizer_cov_trace_cmp2=custom fun:__sanitizer_cov_trace_cmp2=uninstrumented fun:__sanitizer_cov_trace_cmp4=custom fun:__sanitizer_cov_trace_cmp4=uninstrumented fun:__sanitizer_cov_trace_cmp8=custom fun:__sanitizer_cov_trace_cmp8=uninstrumented # Similar for __sanitizer_cov_trace_switch fun:__sanitizer_cov_trace_switch=custom fun:__sanitizer_cov_trace_switch=uninstrumented # Ignores all other __sanitizer callbacks. fun:__sanitizer_cov=uninstrumented fun:__sanitizer_cov=discard fun:__sanitizer_cov_module_init=uninstrumented fun:__sanitizer_cov_module_init=discard fun:__sanitizer_cov_with_check=uninstrumented fun:__sanitizer_cov_with_check=discard fun:__sanitizer_set_death_callback=uninstrumented fun:__sanitizer_set_death_callback=discard -fun:__sanitizer_get_total_unique_coverage=uninstrumented -fun:__sanitizer_get_total_unique_coverage=discard fun:__sanitizer_update_counter_bitset_and_clear_counters=uninstrumented fun:__sanitizer_update_counter_bitset_and_clear_counters=discard # Ignores the dfsan wrappers. fun:__dfsw_*=uninstrumented fun:__dfsw_*=discard # Don't add extra parameters to the Fuzzer callback. fun:LLVMFuzzerTestOneInput=uninstrumented Index: vendor/compiler-rt/dist/lib/lsan/lsan_interceptors.cc =================================================================== --- vendor/compiler-rt/dist/lib/lsan/lsan_interceptors.cc (revision 319464) +++ vendor/compiler-rt/dist/lib/lsan/lsan_interceptors.cc (revision 319465) @@ -1,346 +1,370 @@ //=-- 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_stackdepot.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 +static void BeforeFork() { + if (SANITIZER_LINUX) { + LockAllocator(); + StackDepotLockAll(); + } +} + +static void AfterFork() { + if (SANITIZER_LINUX) { + StackDepotUnlockAll(); + UnlockAllocator(); + } +} + +INTERCEPTOR(int, fork, void) { + ENSURE_LSAN_INITED; + BeforeFork(); + int pid = REAL(fork)(); + AfterFork(); + return pid; +} + #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; } #define OPERATOR_DELETE_BODY \ ENSURE_LSAN_INITED; \ Deallocate(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; } ///// 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); + INTERCEPT_FUNCTION(fork); 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 319464) +++ vendor/compiler-rt/dist/lib/msan/msan_allocator.cc (revision 319465) @@ -1,271 +1,179 @@ //===-- 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; + +Allocator &get_allocator() { return allocator; } void MsanAllocatorInit() { allocator.Init( common_flags()->allocator_may_return_null, 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.ReturnNullOrDieOnBadRequest(); } MsanThread *t = GetCurrentThread(); void *allocated; if (t) { AllocatorCache *cache = GetAllocatorCache(&t->malloc_storage()); allocated = allocator.Allocate(cache, size, alignment, false); } else { SpinMutexLock l(&fallback_mutex); AllocatorCache *cache = &fallback_allocator_cache; allocated = allocator.Allocate(cache, size, alignment, false); } 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)) return allocator.ReturnNullOrDieOnBadRequest(); 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_allocator.h =================================================================== --- vendor/compiler-rt/dist/lib/msan/msan_allocator.h (revision 319464) +++ vendor/compiler-rt/dist/lib/msan/msan_allocator.h (revision 319465) @@ -1,33 +1,130 @@ //===-- msan_allocator.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 a part of MemorySanitizer. // //===----------------------------------------------------------------------===// #ifndef MSAN_ALLOCATOR_H #define MSAN_ALLOCATOR_H #include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_allocator.h" +#include "sanitizer_common/sanitizer_allocator_interface.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; + + +Allocator &get_allocator(); struct MsanThreadLocalMallocStorage { uptr quarantine_cache[16]; // Allocator cache contains atomic_uint64_t which must be 8-byte aligned. ALIGNED(8) uptr allocator_cache[96 * (512 * 8 + 16)]; // Opaque. void CommitBack(); private: // These objects are allocated via mmap() and are zero-initialized. MsanThreadLocalMallocStorage() {} }; } // namespace __msan #endif // MSAN_ALLOCATOR_H Index: vendor/compiler-rt/dist/lib/msan/msan_interceptors.cc =================================================================== --- vendor/compiler-rt/dist/lib/msan/msan_interceptors.cc (revision 319464) +++ vendor/compiler-rt/dist/lib/msan/msan_interceptors.cc (revision 319465) @@ -1,1628 +1,1608 @@ //===-- 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 *, strndup, char *src, SIZE_T n) { - ENSURE_MSAN_INITED(); - GET_STORE_STACK_TRACE; - // On FreeBSD strndup() leverages strnlen(). - InterceptorScope interceptor_scope; - SIZE_T copy_size = REAL(strnlen)(src, n); - char *res = REAL(strndup)(src, n); - CopyShadowAndOrigin(res, src, copy_size, &stack); - __msan_unpoison(res + copy_size, 1); // \0 - return res; -} - -#if !SANITIZER_FREEBSD -INTERCEPTOR(char *, __strndup, char *src, SIZE_T n) { - ENSURE_MSAN_INITED(); - GET_STORE_STACK_TRACE; - SIZE_T copy_size = REAL(strnlen)(src, n); - char *res = REAL(__strndup)(src, n); - CopyShadowAndOrigin(res, src, copy_size, &stack); - __msan_unpoison(res + copy_size, 1); // \0 - return res; -} -#define MSAN_MAYBE_INTERCEPT___STRNDUP INTERCEPT_FUNCTION(__strndup) -#else -#define MSAN_MAYBE_INTERCEPT___STRNDUP -#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() { + get_allocator().ForceLock(); StackDepotLockAll(); ChainedOriginDepotLockAll(); } static void AfterFork() { ChainedOriginDepotUnlockAll(); StackDepotUnlockAll(); + get_allocator().ForceUnlock(); } 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); \ + } 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; } // 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(strndup); - MSAN_MAYBE_INTERCEPT___STRNDUP; 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/tests/msan_test.cc =================================================================== --- vendor/compiler-rt/dist/lib/msan/tests/msan_test.cc (revision 319464) +++ vendor/compiler-rt/dist/lib/msan/tests/msan_test.cc (revision 319465) @@ -1,4557 +1,4566 @@ //===-- 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 = strndup(buf, 3); + 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 = strndup(buf, 2); + 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]); } #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/sanitizer_common/sanitizer_common_interceptors.inc =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_common_interceptors.inc (revision 319464) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_common_interceptors.inc (revision 319465) @@ -1,6361 +1,6403 @@ //===-- 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'; \ + 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); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, 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 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; } Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_coverage_interface.inc =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_coverage_interface.inc (revision 319464) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_coverage_interface.inc (revision 319465) @@ -1,32 +1,27 @@ //===-- sanitizer_coverage_interface.inc ----------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // Sanitizer Coverage interface list. //===----------------------------------------------------------------------===// -INTERFACE_FUNCTION(__sanitizer_cov) INTERFACE_FUNCTION(__sanitizer_cov_dump) -INTERFACE_FUNCTION(__sanitizer_cov_init) -INTERFACE_FUNCTION(__sanitizer_cov_module_init) -INTERFACE_FUNCTION(__sanitizer_cov_with_check) INTERFACE_FUNCTION(__sanitizer_dump_coverage) INTERFACE_FUNCTION(__sanitizer_dump_trace_pc_guard_coverage) -INTERFACE_FUNCTION(__sanitizer_get_total_unique_coverage) INTERFACE_FUNCTION(__sanitizer_maybe_open_cov_file) INTERFACE_WEAK_FUNCTION(__sancov_default_options) INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_cmp) INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_cmp1) INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_cmp2) INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_cmp4) INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_cmp8) INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_div4) INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_div8) INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_gep) INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_pc_guard) INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_pc_guard_init) INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_pc_indir) INTERFACE_WEAK_FUNCTION(__sanitizer_cov_trace_switch) Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_coverage_libcdep.cc =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_coverage_libcdep.cc (revision 319464) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_coverage_libcdep.cc (revision 319465) @@ -1,662 +1,627 @@ //===-- sanitizer_coverage.cc ---------------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Sanitizer Coverage. // This file implements run-time support for a poor man's coverage tool. // // Compiler instrumentation: // For every interesting basic block the compiler injects the following code: // if (Guard < 0) { // __sanitizer_cov(&Guard); // } // At the module start up time __sanitizer_cov_module_init sets the guards // to consecutive negative numbers (-1, -2, -3, ...). // It's fine to call __sanitizer_cov more than once for a given block. // // Run-time: // - __sanitizer_cov(): record that we've executed the PC (GET_CALLER_PC). // and atomically set Guard to -Guard. // - __sanitizer_cov_dump: dump the coverage data to disk. // For every module of the current process that has coverage data // this will create a file module_name.PID.sancov. // // The file format is simple: the first 8 bytes is the magic, // one of 0xC0BFFFFFFFFFFF64 and 0xC0BFFFFFFFFFFF32. The last byte of the // magic defines the size of the following offsets. // The rest of the data is the offsets in the module. // // Eventually, this coverage implementation should be obsoleted by a more // powerful general purpose Clang/LLVM coverage instrumentation. // Consider this implementation as prototype. // // FIXME: support (or at least test with) dlclose. //===----------------------------------------------------------------------===// #include "sanitizer_allocator_internal.h" #include "sanitizer_common.h" #include "sanitizer_libc.h" #include "sanitizer_mutex.h" #include "sanitizer_procmaps.h" #include "sanitizer_stacktrace.h" #include "sanitizer_symbolizer.h" #include "sanitizer_flags.h" using namespace __sanitizer; static const u64 kMagic64 = 0xC0BFFFFFFFFFFF64ULL; static const u64 kMagic32 = 0xC0BFFFFFFFFFFF32ULL; static const uptr kNumWordsForMagic = SANITIZER_WORDSIZE == 64 ? 1 : 2; static const u64 kMagic = SANITIZER_WORDSIZE == 64 ? kMagic64 : kMagic32; static atomic_uint32_t dump_once_guard; // Ensure that CovDump runs only once. static atomic_uintptr_t coverage_counter; // pc_array is the array containing the covered PCs. // To make the pc_array thread- and async-signal-safe it has to be large enough. // 128M counters "ought to be enough for anybody" (4M on 32-bit). // With coverage_direct=1 in ASAN_OPTIONS, pc_array memory is mapped to a file. // In this mode, __sanitizer_cov_dump does nothing, and CovUpdateMapping() // dump current memory layout to another file. static bool cov_sandboxed = false; static fd_t cov_fd = kInvalidFd; static unsigned int cov_max_block_size = 0; static bool coverage_enabled = false; static const char *coverage_dir; namespace __sanitizer { class CoverageData { public: void Init(); void Enable(); void Disable(); void ReInit(); void BeforeFork(); void AfterFork(int child_pid); void Extend(uptr npcs); void Add(uptr pc, u32 *guard); void DumpOffsets(); void DumpAll(); void InitializeGuardArray(s32 *guards); void InitializeGuards(s32 *guards, uptr n, const char *module_name, uptr caller_pc); void ReinitializeGuards(); uptr *data(); uptr size() const; private: struct NamedPcRange { const char *copied_module_name; uptr beg, end; // elements [beg,end) in pc_array. }; void DirectOpen(); void UpdateModuleNameVec(uptr caller_pc, uptr range_beg, uptr range_end); void GetRangeOffsets(const NamedPcRange& r, Symbolizer* s, InternalMmapVector* offsets) const; // Maximal size pc array may ever grow. // We MmapNoReserve this space to ensure that the array is contiguous. static const uptr kPcArrayMaxSize = FIRST_32_SECOND_64(1 << (SANITIZER_ANDROID ? 24 : 26), 1 << 27); // The amount file mapping for the pc array is grown by. static const uptr kPcArrayMmapSize = 64 * 1024; // pc_array is allocated with MmapNoReserveOrDie and so it uses only as // much RAM as it really needs. uptr *pc_array; // Index of the first available pc_array slot. atomic_uintptr_t pc_array_index; // Array size. atomic_uintptr_t pc_array_size; // Current file mapped size of the pc array. uptr pc_array_mapped_size; // Descriptor of the file mapped pc array. fd_t pc_fd; // Vector of coverage guard arrays, protected by mu. InternalMmapVectorNoCtor guard_array_vec; // Vector of module and compilation unit pc ranges. InternalMmapVectorNoCtor comp_unit_name_vec; InternalMmapVectorNoCtor module_name_vec; StaticSpinMutex mu; }; static CoverageData coverage_data; void CovUpdateMapping(const char *path, uptr caller_pc = 0); void CoverageData::DirectOpen() { InternalScopedString path(kMaxPathLength); internal_snprintf((char *)path.data(), path.size(), "%s/%zd.sancov.raw", coverage_dir, internal_getpid()); pc_fd = OpenFile(path.data(), RdWr); if (pc_fd == kInvalidFd) { Report("Coverage: failed to open %s for reading/writing\n", path.data()); Die(); } pc_array_mapped_size = 0; CovUpdateMapping(coverage_dir); } void CoverageData::Init() { pc_fd = kInvalidFd; - - if (!common_flags()->coverage) return; - Printf("**\n***\n***\n"); - Printf("**WARNING: this implementation of SanitizerCoverage is deprecated\n"); - Printf("**WARNING: and will be removed in future versions\n"); - Printf("**WARNING: See https://clang.llvm.org/docs/SanitizerCoverage.html\n"); - Printf("**\n***\n***\n"); } void CoverageData::Enable() { if (pc_array) return; pc_array = reinterpret_cast( MmapNoReserveOrDie(sizeof(uptr) * kPcArrayMaxSize, "CovInit")); atomic_store(&pc_array_index, 0, memory_order_relaxed); if (common_flags()->coverage_direct) { Report("coverage_direct=1 is deprecated, don't use it.\n"); Die(); atomic_store(&pc_array_size, 0, memory_order_relaxed); } else { atomic_store(&pc_array_size, kPcArrayMaxSize, memory_order_relaxed); } } void CoverageData::InitializeGuardArray(s32 *guards) { Enable(); // Make sure coverage is enabled at this point. s32 n = guards[0]; for (s32 j = 1; j <= n; j++) { uptr idx = atomic_load_relaxed(&pc_array_index); atomic_store_relaxed(&pc_array_index, idx + 1); guards[j] = -static_cast(idx + 1); } } void CoverageData::Disable() { if (pc_array) { UnmapOrDie(pc_array, sizeof(uptr) * kPcArrayMaxSize); pc_array = nullptr; } if (pc_fd != kInvalidFd) { CloseFile(pc_fd); pc_fd = kInvalidFd; } } void CoverageData::ReinitializeGuards() { // Assuming single thread. atomic_store(&pc_array_index, 0, memory_order_relaxed); for (uptr i = 0; i < guard_array_vec.size(); i++) InitializeGuardArray(guard_array_vec[i]); } void CoverageData::ReInit() { Disable(); if (coverage_enabled) { if (common_flags()->coverage_direct) { // In memory-mapped mode we must extend the new file to the known array // size. uptr size = atomic_load(&pc_array_size, memory_order_relaxed); uptr npcs = size / sizeof(uptr); Enable(); if (size) Extend(npcs); if (coverage_enabled) CovUpdateMapping(coverage_dir); } else { Enable(); } } // Re-initialize the guards. // We are single-threaded now, no need to grab any lock. CHECK_EQ(atomic_load(&pc_array_index, memory_order_relaxed), 0); ReinitializeGuards(); } void CoverageData::BeforeFork() { mu.Lock(); } void CoverageData::AfterFork(int child_pid) { // We are single-threaded so it's OK to release the lock early. mu.Unlock(); if (child_pid == 0) ReInit(); } // Extend coverage PC array to fit additional npcs elements. void CoverageData::Extend(uptr npcs) { if (!common_flags()->coverage_direct) return; SpinMutexLock l(&mu); uptr size = atomic_load(&pc_array_size, memory_order_relaxed); size += npcs * sizeof(uptr); if (coverage_enabled && size > pc_array_mapped_size) { if (pc_fd == kInvalidFd) DirectOpen(); CHECK_NE(pc_fd, kInvalidFd); uptr new_mapped_size = pc_array_mapped_size; while (size > new_mapped_size) new_mapped_size += kPcArrayMmapSize; CHECK_LE(new_mapped_size, sizeof(uptr) * kPcArrayMaxSize); // Extend the file and map the new space at the end of pc_array. uptr res = internal_ftruncate(pc_fd, new_mapped_size); int err; if (internal_iserror(res, &err)) { Printf("failed to extend raw coverage file: %d\n", err); Die(); } uptr next_map_base = ((uptr)pc_array) + pc_array_mapped_size; void *p = MapWritableFileToMemory((void *)next_map_base, new_mapped_size - pc_array_mapped_size, pc_fd, pc_array_mapped_size); CHECK_EQ((uptr)p, next_map_base); pc_array_mapped_size = new_mapped_size; } atomic_store(&pc_array_size, size, memory_order_release); } void CoverageData::UpdateModuleNameVec(uptr caller_pc, uptr range_beg, uptr range_end) { auto sym = Symbolizer::GetOrInit(); if (!sym) return; const char *module_name = sym->GetModuleNameForPc(caller_pc); if (!module_name) return; if (module_name_vec.empty() || module_name_vec.back().copied_module_name != module_name) module_name_vec.push_back({module_name, range_beg, range_end}); else module_name_vec.back().end = range_end; } void CoverageData::InitializeGuards(s32 *guards, uptr n, const char *comp_unit_name, uptr caller_pc) { // The array 'guards' has n+1 elements, we use the element zero // to store 'n'. CHECK_LT(n, 1 << 30); guards[0] = static_cast(n); InitializeGuardArray(guards); SpinMutexLock l(&mu); uptr range_end = atomic_load(&pc_array_index, memory_order_relaxed); uptr range_beg = range_end - n; comp_unit_name_vec.push_back({comp_unit_name, range_beg, range_end}); guard_array_vec.push_back(guards); UpdateModuleNameVec(caller_pc, range_beg, range_end); } static const uptr kBundleCounterBits = 16; // When coverage_order_pcs==true and SANITIZER_WORDSIZE==64 // we insert the global counter into the first 16 bits of the PC. uptr BundlePcAndCounter(uptr pc, uptr counter) { if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs) return pc; static const uptr kMaxCounter = (1 << kBundleCounterBits) - 1; if (counter > kMaxCounter) counter = kMaxCounter; CHECK_EQ(0, pc >> (SANITIZER_WORDSIZE - kBundleCounterBits)); return pc | (counter << (SANITIZER_WORDSIZE - kBundleCounterBits)); } uptr UnbundlePc(uptr bundle) { if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs) return bundle; return (bundle << kBundleCounterBits) >> kBundleCounterBits; } uptr UnbundleCounter(uptr bundle) { if (SANITIZER_WORDSIZE != 64 || !common_flags()->coverage_order_pcs) return 0; return bundle >> (SANITIZER_WORDSIZE - kBundleCounterBits); } // If guard is negative, atomically set it to -guard and store the PC in // pc_array. void CoverageData::Add(uptr pc, u32 *guard) { atomic_uint32_t *atomic_guard = reinterpret_cast(guard); s32 guard_value = atomic_load(atomic_guard, memory_order_relaxed); if (guard_value >= 0) return; atomic_store(atomic_guard, -guard_value, memory_order_relaxed); if (!pc_array) return; uptr idx = -guard_value - 1; if (idx >= atomic_load(&pc_array_index, memory_order_acquire)) return; // May happen after fork when pc_array_index becomes 0. CHECK_LT(idx, atomic_load(&pc_array_size, memory_order_acquire)); uptr counter = atomic_fetch_add(&coverage_counter, 1, memory_order_relaxed); pc_array[idx] = BundlePcAndCounter(pc, counter); } uptr *CoverageData::data() { return pc_array; } uptr CoverageData::size() const { return atomic_load(&pc_array_index, memory_order_relaxed); } // Block layout for packed file format: header, followed by module name (no // trailing zero), followed by data blob. struct CovHeader { int pid; unsigned int module_name_length; unsigned int data_length; }; static void CovWritePacked(int pid, const char *module, const void *blob, unsigned int blob_size) { if (cov_fd == kInvalidFd) return; unsigned module_name_length = internal_strlen(module); CovHeader header = {pid, module_name_length, blob_size}; if (cov_max_block_size == 0) { // Writing to a file. Just go ahead. WriteToFile(cov_fd, &header, sizeof(header)); WriteToFile(cov_fd, module, module_name_length); WriteToFile(cov_fd, blob, blob_size); } else { // Writing to a socket. We want to split the data into appropriately sized // blocks. InternalScopedBuffer block(cov_max_block_size); CHECK_EQ((uptr)block.data(), (uptr)(CovHeader *)block.data()); uptr header_size_with_module = sizeof(header) + module_name_length; CHECK_LT(header_size_with_module, cov_max_block_size); unsigned int max_payload_size = cov_max_block_size - header_size_with_module; char *block_pos = block.data(); internal_memcpy(block_pos, &header, sizeof(header)); block_pos += sizeof(header); internal_memcpy(block_pos, module, module_name_length); block_pos += module_name_length; char *block_data_begin = block_pos; const char *blob_pos = (const char *)blob; while (blob_size > 0) { unsigned int payload_size = Min(blob_size, max_payload_size); blob_size -= payload_size; internal_memcpy(block_data_begin, blob_pos, payload_size); blob_pos += payload_size; ((CovHeader *)block.data())->data_length = payload_size; WriteToFile(cov_fd, block.data(), header_size_with_module + payload_size); } } } // If packed = false: .. (name = module name). // If packed = true and name == 0: ... // If packed = true and name != 0: .. (name is // user-supplied). static fd_t CovOpenFile(InternalScopedString *path, bool packed, const char *name, const char *extension = "sancov") { path->clear(); if (!packed) { CHECK(name); path->append("%s/%s.%zd.%s", coverage_dir, name, internal_getpid(), extension); } else { if (!name) path->append("%s/%zd.%s.packed", coverage_dir, internal_getpid(), extension); else path->append("%s/%s.%s.packed", coverage_dir, name, extension); } error_t err; fd_t fd = OpenFile(path->data(), WrOnly, &err); if (fd == kInvalidFd) Report("SanitizerCoverage: failed to open %s for writing (reason: %d)\n", path->data(), err); return fd; } void CoverageData::GetRangeOffsets(const NamedPcRange& r, Symbolizer* sym, InternalMmapVector* offsets) const { offsets->clear(); for (uptr i = 0; i < kNumWordsForMagic; i++) offsets->push_back(0); CHECK(r.copied_module_name); CHECK_LE(r.beg, r.end); CHECK_LE(r.end, size()); for (uptr i = r.beg; i < r.end; i++) { uptr pc = UnbundlePc(pc_array[i]); uptr counter = UnbundleCounter(pc_array[i]); if (!pc) continue; // Not visited. uptr offset = 0; sym->GetModuleNameAndOffsetForPC(pc, nullptr, &offset); offsets->push_back(BundlePcAndCounter(offset, counter)); } CHECK_GE(offsets->size(), kNumWordsForMagic); SortArray(offsets->data(), offsets->size()); for (uptr i = 0; i < offsets->size(); i++) (*offsets)[i] = UnbundlePc((*offsets)[i]); } static void GenerateHtmlReport(const InternalMmapVector &cov_files) { if (!common_flags()->html_cov_report) { return; } char *sancov_path = FindPathToBinary(common_flags()->sancov_path); if (sancov_path == nullptr) { return; } InternalMmapVector sancov_argv(cov_files.size() * 2 + 3); sancov_argv.push_back(sancov_path); sancov_argv.push_back(internal_strdup("-html-report")); auto argv_deleter = at_scope_exit([&] { for (uptr i = 0; i < sancov_argv.size(); ++i) { InternalFree(sancov_argv[i]); } }); for (const auto &cov_file : cov_files) { sancov_argv.push_back(internal_strdup(cov_file)); } { ListOfModules modules; modules.init(); for (const LoadedModule &module : modules) { sancov_argv.push_back(internal_strdup(module.full_name())); } } InternalScopedString report_path(kMaxPathLength); fd_t report_fd = CovOpenFile(&report_path, false /* packed */, GetProcessName(), "html"); int pid = StartSubprocess(sancov_argv[0], sancov_argv.data(), kInvalidFd /* stdin */, report_fd /* std_out */); if (pid > 0) { int result = WaitForProcess(pid); if (result == 0) Printf("coverage report generated to %s\n", report_path.data()); } } void CoverageData::DumpOffsets() { auto sym = Symbolizer::GetOrInit(); if (!common_flags()->coverage_pcs) return; + Printf("**\n***\n***\n"); + Printf("**WARNING: this implementation of SanitizerCoverage is deprecated\n"); + Printf("**WARNING: and will be removed in future versions\n"); + Printf("**WARNING: See https://clang.llvm.org/docs/SanitizerCoverage.html\n"); + Printf("**\n***\n***\n"); + CHECK_NE(sym, nullptr); InternalMmapVector offsets(0); InternalScopedString path(kMaxPathLength); InternalMmapVector cov_files(module_name_vec.size()); auto cov_files_deleter = at_scope_exit([&] { for (uptr i = 0; i < cov_files.size(); ++i) { InternalFree(cov_files[i]); } }); for (uptr m = 0; m < module_name_vec.size(); m++) { auto r = module_name_vec[m]; GetRangeOffsets(r, sym, &offsets); uptr num_offsets = offsets.size() - kNumWordsForMagic; u64 *magic_p = reinterpret_cast(offsets.data()); CHECK_EQ(*magic_p, 0ULL); // FIXME: we may want to write 32-bit offsets even in 64-mode // if all the offsets are small enough. *magic_p = kMagic; const char *module_name = StripModuleName(r.copied_module_name); if (cov_sandboxed) { if (cov_fd != kInvalidFd) { CovWritePacked(internal_getpid(), module_name, offsets.data(), offsets.size() * sizeof(offsets[0])); VReport(1, " CovDump: %zd PCs written to packed file\n", num_offsets); } } else { // One file per module per process. fd_t fd = CovOpenFile(&path, false /* packed */, module_name); if (fd == kInvalidFd) continue; WriteToFile(fd, offsets.data(), offsets.size() * sizeof(offsets[0])); CloseFile(fd); cov_files.push_back(internal_strdup(path.data())); VReport(1, " CovDump: %s: %zd PCs written\n", path.data(), num_offsets); } } if (cov_fd != kInvalidFd) CloseFile(cov_fd); GenerateHtmlReport(cov_files); } void CoverageData::DumpAll() { if (!coverage_enabled || common_flags()->coverage_direct) return; if (atomic_fetch_add(&dump_once_guard, 1, memory_order_relaxed)) return; DumpOffsets(); } void CovPrepareForSandboxing(__sanitizer_sandbox_arguments *args) { if (!args) return; if (!coverage_enabled) return; cov_sandboxed = args->coverage_sandboxed; if (!cov_sandboxed) return; cov_max_block_size = args->coverage_max_block_size; if (args->coverage_fd >= 0) { cov_fd = (fd_t)args->coverage_fd; } else { InternalScopedString path(kMaxPathLength); // Pre-open the file now. The sandbox won't allow us to do it later. cov_fd = CovOpenFile(&path, true /* packed */, nullptr); } } fd_t MaybeOpenCovFile(const char *name) { CHECK(name); if (!coverage_enabled) return kInvalidFd; InternalScopedString path(kMaxPathLength); return CovOpenFile(&path, true /* packed */, name); } void CovBeforeFork() { coverage_data.BeforeFork(); } void CovAfterFork(int child_pid) { coverage_data.AfterFork(child_pid); } static void MaybeDumpCoverage() { if (common_flags()->coverage) __sanitizer_cov_dump(); } void InitializeCoverage(bool enabled, const char *dir) { if (coverage_enabled) return; // May happen if two sanitizer enable coverage in the same process. coverage_enabled = enabled; coverage_dir = dir; coverage_data.Init(); if (enabled) coverage_data.Enable(); if (!common_flags()->coverage_direct) Atexit(__sanitizer_cov_dump); AddDieCallback(MaybeDumpCoverage); } void ReInitializeCoverage(bool enabled, const char *dir) { coverage_enabled = enabled; coverage_dir = dir; coverage_data.ReInit(); } void CoverageUpdateMapping() { if (coverage_enabled) CovUpdateMapping(coverage_dir); } } // namespace __sanitizer extern "C" { -SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(u32 *guard) { - coverage_data.Add(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC()), - guard); -} -SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_with_check(u32 *guard) { - atomic_uint32_t *atomic_guard = reinterpret_cast(guard); - if (static_cast( - __sanitizer::atomic_load(atomic_guard, memory_order_relaxed)) < 0) - coverage_data.Add(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC()), - guard); -} -SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init() { - coverage_enabled = true; - coverage_dir = common_flags()->coverage_dir; - coverage_data.Init(); -} SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() { - coverage_data.DumpAll(); __sanitizer_dump_trace_pc_guard_coverage(); } -SANITIZER_INTERFACE_ATTRIBUTE void -__sanitizer_cov_module_init(s32 *guards, uptr npcs, u8 *counters, - const char *comp_unit_name) { - coverage_data.InitializeGuards(guards, npcs, comp_unit_name, GET_CALLER_PC()); - if (!common_flags()->coverage_direct) return; - if (SANITIZER_ANDROID && coverage_enabled) { - // dlopen/dlclose interceptors do not work on Android, so we rely on - // Extend() calls to update .sancov.map. - CovUpdateMapping(coverage_dir, GET_CALLER_PC()); - } - coverage_data.Extend(npcs); -} SANITIZER_INTERFACE_ATTRIBUTE sptr __sanitizer_maybe_open_cov_file(const char *name) { return (sptr)MaybeOpenCovFile(name); } -SANITIZER_INTERFACE_ATTRIBUTE -uptr __sanitizer_get_total_unique_coverage() { - return atomic_load(&coverage_counter, memory_order_relaxed); -} - // Default empty implementations (weak). Users should redefine them. SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp, void) {} SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp1, void) {} SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp2, void) {} SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp4, void) {} SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_cmp8, void) {} SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_switch, void) {} SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_div4, void) {} SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_div8, void) {} SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_gep, void) {} SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_pc_indir, void) {} } // extern "C" Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_coverage_libcdep_new.cc =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_coverage_libcdep_new.cc (revision 319464) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_coverage_libcdep_new.cc (revision 319465) @@ -1,169 +1,169 @@ //===-- sanitizer_coverage_libcdep_new.cc ---------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // Sanitizer Coverage Controller for Trace PC Guard. #include "sancov_flags.h" #include "sanitizer_allocator_internal.h" #include "sanitizer_atomic.h" #include "sanitizer_common.h" #include "sanitizer_symbolizer.h" using namespace __sanitizer; using AddressRange = LoadedModule::AddressRange; namespace __sancov { namespace { static const u64 Magic64 = 0xC0BFFFFFFFFFFF64ULL; static const u64 Magic32 = 0xC0BFFFFFFFFFFF32ULL; static const u64 Magic = SANITIZER_WORDSIZE == 64 ? Magic64 : Magic32; static fd_t OpenFile(const char* path) { error_t err; fd_t fd = OpenFile(path, WrOnly, &err); if (fd == kInvalidFd) Report("SanitizerCoverage: failed to open %s for writing (reason: %d)\n", path, err); return fd; } static void GetCoverageFilename(char* path, const char* name, const char* extension) { CHECK(name); internal_snprintf(path, kMaxPathLength, "%s/%s.%zd.%s", common_flags()->coverage_dir, name, internal_getpid(), extension); } static void WriteModuleCoverage(char* file_path, const char* module_name, const uptr* pcs, uptr len) { GetCoverageFilename(file_path, StripModuleName(module_name), "sancov"); fd_t fd = OpenFile(file_path); WriteToFile(fd, &Magic, sizeof(Magic)); WriteToFile(fd, pcs, len * sizeof(*pcs)); CloseFile(fd); - Printf("SanitizerCoverage: %s %zd PCs written\n", file_path, len); + Printf("SanitizerCoverage: %s: %zd PCs written\n", file_path, len); } static void SanitizerDumpCoverage(const uptr* unsorted_pcs, uptr len) { if (!len) return; char* file_path = static_cast(InternalAlloc(kMaxPathLength)); char* module_name = static_cast(InternalAlloc(kMaxPathLength)); uptr* pcs = static_cast(InternalAlloc(len * sizeof(uptr))); internal_memcpy(pcs, unsorted_pcs, len * sizeof(uptr)); SortArray(pcs, len); bool module_found = false; uptr last_base = 0; uptr module_start_idx = 0; for (uptr i = 0; i < len; ++i) { const uptr pc = pcs[i]; if (!pc) continue; if (!__sanitizer_get_module_and_offset_for_pc(pc, nullptr, 0, &pcs[i])) { - Printf("ERROR: bad pc %x\n", pc); + Printf("ERROR: unknown pc 0x%x (may happen if dlclose is used)\n", pc); continue; } uptr module_base = pc - pcs[i]; if (module_base != last_base || !module_found) { if (module_found) { WriteModuleCoverage(file_path, module_name, &pcs[module_start_idx], i - module_start_idx); } last_base = module_base; module_start_idx = i; module_found = true; __sanitizer_get_module_and_offset_for_pc(pc, module_name, kMaxPathLength, &pcs[i]); } } if (module_found) { WriteModuleCoverage(file_path, module_name, &pcs[module_start_idx], len - module_start_idx); } InternalFree(file_path); InternalFree(module_name); InternalFree(pcs); } // Collects trace-pc guard coverage. // This class relies on zero-initialization. class TracePcGuardController { public: void Initialize() { CHECK(!initialized); initialized = true; InitializeSancovFlags(); pc_vector.Initialize(0); } void InitTracePcGuard(u32* start, u32* end) { if (!initialized) Initialize(); CHECK(!*start); CHECK_NE(start, end); u32 i = pc_vector.size(); for (u32* p = start; p < end; p++) *p = ++i; pc_vector.resize(i); } void TracePcGuard(u32* guard, uptr pc) { atomic_uint32_t* guard_ptr = reinterpret_cast(guard); u32 idx = atomic_exchange(guard_ptr, 0, memory_order_relaxed); if (!idx) return; // we start indices from 1. pc_vector[idx - 1] = pc; } void Dump() { if (!initialized || !common_flags()->coverage) return; __sanitizer_dump_coverage(pc_vector.data(), pc_vector.size()); } private: bool initialized; InternalMmapVectorNoCtor pc_vector; }; static TracePcGuardController pc_guard_controller; } // namespace } // namespace __sancov extern "C" { SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_dump_coverage( // NOLINT const uptr* pcs, uptr len) { return __sancov::SanitizerDumpCoverage(pcs, len); } SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_pc_guard, u32* guard) { if (!*guard) return; __sancov::pc_guard_controller.TracePcGuard(guard, GET_CALLER_PC() - 1); } SANITIZER_INTERFACE_WEAK_DEF(void, __sanitizer_cov_trace_pc_guard_init, u32* start, u32* end) { if (start == end || *start) return; __sancov::pc_guard_controller.InitTracePcGuard(start, end); } SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_dump_trace_pc_guard_coverage() { __sancov::pc_guard_controller.Dump(); } } // extern "C" Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_flags.inc =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_flags.inc (revision 319464) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_flags.inc (revision 319465) @@ -1,235 +1,238 @@ //===-- sanitizer_flags.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 describes common flags available in all sanitizers. // //===----------------------------------------------------------------------===// #ifndef COMMON_FLAG #error "Define COMMON_FLAG prior to including this file!" #endif // COMMON_FLAG(Type, Name, DefaultValue, Description) // Supported types: bool, const char *, int, uptr. // Default value must be a compile-time constant. // Description must be a string literal. COMMON_FLAG( bool, symbolize, true, "If set, use the online symbolizer from common sanitizer runtime to turn " "virtual addresses to file/line locations.") COMMON_FLAG( const char *, external_symbolizer_path, nullptr, "Path to external symbolizer. If empty, the tool will search $PATH for " "the symbolizer.") COMMON_FLAG( bool, allow_addr2line, false, "If set, allows online symbolizer to run addr2line binary to symbolize " "stack traces (addr2line will only be used if llvm-symbolizer binary is " "unavailable.") COMMON_FLAG(const char *, strip_path_prefix, "", "Strips this prefix from file paths in error reports.") COMMON_FLAG(bool, fast_unwind_on_check, false, "If available, use the fast frame-pointer-based unwinder on " "internal CHECK failures.") COMMON_FLAG(bool, fast_unwind_on_fatal, false, "If available, use the fast frame-pointer-based unwinder on fatal " "errors.") COMMON_FLAG(bool, fast_unwind_on_malloc, true, "If available, use the fast frame-pointer-based unwinder on " "malloc/free.") COMMON_FLAG(bool, handle_ioctl, false, "Intercept and handle ioctl requests.") COMMON_FLAG(int, malloc_context_size, 1, "Max number of stack frames kept for each allocation/deallocation.") COMMON_FLAG( const char *, log_path, "stderr", "Write logs to \"log_path.pid\". The special values are \"stdout\" and " "\"stderr\". The default is \"stderr\".") COMMON_FLAG( bool, log_exe_name, false, "Mention name of executable when reporting error and " "append executable name to logs (as in \"log_path.exe_name.pid\").") COMMON_FLAG( bool, log_to_syslog, SANITIZER_ANDROID || SANITIZER_MAC, "Write all sanitizer output to syslog in addition to other means of " "logging.") COMMON_FLAG( int, verbosity, 0, "Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output).") COMMON_FLAG(bool, detect_leaks, !SANITIZER_MAC, "Enable memory leak detection.") COMMON_FLAG( bool, leak_check_at_exit, true, "Invoke leak checking in an atexit handler. Has no effect if " "detect_leaks=false, or if __lsan_do_leak_check() is called before the " "handler has a chance to run.") COMMON_FLAG(bool, allocator_may_return_null, false, "If false, the allocator will crash instead of returning 0 on " "out-of-memory.") COMMON_FLAG(bool, print_summary, true, "If false, disable printing error summaries in addition to error " "reports.") COMMON_FLAG(int, print_module_map, 0, "OS X only (0 - don't print, 1 - print only once before process " "exits, 2 - print after each report).") COMMON_FLAG(bool, check_printf, true, "Check printf arguments.") #define COMMON_FLAG_HANDLE_SIGNAL_HELP(signal) \ "Controls custom tool's " #signal " handler (0 - do not registers the " \ "handler, 1 - register the handler and allow user to set own, " \ "2 - registers the handler and block user from changing it). " COMMON_FLAG(HandleSignalMode, handle_segv, kHandleSignalYes, COMMON_FLAG_HANDLE_SIGNAL_HELP(SIGSEGV)) COMMON_FLAG(HandleSignalMode, handle_sigbus, kHandleSignalYes, COMMON_FLAG_HANDLE_SIGNAL_HELP(SIGBUS)) COMMON_FLAG(HandleSignalMode, handle_abort, kHandleSignalNo, COMMON_FLAG_HANDLE_SIGNAL_HELP(SIGABRT)) COMMON_FLAG(HandleSignalMode, handle_sigill, kHandleSignalNo, COMMON_FLAG_HANDLE_SIGNAL_HELP(SIGILL)) COMMON_FLAG(HandleSignalMode, handle_sigfpe, kHandleSignalYes, COMMON_FLAG_HANDLE_SIGNAL_HELP(SIGFPE)) #undef COMMON_FLAG_HANDLE_SIGNAL_HELP COMMON_FLAG(bool, use_sigaltstack, true, "If set, uses alternate stack for signal handling.") COMMON_FLAG(bool, detect_deadlocks, false, "If set, deadlock detection is enabled.") COMMON_FLAG( uptr, clear_shadow_mmap_threshold, 64 * 1024, "Large shadow regions are zero-filled using mmap(NORESERVE) instead of " "memset(). This is the threshold size in bytes.") COMMON_FLAG(const char *, color, "auto", "Colorize reports: (always|never|auto).") COMMON_FLAG( bool, legacy_pthread_cond, false, "Enables support for dynamic libraries linked with libpthread 2.2.5.") COMMON_FLAG(bool, intercept_tls_get_addr, false, "Intercept __tls_get_addr.") COMMON_FLAG(bool, help, false, "Print the flag descriptions.") COMMON_FLAG(uptr, mmap_limit_mb, 0, "Limit the amount of mmap-ed memory (excluding shadow) in Mb; " "not a user-facing flag, used mosly for testing the tools") COMMON_FLAG(uptr, hard_rss_limit_mb, 0, "Hard RSS limit in Mb." " If non-zero, a background thread is spawned at startup" " which periodically reads RSS and aborts the process if the" " limit is reached") COMMON_FLAG(uptr, soft_rss_limit_mb, 0, "Soft RSS limit in Mb." " If non-zero, a background thread is spawned at startup" " which periodically reads RSS. If the limit is reached" " all subsequent malloc/new calls will fail or return NULL" " (depending on the value of allocator_may_return_null)" " until the RSS goes below the soft limit." " This limit does not affect memory allocations other than" " malloc/new.") COMMON_FLAG(bool, heap_profile, false, "Experimental heap profiler, asan-only") COMMON_FLAG(s32, allocator_release_to_os_interval_ms, kReleaseToOSIntervalNever, "Experimental. Only affects a 64-bit allocator. If set, tries to " "release unused memory to the OS, but not more often than this " "interval (in milliseconds). Negative values mean do not attempt " "to release memory to the OS.\n") COMMON_FLAG(bool, can_use_proc_maps_statm, true, "If false, do not attempt to read /proc/maps/statm." " Mostly useful for testing sanitizers.") COMMON_FLAG( bool, coverage, false, "If set, coverage information will be dumped at program shutdown (if the " "coverage instrumentation was enabled at compile time).") COMMON_FLAG(bool, coverage_pcs, true, "If set (and if 'coverage' is set too), the coverage information " "will be dumped as a set of PC offsets for every module.") COMMON_FLAG(bool, coverage_order_pcs, false, "If true, the PCs will be dumped in the order they've" " appeared during the execution.") COMMON_FLAG(bool, coverage_direct, SANITIZER_ANDROID, "If set, coverage information will be dumped directly to a memory " "mapped file. This way data is not lost even if the process is " "suddenly killed.") COMMON_FLAG(const char *, coverage_dir, ".", "Target directory for coverage dumps. Defaults to the current " "directory.") COMMON_FLAG(bool, full_address_space, false, "Sanitize complete address space; " "by default kernel area on 32-bit platforms will not be sanitized") COMMON_FLAG(bool, print_suppressions, true, "Print matched suppressions at exit.") COMMON_FLAG( bool, disable_coredump, (SANITIZER_WORDSIZE == 64) && !SANITIZER_GO, "Disable core dumping. By default, disable_coredump=1 on 64-bit to avoid" " dumping a 16T+ core file. Ignored on OSes that don't dump core by" " default and for sanitizers that don't reserve lots of virtual memory.") COMMON_FLAG(bool, use_madv_dontdump, true, "If set, instructs kernel to not store the (huge) shadow " "in core file.") COMMON_FLAG(bool, symbolize_inline_frames, true, "Print inlined frames in stacktraces. Defaults to true.") COMMON_FLAG(bool, symbolize_vs_style, false, "Print file locations in Visual Studio style (e.g: " " file(10,42): ...") COMMON_FLAG(int, dedup_token_length, 0, "If positive, after printing a stack trace also print a short " "string token based on this number of frames that will simplify " "deduplication of the reports. " "Example: 'DEDUP_TOKEN: foo-bar-main'. Default is 0.") COMMON_FLAG(const char *, stack_trace_format, "DEFAULT", "Format string used to render stack frames. " "See sanitizer_stacktrace_printer.h for the format description. " "Use DEFAULT to get default format.") COMMON_FLAG(bool, no_huge_pages_for_shadow, true, "If true, the shadow is not allowed to use huge pages. ") COMMON_FLAG(bool, strict_string_checks, false, "If set check that string arguments are properly null-terminated") COMMON_FLAG(bool, intercept_strstr, true, "If set, uses custom wrappers for strstr and strcasestr functions " "to find more errors.") COMMON_FLAG(bool, intercept_strspn, true, "If set, uses custom wrappers for strspn and strcspn function " "to find more errors.") COMMON_FLAG(bool, intercept_strtok, true, "If set, uses a custom wrapper for the strtok function " "to find more errors.") COMMON_FLAG(bool, intercept_strpbrk, true, "If set, uses custom wrappers for strpbrk function " "to find more errors.") COMMON_FLAG(bool, intercept_strlen, true, "If set, uses custom wrappers for strlen and strnlen functions " "to find more errors.") +COMMON_FLAG(bool, intercept_strndup, true, + "If set, uses custom wrappers for strndup functions " + "to find more errors.") COMMON_FLAG(bool, intercept_strchr, true, "If set, uses custom wrappers for strchr, strchrnul, and strrchr " "functions to find more errors.") COMMON_FLAG(bool, intercept_memcmp, true, "If set, uses custom wrappers for memcmp function " "to find more errors.") COMMON_FLAG(bool, strict_memcmp, true, "If true, assume that memcmp(p1, p2, n) always reads n bytes before " "comparing p1 and p2.") COMMON_FLAG(bool, intercept_memmem, true, "If set, uses a wrapper for memmem() to find more errors.") COMMON_FLAG(bool, intercept_intrin, true, "If set, uses custom wrappers for memset/memcpy/memmove " "intrinsics to find more errors.") COMMON_FLAG(bool, intercept_stat, true, "If set, uses custom wrappers for *stat functions " "to find more errors.") COMMON_FLAG(bool, intercept_send, true, "If set, uses custom wrappers for send* functions " "to find more errors.") COMMON_FLAG(bool, decorate_proc_maps, false, "If set, decorate sanitizer " "mappings in /proc/self/maps with " "user-readable names") COMMON_FLAG(int, exitcode, 1, "Override the program exit status if the tool " "found an error") COMMON_FLAG( bool, abort_on_error, SANITIZER_ANDROID || SANITIZER_MAC, "If set, the tool calls abort() instead of _exit() after printing the " "error report.") COMMON_FLAG(bool, suppress_equal_pcs, true, "Deduplicate multiple reports for single source location in " "halt_on_error=false mode (asan only).") COMMON_FLAG(bool, print_cmdline, false, "Print command line on crash " "(asan only).") COMMON_FLAG(bool, html_cov_report, false, "Generate html coverage report.") COMMON_FLAG(const char *, sancov_path, "sancov", "Sancov tool location.") Index: vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_platform_interceptors.h =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_platform_interceptors.h (revision 319464) +++ vendor/compiler-rt/dist/lib/sanitizer_common/sanitizer_platform_interceptors.h (revision 319465) @@ -1,344 +1,358 @@ //===-- 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 #endif // #ifndef SANITIZER_PLATFORM_INTERCEPTORS_H Index: vendor/compiler-rt/dist/lib/sanitizer_common/scripts/sancov.py =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/scripts/sancov.py (revision 319464) +++ vendor/compiler-rt/dist/lib/sanitizer_common/scripts/sancov.py (revision 319465) @@ -1,251 +1,251 @@ #!/usr/bin/env python # Merge or print the coverage data collected by asan's coverage. # Input files are sequences of 4-byte integers. # We need to merge these integers into a set and then # either print them (as hex) or dump them into another file. import array import bisect import glob import os.path import struct import subprocess import sys prog_name = "" def Usage(): sys.stderr.write( "Usage: \n" + \ " " + prog_name + " merge FILE [FILE...] > OUTPUT\n" \ " " + prog_name + " print FILE [FILE...]\n" \ " " + prog_name + " unpack FILE [FILE...]\n" \ " " + prog_name + " rawunpack FILE [FILE ...]\n" \ " " + prog_name + " missing BINARY < LIST_OF_PCS\n" \ "\n") exit(1) def CheckBits(bits): if bits != 32 and bits != 64: raise Exception("Wrong bitness: %d" % bits) def TypeCodeForBits(bits): CheckBits(bits) return 'L' if bits == 64 else 'I' def TypeCodeForStruct(bits): CheckBits(bits) return 'Q' if bits == 64 else 'I' kMagic32SecondHalf = 0xFFFFFF32; kMagic64SecondHalf = 0xFFFFFF64; kMagicFirstHalf = 0xC0BFFFFF; def MagicForBits(bits): CheckBits(bits) if sys.byteorder == 'little': return [kMagic64SecondHalf if bits == 64 else kMagic32SecondHalf, kMagicFirstHalf] else: return [kMagicFirstHalf, kMagic64SecondHalf if bits == 64 else kMagic32SecondHalf] def ReadMagicAndReturnBitness(f, path): magic_bytes = f.read(8) magic_words = struct.unpack('II', magic_bytes); bits = 0 idx = 1 if sys.byteorder == 'little' else 0 if magic_words[idx] == kMagicFirstHalf: if magic_words[1-idx] == kMagic64SecondHalf: bits = 64 elif magic_words[1-idx] == kMagic32SecondHalf: bits = 32 if bits == 0: raise Exception('Bad magic word in %s' % path) return bits def ReadOneFile(path): with open(path, mode="rb") as f: f.seek(0, 2) size = f.tell() f.seek(0, 0) if size < 8: raise Exception('File %s is short (< 8 bytes)' % path) bits = ReadMagicAndReturnBitness(f, path) size -= 8 w = size * 8 // bits s = struct.unpack_from(TypeCodeForStruct(bits) * (w), f.read(size)) sys.stderr.write( "%s: read %d %d-bit PCs from %s\n" % (prog_name, w, bits, path)) return s def Merge(files): s = set() for f in files: s = s.union(set(ReadOneFile(f))) sys.stderr.write( "%s: %d files merged; %d PCs total\n" % (prog_name, len(files), len(s)) ) return sorted(s) def PrintFiles(files): if len(files) > 1: s = Merge(files) else: # If there is just on file, print the PCs in order. s = ReadOneFile(files[0]) sys.stderr.write("%s: 1 file merged; %d PCs total\n" % (prog_name, len(s))) for i in s: print("0x%x" % i) def MergeAndPrint(files): if sys.stdout.isatty(): Usage() s = Merge(files) bits = 32 if max(s) > 0xFFFFFFFF: bits = 64 stdout_buf = getattr(sys.stdout, 'buffer', sys.stdout) array.array('I', MagicForBits(bits)).tofile(stdout_buf) a = struct.pack(TypeCodeForStruct(bits) * len(s), *s) stdout_buf.write(a) def UnpackOneFile(path): with open(path, mode="rb") as f: sys.stderr.write("%s: unpacking %s\n" % (prog_name, path)) while True: header = f.read(12) if not header: return if len(header) < 12: break pid, module_length, blob_size = struct.unpack('iII', header) module = f.read(module_length).decode('utf-8') blob = f.read(blob_size) assert(len(module) == module_length) assert(len(blob) == blob_size) extracted_file = "%s.%d.sancov" % (module, pid) sys.stderr.write("%s: extracting %s\n" % (prog_name, extracted_file)) # The packed file may contain multiple blobs for the same pid/module # pair. Append to the end of the file instead of overwriting. with open(extracted_file, 'ab') as f2: f2.write(blob) # fail raise Exception('Error reading file %s' % path) def Unpack(files): for f in files: UnpackOneFile(f) def UnpackOneRawFile(path, map_path): mem_map = [] with open(map_path, mode="rt") as f_map: sys.stderr.write("%s: reading map %s\n" % (prog_name, map_path)) bits = int(f_map.readline()) if bits != 32 and bits != 64: raise Exception('Wrong bits size in the map') for line in f_map: parts = line.rstrip().split() mem_map.append((int(parts[0], 16), int(parts[1], 16), int(parts[2], 16), ' '.join(parts[3:]))) mem_map.sort(key=lambda m : m[0]) mem_map_keys = [m[0] for m in mem_map] with open(path, mode="rb") as f: sys.stderr.write("%s: unpacking %s\n" % (prog_name, path)) f.seek(0, 2) size = f.tell() f.seek(0, 0) pcs = struct.unpack_from(TypeCodeForStruct(bits) * (size * 8 // bits), f.read(size)) mem_map_pcs = [[] for i in range(0, len(mem_map))] for pc in pcs: if pc == 0: continue map_idx = bisect.bisect(mem_map_keys, pc) - 1 (start, end, base, module_path) = mem_map[map_idx] assert pc >= start if pc >= end: sys.stderr.write("warning: %s: pc %x outside of any known mapping\n" % (prog_name, pc)) continue mem_map_pcs[map_idx].append(pc - base) for ((start, end, base, module_path), pc_list) in zip(mem_map, mem_map_pcs): if len(pc_list) == 0: continue assert path.endswith('.sancov.raw') dst_path = module_path + '.' + os.path.basename(path)[:-4] sys.stderr.write("%s: writing %d PCs to %s\n" % (prog_name, len(pc_list), dst_path)) sorted_pc_list = sorted(pc_list) pc_buffer = struct.pack(TypeCodeForStruct(bits) * len(pc_list), *sorted_pc_list) with open(dst_path, 'ab+') as f2: array.array('I', MagicForBits(bits)).tofile(f2) f2.seek(0, 2) f2.write(pc_buffer) def RawUnpack(files): for f in files: if not f.endswith('.sancov.raw'): raise Exception('Unexpected raw file name %s' % f) f_map = f[:-3] + 'map' UnpackOneRawFile(f, f_map) def GetInstrumentedPCs(binary): # This looks scary, but all it does is extract all offsets where we call: # - __sanitizer_cov() or __sanitizer_cov_with_check(), # - with call or callq, # - directly or via PLT. cmd = "objdump -d %s | " \ - "grep '^\s\+[0-9a-f]\+:.*\scall\(q\|\)\s\+[0-9a-f]\+ <__sanitizer_cov\(_with_check\|\)\(@plt\|\)>' | " \ + "grep '^\s\+[0-9a-f]\+:.*\scall\(q\|\)\s\+[0-9a-f]\+ <__sanitizer_cov\(_with_check\|\|_trace_pc_guard\)\(@plt\|\)>' | " \ "grep '^\s\+[0-9a-f]\+' -o" % binary proc = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, shell=True) proc.stdin.close() # The PCs we get from objdump are off by 4 bytes, as they point to the # beginning of the callq instruction. Empirically this is true on x86 and # x86_64. return set(int(line.strip(), 16) + 4 for line in proc.stdout) def PrintMissing(binary): if not os.path.isfile(binary): raise Exception('File not found: %s' % binary) instrumented = GetInstrumentedPCs(binary) sys.stderr.write("%s: found %d instrumented PCs in %s\n" % (prog_name, len(instrumented), binary)) covered = set(int(line, 16) for line in sys.stdin) sys.stderr.write("%s: read %d PCs from stdin\n" % (prog_name, len(covered))) missing = instrumented - covered sys.stderr.write("%s: %d PCs missing from coverage\n" % (prog_name, len(missing))) if (len(missing) > len(instrumented) - len(covered)): sys.stderr.write( "%s: WARNING: stdin contains PCs not found in binary\n" % prog_name ) for pc in sorted(missing): print("0x%x" % pc) if __name__ == '__main__': prog_name = sys.argv[0] if len(sys.argv) <= 2: Usage(); if sys.argv[1] == "missing": if len(sys.argv) != 3: Usage() PrintMissing(sys.argv[2]) exit(0) file_list = [] for f in sys.argv[2:]: file_list += glob.glob(f) if not file_list: Usage() if sys.argv[1] == "print": PrintFiles(file_list) elif sys.argv[1] == "merge": MergeAndPrint(file_list) elif sys.argv[1] == "unpack": Unpack(file_list) elif sys.argv[1] == "rawunpack": RawUnpack(file_list) else: Usage() Index: vendor/compiler-rt/dist/lib/sanitizer_common/symbolizer/scripts/global_symbols.txt =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/symbolizer/scripts/global_symbols.txt (revision 319464) +++ vendor/compiler-rt/dist/lib/sanitizer_common/symbolizer/scripts/global_symbols.txt (revision 319465) @@ -1,138 +1,139 @@ _GLOBAL_OFFSET_TABLE_ U _ZN11__sanitizer13internal_mmapEPvmiiiy U _ZN11__sanitizer13internal_openEPKcij U _ZN11__sanitizer13internal_statEPKcPv U _ZN11__sanitizer14internal_closeEi U _ZN11__sanitizer14internal_fstatEiPv U _ZN11__sanitizer14internal_lstatEPKcPv U _ZN11__sanitizer15internal_strlenEPKc U _ZN11__sanitizer16internal_iserrorEmPi U _ZN11__sanitizer17internal_snprintfEPcmPKcz U __ctype_b_loc U __ctype_get_mb_cur_max U __cxa_atexit U __divdi3 U __dso_handle U __errno_location U __interceptor_pread w __interceptor_read w __interceptor_realpath w __moddi3 U __sanitizer_symbolize_code T __sanitizer_symbolize_data T __sanitizer_symbolize_demangle T __sanitizer_symbolize_flush T __strdup U __udivdi3 U __umoddi3 U _exit U abort U access U calloc U catclose U catgets U catopen U ceil U clock_gettime U cfgetospeed U dl_iterate_phdr U dlsym U dup U dup2 U environ U execv U exit U fclose U fflush U fileno U fopen U fork U fprintf U fputc U free U freelocale U fwrite U getc U getcwd U getenv U getpagesize U getpid U gettimeofday U ioctl U +isalpha U isatty U isprint U isupper U isxdigit U log10 U lseek U lseek64 U malloc U mbrlen U mbrtowc U mbsnrtowcs U mbsrtowcs U mbtowc U memchr U memcmp U memcpy U memmove U memset U mkdir U munmap U newlocale U perror U posix_spawn U posix_spawn_file_actions_adddup2 U posix_spawn_file_actions_addopen U posix_spawn_file_actions_destroy U posix_spawn_file_actions_init U qsort U rand U readlink U realloc U remove U setvbuf U sigfillset U sigprocmask U snprintf U sprintf U srand U sscanf U stderr U stdin U stdout U strcat U strchr U strcmp U strcpy U strdup U strerror U strerror_r U strftime_l U strncmp U strncpy U strrchr U strsep U strtod_l U strtof_l U strtol U strtold_l U strtoll_l U strtoull_l U tcgetattr U uname U ungetc U unlink U uselocale U vasprintf U vfprintf U vsnprintf U vsscanf U waitpid U wcrtomb U wcslen U wcsnrtombs U wmemcpy U wmemmove U wmemset U write U Index: vendor/compiler-rt/dist/lib/sanitizer_common/tests/sanitizer_test_utils.h =================================================================== --- vendor/compiler-rt/dist/lib/sanitizer_common/tests/sanitizer_test_utils.h (revision 319464) +++ vendor/compiler-rt/dist/lib/sanitizer_common/tests/sanitizer_test_utils.h (revision 319465) @@ -1,127 +1,133 @@ //===-- sanitizer_test_utils.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 a part of *Sanitizer runtime. // Common unit tests utilities. // //===----------------------------------------------------------------------===// #ifndef SANITIZER_TEST_UTILS_H #define SANITIZER_TEST_UTILS_H #if defined(_WIN32) // should always be the first include on Windows. # include // MSVS headers define max/min as macros, so std::max/min gets crazy. # undef max # undef min #endif #if !defined(SANITIZER_EXTERNAL_TEST_CONFIG) # define INCLUDED_FROM_SANITIZER_TEST_UTILS_H # include "sanitizer_test_config.h" # undef INCLUDED_FROM_SANITIZER_TEST_UTILS_H #endif #include #if defined(_MSC_VER) # define NOINLINE __declspec(noinline) #else // defined(_MSC_VER) # define NOINLINE __attribute__((noinline)) #endif // defined(_MSC_VER) #if !defined(_MSC_VER) || defined(__clang__) # define UNUSED __attribute__((unused)) # define USED __attribute__((used)) #else # define UNUSED # define USED #endif #if !defined(__has_feature) #define __has_feature(x) 0 #endif #ifndef ATTRIBUTE_NO_SANITIZE_ADDRESS # if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) # define ATTRIBUTE_NO_SANITIZE_ADDRESS \ __attribute__((no_sanitize_address)) # else # define ATTRIBUTE_NO_SANITIZE_ADDRESS # endif #endif // ATTRIBUTE_NO_SANITIZE_ADDRESS #if __LP64__ || defined(_WIN64) # define SANITIZER_WORDSIZE 64 #else # define SANITIZER_WORDSIZE 32 #endif // Make the compiler thinks that something is going on there. inline void break_optimization(void *arg) { #if !defined(_WIN32) || defined(__clang__) __asm__ __volatile__("" : : "r" (arg) : "memory"); #endif } // This function returns its parameter but in such a way that compiler // can not prove it. template NOINLINE static T Ident(T t) { T ret = t; break_optimization(&ret); return ret; } // Simple stand-alone pseudorandom number generator. // Current algorithm is ANSI C linear congruential PRNG. static inline uint32_t my_rand_r(uint32_t* state) { return (*state = *state * 1103515245 + 12345) >> 16; } static uint32_t global_seed = 0; static inline uint32_t my_rand() { return my_rand_r(&global_seed); } // Set availability of platform-specific functions. #if !defined(__APPLE__) && !defined(__ANDROID__) && !defined(_WIN32) # define SANITIZER_TEST_HAS_POSIX_MEMALIGN 1 #else # define SANITIZER_TEST_HAS_POSIX_MEMALIGN 0 #endif #if !defined(__APPLE__) && !defined(__FreeBSD__) && \ !defined(__ANDROID__) && !defined(_WIN32) # define SANITIZER_TEST_HAS_MEMALIGN 1 # define SANITIZER_TEST_HAS_PVALLOC 1 # define SANITIZER_TEST_HAS_MALLOC_USABLE_SIZE 1 #else # define SANITIZER_TEST_HAS_MEMALIGN 0 # define SANITIZER_TEST_HAS_PVALLOC 0 # define SANITIZER_TEST_HAS_MALLOC_USABLE_SIZE 0 #endif #if !defined(__APPLE__) # define SANITIZER_TEST_HAS_STRNLEN 1 #else # define SANITIZER_TEST_HAS_STRNLEN 0 #endif #if defined(__FreeBSD__) # define SANITIZER_TEST_HAS_PRINTF_L 1 #else # define SANITIZER_TEST_HAS_PRINTF_L 0 #endif +#if !defined(_MSC_VER) +# define SANITIZER_TEST_HAS_STRNDUP 1 +#else +# define SANITIZER_TEST_HAS_STRNDUP 0 +#endif + #endif // SANITIZER_TEST_UTILS_H Index: vendor/compiler-rt/dist/lib/ubsan/ubsan_type_hash_itanium.cc =================================================================== --- vendor/compiler-rt/dist/lib/ubsan/ubsan_type_hash_itanium.cc (revision 319464) +++ vendor/compiler-rt/dist/lib/ubsan/ubsan_type_hash_itanium.cc (revision 319465) @@ -1,261 +1,261 @@ //===-- ubsan_type_hash_itanium.cc ----------------------------------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Implementation of type hashing/lookup for Itanium C++ ABI. // //===----------------------------------------------------------------------===// #include "sanitizer_common/sanitizer_platform.h" #include "ubsan_platform.h" #if CAN_SANITIZE_UB && !SANITIZER_WINDOWS #include "ubsan_type_hash.h" #include "sanitizer_common/sanitizer_common.h" // The following are intended to be binary compatible with the definitions // given in the Itanium ABI. We make no attempt to be ODR-compatible with // those definitions, since existing ABI implementations aren't. namespace std { class type_info { public: virtual ~type_info(); const char *__type_name; }; } namespace __cxxabiv1 { /// Type info for classes with no bases, and base class for type info for /// classes with bases. class __class_type_info : public std::type_info { ~__class_type_info() override; }; /// Type info for classes with simple single public inheritance. class __si_class_type_info : public __class_type_info { public: ~__si_class_type_info() override; const __class_type_info *__base_type; }; class __base_class_type_info { public: const __class_type_info *__base_type; long __offset_flags; enum __offset_flags_masks { __virtual_mask = 0x1, __public_mask = 0x2, __offset_shift = 8 }; }; /// Type info for classes with multiple, virtual, or non-public inheritance. class __vmi_class_type_info : public __class_type_info { public: ~__vmi_class_type_info() override; unsigned int flags; unsigned int base_count; __base_class_type_info base_info[1]; }; } namespace abi = __cxxabiv1; using namespace __sanitizer; // We implement a simple two-level cache for type-checking results. For each // (vptr,type) pair, a hash is computed. This hash is assumed to be globally // unique; if it collides, we will get false negatives, but: // * such a collision would have to occur on the *first* bad access, // * the probability of such a collision is low (and for a 64-bit target, is // negligible), and // * the vptr, and thus the hash, can be affected by ASLR, so multiple runs // give better coverage. // // The first caching layer is a small hash table with no chaining; buckets are // reused as needed. The second caching layer is a large hash table with open // chaining. We can freely evict from either layer since this is just a cache. // // FIXME: Make these hash table accesses thread-safe. The races here are benign: // assuming the unsequenced loads and stores don't misbehave too badly, // the worst case is false negatives or poor cache behavior, not false // positives or crashes. /// Find a bucket to store the given hash value in. static __ubsan::HashValue *getTypeCacheHashTableBucket(__ubsan::HashValue V) { static const unsigned HashTableSize = 65537; static __ubsan::HashValue __ubsan_vptr_hash_set[HashTableSize]; unsigned First = (V & 65535) ^ 1; unsigned Probe = First; for (int Tries = 5; Tries; --Tries) { if (!__ubsan_vptr_hash_set[Probe] || __ubsan_vptr_hash_set[Probe] == V) return &__ubsan_vptr_hash_set[Probe]; Probe += ((V >> 16) & 65535) + 1; if (Probe >= HashTableSize) Probe -= HashTableSize; } // FIXME: Pick a random entry from the probe sequence to evict rather than // just taking the first. return &__ubsan_vptr_hash_set[First]; } /// \brief Determine whether \p Derived has a \p Base base class subobject at /// offset \p Offset. static bool isDerivedFromAtOffset(const abi::__class_type_info *Derived, const abi::__class_type_info *Base, sptr Offset) { if (Derived->__type_name == Base->__type_name || (SANITIZER_NON_UNIQUE_TYPEINFO && !internal_strcmp(Derived->__type_name, Base->__type_name))) return Offset == 0; if (const abi::__si_class_type_info *SI = dynamic_cast(Derived)) return isDerivedFromAtOffset(SI->__base_type, Base, Offset); const abi::__vmi_class_type_info *VTI = dynamic_cast(Derived); if (!VTI) // No base class subobjects. return false; // Look for a base class which is derived from \p Base at the right offset. for (unsigned int base = 0; base != VTI->base_count; ++base) { // FIXME: Curtail the recursion if this base can't possibly contain the // given offset. sptr OffsetHere = VTI->base_info[base].__offset_flags >> abi::__base_class_type_info::__offset_shift; if (VTI->base_info[base].__offset_flags & abi::__base_class_type_info::__virtual_mask) // For now, just punt on virtual bases and say 'yes'. // FIXME: OffsetHere is the offset in the vtable of the virtual base // offset. Read the vbase offset out of the vtable and use it. return true; if (isDerivedFromAtOffset(VTI->base_info[base].__base_type, Base, Offset - OffsetHere)) return true; } return false; } /// \brief Find the derived-most dynamic base class of \p Derived at offset /// \p Offset. static const abi::__class_type_info *findBaseAtOffset( const abi::__class_type_info *Derived, sptr Offset) { if (!Offset) return Derived; if (const abi::__si_class_type_info *SI = dynamic_cast(Derived)) return findBaseAtOffset(SI->__base_type, Offset); const abi::__vmi_class_type_info *VTI = dynamic_cast(Derived); if (!VTI) // No base class subobjects. return nullptr; for (unsigned int base = 0; base != VTI->base_count; ++base) { sptr OffsetHere = VTI->base_info[base].__offset_flags >> abi::__base_class_type_info::__offset_shift; if (VTI->base_info[base].__offset_flags & abi::__base_class_type_info::__virtual_mask) // FIXME: Can't handle virtual bases yet. continue; if (const abi::__class_type_info *Base = findBaseAtOffset(VTI->base_info[base].__base_type, Offset - OffsetHere)) return Base; } return nullptr; } namespace { struct VtablePrefix { /// The offset from the vptr to the start of the most-derived object. /// This will only be greater than zero in some virtual base class vtables /// used during object con-/destruction, and will usually be exactly zero. sptr Offset; /// The type_info object describing the most-derived class type. std::type_info *TypeInfo; }; VtablePrefix *getVtablePrefix(void *Vtable) { VtablePrefix *Vptr = reinterpret_cast(Vtable); - if (!Vptr) + if (!IsAccessibleMemoryRange((uptr)Vptr, sizeof(VtablePrefix))) return nullptr; VtablePrefix *Prefix = Vptr - 1; if (!Prefix->TypeInfo) // This can't possibly be a valid vtable. return nullptr; return Prefix; } } bool __ubsan::checkDynamicType(void *Object, void *Type, HashValue Hash) { // A crash anywhere within this function probably means the vptr is corrupted. // FIXME: Perform these checks more cautiously. // Check whether this is something we've evicted from the cache. HashValue *Bucket = getTypeCacheHashTableBucket(Hash); if (*Bucket == Hash) { __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash; return true; } void *VtablePtr = *reinterpret_cast(Object); VtablePrefix *Vtable = getVtablePrefix(VtablePtr); if (!Vtable) return false; if (Vtable->Offset < -VptrMaxOffsetToTop || Vtable->Offset > VptrMaxOffsetToTop) { // Too large or too small offset are signs of Vtable corruption. return false; } // Check that this is actually a type_info object for a class type. abi::__class_type_info *Derived = dynamic_cast(Vtable->TypeInfo); if (!Derived) return false; abi::__class_type_info *Base = (abi::__class_type_info*)Type; if (!isDerivedFromAtOffset(Derived, Base, -Vtable->Offset)) return false; // Success. Cache this result. __ubsan_vptr_type_cache[Hash % VptrTypeCacheSize] = Hash; *Bucket = Hash; return true; } __ubsan::DynamicTypeInfo __ubsan::getDynamicTypeInfoFromVtable(void *VtablePtr) { VtablePrefix *Vtable = getVtablePrefix(VtablePtr); if (!Vtable) return DynamicTypeInfo(nullptr, 0, nullptr); if (Vtable->Offset < -VptrMaxOffsetToTop || Vtable->Offset > VptrMaxOffsetToTop) return DynamicTypeInfo(nullptr, Vtable->Offset, nullptr); const abi::__class_type_info *ObjectType = findBaseAtOffset( static_cast(Vtable->TypeInfo), -Vtable->Offset); return DynamicTypeInfo(Vtable->TypeInfo->__type_name, -Vtable->Offset, ObjectType ? ObjectType->__type_name : ""); } #endif // CAN_SANITIZE_UB && !SANITIZER_WINDOWS Index: vendor/compiler-rt/dist/test/asan/TestCases/coverage-order-pcs.cc =================================================================== --- vendor/compiler-rt/dist/test/asan/TestCases/coverage-order-pcs.cc (revision 319464) +++ vendor/compiler-rt/dist/test/asan/TestCases/coverage-order-pcs.cc (nonexistent) @@ -1,57 +0,0 @@ -// Test coverage_order_pcs=1 flag which orders the PCs by their appearance. -// RUN: DIR=%T/coverage-order-pcs -// RUN: rm -rf $DIR -// RUN: mkdir $DIR -// RUN: %clangxx_asan -fsanitize-coverage=func %s -o %t -// RUN: %env_asan_opts=coverage_dir=$DIR:coverage=1:coverage_order_pcs=0 %run %t -// RUN: mv $DIR/*sancov $DIR/A - -// RUN: %env_asan_opts=coverage_dir=$DIR:coverage=1:coverage_order_pcs=0 %run %t 1 -// RUN: mv $DIR/*sancov $DIR/B - -// RUN: %env_asan_opts=coverage_dir=$DIR:coverage=1:coverage_order_pcs=1 %run %t -// RUN: mv $DIR/*sancov $DIR/C - -// RUN: %env_asan_opts=coverage_dir=$DIR:coverage=1:coverage_order_pcs=1 %run %t 1 -// RUN: mv $DIR/*sancov $DIR/D -// -// RUN: (%sancov print $DIR/A; %sancov print $DIR/B; %sancov print $DIR/C; %sancov print $DIR/D) | FileCheck %s -// -// RUN: rm -rf $DIR -// Ordering works only in 64-bit mode for now. -// REQUIRES: asan-64-bits, shell -// UNSUPPORTED: android -#include - -void foo() { fprintf(stderr, "FOO\n"); } -void bar() { fprintf(stderr, "BAR\n"); } - -int main(int argc, char **argv) { - if (argc == 2) { - foo(); - bar(); - } else { - bar(); - foo(); - } -} - -// Run A: no ordering -// CHECK: [[FOO:0x[0-9a-f]*]] -// CHECK-NEXT: [[BAR:0x[0-9a-f]*]] -// CHECK-NEXT: [[MAIN:0x[0-9a-f]*]] -// -// Run B: still no ordering -// CHECK-NEXT: [[FOO]] -// CHECK-NEXT: [[BAR]] -// CHECK-NEXT: [[MAIN]] -// -// Run C: MAIN, BAR, FOO -// CHECK-NEXT: [[MAIN]] -// CHECK-NEXT: [[BAR]] -// CHECK-NEXT: [[FOO]] -// -// Run D: MAIN, FOO, BAR -// CHECK-NEXT: [[MAIN]] -// CHECK-NEXT: [[FOO]] -// CHECK-NEXT: [[BAR]] Property changes on: vendor/compiler-rt/dist/test/asan/TestCases/coverage-order-pcs.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: vendor/compiler-rt/dist/test/asan/TestCases/Linux/coverage-missing.cc =================================================================== --- vendor/compiler-rt/dist/test/asan/TestCases/Linux/coverage-missing.cc (revision 319464) +++ vendor/compiler-rt/dist/test/asan/TestCases/Linux/coverage-missing.cc (revision 319465) @@ -1,84 +1,84 @@ // Test for "sancov.py missing ...". // First case: coverage from executable. main() is called on every code path. -// RUN: %clangxx_asan -fsanitize-coverage=func %s -o %t -DFOOBAR -DMAIN +// RUN: %clangxx_asan -fsanitize-coverage=func,trace-pc-guard %s -o %t -DFOOBAR -DMAIN // RUN: rm -rf %T/coverage-missing // RUN: mkdir -p %T/coverage-missing // RUN: cd %T/coverage-missing // RUN: %env_asan_opts=coverage=1:coverage_dir=%T/coverage-missing %run %t // RUN: %sancov print *.sancov > main.txt // RUN: rm *.sancov // RUN: count 1 < main.txt // RUN: %env_asan_opts=coverage=1:coverage_dir=%T/coverage-missing %run %t x // RUN: %sancov print *.sancov > foo.txt // RUN: rm *.sancov // RUN: count 3 < foo.txt // RUN: %env_asan_opts=coverage=1:coverage_dir=%T/coverage-missing %run %t x x // RUN: %sancov print *.sancov > bar.txt // RUN: rm *.sancov // RUN: count 4 < bar.txt // RUN: %sancov missing %t < foo.txt > foo-missing.txt // RUN: sort main.txt foo-missing.txt -o foo-missing-with-main.txt // The "missing from foo" set may contain a few bogus PCs from the sanitizer // runtime, but it must include the entire "bar" code path as a subset. Sorted // lists can be tested for set inclusion with diff + grep. // RUN: diff bar.txt foo-missing-with-main.txt > %t.log || true // RUN: not grep "^<" %t.log // Second case: coverage from DSO. // cd %T -// RUN: %clangxx_asan -fsanitize-coverage=func %s -o %dynamiclib -DFOOBAR -shared -fPIC -// RUN: %clangxx_asan -fsanitize-coverage=func %s %dynamiclib -o %t -DMAIN +// RUN: %clangxx_asan -fsanitize-coverage=func,trace-pc-guard %s -o %dynamiclib -DFOOBAR -shared -fPIC +// RUN: %clangxx_asan -fsanitize-coverage=func,trace-pc-guard %s %dynamiclib -o %t -DMAIN // RUN: cd .. // RUN: rm -rf %T/coverage-missing // RUN: mkdir -p %T/coverage-missing // RUN: cd %T/coverage-missing // RUN: %env_asan_opts=coverage=1:coverage_dir=%T/coverage-missing %run %t x // RUN: %sancov print %xdynamiclib_filename.*.sancov > foo.txt // RUN: rm *.sancov // RUN: count 2 < foo.txt // RUN: %env_asan_opts=coverage=1:coverage_dir=%T/coverage-missing %run %t x x // RUN: %sancov print %xdynamiclib_filename.*.sancov > bar.txt // RUN: rm *.sancov // RUN: count 3 < bar.txt // RUN: %sancov missing %dynamiclib < foo.txt > foo-missing.txt // RUN: diff bar.txt foo-missing.txt > %t.log || true // RUN: not grep "^<" %t.log // REQUIRES: x86-target-arch // XFAIL: android #include void foo1(); void foo2(); void bar1(); void bar2(); void bar3(); #if defined(FOOBAR) void foo1() { fprintf(stderr, "foo1\n"); } void foo2() { fprintf(stderr, "foo2\n"); } void bar1() { fprintf(stderr, "bar1\n"); } void bar2() { fprintf(stderr, "bar2\n"); } void bar3() { fprintf(stderr, "bar3\n"); } #endif #if defined(MAIN) int main(int argc, char **argv) { switch (argc) { case 1: break; case 2: foo1(); foo2(); break; case 3: bar1(); bar2(); bar3(); break; } } #endif Index: vendor/compiler-rt/dist/test/asan/TestCases/Posix/coverage-sandboxing.cc =================================================================== --- vendor/compiler-rt/dist/test/asan/TestCases/Posix/coverage-sandboxing.cc (revision 319464) +++ vendor/compiler-rt/dist/test/asan/TestCases/Posix/coverage-sandboxing.cc (nonexistent) @@ -1,87 +0,0 @@ -// RUN: %clangxx_asan -fsanitize-coverage=bb -DSHARED %s -shared -o %dynamiclib -fPIC %ld_flags_rpath_so -// RUN: %clangxx_asan -fsanitize-coverage=func %s -o %t %ld_flags_rpath_exe - -// RUN: rm -rf %T/coverage_sandboxing_test -// RUN: mkdir %T/coverage_sandboxing_test && cd %T/coverage_sandboxing_test -// RUN: mkdir vanilla && cd vanilla -// RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-vanilla -// RUN: mkdir ../sandbox1 && cd ../sandbox1 -// RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t a 2>&1 | FileCheck %s --check-prefix=CHECK-sandbox -// RUN: %sancov unpack coverage_sandboxing_test.sancov.packed -// RUN: mkdir ../sandbox2 && cd ../sandbox2 -// RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t a b 2>&1 | FileCheck %s --check-prefix=CHECK-sandbox -// RUN: %sancov unpack coverage_sandboxing_test.sancov.packed -// RUN: cd .. -// RUN: %sancov print vanilla/%xdynamiclib_filename*.sancov > vanilla.txt -// RUN: %sancov print sandbox1/%xdynamiclib_filename*.sancov > sandbox1.txt -// RUN: %sancov print sandbox2/%xdynamiclib_filename*.sancov > sandbox2.txt -// RUN: diff vanilla.txt sandbox1.txt -// RUN: diff vanilla.txt sandbox2.txt -// RUN: rm -r %T/coverage_sandboxing_test - -// https://code.google.com/p/address-sanitizer/issues/detail?id=263 -// XFAIL: android -// UNSUPPORTED: ios - -#include -#include -#include -#include -#include - -#include - -#define bb0(n) \ - case n: \ - fprintf(stderr, "foo: %d\n", n); \ - break; - -#define bb1(n) bb0(n) bb0(n + 1) -#define bb2(n) bb1(n) bb1(n + 2) -#define bb3(n) bb2(n) bb2(n + 4) -#define bb4(n) bb3(n) bb3(n + 8) -#define bb5(n) bb4(n) bb4(n + 16) -#define bb6(n) bb5(n) bb5(n + 32) -#define bb7(n) bb6(n) bb6(n + 64) -#define bb8(n) bb7(n) bb7(n + 128) - -#ifdef SHARED -void foo(int i) { - switch(i) { - // 256 basic blocks - bb8(0) - } -} -#else -extern void foo(int i); - -int main(int argc, char **argv) { - assert(argc <= 3); - for (int i = 0; i < 256; i++) foo(i); - fprintf(stderr, "PID: %d\n", getpid()); - if (argc == 1) { - // Vanilla mode, dump to individual files. - return 0; - } - // Dump to packed file. - int fd = creat("coverage_sandboxing_test.sancov.packed", 0660); - __sanitizer_sandbox_arguments args = {0}; - args.coverage_sandboxed = 1; - args.coverage_fd = fd; - if (argc == 2) - // Write to packed file, do not split into blocks. - args.coverage_max_block_size = 0; - else if (argc == 3) - // Write to packed file, split into blocks (as if writing to a socket). - args.coverage_max_block_size = 100; - __sanitizer_sandbox_on_notify(&args); - return 0; -} -#endif - -// CHECK-vanilla: PID: [[PID:[0-9]+]] -// CHECK-vanilla: .so.[[PID]].sancov: 257 PCs written -// CHECK-vanilla: [[PID]].sancov: 1 PCs written - -// CHECK-sandbox: PID: [[PID:[0-9]+]] -// CHECK-sandbox: 257 PCs written to packed file Property changes on: vendor/compiler-rt/dist/test/asan/TestCases/Posix/coverage-sandboxing.cc ___________________________________________________________________ Deleted: svn:eol-style ## -1 +0,0 ## -native \ No newline at end of property Deleted: svn:keywords ## -1 +0,0 ## -FreeBSD=%H \ No newline at end of property Deleted: svn:mime-type ## -1 +0,0 ## -text/plain \ No newline at end of property Index: vendor/compiler-rt/dist/test/asan/TestCases/Posix/coverage-fork.cc =================================================================== --- vendor/compiler-rt/dist/test/asan/TestCases/Posix/coverage-fork.cc (revision 319464) +++ vendor/compiler-rt/dist/test/asan/TestCases/Posix/coverage-fork.cc (revision 319465) @@ -1,37 +1,41 @@ -// RUN: %clangxx_asan -fsanitize-coverage=func %s -o %t +// RUN: %clangxx_asan -fsanitize-coverage=func,trace-pc-guard %s -o %t // RUN: rm -rf %T/coverage-fork // RUN: mkdir -p %T/coverage-fork && cd %T/coverage-fork -// RUN: %env_asan_opts=coverage=1:coverage_direct=0:verbosity=1 %run %t 2>&1 | FileCheck %s +// RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t 2>&1 | FileCheck %s // // UNSUPPORTED: android +// +// Ideally a forked-subprocess should only report it's own coverage, +// not parent's one. But trace-pc-guard currently does nothing special for fork, +// and thus this test is relaxed. #include #include #include __attribute__((noinline)) void foo() { printf("foo\n"); } __attribute__((noinline)) void bar() { printf("bar\n"); } __attribute__((noinline)) void baz() { printf("baz\n"); } int main(int argc, char **argv) { pid_t child_pid = fork(); if (child_pid == 0) { fprintf(stderr, "Child PID: %d\n", getpid()); baz(); } else { fprintf(stderr, "Parent PID: %d\n", getpid()); foo(); bar(); } return 0; } // CHECK-DAG: Child PID: [[ChildPID:[0-9]+]] -// CHECK-DAG: [[ChildPID]].sancov: 1 PCs written +// CHECK-DAG: [[ChildPID]].sancov: {{.*}} PCs written // CHECK-DAG: Parent PID: [[ParentPID:[0-9]+]] // CHECK-DAG: [[ParentPID]].sancov: 3 PCs written Index: vendor/compiler-rt/dist/test/asan/TestCases/Posix/coverage-module-unloaded.cc =================================================================== --- vendor/compiler-rt/dist/test/asan/TestCases/Posix/coverage-module-unloaded.cc (revision 319464) +++ vendor/compiler-rt/dist/test/asan/TestCases/Posix/coverage-module-unloaded.cc (revision 319465) @@ -1,55 +1,51 @@ // Check that unloading a module doesn't break coverage dumping for remaining // modules. -// RUN: %clangxx_asan -fsanitize-coverage=func -DSHARED %s -shared -o %dynamiclib1 -fPIC -// RUN: %clangxx_asan -fsanitize-coverage=func -DSHARED %s -shared -o %dynamiclib2 -fPIC -// RUN: %clangxx_asan -fsanitize-coverage=func %s %libdl -o %t -// RUN: mkdir -p %T/coverage-module-unloaded && cd %T/coverage-module-unloaded -// RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t %dynamiclib1 %dynamiclib2 2>&1 | FileCheck %s -// RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t %dynamiclib1 %dynamiclib2 foo 2>&1 | FileCheck %s -// RUN: rm -r %T/coverage-module-unloaded +// RUN: %clangxx_asan -fsanitize-coverage=func,trace-pc-guard -DSHARED %s -shared -o %dynamiclib1 -fPIC +// RUN: %clangxx_asan -fsanitize-coverage=func,trace-pc-guard -DSHARED %s -shared -o %dynamiclib2 -fPIC +// RUN: %clangxx_asan -fsanitize-coverage=func,trace-pc-guard %s %libdl -o %t.exe +// RUN: mkdir -p %t.tmp/coverage-module-unloaded && cd %t.tmp/coverage-module-unloaded +// RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t.exe %dynamiclib1 %dynamiclib2 2>&1 | FileCheck %s +// RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t.exe %dynamiclib1 %dynamiclib2 foo 2>&1 | FileCheck %s // // https://code.google.com/p/address-sanitizer/issues/detail?id=263 // XFAIL: android // UNSUPPORTED: ios #include #include #include #include #ifdef SHARED extern "C" { void bar() { printf("bar\n"); } } #else int main(int argc, char **argv) { fprintf(stderr, "PID: %d\n", getpid()); assert(argc > 2); void *handle1 = dlopen(argv[1], RTLD_LAZY); // %dynamiclib1 assert(handle1); void (*bar1)() = (void (*)())dlsym(handle1, "bar"); assert(bar1); bar1(); void *handle2 = dlopen(argv[2], RTLD_LAZY); // %dynamiclib2 assert(handle2); void (*bar2)() = (void (*)())dlsym(handle2, "bar"); assert(bar2); bar2(); // It matters whether the unloaded module has a higher or lower address range // than the remaining one. Make sure to test both cases. if (argc < 2) dlclose(bar1 < bar2 ? handle1 : handle2); else dlclose(bar1 < bar2 ? handle2 : handle1); return 0; } #endif // CHECK: PID: [[PID:[0-9]+]] -// CHECK: [[PID]].sancov: 1 PCs written -// CHECK: coverage-module-unloaded{{.*}}1.[[PID]] -// CHECK: coverage-module-unloaded{{.*}}2.[[PID]] -// Even though we've unloaded one of the libs we still dump the coverage file -// for that lib (although the data will be inaccurate, if at all useful) +// CHECK-DAG: exe{{.*}}[[PID]].sancov: {{.*}}PCs written +// CHECK-DAG: dynamic{{.*}}[[PID]].sancov: {{.*}}PCs written Index: vendor/compiler-rt/dist/test/asan/TestCases/Posix/coverage.cc =================================================================== --- vendor/compiler-rt/dist/test/asan/TestCases/Posix/coverage.cc (revision 319464) +++ vendor/compiler-rt/dist/test/asan/TestCases/Posix/coverage.cc (revision 319465) @@ -1,81 +1,76 @@ -// RUN: %clangxx_asan -fsanitize-coverage=func -DSHARED %s -shared -o %dynamiclib -fPIC %ld_flags_rpath_so -// RUN: %clangxx_asan -fsanitize-coverage=func %s %ld_flags_rpath_exe -o %t +// RUN: %clangxx_asan -fsanitize-coverage=func,trace-pc-guard -DSHARED %s -shared -o %dynamiclib -fPIC %ld_flags_rpath_so +// RUN: %clangxx_asan -fsanitize-coverage=func,trace-pc-guard %s %ld_flags_rpath_exe -o %t // RUN: rm -rf %T/coverage && mkdir -p %T/coverage && cd %T/coverage // RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-main // RUN: %sancov print coverage.*sancov 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV1 // RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t foo 2>&1 | FileCheck %s --check-prefix=CHECK-foo // RUN: %sancov print coverage.*sancov 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV2 // RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t bar 2>&1 | FileCheck %s --check-prefix=CHECK-bar // RUN: %sancov print coverage.*sancov 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV2 // RUN: %env_asan_opts=coverage=1:verbosity=1 %run %t foo bar 2>&1 | FileCheck %s --check-prefix=CHECK-foo-bar // RUN: %sancov print coverage.*sancov 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV2 // RUN: %sancov print libcoverage.*sancov 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV1 // RUN: %sancov merge coverage.*sancov > merged-cov // RUN: %sancov print merged-cov 2>&1 | FileCheck %s --check-prefix=CHECK-SANCOV2 // RUN: %env_asan_opts=coverage=1:verbosity=1 not %run %t foo bar 4 2>&1 | FileCheck %s --check-prefix=CHECK-report // RUN: %env_asan_opts=coverage=1:verbosity=1 not %run %t foo bar 4 5 2>&1 | FileCheck %s --check-prefix=CHECK-segv // RUN: rm -r %T/coverage // // https://code.google.com/p/address-sanitizer/issues/detail?id=263 // XFAIL: android // UNSUPPORTED: ios -#include #include #include #include #include #ifdef SHARED void bar() { printf("bar\n"); } #else __attribute__((noinline)) void foo() { printf("foo\n"); } extern void bar(); int G[4]; int main(int argc, char **argv) { fprintf(stderr, "PID: %d\n", getpid()); for (int i = 1; i < argc; i++) { - if (!strcmp(argv[i], "foo")) { - uintptr_t old_coverage = __sanitizer_get_total_unique_coverage(); + if (!strcmp(argv[i], "foo")) foo(); - uintptr_t new_coverage = __sanitizer_get_total_unique_coverage(); - assert(new_coverage > old_coverage); - } if (!strcmp(argv[i], "bar")) bar(); } if (argc == 5) { static volatile char *zero = 0; *zero = 0; // SEGV if argc == 5. } return G[argc]; // Buffer overflow if argc >= 4. } #endif // CHECK-main: PID: [[PID:[0-9]+]] // CHECK-main: [[PID]].sancov: 1 PCs written // CHECK-main-NOT: .so.[[PID]] // // CHECK-foo: PID: [[PID:[0-9]+]] // CHECK-foo: [[PID]].sancov: 2 PCs written // CHECK-foo-NOT: .so.[[PID]] // // CHECK-bar: PID: [[PID:[0-9]+]] -// CHECK-bar: .so.[[PID]].sancov: 1 PCs written -// CHECK-bar: [[PID]].sancov: 1 PCs written +// CHECK-bar-DAG: .so.[[PID]].sancov: 1 PCs written +// CHECK-bar-DAG: [[PID]].sancov: 1 PCs written // // CHECK-foo-bar: PID: [[PID:[0-9]+]] -// CHECK-foo-bar: so.[[PID]].sancov: 1 PCs written -// CHECK-foo-bar: [[PID]].sancov: 2 PCs written +// CHECK-foo-bar-DAG: so.[[PID]].sancov: 1 PCs written +// CHECK-foo-bar-DAG: [[PID]].sancov: 2 PCs written // // CHECK-report: AddressSanitizer: global-buffer-overflow // CHECK-report: PCs written // // CHECK-segv: AddressSanitizer: SEGV // CHECK-segv: PCs written // // CHECK-SANCOV1: 1 PCs total // CHECK-SANCOV2: 2 PCs total Index: vendor/compiler-rt/dist/test/asan/TestCases/Posix/strndup_oob_test.cc =================================================================== --- vendor/compiler-rt/dist/test/asan/TestCases/Posix/strndup_oob_test.cc (nonexistent) +++ vendor/compiler-rt/dist/test/asan/TestCases/Posix/strndup_oob_test.cc (revision 319465) @@ -0,0 +1,27 @@ +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clangxx_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s + +// When built as C on Linux, strndup is transformed to __strndup. +// RUN: %clangxx_asan -O3 -xc %s -o %t && not %run %t 2>&1 | FileCheck %s + +// Unwind problem on arm: "main" is missing from the allocation stack trace. +// UNSUPPORTED: win32,s390,armv7l-unknown-linux-gnueabihf + +#include + +char kString[] = "foo"; + +int main(int argc, char **argv) { + char *copy = strndup(kString, 2); + int x = copy[2 + argc]; // BOOM + // CHECK: AddressSanitizer: heap-buffer-overflow + // CHECK: #0 {{.*}}main {{.*}}strndup_oob_test.cc:[[@LINE-2]] + // CHECK-LABEL: allocated by thread T{{.*}} here: + // CHECK: #{{[01]}} {{.*}}strndup + // CHECK: #{{.*}}main {{.*}}strndup_oob_test.cc:[[@LINE-6]] + // CHECK-LABEL: SUMMARY + // CHECK: strndup_oob_test.cc:[[@LINE-7]] + return x; +} Property changes on: vendor/compiler-rt/dist/test/asan/TestCases/Posix/strndup_oob_test.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/asan/TestCases/Posix/strndup_oob_test2.cc =================================================================== --- vendor/compiler-rt/dist/test/asan/TestCases/Posix/strndup_oob_test2.cc (nonexistent) +++ vendor/compiler-rt/dist/test/asan/TestCases/Posix/strndup_oob_test2.cc (revision 319465) @@ -0,0 +1,22 @@ +// RUN: %clang_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clang_asan -O1 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clang_asan -O2 %s -o %t && not %run %t 2>&1 | FileCheck %s +// RUN: %clang_asan -O3 %s -o %t && not %run %t 2>&1 | FileCheck %s + +// When built as C on Linux, strndup is transformed to __strndup. +// RUN: %clang_asan -O3 -xc %s -o %t && not %run %t 2>&1 | FileCheck %s + +// Unwind problem on arm: "main" is missing from the allocation stack trace. +// UNSUPPORTED: win32,s390,armv7l-unknown-linux-gnueabihf + +#include + +char kChars[] = { 'f', 'o', 'o' }; + +int main(int argc, char **argv) { + char *copy = strndup(kChars, 3); + copy = strndup(kChars, 10); + // CHECK: AddressSanitizer: global-buffer-overflow + // CHECK: {{.*}}main {{.*}}.cc:[[@LINE-2]] + return *copy; +} \ No newline at end of file Property changes on: vendor/compiler-rt/dist/test/asan/TestCases/Posix/strndup_oob_test2.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/asan/TestCases/coverage-and-lsan.cc =================================================================== --- vendor/compiler-rt/dist/test/asan/TestCases/coverage-and-lsan.cc (revision 319464) +++ vendor/compiler-rt/dist/test/asan/TestCases/coverage-and-lsan.cc (revision 319465) @@ -1,20 +1,20 @@ // Make sure coverage is dumped even if there are reported leaks. // -// RUN: %clangxx_asan -fsanitize-coverage=func %s -o %t +// RUN: %clangxx_asan -fsanitize-coverage=func,trace-pc-guard %s -o %t // // RUN: rm -rf %T/coverage-and-lsan // // RUN: mkdir -p %T/coverage-and-lsan/normal // RUN: %env_asan_opts=coverage=1:coverage_dir=%T/coverage-and-lsan:verbosity=1 not %run %t 2>&1 | FileCheck %s // RUN: %sancov print %T/coverage-and-lsan/*.sancov 2>&1 // // REQUIRES: leak-detection int *g = new int; int main(int argc, char **argv) { g = 0; return 0; } // CHECK: LeakSanitizer: detected memory leaks -// CHECK: CovDump: +// CHECK: SanitizerCoverage: {{.*}}PCs written Index: vendor/compiler-rt/dist/test/msan/strndup.cc =================================================================== --- vendor/compiler-rt/dist/test/msan/strndup.cc (nonexistent) +++ vendor/compiler-rt/dist/test/msan/strndup.cc (revision 319465) @@ -0,0 +1,28 @@ +// RUN: %clangxx_msan %s -o %t && not %run %t 2>&1 | FileCheck --check-prefix=ON %s +// RUN: %clangxx_msan %s -o %t && MSAN_OPTIONS=intercept_strndup=0 %run %t 2>&1 | FileCheck --check-prefix=OFF --allow-empty %s + +// When built as C on Linux, strndup is transformed to __strndup. +// RUN: %clangxx_msan -O3 -xc %s -o %t && not %run %t 2>&1 | FileCheck --check-prefix=ON %s + +// UNSUPPORTED: win32 + +#include +#include +#include +#include + +int main(int argc, char **argv) { + char kString[4] = "abc"; + __msan_poison(kString + 2, 1); + char *copy = strndup(kString, 4); // BOOM + assert(__msan_test_shadow(copy, 4) == 2); // Poisoning is preserved. + free(copy); + return 0; + // ON: Uninitialized bytes in __interceptor_{{(__)?}}strndup at offset 2 inside [{{.*}}, 4) + // ON: MemorySanitizer: use-of-uninitialized-value + // ON: #0 {{.*}}main {{.*}}strndup.cc:[[@LINE-6]] + // ON-LABEL: SUMMARY + // ON: {{.*}}strndup.cc:[[@LINE-8]] + // OFF-NOT: MemorySanitizer +} + Property changes on: vendor/compiler-rt/dist/test/msan/strndup.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/sanitizer_common/TestCases/Linux/allocator_fork_no_hang.cc =================================================================== --- vendor/compiler-rt/dist/test/sanitizer_common/TestCases/Linux/allocator_fork_no_hang.cc (nonexistent) +++ vendor/compiler-rt/dist/test/sanitizer_common/TestCases/Linux/allocator_fork_no_hang.cc (revision 319465) @@ -0,0 +1,118 @@ +// https://github.com/google/sanitizers/issues/774 +// Test that sanitizer allocator is fork-safe. +// Run a number of threads that perform memory allocation/deallocation, then fork +// and verify that malloc/free do not deadlock in the child process. + +// RUN: %clangxx -std=c++11 -O0 %s -o %t +// RUN: ASAN_OPTIONS=detect_leaks=0 %run %t 2>&1 | FileCheck %s + +// Fun fact: if test output is redirected to a file (as opposed to +// being piped directly to FileCheck), we may lose some "done"s due to +// a kernel bug: +// https://lkml.org/lkml/2014/2/17/324 + +// UNSUPPORTED: tsan + +// Flaky on PPC64. +// UNSUPPORTED: powerpc64-target-arch +// UNSUPPORTED: powerpc64le-target-arch + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +int done; + +void *worker(void *arg) { + while (true) { + void *p = malloc(4); + if (__atomic_load_n(&done, __ATOMIC_RELAXED)) + return 0; + } + return 0; +} + +// Run through malloc/free in the child process. +// This can deadlock on allocator cache refilling. +void child() { + for (int i = 0; i < 10000; ++i) { + void *p = malloc(4); + } + write(2, "done\n", 5); +} + +void test() { + const int kThreads = 10; + pthread_t t[kThreads]; + for (int i = 0; i < kThreads; ++i) + pthread_create(&t[i], NULL, worker, (void*)(long)i); + usleep(100000); + pid_t pid = fork(); + if (pid) { + // parent + __atomic_store_n(&done, 1, __ATOMIC_RELAXED); + pid_t p; + while ((p = wait(NULL)) == -1) { } + } else { + // child + child(); + } +} + +int main() { + const int kChildren = 30; + for (int i = 0; i < kChildren; ++i) { + pid_t pid = fork(); + if (pid) { + // parent + } else { + test(); + exit(0); + } + } + + for (int i = 0; i < kChildren; ++i) { + pid_t p; + while ((p = wait(NULL)) == -1) { } + } + + return 0; +} + +// Expect 30 (== kChildren) "done" messages. +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done +// CHECK: done Property changes on: vendor/compiler-rt/dist/test/sanitizer_common/TestCases/Linux/allocator_fork_no_hang.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/sanitizer_common/TestCases/sanitizer_coverage_symbolize.cc =================================================================== --- vendor/compiler-rt/dist/test/sanitizer_common/TestCases/sanitizer_coverage_symbolize.cc (revision 319464) +++ vendor/compiler-rt/dist/test/sanitizer_common/TestCases/sanitizer_coverage_symbolize.cc (revision 319465) @@ -1,28 +1,28 @@ // Tests trace pc guard coverage collection. // // REQUIRES: x86_64-linux // XFAIL: tsan // // RUN: DIR=%t_workdir // RUN: rm -rf $DIR // RUN: mkdir -p $DIR // RUN: cd $DIR // RUN: %clangxx -O0 -fsanitize-coverage=trace-pc-guard %s -ldl -o %t // RUN: %env_tool_opts=coverage=1 %t 2>&1 | FileCheck %s // RUN: rm -rf $DIR #include int foo() { fprintf(stderr, "foo\n"); return 1; } int main() { fprintf(stderr, "main\n"); foo(); foo(); } // CHECK: main -// CHECK: SanitizerCoverage: ./sanitizer_coverage_symbolize.{{.*}}.sancov 2 PCs written +// CHECK: SanitizerCoverage: ./sanitizer_coverage_symbolize.{{.*}}.sancov: 2 PCs written Index: vendor/compiler-rt/dist/test/sanitizer_common/TestCases/sanitizer_coverage_trace_pc_guard-dso.cc =================================================================== --- vendor/compiler-rt/dist/test/sanitizer_common/TestCases/sanitizer_coverage_trace_pc_guard-dso.cc (revision 319464) +++ vendor/compiler-rt/dist/test/sanitizer_common/TestCases/sanitizer_coverage_trace_pc_guard-dso.cc (revision 319465) @@ -1,72 +1,72 @@ // Tests trace pc guard coverage collection. // // REQUIRES: has_sancovcc,stable-runtime // XFAIL: tsan,darwin,powerpc64,s390x,mips // // RUN: DIR=%t_workdir // RUN: CLANG_ARGS="-O0 -fsanitize-coverage=trace-pc-guard" // RUN: rm -rf $DIR // RUN: mkdir -p $DIR // RUN: cd $DIR // RUN: %clangxx -DSHARED1 $CLANG_ARGS -shared %s -o %t_1.so -fPIC // RUN: %clangxx -DSHARED2 $CLANG_ARGS -shared %s -o %t_2.so -fPIC // RUN: %clangxx -DMAIN $CLANG_ARGS %s -o %t %t_1.so %t_2.so // RUN: %env_tool_opts=coverage=1 %t 2>&1 | FileCheck %s // RUN: %sancovcc -covered-functions -strip_path_prefix=TestCases/ *.sancov \ // RUN: %t %t_1.so %t_2.so 2>&1 | \ // RUN: FileCheck --check-prefix=CHECK-SANCOV %s // RUN: rm -rf $DIR #include extern "C" { int bar(); int baz(); } #ifdef MAIN int foo() { fprintf(stderr, "foo\n"); return 1; } int main() { fprintf(stderr, "main\n"); foo(); bar(); baz(); } #endif // MAIN extern "C" { #ifdef SHARED1 int bar() { fprintf(stderr, "bar\n"); return 1; } #endif #ifdef SHARED2 int baz() { fprintf(stderr, "baz\n"); return 1; } #endif } // extern "C" // CHECK: main // CHECK-NEXT: foo // CHECK-NEXT: bar // CHECK-NEXT: baz -// CHECK-DAG: SanitizerCoverage: ./sanitizer_coverage_trace_pc_guard-dso.{{.*}}.sancov 2 PCs written -// CHECK-DAG: SanitizerCoverage: ./sanitizer_coverage_trace_pc_guard-dso.{{.*}}_2.so.{{.*}}.sancov 1 PCs written -// CHECK-DAG: SanitizerCoverage: ./sanitizer_coverage_trace_pc_guard-dso.{{.*}}_1.so.{{.*}}.sancov 1 PCs written +// CHECK-DAG: SanitizerCoverage: ./sanitizer_coverage_trace_pc_guard-dso.{{.*}}.sancov: 2 PCs written +// CHECK-DAG: SanitizerCoverage: ./sanitizer_coverage_trace_pc_guard-dso.{{.*}}_2.so.{{.*}}.sancov: 1 PCs written +// CHECK-DAG: SanitizerCoverage: ./sanitizer_coverage_trace_pc_guard-dso.{{.*}}_1.so.{{.*}}.sancov: 1 PCs written // // CHECK-SANCOV: Ignoring {{.*}}_1.so and its coverage because __sanitizer_cov* functions were not found. // CHECK-SANCOV: Ignoring {{.*}}_2.so and its coverage because __sanitizer_cov* functions were not found. // CHECK-SANCOV-NEXT: sanitizer_coverage_trace_pc_guard-dso.cc:29 foo // CHECK-SANCOV-NEXT: sanitizer_coverage_trace_pc_guard-dso.cc:34 main Index: vendor/compiler-rt/dist/test/sanitizer_common/TestCases/sanitizer_coverage_trace_pc_guard.cc =================================================================== --- vendor/compiler-rt/dist/test/sanitizer_common/TestCases/sanitizer_coverage_trace_pc_guard.cc (revision 319464) +++ vendor/compiler-rt/dist/test/sanitizer_common/TestCases/sanitizer_coverage_trace_pc_guard.cc (revision 319465) @@ -1,42 +1,42 @@ // Tests trace pc guard coverage collection. // // REQUIRES: has_sancovcc,stable-runtime // UNSUPPORTED: i386-darwin // XFAIL: tsan,powerpc64,s390x,mips // // RUN: DIR=%t_workdir // RUN: rm -rf $DIR // RUN: mkdir -p $DIR // RUN: cd $DIR // RUN: %clangxx -O0 -fsanitize-coverage=trace-pc-guard %s -ldl -o %t // RUN: %env_tool_opts=coverage=1 %t 2>&1 | FileCheck %s // RUN: %sancovcc -covered-functions -strip_path_prefix=TestCases/ *.sancov %t 2>&1 | \ // RUN: FileCheck --check-prefix=CHECK-SANCOV %s // RUN: %env_tool_opts=coverage=0 %t 2>&1 | FileCheck --check-prefix=CHECK-NOCOV %s // RUN: rm -rf $DIR // Make some room to stabilize line numbers // // // #include int foo() { fprintf(stderr, "foo\n"); return 1; } int main() { fprintf(stderr, "main\n"); foo(); foo(); } // CHECK: main // CHECK-NEXT: foo // CHECK-NEXT: foo -// CHECK-NEXT: SanitizerCoverage: ./sanitizer_coverage_trace_pc_guard.{{.*}}.sancov 2 PCs written +// CHECK-NEXT: SanitizerCoverage: ./sanitizer_coverage_trace_pc_guard.{{.*}}.sancov: 2 PCs written // // CHECK-SANCOV: sanitizer_coverage_trace_pc_guard.cc:23 foo // CHECK-SANCOV-NEXT: sanitizer_coverage_trace_pc_guard.cc:28 main // // CHECK-NOCOV-NOT: SanitizerCoverage Index: vendor/compiler-rt/dist/test/ubsan/TestCases/TypeCheck/PR33221.cpp =================================================================== --- vendor/compiler-rt/dist/test/ubsan/TestCases/TypeCheck/PR33221.cpp (nonexistent) +++ vendor/compiler-rt/dist/test/ubsan/TestCases/TypeCheck/PR33221.cpp (revision 319465) @@ -0,0 +1,28 @@ +// RUN: %clangxx -frtti -fsanitize=vptr -g %s -O3 -o %t +// RUN: %run %t 2>&1 | FileCheck %s + +// REQUIRES: cxxabi + +#include + +class Base { +public: + int i; + virtual void print() {} +}; + +class Derived : public Base { +public: + void print() {} +}; + +int main() { + char *c = new char[sizeof(Derived)]; + memset((void *)c, 0, sizeof(Derived)); + Derived *list = (Derived *)c; + +// CHECK: PR33221.cpp:[[@LINE+2]]:19: runtime error: member access within address {{.*}} which does not point to an object of type 'Base' +// CHECK-NEXT: invalid vptr + int foo = list->i; + return 0; +} Property changes on: vendor/compiler-rt/dist/test/ubsan/TestCases/TypeCheck/PR33221.cpp ___________________________________________________________________ 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