← WebKit Silent-Fix Report — 2026-W22

8ea9912036  Crash under WKWebView _dispatchSetViewLayoutSize: when client overrides -setFrame:

severity medium class UAF confidence 0.60 WebKit UIProcess WKWebView iOS exploitable-grade
Tim Horton Tue May 26 23:15:49 2026 -0700 full: 8ea991203620d2c839702adc6aea4254c2adb498 bug report ↗ view on GitHub ↗
Primitive: deref null _page after client overrides setFrame
Triage note: Adds _page null-checks in reentrant layout callbacks, fixing a null/lifetime deref crash reachable by client subclassing.
Contents

The bug at a glance

A null-pointer dereference crash in WKWebView on iOS: a client that overrides -[WKWebView setFrame:] can drive frame/bounds and view-layout codepaths during WKWebView initialization before WebPageProxy (_page) exists, dereferencing a null _page. The impact is an app crash reachable purely from client subclassing behavior, not from web content; there is no memory corruption. Medium/0.6: it is a reliable reentrancy crash addressed with straightforward null checks, but the trigger requires a client subclass, limiting the attack surface to app code rather than remote pages.

During WKWebView init, before WebPageProxy is created, a client override of -setFrame: can reenter frame-adjustment codepaths (_dispatchSetViewLayoutSize:, _frameOrBoundsWillChange, _frameOrBoundsMayHaveChanged) that assume _page is already set. With _page still null, _page->minimumEffectiveDeviceWidth() and friends dereference null and crash.

Root cause

WKWebView on iOS creates its underlying WebPageProxy (accessed through the _page member) as part of initialization. Several view-geometry callbacks assume _page is valid: -_dispatchSetViewLayoutSize: reads _page->minimumEffectiveDeviceWidth() immediately, and -_frameOrBoundsWillChange / -_frameOrBoundsMayHaveChanged run layout-related work that ultimately touches _page.

These callbacks are triggered by AppKit/UIKit view geometry changes. A client that overrides -[WKWebView setFrame:] can cause a size change to be processed while chaining to super during the view’s own construction – before WKWebView has finished building _page. The order of operations during init means setFrame: (and the resulting _frameOrBoundsMayHaveChanged / view-layout dispatch) can execute at a point where _page is still null. Dereferencing _page then crashes.

The added test FrameOverridingWKWebView demonstrates the exact reentrancy: its -setFrame: calls [super setFrame:CGRectZero] then [super setFrame:frame], forcing an actual size change through super so that -_frameOrBoundsMayHaveChanged is taken during initWithFrame:configuration:. Without the fix that path runs before _page exists and dereferences null.

The fix adds an early if (!_page) return; guard at the top of all three methods (_dispatchSetViewLayoutSize:, _frameOrBoundsWillChange, _frameOrBoundsMayHaveChanged), so any of these reentrant geometry callbacks that fire before WebPageProxy is initialized simply bail out instead of dereferencing null. Once initialization completes and _page is set, the methods run normally.

Key code

WKWebViewIOS.mm: bail out of geometry callbacks when _page is not yet created

- (void)_dispatchSetViewLayoutSize:(WebCore::FloatSize)viewLayoutSize
{
    if (!_page)
        return;

    auto newMinimumEffectiveDeviceWidth = _page->minimumEffectiveDeviceWidth();

Patch walkthrough

  • Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm — Adds if (!_page) return; as the first statement of -_dispatchSetViewLayoutSize:, -_frameOrBoundsWillChange, and -_frameOrBoundsMayHaveChanged. Previously -_dispatchSetViewLayoutSize: immediately called _page->minimumEffectiveDeviceWidth() and the other two performed layout work that reaches _page; the guards make each method bail when _page has not yet been created (e.g. during a client-driven reentrant setFrame: while WKWebView is still initializing).
  • Tools/TestWebKitAPI/SourcesCocoa.txt — Registers the new test source Tests/WebKit/WKWebView/WKWebViewSubclassedSetFrame.mm (@nonARC) so it is built and run.
  • Tools/TestWebKitAPI/Tests/WebKit/WKWebView/WKWebViewSubclassedSetFrame.mm — New test: a FrameOverridingWKWebView subclass overrides -setFrame: to force a super size change (CGRectZero then the real frame), then allocates one with initWithFrame:configuration: and asserts it is non-null, i.e. that the reentrant geometry callback during init no longer crashes.

Background

_page (WebPageProxy) — WKWebView’s handle to the UI-process WebPageProxy that backs the view. It is created during WKWebView initialization; before that point _page is null and must not be dereferenced.

View geometry callbacks — -_dispatchSetViewLayoutSize:, -_frameOrBoundsWillChange, and -_frameOrBoundsMayHaveChanged run in response to frame/bounds changes and propagate layout size/scroll updates that depend on _page.

Client -setFrame: override / reentrancy — A WKWebView subclass overriding -setFrame: can, while chaining to super, trigger a genuine size change that reenters the geometry callbacks during the view’s own construction, before initialization has created _page.

Initialization ordering — AppKit/UIKit may deliver geometry changes as the view is being built; if a callback that assumes _page runs before _page is assigned, it dereferences null.

Vulnerability window

  1. Baseline — The three iOS geometry callbacks assume _page is valid and dereference it without checking.
  2. Trigger — A client overrides -[WKWebView setFrame:] such that a super size change occurs during initWithFrame:, reentering -_frameOrBoundsMayHaveChanged / the layout dispatch before _page is created.
  3. Crash — _page->minimumEffectiveDeviceWidth() (or equivalent) dereferences null and the app crashes (webkit.org/b/315626, rdar://177788035).
  4. Fix — if (!_page) return; guards added to all three methods; a subclassed-setFrame test verifies no crash during init (canonical 313948@main).

Proof of concept

Added as Tools/TestWebKitAPI/Tests/WebKit/WKWebView/WKWebViewSubclassedSetFrame.mm. FrameOverridingWKWebView overrides -setFrame: to call [super setFrame:CGRectZero] then [super setFrame:frame], forcing a real size change through super so -_frameOrBoundsMayHaveChanged runs. The test allocates the subclass with initWithFrame:configuration: – so the geometry callback fires during initialization, before _page is created – and asserts the view is non-null, i.e. the null-_page guards prevented the crash.

@interface FrameOverridingWKWebView : WKWebView
@end

@implementation FrameOverridingWKWebView

- (void)setFrame:(CGRect)frame
{
    // Force a size change on the WKWebView super call so that -_frameOrBoundsMayHaveChanged is taken.
    [super setFrame:CGRectZero];
    [super setFrame:frame];
}

@end

TEST(WKWebView, SubclassedSetFrameDuringInitDoesNotCrash)
{
    RetainPtr configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
    RetainPtr webView = adoptNS([[FrameOverridingWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 480) configuration:configuration.get()]);
    EXPECT_NOT_NULL(webView.get());
}

Exploitation

  1. Subclass setFrame: — An application subclasses WKWebView and overrides -setFrame: in a way that produces a size change through super during the view’s construction.
  2. Reenter during init — The size change reenters -_frameOrBoundsMayHaveChanged / -_dispatchSetViewLayoutSize: before WKWebView finishes creating _page.
  3. Null deref crash — _page is null and is dereferenced, crashing the app. This is client-code-triggered denial of service with no memory-corruption escalation shown. INFERRED impact bound.

Detection & hunting

For defenders and SOC / detection engineers:

  • Null-_page crashes in iOS geometry callbacks
  • setFrame: reentrancy during init

Audit directions

  • Other _page uses in WKWebView geometry/lifecycle
  • Reentrancy via overridable AppKit/UIKit methods
  • Initialization ordering invariants

Before / after

Loading diff…