onnxruntime
f25933d5 - Guard three graph-optimizer passes against unbounded model-supplied indices (#31670)

Commit
60 days ago
Guard three graph-optimizer passes against unbounded model-supplied indices (#31670) ## Description Three graph-optimizer passes that run at model load time index into a vector using a count/axis/index taken directly from the model graph, without validating it against the bounds of the vector being indexed: 1. **`GatherSliceToSplitFusion`** (`onnxruntime/core/optimizer/gather_fusion.cc`): reads an `axis` from a candidate `Gather`/`Slice` consumer and indexes the shared input's shape with it (`shape->dim(axis)`). ONNX's own shape inference for `Gather`/`Slice` only range-checks `axis` when it can resolve the shapes of all relevant inputs; if it cannot (e.g. an unresolvable indices/axis input shape), that check is skipped, and this fusion pass previously had no independent bounds check of its own. 2. **Transpose optimizer's `HandleTile`** (`onnxruntime/core/optimizer/transpose_optimization/onnx_transpose_optimization.cc`): assumes a constant `Tile` `repeats` initializer has one entry per dimension of the preceding `Transpose`'s rank (taken from that node's `perm` attribute), and indexes it accordingly. Similarly, ONNX's own `Tile` shape inference only validates `repeats.size()` against the input rank when it can resolve the data input's shape; if it cannot, this validation is skipped, and there was no independent check before indexing `repeats`. 3. **`WhereDummyDq`** (`onnxruntime/core/optimizer/qdq_transformer/where_dummy_dq.cc`): unconditionally reads `DequantizeLinear`'s 3rd input (`x_zero_point`, at index 2) to fetch its initializer. Per the ONNX spec, `x_zero_point` is an **optional** input, so a valid `DequantizeLinear` node can have only 2 inputs (`x`, `x_scale`), making this an out-of-bounds read on `InputDefs()`. ## Fix Each pass now validates the model-supplied value against the actual bound before using it to index, and safely skips the optimization (rather than asserting/crashing) when the value is out of range: - `GatherSliceToSplitFusion`: skip fusing this candidate if `axis < 0 || axis >= rank`. - `HandleTile`: return `false` (leave the node alone) if `repeats.size() != rank`. - `WhereDummyDq`: log a warning and skip inserting a dummy DQ if the DQ node has fewer than 3 inputs. ## Other execution providers All three passes are execution-provider-agnostic graph transformations that run before EP partitioning, so no EP-specific (e.g. CUDA) equivalent exists or needs a separate fix. ## Testing Added regression tests, using `TestGraphTransformer` (which applies the transformer and inspects the resulting graph without executing the model, since some of the malformed inputs used to reach these code paths are not valid inputs to actually run): - `graph_transform_test.cc`: `GatherSliceToSplitFusion_OutOfRangeAxis` — a `Gather` node with an out-of-range `axis`, where the sibling indices input has no static shape so ONNX's own inference cannot catch it ahead of time. Verifies the fusion pass leaves the graph unchanged. - `transpose_optimizer_test.cc`: `TestTileRepeatsRankMismatchNoOpt` — a `Transpose -> Tile -> Transpose` pattern where `repeats` is shorter than the rank implied by the `Transpose`'s `perm`, with the data input's shape left unresolved. Verifies both Transposes and the Tile remain untouched. - `qdq_transformer_test.cc`: `WhereDummyDqTest_DqWithoutZeroPoint` — a `DequantizeLinear` with only 2 inputs (no zero-point) feeding a `Where` node. Verifies no dummy DQ is inserted and the graph is otherwise unmodified. All three new tests were confirmed to fail (or crash) when the corresponding fix was temporarily reverted, and pass cleanly with the fix in place. The full `GraphTransformationTests`, `TransposeOptimizerTests`, and `QDQTransformerTests` suites were also run locally with no regressions. ## Motivation These three passes run by default during `CreateSession` (default optimization level), processing whatever graph structure the model declares. Guarding the indexing operations against out-of-range model-supplied values makes these passes resilient to malformed or adversarial models without changing behavior for well-formed ones. --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> Copilot-Session: 77dcaf1b-748a-4379-94a7-478f7a924d73
Author
Parents
Loading