← Chrome — 2026-W03

c1860bfe0f359ef83374829ca4d39eef1e64504c  Fix float-cast-overflow UBSAN issues in media_query_evaluator.cc

severity low class undefined-behavior/float-cast-overflow CVSS 5.0 confidence 0.55 signal score 5.8 third_party/blink/renderer AI review CONFIRMED
Primitive: OrientationMediaFeatureEval cast viewport width/height through int, overflowing on extreme values (UBSAN float-cast-overflow). Fix uses doubles.
Triage note: Float-cast-overflow UB in CSS media-query evaluation, reachable from web content. Sanitizer-class UB with no confirmed corruption, consistent with the d7d097 UB precedent. watch.

The bug at a glance

This is a pure undefined-behavior hardening fix with no demonstrated security impact on shipping builds. The pre-fix code cast a floating-point viewport width/height into a plain int; when the source floating value exceeds INT_MAX (or is below INT_MIN, inf, or NaN), the float->int conversion is undefined behavior in C++ and is flagged by UBSAN’s float-cast-overflow check. On a normal optimizing compiler the result is an implementation-defined garbage integer, not a memory-safety fault: the ints are only compared (width > height) inside a media-query orientation evaluation, so the worst realistic consequence is a wrong orientation answer for an absurd viewport, or a UBSAN abort in a sanitizer build. There is no out-of-bounds access, no allocation sized by these values, and no attacker-controlled write. It is honestly watch-tier: real UB removed from a web-reachable path, but with essentially no exploitation potential. cvss is left null because the vendor-supplied 5.0 overstates a sanitizer-only correctness fix; there is no credible confidentiality/integrity/availability impact on release channels to score.

Media-query evaluation runs on every page for every matchMedia() / @media orientation query and consumes viewport metrics that can be pushed to extreme magnitudes via zoom, device-pixel-ratio, iframe sizing, or emulated/print media values. The angle is a well-known C++ footgun: implicitly narrowing a float/double viewport dimension to int at the point of comparison. It is the kind of latent UB that fuzzers and UBSAN builds surface constantly and that occasionally underpins a more serious bug when the truncated value later indexes or sizes something – here it does not, so it is a clean defensive fix rather than a vuln.

Root cause

OrientationMediaFeatureEval() decides portrait vs landscape by comparing viewport width and height. media_values.Width() and Height() return floating-point viewport dimensions (optional). The old code dereferenced them and forced the result into ‘int width’ / ‘int height’. For ordinary viewports this is harmless, but the values are ultimately derived from attacker-influenceable inputs (CSS/zoom/DPR/emulation) and can be made to exceed the representable range of int. A float/double value >= 2^31 (or inf/NaN) converted to int is undefined behavior per the C++ standard; UBSAN’s float-cast-overflow instrumentation traps it. The fix simply changes the locals to ‘double width’ / ‘double height’, keeping the values in their native floating-point domain so no lossy, potentially-overflowing narrowing occurs. The subsequent comparison (width > height for portrait/landscape) works identically for all sane viewports and is now well-defined for extreme ones.

Key code

Keep viewport dimensions as double instead of narrowing to int, eliminating the float-cast-overflow UB.

 static bool OrientationMediaFeatureEval(const MediaQueryExpValue& value,
                                         MediaQueryOperator,
                                         const MediaValues& media_values) {
-  int width = *media_values.Width();
-  int height = *media_values.Height();
+  double width = *media_values.Width();
+  double height = *media_values.Height();
 
   if (value.IsId()) {
     if (width > height) {  // Square viewport is portrait.

Patch walkthrough

  • third_party/blink/renderer/core/css/media_query_evaluator.cc (OrientationMediaFeatureEval) — The only change: the two local variables holding viewport width and height are changed from int to double. media_values.Width()/Height() already return floating-point values, so this removes the implicit float->int narrowing entirely. The downstream logic only compares width vs height to classify portrait/landscape, so double comparison is semantically identical for all realistic inputs and is now defined for out-of-int-range magnitudes, inf and NaN. No test was added; the fix is a correctness/UBSAN cleanup validated by existing sanitizer coverage (Fixed: 440464151).

Background

float-cast-overflow (UBSAN) — A UndefinedBehaviorSanitizer check that fires when a floating-point value is converted to an integer type but does not fit in that type’s range (including inf and NaN). The C++ standard makes such a conversion undefined behavior; UBSAN builds abort, while release builds produce an implementation-defined value. It is a common finding in web-reachable numeric code.

MediaValues::Width()/Height() — Accessors returning the current viewport dimensions used by media-query evaluation. They yield floating-point values (optional) reflecting CSS pixels after zoom/DPR, which can be driven to large magnitudes and were being truncated to int at the comparison site.

orientation media feature — The CSS @media (orientation: portrait|landscape) feature; Blink evaluates it by comparing viewport width and height (square counts as portrait). It runs for every matching media query on a page, making it a broadly reachable, low-severity surface.

Vulnerability window

  1. discovery — Filed as crbug 440464151 (Fixed: tag). The shape – a float-cast-overflow in orientation evaluation on extreme viewport values – is a classic UBSAN/fuzzer finding rather than a reported attack.
  2. fix — Change-Id Ia6799cc…, authored by Philip Rogers, reviewed by Anders Hartvoll Ruud, landed 2026-01-13 at r1568335. One-line rationale: ‘This code can just use doubles and avoid a float cast overflow.’

Proof of concept

No test or exploit shipped with the commit. A UBSAN abort can be reproduced conceptually in a sanitizer build by forcing the viewport width or height above INT_MAX (or to inf/NaN) and evaluating an @media (orientation: portrait) / matchMedia(’(orientation: landscape)’) query – e.g. via extreme zoom, device metrics emulation, or print/emulated MediaValues – so that *media_values.Width() no longer fits in int. On a release build this produces no observable memory-safety effect (at most a garbage orientation result), so no meaningful PoC exists and no exploit is demonstrated or plausible from this diff.

Exploitation

  1. reachability — Any page can invoke orientation media-query evaluation via @media rules or matchMedia(); viewport dimensions can be pushed toward extreme values through zoom/DPR/emulation.
  2. impact — In a UBSAN build, an out-of-int-range value aborts the process. In release builds, undefined behavior yields an implementation-defined int used only for a >/comparison, so at most an incorrect orientation classification. No memory corruption, no controllable primitive; not exploitable for code execution.

Detection & hunting

For defenders and detection engineers:

  • UBSAN float-cast-overflow reports — Sanitizer/fuzzer aborts referencing media_query_evaluator.cc OrientationMediaFeatureEval or float->int conversion in CSS media evaluation.
  • Anomalous viewport metrics — Telemetry/tests exercising extreme zoom, DPR, or emulated device metrics that drive viewport dimensions beyond int range while orientation queries are active.

Audit directions

  • Other media-feature evaluators — Audit sibling MediaFeatureEval functions in media_query_evaluator.cc (resolution, aspect-ratio, width/height ranges, device- features) for the same float/double -> int narrowing pattern on viewport-derived values.
  • Float-to-int narrowing on viewport metrics — Grep Blink layout/CSS code for ‘int x = *media_values.’ and similar implicit narrowings of floating viewport/zoom values; prioritize any case where the narrowed value later sizes an allocation or indexes a container, which would elevate severity beyond this benign comparison-only case.

Before / after

Loading diff…