[tsan] Add interceptors for objc_sync_enter and objc_sync_exit

Objective-C's @synchronize synchronization primitive uses calls to objc_sync_enter and objc_sync_exit runtime functions. In most cases, they end up just calling pthread_mutex_lock/pthread_mutex_unlock, but there are some cases where the synchronization from pthread_mutex_lock/pthread_mutex_unlock interceptors isn't enough. Let's add explicit interceptors for objc_sync_enter and objc_sync_exit to handle all cases.

Differential Revision: https://reviews.llvm.org/D45487

llvm-svn: 329982
This commit is contained in:
Kuba Mracek 2018-04-13 01:05:29 +00:00
parent 223f4c7c99
commit 296ce3bd23
3 changed files with 71 additions and 1 deletions

View File

@ -118,7 +118,7 @@ if(APPLE)
RTUbsan
CFLAGS ${TSAN_RTL_CFLAGS}
LINK_FLAGS ${SANITIZER_COMMON_LINK_FLAGS} ${WEAK_SYMBOL_LINK_FLAGS}
LINK_LIBS ${TSAN_LINK_LIBS}
LINK_LIBS ${TSAN_LINK_LIBS} objc
PARENT_TARGET tsan)
add_compiler_rt_object_libraries(RTTsan_dynamic
OS ${TSAN_SUPPORTED_OS}

View File

@ -294,6 +294,19 @@ TSAN_INTERCEPTOR(void, xpc_connection_cancel, xpc_connection_t connection) {
#endif // #if defined(__has_include) && __has_include(<xpc/xpc.h>)
TSAN_INTERCEPTOR(int, objc_sync_enter, void *obj) {
SCOPED_TSAN_INTERCEPTOR(objc_sync_enter, obj);
int result = REAL(objc_sync_enter)(obj);
if (obj) Acquire(thr, pc, (uptr)obj);
return result;
}
TSAN_INTERCEPTOR(int, objc_sync_exit, void *obj) {
SCOPED_TSAN_INTERCEPTOR(objc_sync_enter, obj);
if (obj) Release(thr, pc, (uptr)obj);
return REAL(objc_sync_exit)(obj);
}
// On macOS, libc++ is always linked dynamically, so intercepting works the
// usual way.
#define STDCXX_INTERCEPTOR TSAN_INTERCEPTOR

View File

@ -0,0 +1,57 @@
// RUN: %clangxx_tsan %s -o %t -framework Foundation -fobjc-arc %darwin_min_target_with_full_runtime_arc_support
// RUN: %run %t 2>&1 | FileCheck %s
#import <Foundation/Foundation.h>
@interface MyClass : NSObject {
long field;
}
@property (nonatomic, readonly) long value;
@end
dispatch_group_t group;
@implementation MyClass
- (void) start {
dispatch_queue_t q = dispatch_queue_create(NULL, NULL);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
for (int i = 0; i < 1000; i++) {
dispatch_async(q, ^{
@synchronized(self) {
self->field = i;
}
});
}
});
}
- (long) value {
@synchronized(self) {
return self->field;
}
}
- (void)dealloc {
dispatch_group_leave(group);
}
@end
int main() {
group = dispatch_group_create();
@autoreleasepool {
for (int j = 0; j < 100; ++j) {
dispatch_group_enter(group);
MyClass *obj = [[MyClass alloc] init];
[obj start];
long x = obj.value;
(void)x;
}
}
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"Hello world");
}
// CHECK: Hello world
// CHECK-NOT: WARNING: ThreadSanitizer