Skip to content
Browse files

[TSan] Add interceptors for mach_vm_[de]allocate

I verified that the test is red without the interceptors.


Reviewed By: kubamracek, vitalybuka

Differential Revision:

llvm-svn: 371439
  • Loading branch information...
yln committed Sep 9, 2019
1 parent 8bc05d7 commit fc910c507e447ae08ca8424ab16353f5371a2e1b
@@ -63,6 +63,7 @@ set(TSAN_CXX_SOURCES
@@ -745,6 +745,8 @@ TSAN_INTERCEPTOR(char*, strdup, const char *str) {
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 @@ static void *mmap_interceptor(ThreadState *thr, uptr pc, Mmap real_mmap,
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);
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;
@@ -0,0 +1,52 @@
//===-- tsan_interceptors_mach_vm.cpp -------------------------------------===//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See 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 <mach/mach.h>

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))
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
@@ -239,6 +239,15 @@ void DontNeedShadowFor(uptr addr, uptr size) {
ReleaseMemoryPagesToOS(MemToShadow(addr), MemToShadow(addr + size));

void UnmapShadow(ThreadState *thr, uptr addr, uptr size) {
if (size == 0) return;
DontNeedShadowFor(addr, size);
ScopedGlobalProcessor sgp;
ctx->metamap.ResetRange(thr->proc(), addr, size);

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 @@ void MemoryRangeImitateWrite(ThreadState *thr, uptr pc, uptr addr, uptr size) {
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);
MemoryResetRange(thr, pc, addr, size);

void FuncEntry(ThreadState *thr, uptr pc) {
StatInc(thr, StatFuncEnter);
@@ -680,6 +680,7 @@ void ALWAYS_INLINE StatSet(ThreadState *thr, StatType typ, u64 n) {
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 ALWAYS_INLINE MemoryWriteAtomic(ThreadState *thr, uptr pc,
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);
@@ -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 <mach/mach.h>
#include <mach/mach_vm.h>
#include <pthread.h>
#include <assert.h>
#include <stdio.h>

#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__);
AnnotateIgnoreWritesEnd(__FILE__, __LINE__);

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);

*global_ptr = 8; // Assignment 2

pthread_join(t, NULL);
return 0;

// CHECK: Done.

0 comments on commit fc910c5

Please sign in to comment.
You can’t perform that action at this time.