Skip to content

Commit dc36389

Browse files
committed
[tsan] Fix crash in objc_sync_enter/objc_sync_exit when using an Obj-C tagged pointer
Objective-C tagged pointers (either bottom-most or top-most bit is 1) are valid Obj-C objects but are not valid pointers. Make sure we don't crash on them when used in objc_sync_enter/objc_sync_exit. Instead, let's synchronize on a global object. Differential Revision: https://reviews.llvm.org/D49707 llvm-svn: 337837
1 parent 634f851 commit dc36389

File tree

2 files changed

+88
-2
lines changed

2 files changed

+88
-2
lines changed

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

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,16 +294,40 @@ TSAN_INTERCEPTOR(void, xpc_connection_cancel, xpc_connection_t connection) {
294294

295295
#endif // #if defined(__has_include) && __has_include(<xpc/xpc.h>)
296296

297+
// Is the Obj-C object a tagged pointer (i.e. isn't really a valid pointer and
298+
// contains data in the pointers bits instead)?
299+
static bool IsTaggedObjCPointer(void *obj) {
300+
const uptr kPossibleTaggedBits = 0x8000000000000001ull;
301+
return ((uptr)obj & kPossibleTaggedBits) != 0;
302+
}
303+
304+
// Return an address on which we can synchronize (Acquire and Release) for a
305+
// Obj-C tagged pointer (which is not a valid pointer). Ideally should be a
306+
// derived address from 'obj', but for now just return the same global address.
307+
// TODO(kubamracek): Return different address for different pointers.
308+
static uptr SyncAddressForTaggedPointer(void *obj) {
309+
(void)obj;
310+
static u64 addr;
311+
return (uptr)&addr;
312+
}
313+
314+
// Address on which we can synchronize for an Objective-C object. Supports
315+
// tagged pointers.
316+
static uptr SyncAddressForObjCObject(void *obj) {
317+
if (IsTaggedObjCPointer(obj)) return SyncAddressForTaggedPointer(obj);
318+
return (uptr)obj;
319+
}
320+
297321
TSAN_INTERCEPTOR(int, objc_sync_enter, void *obj) {
298322
SCOPED_TSAN_INTERCEPTOR(objc_sync_enter, obj);
299323
int result = REAL(objc_sync_enter)(obj);
300-
if (obj) Acquire(thr, pc, (uptr)obj);
324+
if (obj) Acquire(thr, pc, SyncAddressForObjCObject(obj));
301325
return result;
302326
}
303327

304328
TSAN_INTERCEPTOR(int, objc_sync_exit, void *obj) {
305329
SCOPED_TSAN_INTERCEPTOR(objc_sync_enter, obj);
306-
if (obj) Release(thr, pc, (uptr)obj);
330+
if (obj) Release(thr, pc, SyncAddressForObjCObject(obj));
307331
return REAL(objc_sync_exit)(obj);
308332
}
309333

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// RUN: %clangxx_tsan %s -o %t -framework Foundation -fobjc-arc %darwin_min_target_with_full_runtime_arc_support
2+
// RUN: %run %t 2>&1 | FileCheck %s
3+
4+
#import <Foundation/Foundation.h>
5+
6+
NSString *tagged_string = nil;
7+
8+
@interface MyClass : NSObject {
9+
long field;
10+
}
11+
@property(nonatomic, readonly) long value;
12+
@end
13+
14+
dispatch_group_t group;
15+
16+
@implementation MyClass
17+
18+
- (void)start {
19+
dispatch_queue_t q = dispatch_queue_create(NULL, NULL);
20+
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
21+
for (int i = 0; i < 10; i++) {
22+
dispatch_async(q, ^{
23+
@synchronized(tagged_string) {
24+
self->field = i;
25+
}
26+
});
27+
}
28+
});
29+
}
30+
31+
- (long)value {
32+
@synchronized(tagged_string) {
33+
return self->field;
34+
}
35+
}
36+
37+
- (void)dealloc {
38+
dispatch_group_leave(group);
39+
}
40+
41+
@end
42+
43+
int main() {
44+
tagged_string = [NSString stringWithFormat:@"%s", "abc"];
45+
uintptr_t tagged_string_bits = (uintptr_t)tagged_string;
46+
assert((tagged_string_bits & 0x8000000000000001ull) != 0);
47+
group = dispatch_group_create();
48+
@autoreleasepool {
49+
for (int j = 0; j < 100; ++j) {
50+
dispatch_group_enter(group);
51+
MyClass *obj = [[MyClass alloc] init];
52+
[obj start];
53+
long x = obj.value;
54+
(void)x;
55+
}
56+
}
57+
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
58+
NSLog(@"Hello world");
59+
}
60+
61+
// CHECK: Hello world
62+
// CHECK-NOT: WARNING: ThreadSanitizer

0 commit comments

Comments
 (0)