Skip to content

Fix matmulinteger heap oob read#27965

Open
bmehta001 wants to merge 6 commits intomainfrom
fix/matmulinteger-heap-oob-read
Open

Fix matmulinteger heap oob read#27965
bmehta001 wants to merge 6 commits intomainfrom
fix/matmulinteger-heap-oob-read

Conversation

@bmehta001
Copy link
Copy Markdown
Contributor

Description

Motivation and Context

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses a heap out-of-bounds read in MatMulInteger by adding missing shape validation for the 1D×1D (vector dot-product) case, plus additional validation changes across Slice/Pad/Split implementations in native and JS Web backends.

Changes:

  • Add a missing K-dimension compatibility check for 1D×1D MatMul in MatMulComputeHelper.
  • Add regression tests for MatMulInteger 1D dimension mismatch and a valid 1D dot product.
  • Add/adjust validation in WebGPU/CUDA/CPU (and JS Web) implementations for Slice/Pad/Split inputs and computed shapes.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
onnxruntime/test/providers/cpu/math/matmul_integer_test.cc Adds regression tests for 1D MatMulInteger mismatch and valid dot product.
onnxruntime/core/providers/cpu/math/matmul_helper.h Adds missing K-dimension check for 1D×1D MatMul.
onnxruntime/core/providers/webgpu/tensor/slice.cc Adds axis range validation and new output-dimension validation.
onnxruntime/core/providers/webgpu/tensor/pad.cc Adds new output-dimension validation.
onnxruntime/core/providers/cuda/tensor/split.cc Adds validation to reject negative values in split input.
onnxruntime/core/providers/cuda/tensor/pad.cc Adds new output-dimension validation.
onnxruntime/core/providers/cpu/tensor/split.h Adds validation to reject negative values in split input.
onnxruntime/core/providers/cpu/tensor/pad.cc Adds new output-dimension validation in reshaped and final output dims.
js/web/lib/wasm/jsep/webgpu/ops/slice.ts Adds JS WebGPU Slice output-dimension validation.
js/web/lib/wasm/jsep/util.ts Fixes normalizeAxis() condition and adds padShape output-dimension validation.
js/web/lib/onnxjs/util.ts Fixes normalizeAxis() condition and adds padShape output-dimension validation.
js/web/lib/onnxjs/backends/webgl/ops/slice.ts Adds WebGL Slice output-dimension validation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +209 to +210
ORT_RETURN_IF(output_dims[axes[i]] <= 0,
"Output dimension ", axes[i], " is non-positive (", output_dims[axes[i]],
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new validation rejects output dimensions of 0 (empty slices) by checking <= 0. In ONNX, Slice can legally produce empty outputs (e.g., start == end, or a valid slice on an input with a 0 dimension), and the code already has an output_size == 0 fast-return for that case. Consider changing this check to only reject negative dimensions (e.g., < 0) or removing it, so valid empty outputs don’t error.

Suggested change
ORT_RETURN_IF(output_dims[axes[i]] <= 0,
"Output dimension ", axes[i], " is non-positive (", output_dims[axes[i]],
ORT_RETURN_IF(output_dims[axes[i]] < 0,
"Output dimension ", axes[i], " is negative (", output_dims[axes[i]],

Copilot uses AI. Check for mistakes.
Comment on lines +71 to +72
ORT_RETURN_IF(output_dims[i] <= 0,
"Output dimension ", i, " is non-positive (", output_dims[i],
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check treats output dimension 0 as an error (<= 0). Pad should allow 0-sized dimensions (empty tensors) in ONNX; only negative dimensions are invalid. Consider changing the condition to only reject negative dimensions, otherwise valid padding/slicing that results in an empty output will fail unexpectedly.

Suggested change
ORT_RETURN_IF(output_dims[i] <= 0,
"Output dimension ", i, " is non-positive (", output_dims[i],
ORT_RETURN_IF(output_dims[i] < 0,
"Output dimension ", i, " is negative (", output_dims[i],

Copilot uses AI. Check for mistakes.
Comment on lines +253 to +254
ORT_RETURN_IF(output_dims[i] <= 0,
"Output dimension ", i, " is non-positive (", output_dims[i],
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The validation rejects output dims equal to 0 by checking <= 0. ONNX shapes can contain 0 (empty tensors), and Pad can legitimately produce empty outputs; only negative output dimensions should be rejected. Consider changing the condition to only check for < 0 to avoid breaking valid models.

Suggested change
ORT_RETURN_IF(output_dims[i] <= 0,
"Output dimension ", i, " is non-positive (", output_dims[i],
ORT_RETURN_IF(output_dims[i] < 0,
"Output dimension ", i, " is negative (", output_dims[i],

Copilot uses AI. Check for mistakes.
Comment on lines +460 to +469
ORT_RETURN_IF(reshaped_output_dims[i] <= 0,
"Output dimension ", i, " is non-positive (", reshaped_output_dims[i],
"). This may occur from excessive negative padding/slicing.");
}

// Compute true output dimensions
for (size_t i = 0; i < data_rank; i++) {
output_dims[i] += SafeInt<int64_t>(pads[i]) + pads[i + data_rank] + slices[i] + slices[i + data_rank];
ORT_RETURN_IF(output_dims[i] <= 0,
"Output dimension ", i, " is non-positive (", output_dims[i],
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new check makes output dimensions of 0 an error (<= 0). CPU Pad historically supports 0-sized dimensions (empty outputs) and uses Size()==0 handling paths later; only negative dimensions are invalid. Consider changing this validation to reject only negative dimensions so valid empty outputs don’t fail.

Suggested change
ORT_RETURN_IF(reshaped_output_dims[i] <= 0,
"Output dimension ", i, " is non-positive (", reshaped_output_dims[i],
"). This may occur from excessive negative padding/slicing.");
}
// Compute true output dimensions
for (size_t i = 0; i < data_rank; i++) {
output_dims[i] += SafeInt<int64_t>(pads[i]) + pads[i + data_rank] + slices[i] + slices[i + data_rank];
ORT_RETURN_IF(output_dims[i] <= 0,
"Output dimension ", i, " is non-positive (", output_dims[i],
ORT_RETURN_IF(reshaped_output_dims[i] < 0,
"Output dimension ", i, " is negative (", reshaped_output_dims[i],
"). This may occur from excessive negative padding/slicing.");
}
// Compute true output dimensions
for (size_t i = 0; i < data_rank; i++) {
output_dims[i] += SafeInt<int64_t>(pads[i]) + pads[i + data_rank] + slices[i] + slices[i + data_rank];
ORT_RETURN_IF(output_dims[i] < 0,
"Output dimension ", i, " is negative (", output_dims[i],

Copilot uses AI. Check for mistakes.
Comment on lines +460 to +469
ORT_RETURN_IF(reshaped_output_dims[i] <= 0,
"Output dimension ", i, " is non-positive (", reshaped_output_dims[i],
"). This may occur from excessive negative padding/slicing.");
}

// Compute true output dimensions
for (size_t i = 0; i < data_rank; i++) {
output_dims[i] += SafeInt<int64_t>(pads[i]) + pads[i + data_rank] + slices[i] + slices[i + data_rank];
ORT_RETURN_IF(output_dims[i] <= 0,
"Output dimension ", i, " is non-positive (", output_dims[i],
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as above: this validation rejects output dims that become 0 (<= 0), but 0 is a valid ONNX dimension (empty tensor). Consider changing to only reject negative dimensions to avoid breaking valid padding/slicing cases that legitimately produce empty outputs.

Suggested change
ORT_RETURN_IF(reshaped_output_dims[i] <= 0,
"Output dimension ", i, " is non-positive (", reshaped_output_dims[i],
"). This may occur from excessive negative padding/slicing.");
}
// Compute true output dimensions
for (size_t i = 0; i < data_rank; i++) {
output_dims[i] += SafeInt<int64_t>(pads[i]) + pads[i + data_rank] + slices[i] + slices[i + data_rank];
ORT_RETURN_IF(output_dims[i] <= 0,
"Output dimension ", i, " is non-positive (", output_dims[i],
ORT_RETURN_IF(reshaped_output_dims[i] < 0,
"Output dimension ", i, " is negative (", reshaped_output_dims[i],
"). This may occur from excessive negative padding/slicing.");
}
// Compute true output dimensions
for (size_t i = 0; i < data_rank; i++) {
output_dims[i] += SafeInt<int64_t>(pads[i]) + pads[i + data_rank] + slices[i] + slices[i + data_rank];
ORT_RETURN_IF(output_dims[i] < 0,
"Output dimension ", i, " is negative (", output_dims[i],

Copilot uses AI. Check for mistakes.
Comment on lines +169 to +171
if (outputShape[axis] <= 0) {
throw new Error(
`Output dimension ${axis} is non-positive (${outputShape[axis]}). This may occur from excessive negative slicing.`,
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This throws when a computed Slice output dimension is 0 (<= 0). Slice can validly produce empty outputs (e.g., start === end), and ORT generally allows 0-sized dimensions. Consider only rejecting negative dimensions so empty slices don’t throw unexpectedly.

Suggested change
if (outputShape[axis] <= 0) {
throw new Error(
`Output dimension ${axis} is non-positive (${outputShape[axis]}). This may occur from excessive negative slicing.`,
if (outputShape[axis] < 0) {
throw new Error(
`Output dimension ${axis} is negative (${outputShape[axis]}). This may occur from excessive negative slicing.`,

Copilot uses AI. Check for mistakes.
Comment on lines +73 to +75
if (outputShape[normalizedAxes[i]] <= 0) {
throw new Error(
`Output dimension ${normalizedAxes[i]} is non-positive (${outputShape[normalizedAxes[i]]}). This may occur from excessive negative slicing.`,
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new validation throws when the Slice output dimension is 0 (<= 0). In ONNX it’s valid for Slice to return an empty tensor (0-sized dimension). Consider changing the check to only reject negative dimensions so valid empty slices don’t error.

Suggested change
if (outputShape[normalizedAxes[i]] <= 0) {
throw new Error(
`Output dimension ${normalizedAxes[i]} is non-positive (${outputShape[normalizedAxes[i]]}). This may occur from excessive negative slicing.`,
if (outputShape[normalizedAxes[i]] < 0) {
throw new Error(
`Output dimension ${normalizedAxes[i]} is negative (${outputShape[normalizedAxes[i]]}). This may occur from excessive negative slicing.`,

Copilot uses AI. Check for mistakes.
Comment on lines +227 to +229
if (nextDim <= 0) {
throw new Error(
`Output dimension ${i} is non-positive (${nextDim}). This may occur from excessive negative padding.`,
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

padShape() now throws if the computed dimension is 0 (<= 0). ORT/ONNX shapes can legally contain 0 (empty tensors), and padding can legitimately result in an empty dimension. Consider only rejecting negative dimensions so valid empty outputs don’t throw.

Suggested change
if (nextDim <= 0) {
throw new Error(
`Output dimension ${i} is non-positive (${nextDim}). This may occur from excessive negative padding.`,
if (nextDim < 0) {
throw new Error(
`Output dimension ${i} is negative (${nextDim}). This may occur from excessive negative padding.`,

Copilot uses AI. Check for mistakes.
Comment on lines +744 to +752
return dims.map((v, i) => {
const nextDim = v + pad[i] + pad[i + rank];
if (nextDim <= 0) {
throw new Error(
`Output dimension ${i} is non-positive (${nextDim}). This may occur from excessive negative padding.`,
);
}
return nextDim;
});
Copy link

Copilot AI Apr 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

padShape() now throws when nextDim is 0 (<= 0). ONNX allows 0-sized dimensions, and Pad may legitimately produce empty outputs. Consider changing the check to only reject negative dimensions to avoid breaking valid models that include empty tensors.

Copilot uses AI. Check for mistakes.
bmehta001 and others added 6 commits April 4, 2026 00:54
The condition 'axis < -tensorRank && axis >= tensorRank' is always
false (a number cannot simultaneously be less than -r and >= r).
Changed to '||' so out-of-range axes are properly rejected.

Affects both onnxjs and jsep utility implementations.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
After normalizing negative axes, validate that each axis is within
[0, input_rank). Without this check, out-of-range axes could cause
out-of-bounds access on the input shape array.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
When split sizes come from a runtime tensor input (not an attribute),
they were not validated for non-negative values. Crafted negative
split sizes that sum to the correct total could pass the sum check
and create tensors with negative dimensions. The attribute path
already had this validation.

Added non-negative check in both CPU PrepareForCompute and CUDA
PrepareForComputeLocal.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add checks that output dimensions are positive after computing them
from user-supplied pads/slices. Without these checks, excessive
negative padding or slicing could produce tensors with non-positive
dimensions, leading to buffer underflows or undefined behavior.

Affected operators and providers:
- CPU Pad: both reshaped and true output dimensions
- CUDA Pad: output dimensions in compute loop
- WebGPU Pad: output dimensions in compute loop
- WebGPU Slice: output dimensions after ceil computation
- JS onnxjs Slice (WebGL): output shape after ends - starts
- JS onnxjs padShape utility
- JS jsep padShape utility
- JS jsep Slice (WebGPU): output shape after ceil computation

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
MatMulComputeHelper::Compute skipped K-dimension validation when both
inputs were 1D vectors (the vector x vector dot-product case). This
allowed mismatched shapes like A=[5] and B=[1] to reach the MLAS GEMM
backend, which assumed B had K elements and read past its allocation.

Add the missing ORT_RETURN_IF_NOT check in the num_output_dims==0 branch
to reject mismatched K dimensions before dispatch. This closes the
heap-buffer-overflow (CVE-class) in MlasGemmQuantCopyPackB triggered via
MatMulInteger with malformed 1D quantized inputs.

Files changed:
- onnxruntime/core/providers/cpu/math/matmul_helper.h
- onnxruntime/test/providers/cpu/math/matmul_integer_test.cc

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The 1D input tests were running on all EPs including CUDA, which does
not support 1D quantized inputs the same way. Restrict to CPU-only
using the same pattern as other EP-specific tests in this file.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@bmehta001 bmehta001 force-pushed the fix/matmulinteger-heap-oob-read branch from e0ffdbb to f656138 Compare April 4, 2026 05:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants