YAML Metadata Warning:empty or missing yaml metadata in repo card
Check out the documentation for more information.
ONNX opset_import completeness invariant β validator/runtime inconsistency
Summary
This repository contains proof-of-concept artifacts demonstrating a validator/runtime inconsistency in the ONNX ecosystem:
ONNX Runtime accepts and executes a model that ONNX checker rejects because the model uses operators from the
ai.onnx.mldomain without declaring that domain inopset_imports.
The ONNX IR specification (Β§4.1) requires that every domain referenced by a
node in the graph must have a corresponding entry in the model's opset_imports
field. onnx.checker.check_model() enforces this requirement and rejects models
that violate it. ONNX Runtime (onnxruntime.InferenceSession) does not enforce
this requirement: it resolves undeclared domains via its internal operator
registry and executes the model without error.
This is an ONNX opset_import completeness invariant violation at the format-structure level. The runtime output of the structurally invalid model is identical to that of the structurally valid (CLEAN) model. The identical output strengthens the finding: ORT executes a spec-invalid model equivalently to a spec-valid one, without any indication to the caller that the model is structurally invalid.
Affected Product
- ONNX Runtime (
onnxruntime) βInferenceSessiondomain resolution - Tested:
onnxruntime==1.23.2,onnx==1.21.0
Vulnerability Details
The ONNX IR specification Β§4.1 states:
Every domain referenced by a node must appear in the
opset_importsfield of the model. Omitting a domain entry while using operators from that domain is a structural specification violation.
onnx.checker.check_model() enforces this invariant:
No opset import for domain 'ai.onnx.ml'
==> Context: Bad node spec for node. Name: OpType: LinearClassifier
onnxruntime.InferenceSession does not enforce this invariant. It resolves
the ai.onnx.ml domain operators via its internal registry, loads the
checker-rejected model, and executes it successfully.
Structural difference
| Field | CLEAN model | MUTANT model |
|---|---|---|
opset_imports |
{"": 17, "ai.onnx.ml": 3} |
{"": 17} only |
| Nodes | LinearClassifier (ai.onnx.ml) |
LinearClassifier (ai.onnx.ml) |
onnx.checker |
PASS | FAIL |
ORT (load+execute) |
PASS | PASS |
| Runtime output | labels=[1,0,0], scores as below |
Identical |
The only structural difference between CLEAN and MUTANT is the presence or
absence of the ai.onnx.ml entry in opset_imports. All operator attributes
(classlabels_ints, coefficients, intercepts, post_transform) are
identical in both models.
Impact
A deployment path that relies on ONNX Runtime's loading behavior without
separately enforcing onnx.checker.check_model() can load and execute models
that are structurally invalid per the ONNX specification. ONNX Runtime
silently resolves the undeclared ai.onnx.ml operator domain and produces
output equivalent to a spec-compliant model, with no error or warning to the
caller.
This is a format-structure level issue β not a semantic parameter mutation, not a prediction manipulation, and not a checker bypass (the checker correctly identifies and rejects the malformed model).
Proof of Concept
Files
| File | Description |
|---|---|
model_clean.onnx |
Structurally valid ONNX model β both "" and ai.onnx.ml declared in opset_imports |
model_missing_ml_opset_import.onnx |
MUTANT β ai.onnx.ml intentionally absent from opset_imports |
reproduce_onnx_opset_import_invariant_gap.py |
Reproduction script: builds models, runs checker, runs ORT, compares outputs |
inspect_onnx_opset_import_hash_matrix.py |
Structural inspection: confirms sole change is opset_import completeness |
evidence_runtime_results.json |
Runtime evidence: checker/ORT results + SHA256 |
evidence_hash_matrix.json |
Hash and structural isolation matrix |
evidence_distinctness_matrix.json |
Distinctness from prior ONNX findings |
evidence_route_framing.json |
Route information and impact framing |
evidence_top_axis.json |
Top-level axis summary |
requirements.txt |
Python dependencies |
SHA256SUMS.txt |
SHA256 checksums for all files |
Quick start
pip install onnx>=1.14.0 onnxruntime>=1.15.0 numpy>=1.21.0
python reproduce_onnx_opset_import_invariant_gap.py
Expected output (key lines):
CLEAN onnx.checker : PASS
MUTANT onnx.checker : FAIL (expected)
No opset import for domain 'ai.onnx.ml'
==> Context: Bad node spec for node. Name: OpType: LinearClassifier
CLEAN ORT : PASS
labels : [1, 0, 0]
scores : [[-0.5, 1.5], [3.3, -2.3], [1.0, 0.0]]
MUTANT ORT : PASS
labels : [1, 0, 0]
scores : [[-0.5, 1.5], [3.3, -2.3], [1.0, 0.0]]
labels identical : True
scores identical : True
ONNX_OPSET_IMPORT_COMPLETENESS_GAP_CONFIRMED
Runtime Evidence
Tested environment:
onnx==1.21.0onnxruntime==1.23.2- Python 3.10, aarch64 Linux
| Model | SHA256 | onnx.checker | ORT |
|---|---|---|---|
model_clean.onnx |
95c60aaef7cc0915d0bbe1f3beb5aa492089ddbf47335a4b6888366770d70604 |
PASS | PASS |
model_missing_ml_opset_import.onnx |
ab4cf83d835fe2c08f67b1d79d31ba85dd853a83f5414f667254abd76c33cb6c |
FAIL | PASS |
Input: X = [[1.0, -0.5], [-0.3, 2.0], [0.0, 0.0]] (float32, shape [3, 2])
CLEAN runtime output: labels=[1, 0, 0], scores=[[-0.5, 1.5], [3.3, -2.3], [1.0, 0.0]]
MUTANT runtime output: identical to CLEAN.
Checker error message (MUTANT):
No opset import for domain 'ai.onnx.ml'
==> Context: Bad node spec for node. Name: OpType: LinearClassifier
Distinctness
This finding is at the format-structure level (opset_imports metadata completeness), which is distinct from all prior ONNX findings:
| Dimension | This root | Prior ONNX roots |
|---|---|---|
| Mutation level | format-structure (opset_imports) | semantic parameter (op attribute values) |
| Changed field | model.opset_imports |
node attribute values (coefficients, classlabels, etc.) |
onnx.checker behavior |
explicit FAIL | PASS (checker not affected by value mutations) |
| Gap type | validator/runtime inconsistency | loader/inspector inconsistency |
| ONNX spec reference | Β§4.1 opset_import requirement | op schema attribute constraints |
| Runtime output | identical (structural invalid, same computation) | differs (the point of the authority gap) |
Non-Claims
- This is not prediction manipulation.
- This is not class label substitution.
- This is not ONNX external_data checksum.
- This is not ONNX Scaler / Imputer / Binarizer / OneHotEncoder / ArrayFeatureExtractor.
- This is not RCE / ACE.
- This is not memory corruption.
- This is not checker bypass; the checker correctly rejects the malformed model.
- This is not scanner bypass as primary impact.
Recommendation
onnxruntime.InferenceSession should enforce the ONNX IR spec Β§4.1 requirement
that every domain referenced by a node must appear in opset_imports, or should
emit a warning when loading a model that violates this invariant. Currently,
there is a gap between the checker's enforcement of the specification and the
runtime's loading behavior.
References
- ONNX IR Specification Β§4.1 β opset_import completeness
onnx.checker.check_modeldocumentationonnxruntime.InferenceSessiondocumentation