Medium watch ✔︎ verified CVSS 8.8 chrome Logic Error 🔧 Commit mapped

Overview

Medium
Severity
8.8
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactBad cast in Loader
DescriptionBad cast in Loader
ComponentLoader
Sub-componentM138-LTS
Bug ClassLogic Error
Action tierwatch — information disclosure — security-relevant, below the RCE-class bar — revised by independent review by AI
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Independent review by AIConfirmed (tier revised from patch-now)
Reviewer notes: Claimed 'corruption' unsupported: diff shows only wrong-type reads of lineNumber()/columnNumber() (unused for the affected types) -- a bad-cast/OOB read, not demonstrated memory corruption.
A second, independent AI reviewer (not a human) re-derived this analysis from the fixing commit and the vendor advisory, looking for errors.
Tracker457818670
Fix commit53e1827796c7 (chromium/src) +18/-13
CWECWE-704 (Incorrect type conversion)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CISA KEVNot listed
CreditedChrome
Reported2025-11-04 (per vendor advisory)
Fix landed2025-12-17 (public git)
Disclosed2025-12-02

Background

`ReportingContext`
the Blink per-execution-context object (third_party/blink/renderer/core/frame/reporting_context.cc) that takes Report objects produced in the renderer and forwards them to the browser-side reporting service through GetReportingService()->Queue*Report(...).
`ReportBody` subclasses
each report type carries a typed body: LocationReportBody (which has lineNumber() / columnNumber()) is the base for CSP violation and deprecation bodies, but CSPHashReportBody and IntegrityViolationReportBody do 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::kCSPHash and ReportType::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.

Key insight
The code downcast every report body to 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

  1. 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 kCSPHash or kIntegrityViolation report in the renderer.
  2. Report forwarded to the reporting service ReportingContext processes the report for an endpoint and reaches the shared forwarding code.
  3. Bad downcast before the fix, report->body() (really a CSPHashReportBody or IntegrityViolationReportBody) was static_cast to LocationReportBody, and lineNumber() / columnNumber() were read from it.
  4. 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

This is type confusion in the Blink renderer process, triggered by report generation that a web page controls. Before the fix, ordinary web content that sets up CSP hash reporting or Integrity-Policy reporting reached the faulty cast. The patch does not show whether either feature is behind a runtime flag in the affected build. The patch shows only reads of 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

FunctionChangeNotes
if
third_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 dispatch
    look in Blink for static_cast<Derived*>(base) that runs before a switch/if on a type tag, especially where new subclasses were added later (new ReportBody types, new event types, new ScriptWrappable bodies).
  • Class hierarchy drift
    when a new report or body type is added, check that every consumer which assumes a common base (LocationReportBody) still holds. Prefer DynamicTo<> / IsA<> helpers or CHECKed downcasts.
  • Other report consumers
    review ReportingObserver, serialization (toJSON) and browser-side report conversion for similar assumptions about body type.
From 53e1827796c74e99d6a49c6e4cd1024613454f35 Mon Sep 17 00:00:00 2001
From: Yoav Weiss <[email protected]>
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 <[email protected]>
Commit-Queue: Yoav Weiss (@Shopify) <[email protected]>
Reviewed-by: Noam Rosenthal <[email protected]>
Cr-Original-Commit-Position: refs/heads/main@{#1540562}
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7139580
Reviewed-by: Andrew Williams <[email protected]>
Reviewed-by: Yoav Weiss (@Shopify) <[email protected]>
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 =
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.