Repository navigation
Validate runtime tensor ranks in CPU Conv, ConvInteger, QLinearConv, CropAndResize, Einsum, and AffineGrid kernels - #33517
Open
m00dy (professor-moody) wants to merge 1 commit into
Conversation
…CropAndResize, Einsum, and AffineGrid kernels
|
Azure Pipelines: There may be pipelines that require an authorized user to comment /azp run to run. |
mirounga
self-requested a review
October 21, 2026 16:55
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
The targeted guards precede unsafe accesses, preserve valid-input behavior, and have focused regression coverage with no identified blocking issues.
No open issues 🎉
What changed in this PR
This PR hardens six ONNX Runtime CPU operator paths so invalid runtime tensor ranks return errors instead of causing unsafe accesses.
Changes:
- Add rank checks and move validation before dimension indexing.
- Reject excess Einsum labels before accessing input dimensions.
- Add CPU-only regression tests with dynamic shapes.
No actionable findings remain.
| File | Description |
|---|---|
| onnxruntime/test/providers/cpu/tensor/affine_grid_test.cc | Tests scalar and trailing-zero size inputs. |
| onnxruntime/test/providers/cpu/nn/qlinearconv_op_test.cc | Tests scalar input rejection. |
| onnxruntime/test/providers/cpu/nn/conv_op_test.cc | Tests rank-1 and rank-2 rejection. |
| onnxruntime/test/providers/cpu/nn/conv_integer_test.cc | Tests invalid convolution ranks. |
| onnxruntime/test/providers/cpu/math/einsum_test.cc | Tests excess subscript labels. |
| onnxruntime/test/contrib_ops/crop_and_resize_op_test.cc | Tests invalid input ranks. |
| onnxruntime/core/providers/cpu/tensor/affine_grid.cc | Requires rank-1 size input. |
| onnxruntime/core/providers/cpu/quantization/qlinearconv.cc | Validates shapes before indexing. |
| onnxruntime/core/providers/cpu/quantization/conv_integer.cc | Validates shapes before indexing. |
| onnxruntime/core/providers/cpu/nn/conv_attributes.h | Requires convolution rank of at least three. |
| onnxruntime/core/providers/cpu/math/einsum_utils/einsum_compute_preprocessor.h | Checks label bounds before dimension access. |
| onnxruntime/contrib_ops/cpu/crop_and_resize.cc | Enforces ranks before dimension access. |
Review effort: Balanced
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
When input tensor shapes are dynamic or omitted from
ValueInfoProtoat model load time, ONNX schema shape inference does not reject rank mismatches before kernel execution. This PR adds runtime rank checks (and reorders shape validation before dimension indexing) across six CPU and contrib operator paths so invalid runtime inputs return a cleanINVALID_ARGUMENT/FAILStatusinstead of triggeringgsl::spancontract-violationstd::terminate()aborts (or reading past empty buffers when trailing dimensions are zero):ConvAttributes::ValidateInputShape(onnxruntime/core/providers/cpu/nn/conv_attributes.h):input_shape.NumDimensions() >= 3(minimum rank for 1D convolution[N, C, D1]) after verifyinginput_shape.NumDimensions() == weight_shape.NumDimensions()and before indexingweight_shape[0],input_shape[1], andweight_shape[1]. Previously, when bothXandWhad rank 0 or rank 1 at runtime (e.g.,X={3}, W={3}, group=3),input_shape.NumDimensions() != weight_shape.NumDimensions()passed andinput_shape[1]/weight_shape[1]indexed past the 1-elementgsl::span; when both had rank 2 (e.g.,X={1, 1}, W={1, 1}),ValidateInputShapesucceeded with zero spatial dimensions (kernel_rank == 0).ConvInteger::Compute(onnxruntime/core/providers/cpu/quantization/conv_integer.cc):ORT_RETURN_IF_ERROR(conv_attrs_.ValidateInputShape(X, W));before indexingX->Shape()[0],X->Shape()[1], andW->Shape()[0]. Previously, rank-0 or rank-1Xor rank-0WindexedShape()[0..1]out of bounds beforeValidateInputShapewas reached.QLinearConv<ActType>::Compute(onnxruntime/core/providers/cpu/quantization/qlinearconv.cc):ORT_RETURN_IF_ERROR(conv_attrs_.ValidateInputShape(X->Shape(), W_shape, channels_last_));before indexingX->Shape()[0]andW_shape[0].contrib::CropAndResize<T>::Compute(onnxruntime/contrib_ops/cpu/crop_and_resize.cc):x_dims.NumDimensions() == 4,rois_dims.NumDimensions() == 2, andbatch_indices_dims.NumDimensions() == 1before indexingx_dims[1],batch_indices_dims[0], androis_dims[1](which previously ran beforeCheckROIAlignValidInput).gsl::spanaborts, enforcing exact ranks before indexing ensures thatShape().Size()equals the product of the validated dimensions (x_dims[0..3],rois_dims[0..1], andbatch_indices_dims[0]), keepingrois_data,batch_indices_data, andX_datareads in range even when dynamic runtime inputs have a trailing zero dimension (such asroiswith shape{1, 4, 0}orbatch_indiceswith shape{1, 0}).EinsumComputePreprocessor::ProcessSubscripts(onnxruntime/core/providers/cpu/math/einsum_utils/einsum_compute_preprocessor.h):if (dim_counter >= rank)) ahead ofauto dim_value = dims[dim_counter];. Previously,if (++dim_counter > rank)ran at the end of the character loop body afterdims[dim_counter]had already been indexed out of bounds.AffineGrid<T>::Compute(onnxruntime/core/providers/cpu/tensor/affine_grid.cc):size_shape.NumDimensions() == 1before indexingsize_shape.GetDims()[0]and readingsize_data[0..3]/size_data[0..4]. Without this check, a rank-0sizetensor aborts on an emptyGetDims()span, and enforcing rank 1 ensuressize_shape.GetDims()[0]equals the element count ofsize_data.Tests
DefaultCpuExecutionProvider()withtest.AddShapeToTensorData(false)so dynamic runtime shapes reach the CPU kernels:ConvTest.Rank1InputAndWeightRejectedandConvTest.Rank2InputAndWeightRejected(conv_op_test.cc)ConvIntegerTest.Rank1InputRejectedandConvIntegerTest.Rank2InputAndWeightRejected(conv_integer_test.cc)QLinearConvTest.Rank0InputRejected(qlinearconv_op_test.cc)CropAndResizeTest.InvalidInputRanksRejected(crop_and_resize_op_test.cc)Einsum.SubscriptLabelsExceedInputRankRejected(einsum_test.cc)AffineGridTest.size_input_is_rank_0andAffineGridTest.size_input_rank_2_trailing_zero_rejected(affine_grid_test.cc)onnxruntime_test_all(--gtest_filter="ConvTest.*:ConvIntegerTest.*:QLinearConvTest.*:CropAndResizeTest.*:Einsum.*:AffineGridTest.*"): all 9 new regression tests fail or abort before the fix and pass (9/9) with the fix applied, and the full 302-test filter passes with zero regressions (299 PASSED,3 SKIPPED).Note: This patch was prepared with AI assistance and verified by human review and local test execution.