Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SR-14375] Program crashes after using Task.sleep on Windows #56733

Closed
MarSe32m opened this issue Mar 19, 2021 · 9 comments
Closed

[SR-14375] Program crashes after using Task.sleep on Windows #56733

MarSe32m opened this issue Mar 19, 2021 · 9 comments
Assignees
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. concurrency Feature: umbrella label for concurrency language features Windows Platform: Windows

Comments

@MarSe32m
Copy link

Previous ID SR-14375
Radar rdar://problem/79709098
Original Reporter @MarSe32m
Type Bug
Status Closed
Resolution Done
Environment

swift --version
compnerd.org Swift version 5.5-dev (LLVM 9c34c22, Swift 4bffcac)
Target: x86_64-unknown-windows-msvc

Additional Detail from JIRA
Votes 0
Component/s
Labels Bug, Concurrency, Windows
Assignee @compnerd
Priority Medium

md5: 1f95c18cbe1e749f38d19aed6ac05c3e

Issue Description:

func hello(_ i: Int) async {
    print("Hello world", i)
    try? await Task.sleep(nanoseconds: 1_000_000_000)
}

@main
public struct Main {
    public static func main() async {
        var i = 0
        while i < 10 {
            await hello(i)
            i += 1
        }
    }
}
Hello world 0
Hello world 1

and then the program crashes.

@typesanitizer
Copy link

cc @compnerd

@mattneub
Copy link

Well it isn't just Windows. Task.sleep crashes with anything but tiny values on iOS as well. It's more or less useless at the moment.

@typesanitizer
Copy link

Thanks for pointing that out, I hadn't realized that.

@swift-ci create

@mattneub
Copy link

Apologies, I assumed this was well known. I'll try to report more conscientiously.

@swift-ci
Copy link
Collaborator

Comment by Alexander Fringes (JIRA)

FWIW, Arrived here due to the Swift documentation's Concurrency guide recommending the use of Task.sleep for experimentation, with a rather large value.

await Task.sleep(2 * 1_000_000_000)

New here, but will try to file this in the correct project for documentation.

@compnerd
Copy link
Collaborator

(2e7c.2be4): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
swift_Concurrency!$sScs8IteratorVMa+0x2fea:
00007ffa`ae1d350a 48631f          movsxd  rbx,dword ptr [rdi] ds:feeefeee`feeefeee=????????
0:004> kv
# Child-SP          RetAddr               : Args to Child                                                           : Call Site
00 00000027`075ff8a8 000001a9`ac4b90f0     : 00007ffa`ae1da3e0 00007ffa`ae1da437 00007ffa`ae1d3490 000001a9`ac4b90f0 : swift_Concurrency!$sScs8IteratorVMa+0x2fea
01 00000027`075ff8b0 00007ffa`ae1da3df     : 00007ffa`ae1da437 00007ffa`ae1d3490 000001a9`ac4b90f0 00007ffa`ae1d3490 : 0x000001a9`ac4b90f0
02 00000027`075ff8b8 00007ffa`ae1da437     : 00007ffa`ae1d3490 000001a9`ac4b90f0 00007ffa`ae1d3490 00007ffa`ae1e3428 : swift_Concurrency!swift_continuation_await+0x40f
03 00000027`075ff8c0 00007ffa`ae1da3df     : 00000000`00000000 00000000`00000000 00000000`00000000 00007ffa`c9353522 : swift_Concurrency!swift_task_alloc+0x57
04 00000027`075ff900 00000000`00000000     : 00000000`00000000 00000000`00000000 00007ffa`c9353522 000001a9`ac4b9060 : swift_Concurrency!swift_continuation_await+0x40f

Seems like a UaF.

A more useful stack from swiftc -sdk %SDKROOT% -Xfrontend -enable-experimental-concurrency -o asyncbug.exe -emit-executable Application.swift Utility.swift -no-color-diagnostics -libc MD -g -debug-info-format=codeview -Xlinker -debug:

0:000> kv
 # Child-SP          RetAddr               : Args to Child                                                           : Call Site
00 00000064`7d2ff2c0 00007ffa`699eb7ff     : 00000001`de6e9c01 00007ffa`c6af40f8 00000177`94fd2c80 00000177`00000070 : swiftCore!swift_conformsToProtocol+0xfac
01 00000064`7d2ff350 00007ffa`699eb2ff     : 00000000`00000000 00000064`7d2ff490 00000064`7d2ff4d0 00000000`00000001 : swiftCore!swift_conformsToProtocol+0x52f
02 00000064`7d2ff470 00007ffa`699b08b7     : 00007ffa`69a5c950 00000064`7d2ff530 00000064`7d2ff7c0 00000064`7d2ff6d0 : swiftCore!swift_conformsToProtocol+0x2f
03 00000064`7d2ff4d0 00007ffa`699b4e22     : 00000000`00000001 00000000`00000000 00007ffa`69b3ada8 00000000`00000000 : swiftCore!swift_getMangledTypeName+0x5f7
04 00000064`7d2ff500 00007ffa`699b3d00     : 00000000`00000000 00000000`00000000 00007ffa`c6bef4f8 00007ffa`c6bef4f8 : swiftCore!swift_dynamicCast+0x1472
05 00000064`7d2ff570 00007ffa`699b3a19     : 00007ffa`699ce8f0 00007ffa`c6b031ab 00000000`00000200 00007ffa`69a5c290 : swiftCore!swift_dynamicCast+0x350
06 00000064`7d2ff610 00007ffa`69741ace     : 00007ffa`69a5c290 00007ffa`69741aaf 00000000`00000010 00007ffa`c6af517b : swiftCore!swift_dynamicCast+0x69
07 00000064`7d2ff6a0 00007ffa`697abd85     : 00007ffa`c6bef4f8 00000064`7d2ff8e8 00000000`00000001 00000000`00000001 : swiftCore!$ss15_print_unlockedyyx_q_zts16TextOutputStreamR_r0_lF+0x28e
08 00000064`7d2ff860 00007ffa`697ab696     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : swiftCore!$ss10debugPrint_9separator10terminatoryypd_S2StF+0x7f5
09 00000064`7d2ff960 00007ffa`697ab53f     : 00000000`00000000 00007ff7`06e0290c 00000177`94fda220 00000177`94fca430 : swiftCore!$ss10debugPrint_9separator10terminatoryypd_S2StF+0x106
0a 00000064`7d2ff9f0 00007ff7`06e0264b     : 00007ffa`9decf918 00000000`00000000 00000000`00000000 00000177`94fcde50 : swiftCore!$ss5print_9separator10terminatoryypd_S2StF+0x1f
0b 00000064`7d2ffa30 00007ff7`06e017b6     : 00008e0c`0ededd73 00000000`00000000 00000000`00000000 00000177`94fda220 : asyncbug!asyncbug::hello+0xfb [S:\tmp\SR-14375\Sources\asyncbug\Utility.swift @ 2] 
0c 00000064`7d2ffac0 00007ff7`06e01723     : 00000000`00000000 00007ffa`9deca437 00000000`00000000 00000000`00000000 : asyncbug!asyncbug::Main::main+0x86 [S:\tmp\SR-14375\Sources\asyncbug\Application.swift @ 9] 
0d 00000064`7d2ffb10 00007ff7`06e01a11     : 00000000`00000000 00007ffa`9deca437 00000000`00000000 00000177`94fcde50 : asyncbug!asyncbug::Main::main+0x83 [S:\tmp\SR-14375\Sources\asyncbug\Application.swift @ 3] 
0e 00000064`7d2ffb60 00007ff7`06e01b9c     : 00000000`00000018 00000000`00000000 00000000`00000000 00000177`94fcde50 : asyncbug!asyncbug::Main::$main+0x71 [S:\tmp\SR-14375\Sources\asyncbug\Application.swift @ 1] 
0f 00000064`7d2ffbb0 00007ff7`06e02503     : 00000000`00000000 00007ffa`9deca437 00000000`00000000 00000000`00000000 : asyncbug!async_Main+0x4c [S:\tmp\SR-14375\Sources\asyncbug\Application.swift @ 2] 
10 00000064`7d2ffbf0 00007ff7`06e01da4     : 00000000`00000000 00000000`00000000 00000000`00000100 00007ffa`c6b01b86 : asyncbug!$sIetH_yts5Error_pIegHrzo_TR+0x63 [\<compiler-generated> @ 0] 
11 00000064`7d2ffc40 00007ffa`9deca152     : 00000000`02001500 00007ffa`699d7729 00007ff7`06e09220 00007ffa`c6d9248b : asyncbug!$sIetH_yts5Error_pIegHrzo_TRTA+0x64 [\<compiler-generated> @ 0] 
12 00000064`7d2ffc90 00007ffa`9dec719d     : 00000000`00000000 00000000`00000000 00007ff7`06e01d40 00000000`00000000 : swift_Concurrency!swift_continuation_await+0x182
13 00000064`7d2ffcc0 00007ffa`9dec7946     : 00000000`00000000 00000177`94fda220 00000000`00000000 00000000`00000000 : swift_Concurrency!$ss27AsyncThrowingFilterSequenceV8IteratorV10isIncludedySb7ElementQzYaKcvg+0x25ed
14 00000064`7d2ffd00 00007ff7`06e01c90     : 00000000`00000000 00007ff7`06e04269 00000000`0000001f 00000000`00000001 : swift_Concurrency!swift_job_run+0x66
15 00000064`7d2ffd80 00007ff7`06e03c8c     : 00000000`00000000 00007ff7`06e03e15 00000000`00000000 00000000`00000000 : asyncbug!asyncbug::main+0x70 [S:\tmp\SR-14375\Sources\asyncbug\Application.swift @ 0] 
16 (Inline Function) --------`--------     : --------`-------- --------`-------- --------`-------- --------`-------- : asyncbug!invoke_main+0x22 (Inline Function @ 00007ff7`06e03c8c) [d:\a01\_work\20\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 78] 
17 00000064`7d2ffdc0 00007ffa`c8846ab0     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : asyncbug!__scrt_common_main_seh+0x10c [d:\a01\_work\20\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl @ 288] 
18 00000064`7d2ffe00 00007ffa`c9331dbb     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : KERNEL32!BaseThreadInitThunk+0x10
19 00000064`7d2ffe30 00000000`00000000     : 00000000`00000000 00000000`00000000 00000000`00000000 00000000`00000000 : ntdll!RtlUserThreadStart+0x2b

lm identifies the image base for swiftCore as 00007ffa`696d0000. This identifies rip as 00000001`8031c27c. Scanning backwards to find a prologue shows this:

 18031c260: 56                              pushq   %rsi
18031c261: 57                           pushq   %rdi
18031c262: 53                           pushq   %rbx
18031c263: 48 83 ec 70                  subq    $112, %rsp
18031c267: 48 89 d7                     movq    %rdx, %rdi
18031c26a: 48 89 ce                     movq    %rcx, %rsi
18031c26d: 48 8b 05 cc ac 13 00         movq    1289420(%rip), %rax     # 0x180456f40 <_swiftEmptySetSingleton+0x48>
18031c274: 48 31 e0                     xorq    %rsp, %rax
18031c277: 48 89 44 24 68               movq    %rax, 104(%rsp)
18031c27c: 48 63 0a                     movslq  (%rdx), %rcx       <----------- $rip
18031c27f: 48 85 c9                     testq   %rcx, %rcx
18031c282: 74 14                        je  0x18031c298 <swift_conformsToProtocol+0xfc8>
18031c284: 48 89 c8                     movq    %rcx, %rax
18031c287: 48 83 e0 fe                  andq    $-2, %rax
18031c28b: 48 01 f8                     addq    %rdi, %rax
18031c28e: f6 c1 01                     testb   $1, %cl
18031c291: 74 07                        je  0x18031c29a <swift_conformsToProtocol+0xfca>
18031c293: 48 8b 00                     movq    (%rax), %rax
18031c296: eb 02                        jmp 0x18031c29a <swift_conformsToProtocol+0xfca>
18031c298: 31 c0                        xorl    %eax, %eax
18031c29a: 48 8b 0e                     movq    (%rsi), %rcx
18031c29d: 48 3b 01                     cmpq    (%rcx), %rax
18031c2a0: 0f 85 cf 00 00 00            jne 0x18031c375 <swift_conformsToProtocol+0x10a5>
18031c2a6: 48 c7 44 24 58 00 00 00 00   movq    $0, 88(%rsp)
18031c2af: c6 44 24 60 00               movb    $0, 96(%rsp)
18031c2b4: 48 8d 47 04                  leaq    4(%rdi), %rax
18031c2b8: 8b 4f 0c                     movl    12(%rdi), %ecx
18031c2bb: c1 e9 03                     shrl    $3, %ecx
18031c2be: 83 e1 07                     andl    $7, %ecx
18031c2c1: 83 f9 01                     cmpl    $1, %ecx
18031c2c4: 74 2a                        je  0x18031c2f0 <swift_conformsToProtocol+0x1020>
18031c2c6: 85 c9                        testl   %ecx, %ecx
18031c2c8: 75 10                        jne 0x18031c2da <swift_conformsToProtocol+0x100a>
18031c2ca: 48 63 08                     movslq  (%rax), %rcx
18031c2cd: 48 85 c9                     testq   %rcx, %rcx
18031c2d0: 74 08                        je  0x18031c2da <swift_conformsToProtocol+0x100a>
18031c2d2: 48 01 c1                     addq    %rax, %rcx
18031c2d5: 48 85 c9                     testq   %rcx, %rcx
18031c2d8: 75 22                        jne 0x18031c2fc <swift_conformsToProtocol+0x102c>
18031c2da: 48 89 f9                     movq    %rdi, %rcx
18031c2dd: e8 be da ff ff               callq   0x180319da0 <swift_once+0xd0>
18031c2e2: 48 85 c0                     testq   %rax, %rax
18031c2e5: 74 20                        je  0x18031c307 <swift_conformsToProtocol+0x1037>
18031c2e7: 48 89 44 24 58               movq    %rax, 88(%rsp)
18031c2ec: b0 01                        movb    $1, %al
18031c2ee: eb 13                        jmp 0x18031c303 <swift_conformsToProtocol+0x1033>
18031c2f0: 48 63 08                     movslq  (%rax), %rcx
18031c2f3: 48 8b 0c 01                  movq    (%rcx,%rax), %rcx
18031c2f7: 48 85 c9                     testq   %rcx, %rcx
18031c2fa: 74 de                        je  0x18031c2da <swift_conformsToProtocol+0x100a>
18031c2fc: 48 89 4c 24 58               movq    %rcx, 88(%rsp)
18031c301: 31 c0                        xorl    %eax, %eax
18031c303: 88 44 24 60                  movb    %al, 96(%rsp)
18031c307: 48 8b 46 08                  movq    8(%rsi), %rax
18031c30b: 48 8b 4e 10                  movq    16(%rsi), %rcx
18031c30f: 44 8a 01                     movb    (%rcx), %r8b
18031c312: 48 8b 10                     movq    (%rax), %rdx
18031c315: 48 8d 4c 24 58               leaq    88(%rsp), %rcx
18031c31a: e8 c1 ed ff ff               callq   0x18031b0e0 <swift_registerProtocolConformances+0x320>
18031c31f: 48 85 c0                     testq   %rax, %rax
18031c322: 74 51                        je  0x18031c375 <swift_conformsToProtocol+0x10a5>
18031c324: 48 89 c3                     movq    %rax, %rbx
18031c327: 48 89 f9                     movq    %rdi, %rcx
18031c32a: 48 89 c2                     movq    %rax, %rdx
18031c32d: e8 0e db ff ff               callq   0x180319e40 <swift_once+0x170>
18031c332: 48 89 c7                     movq    %rax, %rdi
18031c335: 48 8b 06                     movq    (%rsi), %rax
18031c338: 48 8b 4e 18                  movq    24(%rsi), %rcx
18031c33c: 4c 8b 00                     movq    (%rax), %r8
18031c33f: 48 c7 44 24 20 00 00 00 00   movq    $0, 32(%rsp)
18031c348: 48 89 da                     movq    %rbx, %rdx
18031c34b: 49 89 f9                     movq    %rdi, %r9
18031c34e: e8 9d f7 ff ff               callq   0x18031baf0 <swift_conformsToProtocol+0x820>
18031c353: 48 8b 4e 20                  movq    32(%rsi), %rcx
18031c357: 48 89 5c 24 48               movq    %rbx, 72(%rsp)
18031c35c: 4c 8d 4c 24 50               leaq    80(%rsp), %r9
18031c361: 48 89 7c 24 50               movq    %rdi, 80(%rsp)
18031c366: 48 8d 54 24 30               leaq    48(%rsp), %rdx
18031c36b: 4c 8d 44 24 48               leaq    72(%rsp), %r8
18031c370: e8 1b 00 00 00               callq   0x18031c390 <swift_conformsToProtocol+0x10c0>
18031c375: 48 8b 4c 24 68               movq    104(%rsp), %rcx
18031c37a: 48 31 e1                     xorq    %rsp, %rcx
18031c37d: e8 7e 5e 04 00               callq   0x180362200 <_swift_stdlib_overrideUnsafeArgvArgc+0x7a0>
18031c382: 90                           nop
18031c383: 48 83 c4 70                  addq    $112, %rsp
18031c387: 5b                           popq    %rbx
18031c388: 5f                           popq    %rdi
18031c389: 5e                           popq    %rsi
18031c38a: c3                           retq

Whatever this is, it seems that parameter 1 is the UaF pointer (Windows x64 CC differs from SysV).

0:000> x swiftCore!_swift_conformsToProtocol
00007ffa`699eb2d0 swiftCore!swift_conformsToProtocol (<no parameter info>)
:000> ?00007ffa`699eb2d0-00007ffa`696d0000
Evaluate expression: 3257040 = 00000000`0031b2d0
0:000> ?00007ffa`699eb2d0-00007ffa`696d0000+0x2f
Evaluate expression: 3257087 = 00000000`0031b2ff

Seems that at least frame 2 gives us a reasonable starting point ... that address looks within the function.

000000018031b2d0 <swift_conformsToProtocol>:
18031b2d0: 56                           pushq   %rsi
18031b2d1: 57                           pushq   %rdi
18031b2d2: 53                           pushq   %rbx
18031b2d3: 48 83 ec 40                  subq    $64, %rsp
18031b2d7: 48 89 d6                     movq    %rdx, %rsi
18031b2da: 48 89 cf                     movq    %rcx, %rdi
18031b2dd: 48 8b 05 5c bc 13 00         movq    1293404(%rip), %rax     # 0x180456f40 <_swiftEmptySetSingleton+0x48>
18031b2e4: 48 31 e0                     xorq    %rsp, %rax
18031b2e7: 48 89 44 24 38               movq    %rax, 56(%rsp)
18031b2ec: 48 8d 4c 24 28               leaq    40(%rsp), %rcx
18031b2f1: 48 89 fa                     movq    %rdi, %rdx
18031b2f4: 49 89 f0                     movq    %rsi, %r8
18031b2f7: 45 31 c9                     xorl    %r9d, %r9d
18031b2fa: e8 41 00 00 00               callq   0x18031b340 <swift_conformsToProtocol+0x70>
18031b2ff: 48 8b 5c 24 28               movq    40(%rsp), %rbx        <-------- retaddr
18031b304: 48 85 db                     testq   %rbx, %rbx
18031b307: 75 1f                        jne 0x18031b328 <swift_conformsToProtocol+0x58>
18031b309: 80 7c 24 30 00               cmpb    $0, 48(%rsp)
18031b30e: 74 18                        je  0x18031b328 <swift_conformsToProtocol+0x58>
18031b310: 48 8d 4c 24 28               leaq    40(%rsp), %rcx
18031b315: 48 89 fa                     movq    %rdi, %rdx
18031b318: 49 89 f0                     movq    %rsi, %r8
18031b31b: 41 b1 01                     movb    $1, %r9b
18031b31e: e8 1d 00 00 00               callq   0x18031b340 <swift_conformsToProtocol+0x70>
18031b323: 48 8b 5c 24 28               movq    40(%rsp), %rbx
18031b328: 48 8b 4c 24 38               movq    56(%rsp), %rcx
18031b32d: 48 31 e1                     xorq    %rsp, %rcx
18031b330: e8 cb 6e 04 00               callq   0x180362200 <_swift_stdlib_overrideUnsafeArgvArgc+0x7a0>
18031b335: 48 89 d8                     movq    %rbx, %rax
18031b338: 48 83 c4 40                  addq    $64, %rsp
18031b33c: 5b                           popq    %rbx
18031b33d: 5f                           popq    %rdi
18031b33e: 5e                           popq    %rsi
18031b33f: c3                           retq

I believe this is the following call:

  std::tie(table, hasUninstantiatedSuperclass) =
      swift_conformsToProtocolMaybeInstantiateSuperclasses(
          type, protocol, false /*instantiateSuperclassMetadata*/);

Scanning through the assembly, I think that we are accidentally caching a protocol conformance which is heap allocated and then trying to use that subsequently. Sadly I do not have the ability to generate the map file which would help verify that this is what is happening. I think it may be useful to actually add some tracing for the cache update. But the long delay does line up well - the heap allocation is restored to the freelist in the heap allocator, which would trigger the UaF.

@compnerd
Copy link
Collaborator

It occurred to me that this bug is far more tricky and the reason that the output keeps changing is because I wasn't thinking about what this is executing. The thing to realize is that this is running with libdispatch.

func hello(_ i: Int) async {
    print("Hello world", i)
    try? await Task.sleep(nanoseconds: 1_000_000_000)
}

is the interesting bit.

The first portion of the function executes normally, the Task.sleep is going to invoke dispatch to sleep. This is going to push a timer and a resumption to the tail of the function. This actually can cause a thread switch on the resumption, which I think is breaking as a result of thread switches. Adding in some logging around the Task.sleep to record the thread id via GetCurrentThreadId shows the thread ID changing:

Hello world 0
Pre Thread Id: 14524
Post Thread Id: 1988
Hello world 1
Pre Thread Id: 11524

@compnerd
Copy link
Collaborator

compnerd commented Dec 7, 2021

This is fixed on main, need to wait for the next snapshot to pick it up.

@MarSe32m
Copy link
Author

MarSe32m commented Jan 4, 2022

I can confirm that this works now with the latest snapshot (swift-DEVELOPMENT-SNAPSHOT-2021-12-23-a).

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. concurrency Feature: umbrella label for concurrency language features Windows Platform: Windows
Projects
None yet
Development

No branches or pull requests

5 participants