swift: XCode 14.2/Swift 5.7 memory corruption on Task creation when app is launched on iOS < 16.
Description Task creation in some contexts can lead to corruption of the application’s memory. Examples can be seen in the demo application code.
Steps to reproduce
- Download demo application source code: https://github.com/mstyura/Swift-Task-Heap-Curruption-on-iOS-15-and-earlier.
- Select scheme
TaskCrasher - Release - libgmalloc - use with arm64 simulator
; - Run demo app on
arm64
iOS simulator withiOS 15.4
;
Expected behavior Application is not crashed.
Actual behavior
Demo app crashes with libgmalloc
enabled. Address sanitizer unfortunately unable to catch the issue.
I’ve actually encountered problem on real production application on iOS device where app is randomly crashed on memory access to address 0x100000000 + offset
where offset is usually +0x10
, +0x20
or -0x8
in application or system library code.
Also in real application libmalloc
were capable to detect it was corrupted on some point, but it was unclear who was responsible for corruption.
Environment
- Swift compiler version info
swift-driver version: 1.62.15 Apple Swift version 5.7.2 (swiftlang-5.7.2.135.5 clang-1400.0.29.51)
Target: arm64-apple-macosx13.0
- Xcode version info
Xcode 14.2
Build version 14C18
- Deployment target: checked that issue happen on iOS 14 and iOS 13 and iOS 15 as deployment target.
Additional context:
An app that I observed crashing worked fine when built with XCode 13.4.1
and Swift 5.6
, but experienced significant, random crashes with “bad access” errors on addresses like 0x100000020
when built with XCode 14.2
or XCode 14.1
and Swift 5.7
. As a result, it’s currently not feasible to target iOS versions lower than 16 and use Swift 5.7
with async functions and Tasks without encountering random crashes.
UPD: On demo application issue is only happen when compiled with -O
(optimize for speed), but not when compiled with -Osize
(optimize for size) or -Onone
.
About this issue
- Original URL
- State: closed
- Created a year ago
- Comments: 17 (10 by maintainers)
When creating a task, the compiler generates an “initial context size” which tells the runtime how much extra space to allocate. This space is then used for various task-local stuff, like variables that persist across suspension points. The runtime also puts an
AsyncContext
at the start of this area. The way it’s supposed to look is:That’s on newer concurrency runtimes, after the commit I linked above. On older concurrency runtimes, it’s supposed to look like this:
However, with a new compiler that thinks
AsyncContext
is always smaller, it actually looks like this when running on an older runtime:The
Flags
field overlaps with what follows, which is, forgive the use of technical jargon, Bad.It looks like this is usually harmless, as the runtime sets
Flags
and then it’s never used again. This happens before any other stuff is written, so that space will be reused and everything works fine. The problem occurs when there is no other stuff. Then you end up with this picture:If nothing else needs to be stored, the compiler asks the runtime to allocate just enough space for
Parent
andResumeParent
. That means thatFlags
points off the end of the allocation. If we’re lucky (or running with Guard Malloc),Flags
lies in an unmapped page and crashes. If we’re unlucky,Flags
lines up with some other random chunk of memory, and smashes whatever happens to be there.I don’t think we need to go so far as to reintroduce
Flags
, but we do need to ensure that the compiler reserves enough space for it in the allocation when targeting older OSes.@jasonmolenda thanks for a hint.
~/Library/Logs/DiagnosticReports
had a crash report:Report
Basically swift toolchain has
LLDB
which attempts to loadPython3.framework
, but toolchain is absentPython3.framework
. Manually added symlink toXCode
sPython3.framework
to resolve issue:sudo ln -nfs /Applications/Xcode_14.2.app/Contents/Developer/Library/Frameworks/Python3.framework /Library/Developer/Toolchains/swift-5.8-DEVELOPMENT-SNAPSHOT-2023-02-20-a.xctoolchain/System/Library/PrivateFrameworks/Python3.framework
. Now I can debug apps with custom swift toolchains. Thanks for a help!@mstyura excellent, thanks for getting to the bottom of it. Ah, I see from the LLDB LC_RPATHs that LLDB looks for the installed Xcode.app as /Applications/Xcode.app to find Python, but you have Xcode installed as Xcode_14.2.app. The Xcode app bundle is normally self contained, but the CommandLineTools lldb has an assumption about the name. I’ll look into what we might be able to do to make this more resilient.
@mstyura Thanks a lot for the nice reproducer. I’m able to replicate the crash here. I’ll see if I can figure out what’s going on.