CVE-2026-5915
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
switchservices/webnn/public/cpp/graph_validation_utils.cc |
modified | |
ifservices/webnn/public/cpp/graph_validation_utils.cc |
modified |
Files Changed
services/webnn/public/cpp/graph_validation_utils.ccthird_party/blink/web_tests/external/wpt/webnn/validation_tests/conv2d.https.any.js
Patch
From 63c83256973f29cfe0ddb2d408267b2c2da905be Mon Sep 17 00:00:00 2001
From: Phillis Tang <phillis@chromium.org>
Date: Fri, 20 Mar 2026 19:15:48 -0700
Subject: [PATCH] webnn: validate intermediate padded tensor
Validate intermediate padded tensor descriptor for conv2d and pooling.
Bug: 494341335
Change-Id: I2875b72a6f9c6fd5bc92692b8eeea9d3b9ea0176
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7689396
Reviewed-by: Phillis Tang <phillis@chromium.org>
Reviewed-by: Reilly Grant <reillyg@chromium.org>
Commit-Queue: Phillis Tang <phillis@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1602955}
---
diff --git a/services/webnn/public/cpp/graph_validation_utils.cc b/services/webnn/public/cpp/graph_validation_utils.cc
index 0b685a9..1c4529a0 100644
--- a/services/webnn/public/cpp/graph_validation_utils.cc
+++ b/services/webnn/public/cpp/graph_validation_utils.cc
@@ -32,6 +32,13 @@
namespace {
+struct Conv2dInputOutputInfo {
+ uint32_t batches;
+ uint32_t channels;
+ uint32_t height;
+ uint32_t width;
+};
+
// The error message labels for corresponding operands.
static constexpr char kBiasParam[] = "bias";
static constexpr char kCellStateParam[] = "cellState";
@@ -57,6 +64,51 @@
static constexpr char kWeightParam[] = "weight";
static constexpr char kZeroPointParam[] = "zeroPoint";
+// Validate that the intermediate padded shape is within the limits of
+// OperandDescriptor. This is useful for convolution and pooling operations
+// that may be implemented by padding the input tensor first.
+base::expected<void, std::string> ValidateIntermediatePaddedDescriptor(
+ const ContextProperties& context_properties,
+ const OperandDescriptor& input,
+ const Padding2d& padding,
+ const InputOperandLayout& input_layout,
+ const Conv2dInputOutputInfo& input_info,
+ std::string_view label) {
+ uint32_t padded_height;
+ uint32_t padded_width;
+ if (!(base::CheckedNumeric<uint32_t>(input_info.height) +
+ padding.beginning.height + padding.ending.height)
+ .AssignIfValid(&padded_height) ||
+ !(base::CheckedNumeric<uint32_t>(input_info.width) +
+ padding.beginning.width + padding.ending.width)
+ .AssignIfValid(&padded_width)) {
+ return base::unexpected(
+ ErrorWithLabel(label, "The padded intermediate shape is too large."));
+ }
+
+ std::array<uint32_t, 4> padded_shape;
+ switch (input_layout) {
+ case InputOperandLayout::kNchw:
+ padded_shape = {input_info.batches, input_info.channels, padded_height,
+ padded_width};
+ break;
+ case InputOperandLayout::kNhwc:
+ padded_shape = {input_info.batches, padded_height, padded_width,
+ input_info.channels};
+ break;
+ }
+
+ auto padded_descriptor = OperandDescriptor::Create(
+ context_properties, input.data_type(), padded_shape, label);
+ if (!padded_descriptor.has_value()) {
+ return base::unexpected(ErrorWithLabel(
+ label, base::StrCat({"The padded intermediate operand is invalid: ",
+ padded_descriptor.error()})));
+ }
+
+ return base::ok();
+}
+
// Validate and calculate the output spatial dimensions of convTranspose2d given
// input sizes, filter sizes, padding, strides, dilations and output padding.
base::expected<Size2d<uint32_t>, std::string>
@@ -109,13 +161,6 @@
.width = output_width.value()};
}
-struct Conv2dInputOutputInfo {
- uint32_t batches;
- uint32_t channels;
- uint32_t height;
- uint32_t width;
-};
-
// Get the input info of 2-D direct and transposed convolution
// operation given input operand and attributes.
Conv2dInputOutputInfo GetConv2dInputInfo(
@@ -738,6 +783,10 @@
label, "The groups must evenly divide the output channels."));
}
+ RETURN_IF_ERROR(ValidateIntermediatePaddedDescriptor(
+ context_properties, input, attributes.padding, attributes.input_layout,
+ input_info, label));
+
// Validate and calculate output sizes.
ASSIGN_OR_RETURN(
Size2d<double> output_sizes,
@@ -844,6 +893,10 @@
}
const uint32_t output_channels = checked_output_channels.ValueOrDie();
+ RETURN_IF_ERROR(ValidateIntermediatePaddedDescriptor(
+ context_properties, input, attributes.padding, attributes.input_layout,
+ input_info, label));
+
// Validate and calculate output sizes.
uint32_t output_height, output_width;
if (attributes.output_sizes) {
@@ -2245,6 +2298,14 @@
window_width = attributes.window_dimensions->width;
}
+ RETURN_IF_ERROR(ValidateIntermediatePaddedDescriptor(
+ context_properties, input, attributes.padding, attributes.layout,
+ Conv2dInputOutputInfo{.batches = input_batches,
+ .channels = input_channels,
+ .height = input_height,
+ .width = input_width},
+ label));
+
// Reuse ValidateAndCalculateConv2dOutputSizes to calculate pool2d output
// sizes.
ASSIGN_OR_RETURN(
diff --git a/third_party/blink/web_tests/external/wpt/webnn/validation_tests/conv2d.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/validation_tests/conv2d.https.any.js
index 1da5895b..eff4972d 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/validation_tests/conv2d.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/webnn/validation_tests/conv2d.https.any.js
@@ -534,6 +534,21 @@
label: label,
},
},
+ {
+ name: '[conv2d] Throw if the intermediate padded tensor is too large.',
+ input: {dataType: 'float32', shape: [5354, 5, 1, 33]},
+ filter: {dataType: 'float32', shape: [1, 6, 1, 33]},
+ options: {
+ inputLayout: 'nhwc',
+ filterLayout: 'ohwi',
+ padding: [0, 32767, 32766, 5],
+ dilations: [4963, 1],
+ strides: [5390, 21245],
+ bias: {dataType: 'float32', shape: [1]},
+ groups: 1,
+ label: label,
+ },
+ },
];
tests.forEach(
Regression Test / PoC
diff --git a/third_party/blink/web_tests/external/wpt/webnn/validation_tests/conv2d.https.any.js b/third_party/blink/web_tests/external/wpt/webnn/validation_tests/conv2d.https.any.js
index 1da5895b..eff4972d 100644
--- a/third_party/blink/web_tests/external/wpt/webnn/validation_tests/conv2d.https.any.js
+++ b/third_party/blink/web_tests/external/wpt/webnn/validation_tests/conv2d.https.any.js
@@ -534,6 +534,21 @@
label: label,
},
},
+ {
+ name: '[conv2d] Throw if the intermediate padded tensor is too large.',
+ input: {dataType: 'float32', shape: [5354, 5, 1, 33]},
+ filter: {dataType: 'float32', shape: [1, 6, 1, 33]},
+ options: {
+ inputLayout: 'nhwc',
+ filterLayout: 'ohwi',
+ padding: [0, 32767, 32766, 5],
+ dilations: [4963, 1],
+ strides: [5390, 21245],
+ bias: {dataType: 'float32', shape: [1]},
+ groups: 1,
+ label: label,
+ },
+ },
];
tests.forEach(
Original Bug Report
Heap Buffer Overflow in TFLite Pad operator due to unhandled allocation failure
VULNERABILITY DETAILS
A heap buffer overflow exists in TFLite’s SimpleMemoryArena due to unhandled memory allocation failure. When WebNN processes a conv2d operation with large padding values, the resulting padded tensor dimensions cause XNNPACK to request a very large memory arena resize. The AlignedRealloc call fails and returns a null pointer, but ResizableAlignedBuffer::Resize does not check for this failure — it overwrites the buffer pointer with null, sets data_size_ to the requested (large) size, and reports success. Subsequently, SimpleMemoryArena::Commit marks the arena as committed despite having no backing memory, and XNNPACK proceeds to compute pad offsets and write fill data through the null/invalid pointer, causing an out-of-bounds write.
Triggering conv2d parameters:
- Input shape: [5354, 5, 1, 33] (NHWC layout)
- Filter shape: [1, 6, 1, 33] (OHWI layout)
- Bias shape: [1]
- Padding: [beginningHeight=0, endingHeight=32767, beginningWidth=32766, endingWidth=5]
- Dilations: [4963, 1]
- Strides: [5390, 21245]
- Groups: 1
- Data type: float32
The large padding values (32767, 32766) combined with the input dimensions produce a padded intermediate tensor with dimensions large enough that the total byte size exceeds addressable memory. When TFLite’s arena attempts to allocate this, AlignedRealloc fails but the failure is silently ignored.
VERSION Chrome Version: asan build of content_shell at commit f8ac9f305a5e120b7c9e511a113e0c56a27290e1 Operating System: Windows, Linux, Mac where WebNN TFLite backend is used
REPRODUCTION CASE On Windows
set ASAN_OPTIONS=allocator_may_return_null=1
out\asan\content_shell.exe --no-sandbox --enable-features=WebMachineLearningNeuralNetwork --disable-features=WebNNOnnxRuntime,WebNNDirectML --enable-logging=stderr poc.html
CREDIT INFORMATION @sh…@intel.com found this issue and created the reproducer and fix proposal.