Skip to content

Commit b5f42f8

Browse files
authored
Merge pull request #153 from apple/cherry-pick-40334350
[TSan] Add interceptors for mach_vm_[de]allocate
2 parents 053918d + 3112b8f commit b5f42f8

File tree

6 files changed

+150
-10
lines changed

6 files changed

+150
-10
lines changed

compiler-rt/lib/tsan/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ set(TSAN_CXX_SOURCES
6161
if(APPLE)
6262
list(APPEND TSAN_SOURCES
6363
rtl/tsan_interceptors_mac.cc
64+
rtl/tsan_interceptors_mach_vm.cpp
6465
rtl/tsan_platform_mac.cc
6566
rtl/tsan_platform_posix.cc)
6667
elseif(UNIX)

compiler-rt/lib/tsan/rtl/tsan_interceptors.cc

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,8 @@ TSAN_INTERCEPTOR(char*, strdup, const char *str) {
768768
return REAL(strdup)(str);
769769
}
770770

771+
// Zero out addr if it points into shadow memory and was provided as a hint
772+
// only, i.e., MAP_FIXED is not set.
771773
static bool fix_mmap_addr(void **addr, long_t sz, int flags) {
772774
if (*addr) {
773775
if (!IsAppMem((uptr)*addr) || !IsAppMem((uptr)*addr + sz - 1)) {
@@ -790,22 +792,14 @@ static void *mmap_interceptor(ThreadState *thr, uptr pc, Mmap real_mmap,
790792
void *res = real_mmap(addr, sz, prot, flags, fd, off);
791793
if (res != MAP_FAILED) {
792794
if (fd > 0) FdAccess(thr, pc, fd);
793-
if (thr->ignore_reads_and_writes == 0)
794-
MemoryRangeImitateWrite(thr, pc, (uptr)res, sz);
795-
else
796-
MemoryResetRange(thr, pc, (uptr)res, sz);
795+
MemoryRangeImitateWriteOrResetRange(thr, pc, (uptr)res, sz);
797796
}
798797
return res;
799798
}
800799

801800
TSAN_INTERCEPTOR(int, munmap, void *addr, long_t sz) {
802801
SCOPED_TSAN_INTERCEPTOR(munmap, addr, sz);
803-
if (sz != 0) {
804-
// If sz == 0, munmap will return EINVAL and don't unmap any memory.
805-
DontNeedShadowFor((uptr)addr, sz);
806-
ScopedGlobalProcessor sgp;
807-
ctx->metamap.ResetRange(thr->proc(), (uptr)addr, (uptr)sz);
808-
}
802+
UnmapShadow(thr, (uptr)addr, sz);
809803
int res = REAL(munmap)(addr, sz);
810804
return res;
811805
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//===-- tsan_interceptors_mach_vm.cpp -------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
//
9+
// This file is a part of ThreadSanitizer (TSan), a race detector.
10+
//
11+
// Interceptors for mach_vm_* user space memory routines on Darwin.
12+
//===----------------------------------------------------------------------===//
13+
14+
#include "interception/interception.h"
15+
#include "tsan_interceptors.h"
16+
#include "tsan_platform.h"
17+
18+
#include <mach/mach.h>
19+
20+
namespace __tsan {
21+
22+
static bool intersects_with_shadow(mach_vm_address_t *address,
23+
mach_vm_size_t size, int flags) {
24+
// VM_FLAGS_FIXED is 0x0, so we have to test for VM_FLAGS_ANYWHERE.
25+
if (flags & VM_FLAGS_ANYWHERE) return false;
26+
uptr ptr = *address;
27+
return !IsAppMem(ptr) || !IsAppMem(ptr + size - 1);
28+
}
29+
30+
TSAN_INTERCEPTOR(kern_return_t, mach_vm_allocate, vm_map_t target,
31+
mach_vm_address_t *address, mach_vm_size_t size, int flags) {
32+
SCOPED_TSAN_INTERCEPTOR(mach_vm_allocate, target, address, size, flags);
33+
if (target != mach_task_self())
34+
return REAL(mach_vm_allocate)(target, address, size, flags);
35+
if (intersects_with_shadow(address, size, flags))
36+
return KERN_NO_SPACE;
37+
kern_return_t res = REAL(mach_vm_allocate)(target, address, size, flags);
38+
if (res == KERN_SUCCESS)
39+
MemoryRangeImitateWriteOrResetRange(thr, pc, *address, size);
40+
return res;
41+
}
42+
43+
TSAN_INTERCEPTOR(kern_return_t, mach_vm_deallocate, vm_map_t target,
44+
mach_vm_address_t address, mach_vm_size_t size) {
45+
SCOPED_TSAN_INTERCEPTOR(mach_vm_deallocate, target, address, size);
46+
if (target != mach_task_self())
47+
return REAL(mach_vm_deallocate)(target, address, size);
48+
UnmapShadow(thr, address, size);
49+
return REAL(mach_vm_deallocate)(target, address, size);
50+
}
51+
52+
} // namespace __tsan

compiler-rt/lib/tsan/rtl/tsan_rtl.cc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,15 @@ void DontNeedShadowFor(uptr addr, uptr size) {
238238
ReleaseMemoryPagesToOS(MemToShadow(addr), MemToShadow(addr + size));
239239
}
240240

241+
#if !SANITIZER_GO
242+
void UnmapShadow(ThreadState *thr, uptr addr, uptr size) {
243+
if (size == 0) return;
244+
DontNeedShadowFor(addr, size);
245+
ScopedGlobalProcessor sgp;
246+
ctx->metamap.ResetRange(thr->proc(), addr, size);
247+
}
248+
#endif
249+
241250
void MapShadow(uptr addr, uptr size) {
242251
// Global data is not 64K aligned, but there are no adjacent mappings,
243252
// so we can get away with unaligned mapping.
@@ -986,6 +995,14 @@ void MemoryRangeImitateWrite(ThreadState *thr, uptr pc, uptr addr, uptr size) {
986995
MemoryRangeSet(thr, pc, addr, size, s.raw());
987996
}
988997

998+
void MemoryRangeImitateWriteOrResetRange(ThreadState *thr, uptr pc, uptr addr,
999+
uptr size) {
1000+
if (thr->ignore_reads_and_writes == 0)
1001+
MemoryRangeImitateWrite(thr, pc, addr, size);
1002+
else
1003+
MemoryResetRange(thr, pc, addr, size);
1004+
}
1005+
9891006
ALWAYS_INLINE USED
9901007
void FuncEntry(ThreadState *thr, uptr pc) {
9911008
StatInc(thr, StatFuncEnter);

compiler-rt/lib/tsan/rtl/tsan_rtl.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,7 @@ void ALWAYS_INLINE StatSet(ThreadState *thr, StatType typ, u64 n) {
681681
void MapShadow(uptr addr, uptr size);
682682
void MapThreadTrace(uptr addr, uptr size, const char *name);
683683
void DontNeedShadowFor(uptr addr, uptr size);
684+
void UnmapShadow(ThreadState *thr, uptr addr, uptr size);
684685
void InitializeShadowMemory();
685686
void InitializeInterceptors();
686687
void InitializeLibIgnore();
@@ -760,6 +761,8 @@ void ALWAYS_INLINE MemoryWriteAtomic(ThreadState *thr, uptr pc,
760761
void MemoryResetRange(ThreadState *thr, uptr pc, uptr addr, uptr size);
761762
void MemoryRangeFreed(ThreadState *thr, uptr pc, uptr addr, uptr size);
762763
void MemoryRangeImitateWrite(ThreadState *thr, uptr pc, uptr addr, uptr size);
764+
void MemoryRangeImitateWriteOrResetRange(ThreadState *thr, uptr pc, uptr addr,
765+
uptr size);
763766

764767
void ThreadIgnoreBegin(ThreadState *thr, uptr pc, bool save_stack = true);
765768
void ThreadIgnoreEnd(ThreadState *thr, uptr pc);
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Test that mach_vm_[de]allocate resets shadow memory status.
2+
//
3+
// RUN: %clang_tsan %s -o %t
4+
// RUN: %run %t 2>&1 | FileCheck %s --implicit-check-not='ThreadSanitizer'
5+
6+
#include <mach/mach.h>
7+
#include <mach/mach_vm.h>
8+
#include <pthread.h>
9+
#include <assert.h>
10+
#include <stdio.h>
11+
12+
#include "../test.h"
13+
14+
void AnnotateIgnoreReadsBegin(const char *f, int l);
15+
void AnnotateIgnoreReadsEnd(const char *f, int l);
16+
void AnnotateIgnoreWritesBegin(const char *f, int l);
17+
void AnnotateIgnoreWritesEnd(const char *f, int l);
18+
19+
static int *global_ptr;
20+
const mach_vm_size_t alloc_size = sizeof(int);
21+
22+
static int *alloc() {
23+
mach_vm_address_t addr;
24+
kern_return_t res =
25+
mach_vm_allocate(mach_task_self(), &addr, alloc_size, VM_FLAGS_ANYWHERE);
26+
assert(res == KERN_SUCCESS);
27+
return (int *)addr;
28+
}
29+
30+
static void alloc_fixed(int *ptr) {
31+
mach_vm_address_t addr = (mach_vm_address_t)ptr;
32+
kern_return_t res =
33+
mach_vm_allocate(mach_task_self(), &addr, alloc_size, VM_FLAGS_FIXED);
34+
assert(res == KERN_SUCCESS);
35+
}
36+
37+
static void dealloc(int *ptr) {
38+
kern_return_t res =
39+
mach_vm_deallocate(mach_task_self(), (mach_vm_address_t)ptr, alloc_size);
40+
assert(res == KERN_SUCCESS);
41+
}
42+
43+
static void *Thread(void *arg) {
44+
*global_ptr = 7; // Assignment 1
45+
46+
// We want to test that TSan does not report a race between the two
47+
// assignments to global_ptr when memory is re-allocated here. The calls to
48+
// the API itself are racy though, so ignore them.
49+
AnnotateIgnoreWritesBegin(__FILE__, __LINE__);
50+
dealloc(global_ptr);
51+
alloc_fixed(global_ptr);
52+
AnnotateIgnoreWritesEnd(__FILE__, __LINE__);
53+
54+
barrier_wait(&barrier);
55+
return NULL;;
56+
}
57+
58+
int main(int argc, const char *argv[]) {
59+
barrier_init(&barrier, 2);
60+
global_ptr = alloc();
61+
pthread_t t;
62+
pthread_create(&t, NULL, Thread, NULL);
63+
64+
barrier_wait(&barrier);
65+
*global_ptr = 8; // Assignment 2
66+
67+
pthread_join(t, NULL);
68+
dealloc(global_ptr);
69+
printf("Done.\n");
70+
return 0;
71+
}
72+
73+
// CHECK: Done.

0 commit comments

Comments
 (0)