YAML Metadata Warning:empty or missing yaml metadata in repo card
Check out the documentation for more information.
ONNX ArrayFeatureExtractor Feature Selection Authority PoC
Summary
An ONNX model file contains a runtime-consumed feature selection operator (ai.onnx.ml.ArrayFeatureExtractor) whose model-internal idx initializer controls which input feature column is selected and passed to downstream scoring. Mutating the idx initializer while keeping all downstream LinearClassifier coefficients, intercepts, and classlabels_ints byte-identical causes the same input tensor [[10.0, -10.0]] to supply a different numeric value to the scorer (10.0 vs -10.0), producing different numeric scores and a flipped prediction (label 1 → 0).
This is pre-score feature selection authority — not class label substitution, not affine scaling, not sentinel replacement, not threshold activation, not categorical one-hot binding.
Affected Product
- Format: ONNX (.onnx), operator
ai.onnx.ml.ArrayFeatureExtractor(domainai.onnx.mlv1), model-internalidxinitializer (INT64) - Runtime: onnxruntime (CPUExecutionProvider)
- Maintainers: Microsoft & Meta
Vulnerability Details
The idx initializer consumed by ai.onnx.ml.ArrayFeatureExtractor directly controls which column of the input tensor reaches the downstream scorer. A mutation confined to this initializer produces a prediction change that the downstream weights (coefficients, intercepts, class labels) do not reveal.
| Field | clean.onnx | mutant.onnx |
|---|---|---|
ArrayFeatureExtractor idx initializer |
[0] |
[1] ← only change |
LinearClassifier.coefficients |
[-1.0, 1.0] |
[-1.0, 1.0] identical |
LinearClassifier.intercepts |
[1.0, -1.0] |
[1.0, -1.0] identical |
LinearClassifier.classlabels_ints |
[0, 1] |
[0, 1] identical |
Input [[10.0, -10.0]]:
- clean: extract index 0 → post-extract
[[10.0]]→ scores[-9.0, 9.0]→ label 1 - mutant: extract index 1 → post-extract
[[-10.0]]→ scores[11.0, -11.0]→ label 0
Impact
A crafted ONNX model with a mutated idx initializer produces different inference output from an otherwise structurally identical model. Auditing tools and users examining classifier coefficients, intercepts, and output labels observe no difference — the divergence is entirely in the pre-score feature selection field. This is not a claim of RCE, ACE, or memory corruption.
Proof of Concept
PoC repository: PLACEHOLDER
pip install onnx>=1.14.0 onnxruntime>=1.16.0 numpy>=1.24.0
python reproduce_onnx_feature_selection_flip.py
Expected output:
clean label: 1, scores: [-9.0, 9.0]
mutant label: 0, scores: [11.0, -11.0]
reproducibility: clean 5/5=True, mutant 5/5=True
A1: same input [[10.0,-10.0]] used -> PASS
A2: extraction index differs (clean=0, mutant=1) -> PASS
A3: downstream coefficients identical [-1.0, 1.0] -> PASS
A4: downstream intercepts identical [1.0, -1.0] -> PASS
A5: clean post-extract positive (index 0 → 10.0) -> PASS
A6: mutant post-extract negative (index 1 → -10.0) -> PASS
A7: prediction flip 1->0 (zero coeff change) -> PASS
A8: clean 5/5 repro -> PASS
A9: mutant 5/5 repro -> PASS
ONNX_ARRAY_FEATURE_EXTRACTOR_SELECTION_FLIP_CONFIRMED
Runtime Evidence
| Item | Value |
|---|---|
| Input | [[10.0, -10.0]] (continuous numeric, same for both) |
| clean idx / post-extract / scores / label | [0] / [[10.0]] / [-9.0, 9.0] / 1 |
| mutant idx / post-extract / scores / label | [1] / [[-10.0]] / [11.0, -11.0] / 0 |
| coefficients (both) | [-1.0, 1.0] (byte-identical) |
| intercepts (both) | [1.0, -1.0] (byte-identical) |
| classlabels_ints (both) | [0, 1] (byte-identical) |
| Reproducibility | 5/5 |
| Assertions | 9/9 PASS |
| Hash matrix | 11/11 PASS |
| clean SHA256 | b07ac968a6f2be7c566ea19329b0ac21fe9bb59b06f3b7773661544b8f441c8c |
| mutant SHA256 | 1f37b3d32a76fc48a87ac9926d37044ae45d7e5e09c0dde9d69f0a32c914de14 |
Distinctness
| Prior Finding | Root | Distinct |
|---|---|---|
ai.onnx.ml.Binarizer.threshold |
Threshold activation (0/1 gate per feature value) | Binarizer maps continuous→binary; ArrayFeatureExtractor selects which column reaches scorer |
ai.onnx.ml.Scaler.scale |
Continuous affine transform (all inputs) | Scaler.scale is not Scaler.scale; ArrayFeatureExtractor selects a column index, not a scale factor |
ai.onnx.ml.Imputer.imputed_value_floats |
Sentinel replacement (triggers on missing value) | Imputer fills gaps; ArrayFeatureExtractor selects which feature position feeds the scorer |
ai.onnx.ml.OneHotEncoder.cats_strings |
Categorical column binding | OneHotEncoder converts strings to one-hot; ArrayFeatureExtractor selects numeric column by index |
ai.onnx.ml.SVMClassifier.classlabels_strings |
Post-inference label rendering | Acts after scoring; ArrayFeatureExtractor acts before scoring |
TFLite NormalizationOptions |
FlatBuffer metadata normalization | Different format (.tflite), different runtime |
SafeTensors preprocessor_config.json image_mean |
HF sidecar JSON image normalization | Different format (sidecar JSON), different modality |
Joblib CountVectorizer.vocabulary_ |
NLP token-to-column binding | Different format (.joblib), different runtime |
SafeTensors tokenizer.json model.vocab |
NLP token-to-ID binding | Different format (sidecar JSON), different modality |
OpenVINO rt_info labels |
OpenVINO IR label map | Different format (OpenVINO IR), different runtime |
TFJS signature.outputs |
TFJS output tensor binding | Different format (.tfjs), different runtime |
Non-Claims
This PoC does not claim RCE, ACE, memory corruption, scanner bypass as primary impact, classlabels_strings label substitution, OneHotEncoder.cats_strings categorical binding, Imputer.imputed_value_floats sentinel replacement, Scaler.scale affine transform, Binarizer.threshold threshold activation, TFLite FlatBuffer normalization, or SafeTensors sidecar JSON preprocessing. Root claim: ONNX ai.onnx.ml.ArrayFeatureExtractor model-internal idx initializer pre-score feature selection authority.
Recommendation
ONNX model auditing tools and consumers should validate preprocessing operator inputs and initializers (ArrayFeatureExtractor idx initializer) alongside downstream classifier weights. The idx initializer directly controls which input feature column is selected before downstream scoring and can flip predictions without any change to coefficients, intercepts, or class labels.
References
- ONNX spec: ai.onnx.ml.ArrayFeatureExtractor — https://onnx.ai/onnx/operators/onnx_ml_doc_ArrayFeatureExtractor.html
- onnxruntime — https://github.com/microsoft/onnxruntime