Skip to content

Validate initializer data length and guard element-count computation in contrib Range shape inference#29265

Open
titaiwangms wants to merge 7 commits into
microsoft:mainfrom
titaiwangms:fix/contrib-range-input-validation
Open

Validate initializer data length and guard element-count computation in contrib Range shape inference#29265
titaiwangms wants to merge 7 commits into
microsoft:mainfrom
titaiwangms:fix/contrib-range-input-validation

Conversation

@titaiwangms

Copy link
Copy Markdown
Contributor

Summary

The com.microsoft Range operator's shape-inference helper read a fixed number of bytes from an initializer's raw_data without first checking the buffer length, and the element-count computation could pass non-finite or out-of-range values to an int64 cast. This change adds the missing validations and aligns the shape-inference and CPU kernel paths.

Changes

  • GetFirstElement now checks raw_data length is at least the element size before reading, and reads via std::memcpy into an aligned local.
  • CalcRangeDim and the CPU kernel ComputeRange now reject non-finite computed counts, handle non-positive counts before the int64 cast, and reject counts that are not representable as int64 (>= 2^63). Both paths use identical messages and semantics.
  • The output dimension for empty/backward ranges is clamped to 0 in shape inference to match the kernel.

Tests

Added contrib Range model-load regression tests in range_test.cc covering:

  • truncated raw_data for start, limit, and delta (double, plus float and int64 element types),
  • zero delta,
  • a finite-but-too-large element count,
  • an exact-size success boundary,
  • backward-range zero-dimension inference.

Tests assert on Status (safe for no-exception builds) and are guarded by #ifndef DISABLE_CONTRIB_OPS (throwing cases additionally by !defined(ORT_NO_EXCEPTIONS)). 21/21 RangeTest cases pass locally.

Follow-up

int16 inputs are currently supported only via raw_data (there is no get_data<int16_t> specialization for the non-raw-data path); this is left as a separate follow-up.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

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 hardens the com.microsoft Range operator’s shape inference and the CPU kernel against malformed initializer raw_data and invalid/overflowing element-count computations, ensuring model load and runtime execution fail cleanly instead of reading out-of-bounds or hitting undefined conversions.

Changes:

  • Added raw_data length validation + aligned reads (via memcpy) in contrib Range shape inference.
  • Added non-finite and out-of-int64_t-range guards for the computed element count in both shape inference and the CPU kernel; clamped empty/backward ranges to dimension 0.
  • Added contrib Range model-load regression tests covering truncated raw_data, zero delta, too-large counts, and backward-range inferred zero-dim.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
onnxruntime/core/graph/contrib_ops/range_schema_defs.cc Adds raw_data length checks/aligned reads and validates/clamps computed output length during shape inference.
onnxruntime/core/providers/cpu/generator/range.cc Adds runtime validation for computed element counts (finite + representable in int64_t) and clamps empty/backward ranges.
onnxruntime/test/providers/cpu/generator/range_test.cc Adds model-load regression tests for contrib Range shape inference failure/success boundary cases.

Comment thread onnxruntime/core/providers/cpu/generator/range.cc Outdated
Comment thread onnxruntime/test/providers/cpu/generator/range_test.cc
titaiwangms added a commit to titaiwangms/onnxruntime that referenced this pull request Jun 25, 2026
…e kernel guards

Address automated review feedback on PR microsoft#29265:
- range.cc ComputeRange now computes the element count as
  ceil((double(limit) - double(start)) / double(delta)), byte-identical to the
  shape-inference path in CalcRangeDim, so integral inputs are promoted to double
  before the subtraction and cannot overflow in T.
- range_schema_defs.cc CalcRangeDim uses the same expression; comments on both
  sites note they must stay identical.
- Added two CPU-EP-pinned kernel-execution tests with runtime (non-constant)
  inputs so the ComputeRange element-count guards are exercised at execution
  time: a count that exceeds the int64 range and a non-finite count.

Agent-signed-off: Developer (b2fe149b) [claude-opus-4.8 via copilot]
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
titaiwangms and others added 4 commits June 30, 2026 20:46
…e inference

GetFirstElement<T> read sizeof(T) bytes from a graph initializer's raw_data guarded only by the presence bit. Add a length check before the cast and fail shape inference cleanly when raw_data is shorter than the element size. Also clamp the computed element count to >= 0 in CalcRangeDim so shape inference matches the CPU kernel for empty/backward ranges. Add model-load regression tests in range_test.cc (guarded by #ifndef DISABLE_CONTRIB_OPS, Status-only assertions for no-exception builds).

Agent-signed-off: Developer (b2fe149b) [claude-opus-4.8 via copilot]
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- GetFirstElement<T>: read raw_data via std::memcpy into an aligned local after the
  length check, avoiding an unaligned access on strict-alignment targets.
- CalcRangeDim<T> and the CPU kernel ComputeRange: guard the element-count cast with
  std::isfinite so a non-finite computed count fails cleanly instead of an out-of-range
  cast (fail_shape_inference in the schema, a non-OK Status in the kernel).
- range_test.cc: generalize the model-building helper over element type, dimensions and
  per-input bytes. Truncated raw_data is now modeled as dims=[0] with empty raw_data so
  the cases are tight regressions for the length check; add coverage for start/limit/delta
  positions, float and int64 element types, a zero-delta failure, and an exact-size success
  boundary. Throwing tests are additionally guarded by !defined(ORT_NO_EXCEPTIONS).

Agent-signed-off: Developer (b2fe149b) [claude-opus-4.8 via copilot]
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…cast

The std::isfinite guard catches inf/NaN but not a finite count larger than the
int64 range, where static_cast<int64_t>(count) is out of range. Handle the
non-positive case before the cast (so a large-magnitude negative count cannot
overflow it) and reject counts at or above 2^63 with a neutral message. Applied
identically in the schema CalcRangeDim (fail_shape_inference) and the CPU kernel
ComputeRange (non-OK Status), keeping the two sites consistent.

Add a guarded regression test (start=0, limit=1e19, delta=1) asserting a clean
non-OK status; verified it fails without the new guard.

Agent-signed-off: Developer (b2fe149b) [claude-opus-4.8 via copilot]
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…e kernel guards

Address automated review feedback on PR microsoft#29265:
- range.cc ComputeRange now computes the element count as
  ceil((double(limit) - double(start)) / double(delta)), byte-identical to the
  shape-inference path in CalcRangeDim, so integral inputs are promoted to double
  before the subtraction and cannot overflow in T.
- range_schema_defs.cc CalcRangeDim uses the same expression; comments on both
  sites note they must stay identical.
- Added two CPU-EP-pinned kernel-execution tests with runtime (non-constant)
  inputs so the ComputeRange element-count guards are exercised at execution
  time: a count that exceeds the int64 range and a non-finite count.

Agent-signed-off: Developer (b2fe149b) [claude-opus-4.8 via copilot]
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@titaiwangms titaiwangms force-pushed the fix/contrib-range-input-validation branch from a13bb91 to 7fa0558 Compare June 30, 2026 20:47
@titaiwangms titaiwangms requested a review from apsonawane June 30, 2026 21:21
@yuslepukhin

Copy link
Copy Markdown
Contributor
  • The CUDA kernel (range.cc) has the same non-finite UB and additionally uses int (32-bit) for count — truncation risk. The PR only touches the CPU kernel and shape inference.
  • The int16_t non-raw-data path (acknowledged as a separate follow-up in the PR).
  • The MSVC #pragma warning(disable : 26451) cleanup.

titaiwangms and others added 2 commits July 1, 2026 23:34
…the launch path to int64

Mirror the CPU kernel and shape-inference guards in the standard onnx-domain CUDA Range
kernel so all three paths stay consistent:
- range.cc ComputeRange now rejects a non-finite computed count, handles the non-positive
  case before the cast, and rejects counts at or above 2^63 (not representable as int64)
  before converting to int64_t. Messages are identical to the CPU kernel.
- Widen the launch path (range_impl.h/.cu) from int to int64_t for the element count and
  kernel index, compute the block count in int64_t, cap the grid x-dimension to 2^31-1,
  and use a 64-bit grid-stride loop so valid counts larger than a single launch grid (and
  larger than INT_MAX) are produced without index truncation or grid-dimension overflow.
- Add CUDA-EP tests (guarded by USE_CUDA, skipped when no CUDA provider) exercising the
  non-finite and >=2^63 count guards at execution time with runtime (non-constant) inputs.

Agent-signed-off: Developer (b2fe149b) [claude-opus-4.8 via copilot]
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…VC 26451 pragma

Add get_data<int16_t> in range_schema_defs.cc so a com.microsoft Range with an INT16
initializer stored in the typed int32_data field (the ONNX packing for INT16) resolves
shape inference instead of hitting the generic template. int16 is an advertised T type
constraint and CalcResultDim already dispatched to CalcRangeDim<int16_t>; only the
typed-field accessor was missing.

Remove the file-wide MSVC 26451 pragma and its TODO in cpu/generator/range.cc; the
double-promoted count computation removed the arithmetic-overflow root cause.

Agent-signed-off: Developer (fc32ad9a) [claude-opus-4.8 via copilot]
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@titaiwangms

Copy link
Copy Markdown
Contributor Author

Thanks @yuslepukhin for the thorough review — all three points are now addressed in the latest revision:

  1. CUDA Range kernel (non-finite cast + 32-bit count): Fixed. ComputeRange now applies the same guards as the CPU kernel before the cast — an std::isfinite check, non-positive handling before the conversion, and a rejection of counts at or above 2^63 — with error messages identical to the CPU kernel. The whole launch path (element count, kernel index, and block-count arithmetic) is widened from int to int64_t, using a 64-bit grid-stride loop with the grid x-dimension capped at 2^31−1 so large valid counts are produced without truncation or grid-dimension overflow. Verified on a CUDA build (nvcc 12.9, A100): a valid count above INT_MAX (start=0, delta=1, limit=INT_MAX+16 → count 2147483663) now produces the correct output shape 2147483663, whereas before the change it produced shape 0 due to the 32-bit truncation. Two CUDA-EP tests were added covering the non-finite and ≥2^63 guards.

  2. int16 non-raw-data path: Fixed. Added the missing get_data<int16_t> shape-inference specialization (INT16 initializer values are packed into int32_data), plus a red→green regression test that exercises the typed-field path.

  3. MSVC #pragma warning(disable : 26451): Removed. The count is now computed entirely in double, which eliminates the 32-bit-arithmetic-then-widen pattern that C26451 flagged; the Windows /analyze CI leg will confirm it stays clean.

Appreciate the careful look — the CUDA parity in particular is a good catch.

@titaiwangms titaiwangms requested a review from tianleiwu July 1, 2026 23:47
Add ContribOp_Int16TypedField_InfersDim: a com.microsoft Range model whose INT16
start/limit/delta initializers are carried in the typed int32_data field (the ONNX
packing for INT16) rather than raw_data. This exercises the get_data<int16_t>
typed-field path added in the schema fix; without that specialization the generic
template throws during shape inference. Extends the RangeInputSpec test harness with
an optional int32_data payload and an Int16TypedInput helper.

Agent-signed-off: Developer (fc32ad9a) [claude-opus-4.8 via copilot]
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@titaiwangms titaiwangms force-pushed the fix/contrib-range-input-validation branch from 36ef141 to d1ce9b1 Compare July 1, 2026 23: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.

3 participants