Medium watch CVSS 6.5 chrome Input Validation 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
6.5
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient validation of untrusted input in WebML
DescriptionInsufficient validation of untrusted input in WebML
ComponentWebML
Sub-componentWebNN
Bug ClassInput Validation
Action tierwatch — dos-class only — no demonstrated corruption path
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.
Tracker485203823
Fix commitbf5c0c7bfac1 (chromium/src) +14/-1
CWECWE-20 (Improper input validation)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N
CISA KEVNot listed
CreditedBryan Bernhart
Reported2026-02-17 (per vendor advisory)
Fix landed2026-02-20 (public git)
Disclosed2026-04-07
Patch gap46 days — fix public in source before the advisory

Background

WebNN (`services/webnn`)
the Web Neural Network service that builds and runs ML graphs on behalf of renderer-supplied mojom operation descriptions.
ORT backend / EP
the ONNX Runtime backend; an Execution Provider (EP) is a hardware-specific implementation (e.g. CPU, DirectML) that consumes the graph nodes WebNN emits.
`reverse` operation / `axes`
a WebNN operator that reverses a tensor along the dimensions listed in axes; an empty axes is spec-defined as a no-op.
Slice vs. Identity node
the ORT graph builder previously lowered reverse to a Slice node; an Identity node simply copies its input to its output.

Root Cause Analysis

GraphBuilderOrt::AddReverseOperation unconditionally lowered a reverse operator into ORT Slice-node machinery even when reverse.axes was empty. Emitting a Slice node with zero axes is a degenerate construction that, per the commit message, certain EPs mishandle, leading to backend-specific crashes and potential out-of-bounds access during graph initialization. The invariant violated is that every emitted node must be well-formed for the backend, which a zero-axis Slice is not.

The fix adds an early branch: when reverse.axes.empty(), it builds a spec-compliant no-op Identity node (named via kInserted/kOpTypeIdentity/kToEmulate) and returns before any axis-based Slice code runs. This keeps the empty-axes case entirely out of the vulnerable path while preserving correct behavior.

Key insight
The single mistake was handing an empty axes array to Slice-node generation, producing a zero-axis node the ORT EPs mishandle; the fix detects the empty case up front and emits a harmless Identity node instead, returning before the Slice path.

Attack Path

  1. Reach WebNN Web content with access to the WebNN API (ORT backend) constructs a graph containing a reverse operation.
  2. Send empty axes The renderer supplies the reverse op with an empty axes array over mojom to the WebNN service.
  3. Degenerate node emitted The pre-patch GraphBuilderOrt::AddReverseOperation generates a Slice node with zero axes.
  4. Backend mishandling During graph initialization an EP mishandles the empty-axis node, causing a crash and potential out-of-bounds access (inferred; the specific OOB sink is backend-dependent and not in the diff).

Impact Assessment

An attacker able to drive the WebNN ORT backend from the renderer can submit a reverse op with empty axes to crash the graph build and potentially trigger out-of-bounds access during initialization in the service process. Reachability depends on WebNN being available on the platform/configuration (the ORT backend is not universally default-on), so this is not plainly reachable by all stock-build web content. The vendor advisory rates it medium (insufficient validation of untrusted input) and does not report in-the-wild exploitation; the OOB consequence beyond a crash is (inferred).

Files Changed

  • services/webnn/ort/graph_builder_ort.cc

Audit Directions

  • Empty collections into backend nodes
    audit other WebNN/ORT operator builders for operators that accept empty axes/dims/index arrays and pass them straight into node construction without a degenerate-case guard.
  • Spec no-ops lowered to real ops
    wherever a spec-defined no-op is implemented by emitting a backend operator, verify the no-op is special-cased rather than producing a malformed node.
  • Backend-specific validation gaps
    review assumptions that the renderer-side validation suffices; EPs with differing requirements may need the graph builder to normalize or reject edge-case operands.
From bf5c0c7bfac1e6fde12d7c903c88c2632d8aa8fe Mon Sep 17 00:00:00 2001
From: Bryan Bernhart <bryan.bernhart@intel.com>
Date: Fri, 20 Feb 2026 13:26:41 -0800
Subject: [PATCH] WebNN: handle empty axes in ORT reverse operation

Previously, the ORT graph builder would attempt to generate a Slice node
with zero axes, which could lead to backend-specific crashes in certain
EPs. This CL ensures that empty axes are handled by emitting an Identity
node instead, maintaining spec compliance and preventing potential OOB
access during graph initialization.

Bug: 485203823
Change-Id: I80b310cb3609beb7907ab8ffbc823af1c6c74a90
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7595579
Reviewed-by: Reilly Grant <reillyg@chromium.org>
Commit-Queue: Bernhart, Bryan <bryan.bernhart@intel.com>
Cr-Commit-Position: refs/heads/main@{#1588047}
---

diff --git a/services/webnn/ort/graph_builder_ort.cc b/services/webnn/ort/graph_builder_ort.cc
index 4126bbe..131a0046 100644
--- a/services/webnn/ort/graph_builder_ort.cc
+++ b/services/webnn/ort/graph_builder_ort.cc
@@ -2781,13 +2781,26 @@
 }
 
 void GraphBuilderOrt::AddReverseOperation(const mojom::Reverse& reverse) {
-  const std::string node_name = GenerateNodeName(reverse.label);
   const std::string input = GetOperandNameById(reverse.input_operand_id);
   const std::string output = GetOperandNameById(reverse.output_operand_id);
 
   CHECK(context_properties_.data_type_limits.reverse_input.Supports(
       GetOperand(reverse.input_operand_id).descriptor));
 
+  // Workaround: explicitly empty axes for a reverse operation should result in
+  // a no-op per spec. But we map this to an Identity node to prevent ORT
+  // EPs from mishandling empty arrays.
+  if (reverse.axes.empty()) {
+    const std::string node_name = GenerateNodeName(base::JoinString(
+        {kInserted, kOpTypeIdentity, kToEmulate, reverse.label}, kUnderscore));
+    std::array<const char*, 1> inputs = {input.c_str()};
+    std::array<const char*, 1> outputs = {output.c_str()};
+    model_editor_.AddNode(kOpTypeIdentity, node_name, inputs, outputs);
+    return;
+  }
+
+  const std::string node_name = GenerateNodeName(reverse.label);
+
   // Axes can be empty, which means no dimensions are reversed.
   base::FixedArray<int64_t> axes(reverse.axes.begin(), reverse.axes.end());
   size_t axes_size = axes.size();
Loading diff…

Original Bug Report

reported by br...@intel.com

[ORT] GPU process AV during reverse with empty axes

Latest stable release of WinML (OpenVINO EP 1.8 / OV 2025.4) contains a deterministic GPU process crash when executing a WebNN reverse operation with an empty axes array (options.axes = []).

Reproduction WPT:

https://wpt.live/webnn/conformance_tests/reverse.https.any.html?device=gpu&tc=reverse%20float32%204D%20input%20options.axes=%5b%5d

The crash results into a STATUS_ACCESS_VIOLATION due to an unchecked iterator dereference. OpenVINO attempts to find the max. axis index to calculate the required buffer length: length = *std::max_element(axis.begin(), axis.end()). When axis is empty, std::max_element returns the end() iterator. The subsequent dereference (*) attempts to read invalid memory, triggering an immediate hardware-level fault.

The issue is verified as fixed in OpenVINO nightly (due for OV 2026.1), but the fix has not been backported to the stable OV EP release or delivered to Chromium.

View on issue tracker