mirror of
https://github.com/apple/swift.git
synced 2026-11-01 23:11:50 +01:00
```
*** Program crashed: Bad pointer dereference at 0xcdcdcdcdcdcdcdd5 ***
Thread 1 crashed:
0 0x00000001049aa564 TaskGroup.next(isolation:) + 40 in libswift_Concurrency.dylib
1 [async] 0x00000001049a9958 TaskGroup.awaitAllRemainingTasks() in libswift_Concurrency.dylib
2 [async] 0x00000001049a9bd0 withTaskGroup<A, B>(of:returning:isolation:body:) in libswift_Concurrency.dylib
3 [async] withTaskGroup<A, B>(of:returning:isolation:body:) in a.out
Registers:
x22 0xcdcdcdcdcdcdcdcd (StackAllocator::magicUninitialized)
x10 0xdeadbeafdeadbeaf (StackAllocator::magicEndOfAllocation)
pc 0x00000001049aa564 c0 06 40 f9 -> ldr x0, [x22, #8]
lr 0x00000001049aa560 d6 02 40 f9 -> ldr x22, [x22]
```
8b07a6f311 changed swift_task_alloc/swift_task_dealloc from
`memory(argmem: readwrite)` to `memory(inaccessiblemem: readwrite)` so
that they can no longer be reordered with respect to one another. For
the deallocation entry points that is unsound: deallocating invalidates
the storage the argument points to, so it has to be modelled as a write
to argument memory as well -- this is exactly why LLVM gives `free`
`memory(argmem: readwrite, inaccessiblemem: readwrite)`. Advertising
inaccessible memory only lets any pass move accesses to the deallocated
buffer across the call. Concretely, InstCombine's
`isSafeAndProfitableToSinkLoad` treats a call that
`onlyAccessesInaccessibleMemory()` as not blocking a sink, so
`foldPHIArgLoadIntoPHI` sank the reload of `calleeContext->Parent` in
the two-suspend back-deployment thunk of `TaskGroup.next(isolation:)`
below `swift_task_dealloc(calleeContext)`. The resulting funclet reads
the freed async context, which an asserts-build task allocator has
already poisoned with `magicUninitialized`, and the return branches
through 0xcdcdcdcdcdcdcdcd.
swift_task_alloc keeps `memory(inaccessiblemem: readwrite)`: its only
argument is a size, so argument memory is vacuous there, and the storage
it returns cannot alias anything already in flight.
Assisted-by: Claude Code
29 lines
1.0 KiB
Plaintext
29 lines
1.0 KiB
Plaintext
// RUN: %target-swift-frontend -primary-file %s -emit-irgen -sil-verify-all | %FileCheck %s
|
|
|
|
// REQUIRES: concurrency
|
|
|
|
// Deallocating invalidates the storage the argument points to, so the memory
|
|
// effects of the swift_task_dealloc entry points must cover argument memory
|
|
// (this is also how LLVM models `free`). Advertising inaccessible memory only
|
|
// lets LLVM move accesses to the deallocated async context across the call;
|
|
// InstCombine's phi-of-load fold in particular does exactly that.
|
|
|
|
sil_stage canonical
|
|
|
|
import Builtin
|
|
import Swift
|
|
import _Concurrency
|
|
|
|
sil hidden_external @callee : $@convention(thin) @async () -> ()
|
|
|
|
sil hidden @caller : $@convention(thin) @async () -> () {
|
|
bb0:
|
|
%0 = function_ref @callee : $@convention(thin) @async () -> ()
|
|
%1 = apply %0() : $@convention(thin) @async () -> ()
|
|
%2 = tuple ()
|
|
return %2 : $()
|
|
}
|
|
|
|
// CHECK: declare{{( dllimport)?}} swiftcc void @swift_task_dealloc(ptr) #[[DEALLOC_ATTRS:[0-9]+]]
|
|
// CHECK: attributes #[[DEALLOC_ATTRS]] = { nounwind memory(argmem: readwrite, inaccessiblemem: readwrite) }
|