How a Hidden iOS 16 Keyboard Crash Was Discovered, Analyzed, and Patched
An Ant Group engineer reverse‑engineered a severe iOS 16 keyboard crash affecting the Alipay app, traced it to a faulty lock in UIKeyboardTaskQueue, and implemented an assembly‑level patch that eliminated the bug across millions of devices.
Background
Ant Group launched a technical challenge to solve a high‑frequency keyboard crash on iOS 16 observed in the Alipay app (version 10.5.0.6000). The crash caused SIGSEGV at address 0x2ab3106e0, originating from _objc_retain in the system keyboard task queue.
Crash Information
Key crash details include hardware model iPhone 12 Pro Max, iOS 16.6, and the stack trace showing UIKeyboardTaskQueue methods such as performDeferredTaskIfIdle and continueExecutionOnMainThread. The crash is caused by a dangling pointer when the array _deferredTasks is accessed.
Analysis Process
Collected crash logs and identified the offending function offsets.
Used lldb commands (breakpoints, backtrace, frame select) to locate the exact code paths.
Extracted all methods of UIKeyboardTaskQueue via a custom script and examined the assembly.
Mapped reads and writes of the _deferredTasks and _lock members, discovering six read and four write methods.
Found that -[UIKeyboardTaskQueue continueExecutionOnMainThread] calls tryLockWhenReadyForMainThread but does not return when the lock fails, leading to unsafe array access and a dangling pointer.
Root Cause
The bug lies in -[UIKeyboardTaskQueue continueExecutionOnMainThread]: after a failed tryLockWhenReadyForMainThread, the code continues to read _deferredTasks and later calls _objc_retain, causing a crash. This lock‑failure path is only present in iOS 16; iOS 15 and iOS 17 correctly return early.
Solution
Two patch strategies were considered. The chosen approach rewrites -[UIKeyboardTaskQueue tryLockWhenReadyForMainThread] in assembly to return early on lock failure, preventing the unsafe execution path.
#ifdef __arm64__
// Assembly implementation of the fixed method
_fix_UIKeyboardTaskQueue_tryLockWhenReadyForMainThread:
...
cbz x0, 1f // If lock fails, jump to early return
ret // Lock succeeded, continue normally
1:
// Simulate return to caller without executing unsafe code
ldp x29, x30, [sp, #0x20]
ldp x20, x19, [sp, #0x10]
add sp, sp, #0x30
ret
#endifThe patch is injected at app launch using class_replaceMethod for iOS 16 devices.
Results
After enabling the patch in Alipay version 10.5.16.6000, daily crash PV dropped to zero, reducing overall crash volume by ~90% across all versions.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
