Index: compiler-rt/trunk/lib/tsan/CMakeLists.txt =================================================================== --- compiler-rt/trunk/lib/tsan/CMakeLists.txt +++ compiler-rt/trunk/lib/tsan/CMakeLists.txt @@ -63,6 +63,7 @@ if(APPLE) list(APPEND TSAN_SOURCES rtl/tsan_interceptors_mac.cpp + rtl/tsan_interceptors_mach_vm.cpp rtl/tsan_platform_mac.cpp rtl/tsan_platform_posix.cpp ) Index: compiler-rt/trunk/lib/tsan/rtl/tsan_interceptors.cpp =================================================================== --- compiler-rt/trunk/lib/tsan/rtl/tsan_interceptors.cpp +++ compiler-rt/trunk/lib/tsan/rtl/tsan_interceptors.cpp @@ -745,6 +745,8 @@ return REAL(strdup)(str); } +// Zero out addr if it points into shadow memory and was provided as a hint +// only, i.e., MAP_FIXED is not set. static bool fix_mmap_addr(void **addr, long_t sz, int flags) { if (*addr) { if (!IsAppMem((uptr)*addr) || !IsAppMem((uptr)*addr + sz - 1)) { @@ -767,22 +769,14 @@ void *res = real_mmap(addr, sz, prot, flags, fd, off); if (res != MAP_FAILED) { if (fd > 0) FdAccess(thr, pc, fd); - if (thr->ignore_reads_and_writes == 0) - MemoryRangeImitateWrite(thr, pc, (uptr)res, sz); - else - MemoryResetRange(thr, pc, (uptr)res, sz); + MemoryRangeImitateWriteOrResetRange(thr, pc, (uptr)res, sz); } return res; } TSAN_INTERCEPTOR(int, munmap, void *addr, long_t sz) { SCOPED_TSAN_INTERCEPTOR(munmap, addr, sz); - if (sz != 0) { - // If sz == 0, munmap will return EINVAL and don't unmap any memory. - DontNeedShadowFor((uptr)addr, sz); - ScopedGlobalProcessor sgp; - ctx->metamap.ResetRange(thr->proc(), (uptr)addr, (uptr)sz); - } + UnmapShadow(thr, (uptr)addr, sz); int res = REAL(munmap)(addr, sz); return res; } Index: compiler-rt/trunk/lib/tsan/rtl/tsan_interceptors_mach_vm.cpp =================================================================== --- compiler-rt/trunk/lib/tsan/rtl/tsan_interceptors_mach_vm.cpp +++ compiler-rt/trunk/lib/tsan/rtl/tsan_interceptors_mach_vm.cpp @@ -0,0 +1,52 @@ +//===-- tsan_interceptors_mach_vm.cpp -------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +// Interceptors for mach_vm_* user space memory routines on Darwin. +//===----------------------------------------------------------------------===// + +#include "interception/interception.h" +#include "tsan_interceptors.h" +#include "tsan_platform.h" + +#include + +namespace __tsan { + +static bool intersects_with_shadow(mach_vm_address_t *address, + mach_vm_size_t size, int flags) { + // VM_FLAGS_FIXED is 0x0, so we have to test for VM_FLAGS_ANYWHERE. + if (flags & VM_FLAGS_ANYWHERE) return false; + uptr ptr = *address; + return !IsAppMem(ptr) || !IsAppMem(ptr + size - 1); +} + +TSAN_INTERCEPTOR(kern_return_t, mach_vm_allocate, vm_map_t target, + mach_vm_address_t *address, mach_vm_size_t size, int flags) { + SCOPED_TSAN_INTERCEPTOR(mach_vm_allocate, target, address, size, flags); + if (target != mach_task_self()) + return REAL(mach_vm_allocate)(target, address, size, flags); + if (intersects_with_shadow(address, size, flags)) + return KERN_NO_SPACE; + kern_return_t res = REAL(mach_vm_allocate)(target, address, size, flags); + if (res == KERN_SUCCESS) + MemoryRangeImitateWriteOrResetRange(thr, pc, *address, size); + return res; +} + +TSAN_INTERCEPTOR(kern_return_t, mach_vm_deallocate, vm_map_t target, + mach_vm_address_t address, mach_vm_size_t size) { + SCOPED_TSAN_INTERCEPTOR(mach_vm_deallocate, target, address, size); + if (target != mach_task_self()) + return REAL(mach_vm_deallocate)(target, address, size); + UnmapShadow(thr, address, size); + return REAL(mach_vm_deallocate)(target, address, size); +} + +} // namespace __tsan Index: compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.h =================================================================== --- compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.h +++ compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.h @@ -680,6 +680,7 @@ void MapShadow(uptr addr, uptr size); void MapThreadTrace(uptr addr, uptr size, const char *name); void DontNeedShadowFor(uptr addr, uptr size); +void UnmapShadow(ThreadState *thr, uptr addr, uptr size); void InitializeShadowMemory(); void InitializeInterceptors(); void InitializeLibIgnore(); @@ -759,6 +760,8 @@ void MemoryResetRange(ThreadState *thr, uptr pc, uptr addr, uptr size); void MemoryRangeFreed(ThreadState *thr, uptr pc, uptr addr, uptr size); void MemoryRangeImitateWrite(ThreadState *thr, uptr pc, uptr addr, uptr size); +void MemoryRangeImitateWriteOrResetRange(ThreadState *thr, uptr pc, uptr addr, + uptr size); void ThreadIgnoreBegin(ThreadState *thr, uptr pc, bool save_stack = true); void ThreadIgnoreEnd(ThreadState *thr, uptr pc); Index: compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.cpp =================================================================== --- compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.cpp +++ compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.cpp @@ -239,6 +239,15 @@ ReleaseMemoryPagesToOS(MemToShadow(addr), MemToShadow(addr + size)); } +#if !SANITIZER_GO +void UnmapShadow(ThreadState *thr, uptr addr, uptr size) { + if (size == 0) return; + DontNeedShadowFor(addr, size); + ScopedGlobalProcessor sgp; + ctx->metamap.ResetRange(thr->proc(), addr, size); +} +#endif + void MapShadow(uptr addr, uptr size) { // Global data is not 64K aligned, but there are no adjacent mappings, // so we can get away with unaligned mapping. @@ -987,6 +996,14 @@ MemoryRangeSet(thr, pc, addr, size, s.raw()); } +void MemoryRangeImitateWriteOrResetRange(ThreadState *thr, uptr pc, uptr addr, + uptr size) { + if (thr->ignore_reads_and_writes == 0) + MemoryRangeImitateWrite(thr, pc, addr, size); + else + MemoryResetRange(thr, pc, addr, size); +} + ALWAYS_INLINE USED void FuncEntry(ThreadState *thr, uptr pc) { StatInc(thr, StatFuncEnter); Index: compiler-rt/trunk/test/tsan/Darwin/mach_vm_allocate.c =================================================================== --- compiler-rt/trunk/test/tsan/Darwin/mach_vm_allocate.c +++ compiler-rt/trunk/test/tsan/Darwin/mach_vm_allocate.c @@ -0,0 +1,73 @@ +// Test that mach_vm_[de]allocate resets shadow memory status. +// +// RUN: %clang_tsan %s -o %t +// RUN: %run %t 2>&1 | FileCheck %s --implicit-check-not='ThreadSanitizer' + +#include +#include +#include +#include +#include + +#include "../test.h" + +void AnnotateIgnoreReadsBegin(const char *f, int l); +void AnnotateIgnoreReadsEnd(const char *f, int l); +void AnnotateIgnoreWritesBegin(const char *f, int l); +void AnnotateIgnoreWritesEnd(const char *f, int l); + +static int *global_ptr; +const mach_vm_size_t alloc_size = sizeof(int); + +static int *alloc() { + mach_vm_address_t addr; + kern_return_t res = + mach_vm_allocate(mach_task_self(), &addr, alloc_size, VM_FLAGS_ANYWHERE); + assert(res == KERN_SUCCESS); + return (int *)addr; +} + +static void alloc_fixed(int *ptr) { + mach_vm_address_t addr = (mach_vm_address_t)ptr; + kern_return_t res = + mach_vm_allocate(mach_task_self(), &addr, alloc_size, VM_FLAGS_FIXED); + assert(res == KERN_SUCCESS); +} + +static void dealloc(int *ptr) { + kern_return_t res = + mach_vm_deallocate(mach_task_self(), (mach_vm_address_t)ptr, alloc_size); + assert(res == KERN_SUCCESS); +} + +static void *Thread(void *arg) { + *global_ptr = 7; // Assignment 1 + + // We want to test that TSan does not report a race between the two + // assignments to global_ptr when memory is re-allocated here. The calls to + // the API itself are racy though, so ignore them. + AnnotateIgnoreWritesBegin(__FILE__, __LINE__); + dealloc(global_ptr); + alloc_fixed(global_ptr); + AnnotateIgnoreWritesEnd(__FILE__, __LINE__); + + barrier_wait(&barrier); + return NULL;; +} + +int main(int argc, const char *argv[]) { + barrier_init(&barrier, 2); + global_ptr = alloc(); + pthread_t t; + pthread_create(&t, NULL, Thread, NULL); + + barrier_wait(&barrier); + *global_ptr = 8; // Assignment 2 + + pthread_join(t, NULL); + dealloc(global_ptr); + printf("Done.\n"); + return 0; +} + +// CHECK: Done.