CVE-2026-11063
Overview
Files Changed
services/webnn/webnn_graph_builder_impl.cc
Patch
From 27d53b3a9c4772b0bab0ff99ee3e31db916c93a0 Mon Sep 17 00:00:00 2001 From: Shiyi Zou <[email protected]> Date: Thu, 09 Apr 2026 02:03:18 -0700 Subject: [PATCH] webnn: reject slice with scalar input in mojo validation Slicing a scalar is a no-op that the blink side already handles [1], so this path should never be reached from a well-behaved renderer. [1] https://source.chromium.org/chromium/chromium/src/+/main:third_party/blink/renderer/modules/ml/webnn/ml_graph_builder.cc;l=3150 Bug: 499051067 Change-Id: I772a25de722c6ce8e189aa1d4412fc055ad590be Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7740566 Commit-Queue: Zou, Shiyi <[email protected]> Reviewed-by: Hu, Ningxin <[email protected]> Cr-Commit-Position: refs/heads/main@{#1612050} --- diff --git a/services/webnn/webnn_graph_builder_impl.cc b/services/webnn/webnn_graph_builder_impl.cc index afafdc9..bac2399 100644 --- a/services/webnn/webnn_graph_builder_impl.cc +++ b/services/webnn/webnn_graph_builder_impl.cc @@ -2358,6 +2358,10 @@ // The slice operator is invalid. return false; } + if (input->descriptor.Rank() == 0) { + // Slicing a scalar is a no-op that the blink side has handled. + return false; + } const base::expected<OperandDescriptor, std::string> validated_output = ValidateSliceAndInferOutput(*context_properties_, input->descriptor,
Original Bug Report
Potential OOB access in GPU process via rank-0 Slice in WebNN ORT backend
Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports without the security team.
Overview: A compromised renderer can bypass Blink’s validation and send a rank-0 Slice operation with empty parameters to the GPU process. The GPU validation fails to enforce a non-scalar rank constraint, allowing zero-element arrays to be passed to the ONNX Runtime backend, potentially leading to an out-of-bounds access.
Affected files:
services/webnn/ort/graph_builder_ort.ccservices/webnn/webnn_context_impl.ccservices/webnn/webnn_graph_builder_impl.ccservices/webnn/ort/graph_impl_ort.cc
Estimated timestamp from git blame: 2025-07-02
Description
In WebNN, the Slice operation is expected to operate on tensors and slice along defined dimensions. In the Blink renderer (MLGraphBuilder::slice), if a rank-0 (scalar) input is provided with empty dimensions, it is safely converted to an Identity operation. However, a compromised renderer can bypass this and send a raw mojom::Slice operation to the GPU process with a rank-0 input and an empty ranges array.
The GPU-side validation in ValidateSliceAndInferOutput (services/webnn/public/cpp/graph_validation_utils.cc) attempts to validate the operation. It checks if the backend supports the input data type and rank via context_properties.data_type_limits.slice_input. In the ONNX Runtime (ORT) backend (services/webnn/ort/context_impl_ort.cc), slice_input allows up to kMaxRank (which includes 0). Unlike other operations, WebNNContextImpl::IntersectWithBaseProperties does not restrict slice_input to kNonScalarMaxRank.
Because the input rank is 0, length checks like attributes.starts.size() != input_rank evaluate to 0 != 0 (False). Furthermore, the bounds validation loop (for (uint32_t i = 0; i < input_rank; ++i)) is entirely skipped. The GPU process therefore deems the malformed scalar Slice valid.
The operation is then dispatched to GraphBuilderOrt::AddSliceOperation (services/webnn/ort/graph_builder_ort.cc), which initializes starts, ends, steps, and axes based on the empty ranges array. This results in the creation of ONNX 1D initializer tensors with a shape of [0]. These empty tensors are added to the ONNX model as the Slice parameters.
When the ONNX model is initialized in the ORT Execution Provider (e.g., DirectML), the underlying engine is forced to parse a Slice node parameterized by zero-length arrays. As previously documented by Chrome developers in the same file (see AddReverseOperation workaround: “we map this to an Identity node to prevent ORT EPs from mishandling empty arrays”), ORT Execution Providers mishandle empty configuration arrays. This can lead to the Execution Provider assuming at least one dimension is present, performing unchecked memory operations, and resulting in an out-of-bounds read or write within the highly privileged GPU process.
Potential Steps to Reproduce
- From a compromised renderer, instantiate a WebNN graph builder.
- Create a rank-0 scalar input operand.
- Construct a
mojom::Sliceoperation targeting the rank-0 operand, providing an explicitly emptyrangesarray. - Send the
CreateGraphIPC message to the GPU process. - The GPU process validates the operation, forwards the empty arrays to the ORT backend, and potentially triggers an OOB access during graph initialization or execution.
Suggested Fix
In services/webnn/webnn_context_impl.cc, update IntersectWithBaseProperties to correctly restrict slice_input to non-scalar ranks by intersecting it with kNonScalarMaxRank. Alternatively, explicitly reject rank-0 inputs or explicitly handle empty range arrays in ValidateSliceAndInferOutput.
Evaluated with Chrome root at commit: ff3d2b74fa39431785bd60e51463b08fcc71ee33
Results so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.