Skip to content

Commit fc910c5

Browse files
committed
[TSan] Add interceptors for mach_vm_[de]allocate
I verified that the test is red without the interceptors. rdar://40334350 Reviewed By: kubamracek, vitalybuka Differential Revision: https://reviews.llvm.org/D66616 llvm-svn: 371439
1 parent 8bc05d7 commit fc910c5

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
@@ -63,6 +63,7 @@ set(TSAN_CXX_SOURCES
6363
if(APPLE)
6464
list(APPEND TSAN_SOURCES
6565
rtl/tsan_interceptors_mac.cpp
66+
rtl/tsan_interceptors_mach_vm.cpp
6667
rtl/tsan_platform_mac.cpp
6768
rtl/tsan_platform_posix.cpp
6869
)

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

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

748+
// Zero out addr if it points into shadow memory and was provided as a hint
749+
// only, i.e., MAP_FIXED is not set.
748750
static bool fix_mmap_addr(void **addr, long_t sz, int flags) {
749751
if (*addr) {
750752
if (!IsAppMem((uptr)*addr) || !IsAppMem((uptr)*addr + sz - 1)) {
@@ -767,22 +769,14 @@ static void *mmap_interceptor(ThreadState *thr, uptr pc, Mmap real_mmap,
767769
void *res = real_mmap(addr, sz, prot, flags, fd, off);
768770
if (res != MAP_FAILED) {
769771
if (fd > 0) FdAccess(thr, pc, fd);
770-
if (thr->ignore_reads_and_writes == 0)
771-
MemoryRangeImitateWrite(thr, pc, (uptr)res, sz);
772-
else
773-
MemoryResetRange(thr, pc, (uptr)res, sz);
772+
MemoryRangeImitateWriteOrResetRange(thr, pc, (uptr)res, sz);
774773
}
775774
return res;
776775
}
777776

778777
TSAN_INTERCEPTOR(int, munmap, void *addr, long_t sz) {
779778
SCOPED_TSAN_INTERCEPTOR(munmap, addr, sz);
780-
if (sz != 0) {
781-
// If sz == 0, munmap will return EINVAL and don't unmap any memory.
782-
DontNeedShadowFor((uptr)addr, sz);
783-
ScopedGlobalProcessor sgp;
784-
ctx->metamap.ResetRange(thr->proc(), (uptr)addr, (uptr)sz);
785-
}
779+
UnmapShadow(thr, (uptr)addr, sz);
786780
int res = REAL(munmap)(addr, sz);
787781
return res;
788782
}
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.cpp

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

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

999+
void MemoryRangeImitateWriteOrResetRange(ThreadState *thr, uptr pc, uptr addr,
1000+
uptr size) {
1001+
if (thr->ignore_reads_and_writes == 0)
1002+
MemoryRangeImitateWrite(thr, pc, addr, size);
1003+
else
1004+
MemoryResetRange(thr, pc, addr, size);
1005+
}
1006+
9901007
ALWAYS_INLINE USED
9911008
void FuncEntry(ThreadState *thr, uptr pc) {
9921009
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
@@ -680,6 +680,7 @@ void ALWAYS_INLINE StatSet(ThreadState *thr, StatType typ, u64 n) {
680680
void MapShadow(uptr addr, uptr size);
681681
void MapThreadTrace(uptr addr, uptr size, const char *name);
682682
void DontNeedShadowFor(uptr addr, uptr size);
683+
void UnmapShadow(ThreadState *thr, uptr addr, uptr size);
683684
void InitializeShadowMemory();
684685
void InitializeInterceptors();
685686
void InitializeLibIgnore();
@@ -759,6 +760,8 @@ void ALWAYS_INLINE MemoryWriteAtomic(ThreadState *thr, uptr pc,
759760
void MemoryResetRange(ThreadState *thr, uptr pc, uptr addr, uptr size);
760761
void MemoryRangeFreed(ThreadState *thr, uptr pc, uptr addr, uptr size);
761762
void MemoryRangeImitateWrite(ThreadState *thr, uptr pc, uptr addr, uptr size);
763+
void MemoryRangeImitateWriteOrResetRange(ThreadState *thr, uptr pc, uptr addr,
764+
uptr size);
762765

763766
void ThreadIgnoreBegin(ThreadState *thr, uptr pc, bool save_stack = true);
764767
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)