CVE-2025-13720
Overview
Background
- `ReportingContext`
- the Blink per-execution-context object (
third_party/blink/renderer/core/frame/reporting_context.cc) that takesReportobjects produced in the renderer and forwards them to the browser-side reporting service throughGetReportingService()->Queue*Report(...). - `ReportBody` subclasses
- each report type carries a typed body:
LocationReportBody(which haslineNumber()/columnNumber()) is the base for CSP violation and deprecation bodies, butCSPHashReportBodyandIntegrityViolationReportBodydo not derive from it, as the patch’s added comment says. - `static_cast` downcast
- an unchecked C++ cast. If the object’s real dynamic type is not the target type, member access reads the wrong layout (type confusion).
- CSP hash / Integrity Policy reports
- reports of type
ReportType::kCSPHashandReportType::kIntegrityViolation, produced when a page’s CSP asks for subresource hash reporting or when an Integrity-Policy check fails or would fail in report-only mode.
Root Cause Analysis
In the report-forwarding routine in reporting_context.cc, every report that got past the early checks was downcast with static_cast<LocationReportBody*>(report->body()). The code then read location_body->lineNumber() and location_body->columnNumber(), and only after that branched on type. The hunk does not name the enclosing function. Given the file and its callers it is likely ReportingContext::SendToReportingService (inferred). Reports of type kCSPHash and kIntegrityViolation also went through that cast, even though their bodies (CSPHashReportBody, IntegrityViolationReportBody) are not LocationReportBody objects. This is a bad cast, and the line/column accessors read fields of the wrong object type.
The fix moves the KURL url computation up and handles kCSPHash and kIntegrityViolation first, each casting to its correct body type and returning, before the LocationReportBody downcast. Only location-bearing report types now reach that cast. The commit lists three bug ids (455600228, 438514989, 457818670) and is an M138-LTS cherry-pick.
LocationReportBody before dispatching on the report type, which assumed all report bodies share that base. The fix dispatches the two non-location types first and returns early, so they are never cast to the wrong class.Attack Path
- Generate a CSP hash or integrity report
a web page sets up a Content Security Policy with hash reporting, or an Integrity-Policy with a reporting endpoint, and loads a subresource that produces a
kCSPHashorkIntegrityViolationreport in the renderer. - Report forwarded to the reporting service
ReportingContextprocesses the report for an endpoint and reaches the shared forwarding code. - Bad downcast
before the fix,
report->body()(really aCSPHashReportBodyorIntegrityViolationReportBody) wasstatic_casttoLocationReportBody, andlineNumber()/columnNumber()were read from it. - Type-confused read those accessors read memory laid out for a different class. The result is reading unrelated fields as optional integers, or possibly reading past the object (inferred), before the correct type-specific branch runs.
Impact Assessment
lineNumber() / columnNumber() through the wrong type. Any memory disclosure or corruption beyond that is (inferred), and any exploit would still be confined to the sandboxed renderer (inferred). The vendor rates it medium severity (“Bad cast in Loader”), and the advisory data given does not report exploitation in the wild.Changed Functions
| Function | Change | Notes |
|---|---|---|
ifthird_party/blink/renderer/core/frame/reporting_context.cc |
modified |
Files Changed
third_party/blink/renderer/core/frame/reporting_context.cc
Audit Directions
- Unconditional downcasts before type dispatchlook in Blink for
static_cast<Derived*>(base)that runs before aswitch/ifon a type tag, especially where new subclasses were added later (newReportBodytypes, new event types, newScriptWrappablebodies). - Class hierarchy driftwhen a new report or body type is added, check that every consumer which assumes a common base (
LocationReportBody) still holds. PreferDynamicTo<>/IsA<>helpers orCHECKed downcasts. - Other report consumersreview
ReportingObserver, serialization (toJSON) and browser-side report conversion for similar assumptions about body type.
Patch
From 53e1827796c74e99d6a49c6e4cd1024613454f35 Mon Sep 17 00:00:00 2001
From: Yoav Weiss <yoavweiss@chromium.org>
Date: Wed, 17 Dec 2025 12:48:35 -0800
Subject: [PATCH] [M138-LTS] Avoid downcasting Hash and Integrity reports
(cherry picked from commit a31cbeed5522815651f4b620ffb63f004f3e9dd0)
Bug: 455600228, 438514989, 457818670
Change-Id: I7ea47b5cbf9d43c8997a29f2e47e1394db7bac5c
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7122839
Reviewed-by: Antonio Sartori <antoniosartori@chromium.org>
Commit-Queue: Yoav Weiss (@Shopify) <yoavweiss@chromium.org>
Reviewed-by: Noam Rosenthal <nrosenthal@google.com>
Cr-Original-Commit-Position: refs/heads/main@{#1540562}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7139580
Reviewed-by: Andrew Williams <awillia@chromium.org>
Reviewed-by: Yoav Weiss (@Shopify) <yoavweiss@chromium.org>
Cr-Commit-Position: refs/branch-heads/7204@{#3465}
Cr-Branched-From: d5de512dc9dc8ddfe4e6d71b0637578bb6158683-refs/heads/main@{#1465706}
---
diff --git a/third_party/blink/renderer/core/frame/reporting_context.cc b/third_party/blink/renderer/core/frame/reporting_context.cc
index 4a121f1..1ef0689 100644
--- a/third_party/blink/renderer/core/frame/reporting_context.cc
+++ b/third_party/blink/renderer/core/frame/reporting_context.cc
@@ -195,11 +195,28 @@
return;
}
+ KURL url = KURL(report->url());
+ // CSP Hash and IntegrityPolicy reports are not a LocationReportBody.
+ if (type == ReportType::kCSPHash) {
+ const CSPHashReportBody* body =
+ static_cast<CSPHashReportBody*>(report->body());
+ GetReportingService()->QueueCSPHashReport(
+ url, endpoint, body->subresourceURL(), body->hash(), body->type(),
+ body->destination());
+ return;
+ } else if (type == ReportType::kIntegrityViolation) {
+ const IntegrityViolationReportBody* body =
+ static_cast<IntegrityViolationReportBody*>(report->body());
+ GetReportingService()->QueueIntegrityViolationReport(
+ url, endpoint, body->documentURL(), body->blockedURL(),
+ body->destination(), body->reportOnly());
+ return;
+ }
+
const LocationReportBody* location_body =
static_cast<LocationReportBody*>(report->body());
int line_number = location_body->lineNumber().value_or(0);
int column_number = location_body->columnNumber().value_or(0);
- KURL url = KURL(report->url());
if (type == ReportType::kCSPViolation) {
// Send the CSP violation report.
@@ -212,12 +229,6 @@
body->originalPolicy() ? body->originalPolicy() : "",
body->sourceFile(), body->sample(), body->disposition().AsString(),
body->statusCode(), line_number, column_number);
- } else if (type == ReportType::kCSPHash) {
- const CSPHashReportBody* body =
- static_cast<CSPHashReportBody*>(report->body());
- GetReportingService()->QueueCSPHashReport(
- url, endpoint, body->subresourceURL(), body->hash(), body->type(),
- body->destination());
} else if (type == ReportType::kDeprecation) {
// Send the deprecation report.
const DeprecationReportBody* body =
@@ -226,12 +237,6 @@
url, body->id(), body->AnticipatedRemoval(),
body->message().IsNull() ? g_empty_string : body->message(),
body->sourceFile(), line_number, column_number);
- } else if (type == ReportType::kIntegrityViolation) {
- const IntegrityViolationReportBody* body =
- static_cast<IntegrityViolationReportBody*>(report->body());
- GetReportingService()->QueueIntegrityViolationReport(
- url, endpoint, body->documentURL(), body->blockedURL(),
- body->destination(), body->reportOnly());
} else if (type == ReportType::kPermissionsPolicyViolation) {
// Send the permissions policy violation report.
const PermissionsPolicyViolationReportBody* body =