← WebKit — 2026-W36

dfec1c79caab17a47ca5e12f82f287cb5564d809  Use-after-free of WKTextInteractionWrapper in -[WKContentView _updateChangedSelection:]

severity low class UAF confidence 0.2 signal score 6.2 Source/WebKit/UIProcess
Primitive: _textInteractionWrapper dereferenced without retaining it across potentially re-entrant calls.
Triage note: Wraps numerous [_textInteractionWrapper ...] message sends in WKContentViewInteraction with protect()/RetainPtr. This is a blanket SaferCPP-style retain hardening with no identified attacker-reachable use-after-free, matching the protect()-migration false-positive pattern.
Structural signals: null_guard (+1.2), msg_strong (+3.0), msg_medium (+2.0)

Commit message

Use-after-free of WKTextInteractionWrapper in -[WKContentView _updateChangedSelection:]
<https://bugs.webkit.org/show_bug.cgi?id=322911>
<rdar://186164074>

Reviewed by Wenson Hsieh.

Retain the text interaction wrapper for the duration of each send that
can re-enter WebKit. `WKContentView` owns the wrapper through its
`_textInteractionWrapper` member and drops that reference in
`-cleanUpInteraction`, but callers borrow the pointer without taking
one of their own. The borrowed sends forward into UIKit or
BrowserEngineKit, which broadcast notifications and invoke delegates
before returning. That arbitrary code can tear the interaction down
and release the wrapper's only strong reference while the wrapper is
still on the stack, so the member access it performs after the callout
reads freed memory.

The reported crash faults in `-[WKTextInteractionWrapper
selectionChanged]`, entered from the `selectionChanged` send in
`-[WKContentView _updateChangedSelection:]`. The other invocations share
the same unsafe borrow and are corrected as well, though no crash has
been observed at any of them. Sends left unchanged are those whose
callee is a plain getter, or whose callee performs no synchronous
callout before its final use of the receiver, except where such a send
shares a block with a protected one and so takes the same stack
reference.

Test: Tools/TestWebKitAPI/Tests/WebKit/WKWebView/ios/SelectionByWord.mm

* Source/WebKit/UIProcess/ios/WKContentView.mm:
(-[WKContentView _didExitStableState]):
* Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView becomeFirstResponderForWebView]):
(-[WKContentView endEditingAndUpdateFocusAppearanceWithReason:]):
(-[WKContentView _scrollingNodeScrollingWillBegin:]):
(-[WKContentView setUpTextSelectionAssistant]):
(-[WKContentView _willStartScrollingOrZooming]):
(-[WKContentView _didEndScrollingOrZooming]):
(-[WKContentView shareForWebView:]):
(-[WKContentView translateForWebView:]):
(-[WKContentView addShortcutForWebView:]):
(-[WKContentView promptForReplaceForWebView:]):
(-[WKContentView transliterateChineseForWebView:]):
(-[WKContentView tintColorDidChange]):
(-[WKContentView selectForWebView:]):
(-[WKContentView selectAllForWebView:]):
(-[WKContentView changeSelectionWithGestureAt:withGesture:withState:withFlags:]):
(-[WKContentView updateSelectionWithTouchAt:withSelectionTouch:baseIsStart:withFlags:]):
(-[WKContentView changeSelectionWithTouchesFrom:to:withGesture:withState:]):
(-[WKContentView _showKeyboard]):
(-[WKContentView _hideKeyboard:]):
(-[WKContentView _continueElementDidFocus:requiresStrongPasswordAssistance:focusedElementInfo:activityStateChanges:restoreValues:]):
(-[WKContentView _updateChangedSelection:]):
(-[WKContentView _updateSelectionViewsIfNeeded]):
(-[WKContentView _internalSelectTextForContextMenuWithLocationInView:completionHandler:]):
(-[WKContentView _startSuppressingSelectionAssistantForReason:]):
(-[WKContentView _stopSuppressingSelectionAssistantForReason:]):
(-[WKContentView dragInteraction:willAnimateLiftWithAnimator:session:]):
(-[WKContentView _registerPreview]):
* Tools/TestWebKitAPI/Tests/WebKit/WKWebView/ios/SelectionByWord.mm:
(TestWebKitAPI::TEST(SelectionTests, TextInteractionWrapperOutlivesTextInteractionTearDown)): Add.

Identifier: 316606.649@safari-7625-branch

Before / after

Loading diff…