Sanitizing Threads for Fun & Profit

06609d73ad2165c4aafcf65a1ddb9563?s=47 Greg Heo
October 19, 2017

Sanitizing Threads for Fun & Profit

06609d73ad2165c4aafcf65a1ddb9563?s=128

Greg Heo

October 19, 2017
Tweet

Transcript

  1. Sanitizing Threads For Fun And Profit iOS Conf SG @gregheo

  2. Uncertainty

  3. None
  4. None
  5. None
  6. Uncertainty

  7. None
  8. What? Why? How?

  9. What? Why? How?

  10. a = 10 a = 42 print(a)

  11. DispatchQueue.global().async { a = 10 } DispatchQueue.main.async { a =

    42 } print(a)
  12. a = 10 a = 42 print(a)

  13. a = 10 a = 42 print(a)

  14. Complexity Level This Example Your Code

  15. var a = 88
 
 DispatchQueue.global().async { print(a) } DispatchQueue.main.async

    { print(a) }
  16. 42 reads reads Thread 1 Thread 2 Thread 3

  17. 88 42 reads Thread 1 Thread 2 Thread 3 Thread

    4 writes
  18. None
  19. $ swiftc -sanitize=thread
 
 $ clang -fsanitize=thread

  20. DispatchQueue.global().async { a = 10 } DispatchQueue.main.async { a =

    42 } print(a)
  21. WARNING: ThreadSanitizer: data race (pid=12968) Write of size 8 at

    0x0001015c51b0 by thread T1: #0 closure #1 in assign.swift:5 (assign:x86_64+0x1000019cc) #1 thunk for @callee_owned () $-> () assign.swift (assign:x86_64+0x100001a4c) #2 $__tsan$::invoke_and_release_block(void*) <null>:1063616 (libclang_rt.tsan_osx_dynamic.dylib:x86_64h+0x6657b) #3 _dispatch_client_callout <null>:1063616 (libdispatch.dylib:x86_64+0x1f63) Previous read of size 8 at 0x0001015c51b0 by main thread: #0 main assign.swift:9 (assign:x86_64+0x1000017ef) Location is global 'a' at 0x0001015c51b0 (assign+0x0001000021b0) Thread T1 (tid=4890272, running) is a GCD worker thread
  22. None
  23. thread another thread where? over here? thread race?

  24. var a = 10 print(a) a = 20 print(a)

  25. 0000000 cf fa ed fe 07 00 00 01 03

    00 00 80 02 00 00 00 0000010 19 00 00 00 28 0d 00 00 85 00 20 00 00 00 00 00 0000020 19 00 00 00 48 00 00 00 5f 5f 50 41 47 45 5a 45 0000030 52 4f 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000040 00 00 00 00 01 00 00 00 00 00 00 00 00 00 00 00 0000050 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000060 00 00 00 00 00 00 00 00 19 00 00 00 b8 03 00 00 0000070 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 0000080 00 00 00 00 01 00 00 00 00 40 00 00 00 00 00 00 0000090 00 00 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00000a0 07 00 00 00 05 00 00 00 0b 00 00 00 00 00 00 00 00000b0 5f 5f 74 65 78 74 00 00 00 00 00 00 00 00 00 00 00000c0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 00000d0 40 10 00 00 01 00 00 00 d5 23 00 00 00 00 00 00 00000e0 40 10 00 00 04 00 00 00 00 00 00 00 00 00 00 00 00000f0 00 04 00 80 00 00 00 00 00 00 00 00 00 00 00 00 0000100 5f 5f 73 74 75 62 73 00 00 00 00 00 00 00 00 00 0000110 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 0000120 16 34 00 00 01 00 00 00 6e 01 00 00 00 00 00 00 0000130 16 34 00 00 01 00 00 00 00 00 00 00 00 00 00 00 0000140 08 04 00 80 00 00 00 00 06 00 00 00 00 00 00 00 0000150 5f 5f 73 74 75 62 5f 68 65 6c 70 65 72 00 00 00 0000160 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 0000170 84 35 00 00 01 00 00 00 72 02 00 00 00 00 00 00 0000180 84 35 00 00 02 00 00 00 00 00 00 00 00 00 00 00 0000190 00 04 00 80 00 00 00 00 00 00 00 00 00 00 00 00 00001a0 5f 5f 67 63 63 5f 65 78 63 65 70 74 5f 74 61 62 00001b0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 00001c0 f8 37 00 00 01 00 00 00 70 00 00 00 00 00 00 00 00001d0 f8 37 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00001e0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00001f0 5f 5f 63 6f 6e 73 74 00 00 00 00 00 00 00 00 00 0000200 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 0000210 68 38 00 00 01 00 00 00 10 00 00 00 00 00 00 00 0000220 68 38 00 00 03 00 00 00 00 00 00 00 00 00 00 00 0000230 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000240 5f 5f 6f 62 6a 63 5f 63 6c 61 73 73 6e 61 6d 65 0000250 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 0000260 78 38 00 00 01 00 00 00 48 00 00 00 00 00 00 00 0000270 78 38 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000280 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000290 5f 5f 6f 62 6a 63 5f 6d 65 74 68 6e 61 6d 65 00 00002a0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 00002b0 c0 38 00 00 01 00 00 00 28 02 00 00 00 00 00 00 00002c0 c0 38 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00002d0 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00002e0 5f 5f 6f 62 6a 63 5f 6d 65 74 68 74 79 70 65 00 00002f0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 0000300 e8 3a 00 00 01 00 00 00 36 00 00 00 00 00 00 00 0000310 e8 3a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000320 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000330 5f 5f 63 73 74 72 69 6e 67 00 00 00 00 00 00 00 0000340 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 0000350 1e 3b 00 00 01 00 00 00 61 03 00 00 00 00 00 00 0000360 1e 3b 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000370 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0000380 5f 5f 75 6e 77 69 6e 64 5f 69 6e 66 6f 00 00 00 0000390 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 00003a0 80 3e 00 00 01 00 00 00 04 01 00 00 00 00 00 00 00003b0 80 3e 00 00 02 00 00 00 00 00 00 00 00 00 00 00 00003c0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00003d0 5f 5f 65 68 5f 66 72 61 6d 65 00 00 00 00 00 00 00003e0 5f 5f 54 45 58 54 00 00 00 00 00 00 00 00 00 00 00003f0 88 3f 00 00 01 00 00 00 70 00 00 00 00 00 00 00
  26. SIL IR Object LLVM IRGen AST Sema SILGen Parse

  27. $ swiftc -emit-sil simpleprint.swift
 > simpleprint.orig.sil $ swiftc -emit-sil -sanitize=thread

    simpleprint.swift
 > simpleprint.tsan.sil 
 -rw-r"--r"-- 1 gth staff 61813 simpleprint.orig.sil
 -rw-r"--r"-- 1 gth staff 61813 simpleprint.tsan.sil
  28. SIL IR Object LLVM IRGen AST Sema SILGen Parse

  29. $ swiftc -emit-ir simpleprint.swift
 > simpleprint.orig.ir $ swiftc -emit-ir -sanitize=thread

    simpleprint.swift
 > simpleprint.tsan.ir 
 -rw-r"--r"-- 1 gth staff 9443 simpleprint.orig.ir
 -rw-r"--r"-- 1 gth staff 19009 simpleprint.tsan.ir
  30. define i32 @main(i32, i8"**) #0 { entry: %access-scratch = alloca

    [24 x i8], align 8 %access-scratch1 = alloca [24 x i8], align 8 %access-scratch2 = alloca [24 x i8], align 8 %2 = bitcast i8"** %1 to i8* store i64 10, i64* getelementptr inbounds (%TSi, %TSi* @_T011simpleprint1aSiv, i32 0, i32 0), align 8 %3 = call %swift.type* @_T0ypMa() #5 %4 = call swiftcc { %swift.bridge*, i8* } @_T0s27_allocateUninitializedArraySayxG_BptBwlF(i64 1, %swift.type* %3) %5 = extractvalue { %swift.bridge*, i8* } %4, 0 %6 = extractvalue { %swift.bridge*, i8* } %4, 1 %7 = call %swift.bridge* @swift_bridgeObjectRetain(%swift.bridge* %5) #6 call void @swift_bridgeObjectRelease(%swift.bridge* %5) #6
  31. define i32 @main(i32, i8"**) #0 { entry: %access-scratch = alloca

    [24 x i8], align 8 %access-scratch1 = alloca [24 x i8], align 8 %access-scratch2 = alloca [24 x i8], align 8 %2 = bitcast i8"** %1 to i8* store i64 10, i64* getelementptr inbounds (%TSi, %TSi* @_T011simpleprint1aSiv, i32 0, i32 0), align 8 %3 = call %swift.type* @_T0ypMa() #5 %4 = call swiftcc { %swift.bridge*, i8* } @_T0s27_allocateUninitializedArraySayxG_BptBwlF(i64 1, %swift.type* %3) %5 = extractvalue { %swift.bridge*, i8* } %4, 0 %6 = extractvalue { %swift.bridge*, i8* } %4, 1 %7 = call %swift.bridge* @swift_bridgeObjectRetain(%swift.bridge* %5) #6 call void @swift_bridgeObjectRelease(%swift.bridge* %5) #6
  32. define i32 @main(i32, i8"**) #0 { entry: %access-scratch = alloca

    [24 x i8], align 8 %access-scratch1 = alloca [24 x i8], align 8 %access-scratch2 = alloca [24 x i8], align 8 %2 = bitcast i8"** %1 to i8* store i64 10, i64* getelementptr inbounds (%TSi, %TSi* @_T011simpleprint1aSiv, i32 0, i32 0), align 8 %3 = call %swift.type* @_T0ypMa() #5 %4 = call swiftcc { %swift.bridge*, i8* } @_T0s27_allocateUninitializedArraySayxG_BptBwlF(i64 1, %swift.type* %3) %5 = extractvalue { %swift.bridge*, i8* } %4, 0 %6 = extractvalue { %swift.bridge*, i8* } %4, 1 %7 = call %swift.bridge* @swift_bridgeObjectRetain(%swift.bridge* %5) #6 call void @swift_bridgeObjectRelease(%swift.bridge* %5) #6
  33. define i32 @main(i32, i8"**) #0 { entry: %access-scratch = alloca

    [24 x i8], align 8 %access-scratch1 = alloca [24 x i8], align 8 %access-scratch2 = alloca [24 x i8], align 8 %2 = bitcast i8"** %1 to i8*
 call void @"__tsan_write8(i8* bitcast
 (%TSi* @_T011simpleprint1aSiv to i8*))
 store i64 10, i64* getelementptr inbounds (%TSi, %TSi* @_T011simpleprint1aSiv, i32 0, i32 0), align 8 %3 = call %swift.type* @_T0ypMa() #5 %4 = call swiftcc { %swift.bridge*, i8* } @_T0s27_allocateUninitializedArraySayxG_BptBwlF(i64 1, %swift.type* %3) %5 = extractvalue { %swift.bridge*, i8* } %4, 0 %6 = extractvalue { %swift.bridge*, i8* } %4, 1 TSan addition!
  34. call void @llvm.lifetime.start(i64 -1, i8* %13) call void @swift_beginAccess(i8* bitcast

    (%TSi* @_T011simpleprint1aSiv to i8*), [24 x i8]* %access-scratch, i64 0, i8* null) #6 %14 = load i64, i64* getelementptr inbounds (%TSi, %TSi* @_T011simpleprint1aSiv, i32 0, i32 0), align 8 call void @swift_endAccess([24 x i8]* %access-scratch) #6 %15 = bitcast [24 x i8]* %access-scratch to i8* call void @llvm.lifetime.end(i64 -1, i8* %15) %._value = getelementptr inbounds %TSi, %TSi* %12, i32 0, i32 0 store i64 %14, i64* %._value, align 8 %16 = call swiftcc { i64, i64, i64 } @_T0s5printySayypGd_SS9separatorSS10terminatortFfA0_() %17 = extractvalue { i64, i64, i64 } %16, 0 %18 = extractvalue { i64, i64, i64 } %16, 1 %19 = extractvalue { i64, i64, i64 } %16, 2
  35. call void @llvm.lifetime.start(i64 -1, i8* %13) call void @swift_beginAccess(i8* bitcast

    (%TSi* @_T011simpleprint1aSiv to i8*), [24 x i8]* %access-scratch, i64 0, i8* null) #6
 
 call void @"__tsan_read8(i8* bitcast (%TSi* @_T011simpleprint1aSiv to i8*)) %14 = load i64, i64* getelementptr inbounds (%TSi, %TSi* @_T011simpleprint1aSiv, i32 0, i32 0), align 8 call void @swift_endAccess([24 x i8]* %access-scratch) #6 %15 = bitcast [24 x i8]* %access-scratch to i8* call void @llvm.lifetime.end(i64 -1, i8* %15) %._value = getelementptr inbounds %TSi, %TSi* %12, i32 0, i32 0 store i64 %14, i64* %._value, align 8 %16 = call swiftcc { i64, i64, i64 } @_T0s5printySayypGd_SS9separatorSS10terminatortFfA0_() TSan addition!
  36. void "__tsan_write8(void *addr) { MemoryWrite(cur_thread(), CALLERPC, (uptr)addr, kSizeLog8); }

  37. Thread ID Memory Address Size Read or Write? 1 0x083817a81

    8 read 4 0xdeadbeef 4 read 7 0x4c898a40 8 write 2 0x738d4778 1 read 11 0xc020a1f0 4 write 1 0x5b430838 2 write 3 0x80dff8db 8 read 1 0x2f471de2 8 write
  38. 1. Memory addresses intersect 2. Different threads 3. At least

    one write
  39. Uncertainty Threads & Race Conditions Thread Sanitizer? SIL & LLVM

    IR Thread Sanitizer Analysis
  40. Uncertainty ☺

  41. Lessons
 Learned using Thread Sanitizer analyzing Thread Sanitizer

  42. Thread sanitize all the things (tests too!) 1

  43. Serial queues, Concurrent queues,
 Target queues, Dispatch barriers 2

  44. 3Collect the right data

  45. 4Go one more level down

  46. threads synchronized!!! success iOS Conf SG @gregheo