Branch: refs/heads/main
Home: https://github.com/WebKit/WebKit
Commit: e75354878ca37205ff0f69e9e8dedefd20d8c479
https://github.com/WebKit/WebKit/commit/e75354878ca37205ff0f69e9e8dedefd20d8c479
Author: Chris Dumez <[email protected]>
Date: 2026-07-01 (Wed, 01 Jul 2026)
Changed paths:
A
LayoutTests/navigation-api/navigation-navigate-no-arguments-crash-expected.txt
A LayoutTests/navigation-api/navigation-navigate-no-arguments-crash.html
M Source/WebCore/bindings/js/JSDOMPromiseDeferred.h
Log Message:
-----------
Type confusion via raw Exception cell returned to script from
callPromisePairFunction when throwVMError bypasses sentinel check
https://bugs.webkit.org/show_bug.cgi?id=314944
rdar://177031898
Reviewed by Ryosuke Niwa.
callPromisePairFunction's sentinel check `!JSValue::decode(result)` only
catches the
zero empty-value sentinel returned by IDL argument conversion failures. When a
[ReturnsPromisePair] operation is
called with fewer than the mandatory number of
arguments, the generated bindings hit the missing-argument check emitted by
CodeGeneratorJS.pm and `return throwVMError(...)`, which encodes a non-zero
JSC::Exception* cell rather than the empty sentinel. After
rejectPromisesWithExceptionIfAny clears the pending exception, the sentinel
check
evaluates false and the raw Exception cell is returned to JavaScript.
The Exception cell has JSType=CellType (0), not JSObject. A subsequent property
store such as `victim.foo = 0xdead` goes through JSCell::putInline ->
overridesPut() == false -> asObject(this) -> jsCast<JSObject*>. On production
ARM64 builds, ASSERT_WITH_SECURITY_IMPLICATION compiles to ((void)0), so the
cast
is a bare static_cast — type confusion. The resulting put allocates a Butterfly,
clobbers Exception::m_value (offset +0x08) with the Butterfly pointer, and
writes the attacker-controlled value into property storage. The next GC then
crashes in
Exception::visitChildrenImpl when visitor.append(m_value) treats the
Butterfly as a JSCell and SlotVisitor::drain dereferences an invalid
StructureID.
This affects all [ReturnsPromisePair] IDL operations with mandatory arguments,
notably Navigation.navigate() / reload() / traverseTo() / back() / forward().
Fix callPromisePairFunction to capture catchScope.exception() before
rejectPromisesWithExceptionIfAny clears it. If the functor threw, always rebuild
a valid result dictionary from the (now rejected) promises via
convertDictionaryToJS rather than returning the functor's return value. This
covers throwVMError, any future throw paths inside the functor, and the existing
empty-sentinel case from IDL argument conversion failures.
The non-pair callPromiseFunction is unaffected: it already discards the
functor's
return value.
Test: navigation-api/navigation-navigate-no-arguments-crash.html
*
LayoutTests/navigation-api/navigation-navigate-no-arguments-crash-expected.txt:
Added.
* LayoutTests/navigation-api/navigation-navigate-no-arguments-crash.html: Added.
* Source/WebCore/bindings/js/JSDOMPromiseDeferred.h:
(WebCore::callPromisePairFunction):
Originally-landed-as: 305413.921@safari-7624-branch (5977997682c7).
rdar://181074612
Canonical link:
https://flagged.apple.com:443/proxy?t2=Ds7i9N9rG8&o=aHR0cHM6Ly9jb21taXRzLndlYmtpdC5vcmcvMzE2MzMyQG1haW4=&emid=631f45f9-efc0-461f-9f2a-856601777998&c=11
To unsubscribe from these emails, change your notification settings at
https://github.com/WebKit/WebKit/settings/notifications