Chrome · PerformanceAPIs
CVE-2026-113014
UAF in PerformanceAPIs
Overview
Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifthird_party/blink/web_tests/external/wpt/long-animation-frame/loaf-source-location-redirect.html |
modified |
Files Changed
third_party/blink/web_tests/external/wpt/long-animation-frame/loaf-source-location-redirect.htmlthird_party/blink/renderer/core/frame/animation_frame_timing_monitor.ccthird_party/blink/renderer/platform/bindings/lazy_source_location.ccthird_party/blink/renderer/platform/bindings/lazy_source_location.h
Patch
From ea6b96ccdc2b54eea1486e0b37205717cc1f097f Mon Sep 17 00:00:00 2001 From: Joone Hur <[email protected]> Date: Mon, 28 Sep 2026 18:34:05 -0700 Subject: [PATCH] Redact opaque script positions in LoAF promise attribution Redact character, line, and column positions when capturing source locations for opaque scripts. Requires a companion V8 change providing StackFrame::IsScriptOpaque(): https://chromium-review.googlesource.com/c/v8/v8/+/8422987 Bug: 562158081, 502251640 Change-Id: Ic64040f8663b26c6ace883cc47a1e405a94e5ef4 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8421802 Reviewed-by: Michal Mocny <[email protected]> Reviewed-by: Noam Rosenthal <[email protected]> Commit-Queue: Joone Hur <[email protected]> Cr-Commit-Position: refs/heads/main@{#1706573} --- diff --git a/third_party/blink/renderer/core/frame/animation_frame_timing_monitor.cc b/third_party/blink/renderer/core/frame/animation_frame_timing_monitor.cc index 43066f8..9fbdeb2 100644 --- a/third_party/blink/renderer/core/frame/animation_frame_timing_monitor.cc +++ b/third_party/blink/renderer/core/frame/animation_frame_timing_monitor.cc @@ -944,14 +944,9 @@ .class_like_name = class_like_name, .property_like_name = property_like_name, .source_location = {.url = location->Url(script_state->GetIsolate()), - .char_position = location->CharPosition()}}; - - if (RuntimeEnabledFeatures::LongAnimationFrameSourceLineColumnEnabled()) { - pending_script_info_->source_location.line_number = - location->LineNumber() + 1; - pending_script_info_->source_location.column_number = - location->ColumnNumber() + 1; - } + .char_position = location->CharPosition(), + .line_number = location->LineNumber(), + .column_number = location->ColumnNumber()}}; } void AnimationFrameTimingMonitor::Will( diff --git a/third_party/blink/renderer/platform/bindings/lazy_source_location.cc b/third_party/blink/renderer/platform/bindings/lazy_source_location.cc index 4f4212f..e17d6c1 100644 --- a/third_party/blink/renderer/platform/bindings/lazy_source_location.cc +++ b/third_party/blink/renderer/platform/bindings/lazy_source_location.cc @@ -26,10 +26,16 @@ } v8::Local<v8::StackFrame> stack_frame = stack_trace->GetFrame(isolate, 0); - int script_position = stack_frame->GetSourcePosition(); v8::Local<v8::String> script_name_v8 = stack_frame->GetScriptNameOrSourceURL(); + if (stack_frame->IsScriptOpaque()) { + return MakeGarbageCollected<LazySourceLocation>(isolate, script_name_v8, + /*char_position=*/-1); + } + + int script_position = stack_frame->GetSourcePosition(); + if (RuntimeEnabledFeatures::LongAnimationFrameSourceLineColumnEnabled()) { v8::Location location = stack_frame->GetLocation(); return MakeGarbageCollected<LazySourceLocation>( diff --git a/third_party/blink/renderer/platform/bindings/lazy_source_location.h b/third_party/blink/renderer/platform/bindings/lazy_source_location.h index 729118e2..f0625a1 100644 --- a/third_party/blink/renderer/platform/bindings/lazy_source_location.h +++ b/third_party/blink/renderer/platform/bindings/lazy_source_location.h @@ -41,16 +41,18 @@ static LazySourceLocation* FromCurrentStack(v8::Isolate* isolate); const String& Url(v8::Isolate* isolate); int CharPosition() const { return char_position_; } - int LineNumber() const { return line_number_; } - int ColumnNumber() const { return column_number_; } + int LineNumber() const { return line_number_ < 0 ? -1 : line_number_ + 1; } + int ColumnNumber() const { + return column_number_ < 0 ? -1 : column_number_ + 1; + } void Trace(Visitor* visitor) const; private: TraceWrapperV8Reference<v8::String> v8_url_; String url_; int char_position_ = -1; - int line_number_ = 0; - int column_number_ = 0; + int line_number_ = -1; + int column_number_ = -1; }; } // namespace blink diff --git a/third_party/blink/web_tests/external/wpt/long-animation-frame/loaf-source-location-redirect.html b/third_party/blink/web_tests/external/wpt/long-animation-frame/loaf-source-location-redirect.html index c1a631de..2459a69 100644 --- a/third_party/blink/web_tests/external/wpt/long-animation-frame/loaf-source-location-redirect.html +++ b/third_party/blink/web_tests/external/wpt/long-animation-frame/loaf-source-location-redirect.html @@ -63,7 +63,11 @@ assert_not_equals(result, "empty", "CORS-ok scripts should expose sourceLocation"); } else { assert_equals(result, "pre-redirect", "No-CORS classic scripts should expose pre-redirect URL"); - assert_equals(script.sourceCharPosition, type === "script-block" || type === "resolve-promise" ? 0 : -1, "No-CORS classic scripts should not expose character index"); + assert_equals(script.sourceCharPosition, type === "script-block" ? 0 : -1, "No-CORS scripts should not expose character indices"); + } + if (type === "resolve-promise") { + assert_equals(script.sourceLine, cors ? 1 : -1, "Promise source line should be one-based or redacted"); + assert_equals(script.sourceColumn, cors ? 1 : -1, "Promise source column should be one-based or redacted"); } }, `Test ${type} with ${scriptType}`); }
Loading diff…
Regression Test / PoC
shipped with the fix
diff --git a/third_party/blink/web_tests/external/wpt/long-animation-frame/loaf-source-location-redirect.html b/third_party/blink/web_tests/external/wpt/long-animation-frame/loaf-source-location-redirect.html
index c1a631de..2459a69 100644
--- a/third_party/blink/web_tests/external/wpt/long-animation-frame/loaf-source-location-redirect.html
+++ b/third_party/blink/web_tests/external/wpt/long-animation-frame/loaf-source-location-redirect.html
@@ -63,7 +63,11 @@
assert_not_equals(result, "empty", "CORS-ok scripts should expose sourceLocation");
} else {
assert_equals(result, "pre-redirect", "No-CORS classic scripts should expose pre-redirect URL");
- assert_equals(script.sourceCharPosition, type === "script-block" || type === "resolve-promise" ? 0 : -1, "No-CORS classic scripts should not expose character index");
+ assert_equals(script.sourceCharPosition, type === "script-block" ? 0 : -1, "No-CORS scripts should not expose character indices");
+ }
+ if (type === "resolve-promise") {
+ assert_equals(script.sourceLine, cors ? 1 : -1, "Promise source line should be one-based or redacted");
+ assert_equals(script.sourceColumn, cors ? 1 : -1, "Promise source column should be one-based or redacted");
}
}, `Test ${type} with ${scriptType}`);
}
Loading diff…
Original Bug Report
The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.
References
On This Page