YAML Metadata Warning:empty or missing yaml metadata in repo card
Check out the documentation for more information.
ONNX SVMClassifier classlabels_strings Label Authority
Summary
ai.onnx.ml.SVMClassifier returns a string label at inference time by looking up the predicted class index in the classlabels_strings attribute. The predicted class index is determined entirely by SVM kernel computation. The classlabels_strings attribute is read directly from the model file and is not schema-validated at load time or at inference time.
This means an attacker who can modify an ONNX model file can substitute arbitrary string labels in classlabels_strings without changing any SVM computation attribute, the score tensor, or the predicted class index. ONNX Runtime loads and executes the model without warning.
Affected Product
- Operator:
ai.onnx.ml.SVMClassifier(ONNX ML domain, opset 3) - Runtime: ONNX Runtime (onnxruntime, pip)
- Affected attribute:
classlabels_strings - Trigger: Any inference call on a mutated SVMClassifier model
Vulnerability Details
The classlabels_strings attribute maps integer class indices (0, 1, ..., n_classes-1) to output string labels. ONNX Runtime reads this attribute from the serialized model proto at session creation and uses it verbatim at inference to produce the string label output tensor.
At inference:
- The SVM discriminant
f(x) = Σ_k(coeff_k × dot(sv_k, x)) + ρis computed over support vectors. - For binary classification with
post_transform=NONE:scores[0] = -f(x),scores[1] = +f(x). - The predicted class index is
argmin(scores). - The output label is
classlabels_strings[argmin(scores)].
Steps 1–3 are controlled by SVM computation attributes (support_vectors, coefficients, rho, kernel_type, kernel_params, vectors_per_class). Step 4 is controlled solely by classlabels_strings. There is no runtime check that classlabels_strings matches the model's training-time labels.
Impact
Mutating classlabels_strings in a deployed SVMClassifier model file causes the model to return attacker-chosen string labels for all inputs. The SVM score tensor and predicted class index remain unchanged. A downstream consumer reading the label output will receive the attacker-controlled string without any indication that it differs from the original.
Proof of Concept
pip install onnx onnxruntime numpy
python reproduce_onnx_svmclassifier_label_flip.py
Expected output (abbreviated):
[CLEAN MODEL]
x=[1.0, 0.0] label='cat' scores=[-1.0, 1.0] argmin=0
x=[0.0, 1.0] label='dog' scores=[1.0, -1.0] argmin=1
[MUTATED MODEL]
x=[1.0, 0.0] label='helicopter' scores=[-1.0, 1.0] argmin=0
x=[0.0, 1.0] label='submarine' scores=[1.0, -1.0] argmin=1
[VERIFIED] scores_identical = True
[VERIFIED] argmin(scores) identical = [0, 1]
[VERIFIED] labels_clean = ['cat', 'dog']
[VERIFIED] labels_mutant = ['helicopter', 'submarine']
ONNX_SVMCLASSIFIER_CLASSLABELS_FLIP_CONFIRMED
To confirm attribute isolation:
python inspect_onnx_svmclassifier_hash_matrix.py
Evidence Files
| File | Description |
|---|---|
clean_svmclassifier.onnx |
Original model (classlabels_strings=['cat','dog']) |
mutated_svmclassifier.onnx |
Mutated model (classlabels_strings=['helicopter','submarine']) |
evidence_runtime_results.json |
ORT inference output for both models |
evidence_hash_matrix.json |
SHA256 hashes and attribute diff |
evidence_score_tensor_semantics.json |
Score convention and argmin verification |
evidence_distinctness_matrix.json |
9-dim comparison vs LinearClassifier |
evidence_triage_risk_matrix.json |
Non-claims and triage notes |
Score Tensor Semantics
For ai.onnx.ml.SVMClassifier (binary, post_transform=NONE) in ONNX Runtime:
scores[i][0] = -f(x_i),scores[i][1] = +f(x_i)f(x) = Σ_k(coeff_k × dot(sv_k, x)) + rho- Predicted class index =
argmin(scores[i]) - Output label =
classlabels_strings[argmin(scores[i])]
The scores tensor is determined entirely by the SVM computation attributes and the input X. classlabels_strings has no effect on f(x), scores, or argmin(scores).
Distinctness from LinearClassifier
This finding is distinct from the prior ai.onnx.ml.LinearClassifier classlabels_strings submission:
- Different ONNX operator (
SVMClassifiervsLinearClassifier- separate schemas) - Different ML model family (kernel SVM with support vectors vs linear WBx+b)
- Different ORT C++ kernel (svmclassifier.cc vs linear_classifier.cc - no shared computation path)
- Different attribute set (support_vectors, vectors_per_class, rho, kernel_type, kernel_params do not exist in LinearClassifier)
- Different coefficients semantics (dual a-i_y-i[n_sv] vs primal weight matrix W[n_class*n_feature])
- Different score convention (argmin vs argmax label selection in ORT)
Both findings share the same vulnerability class (classlabels_strings label authority). Both require independent fixes in separate ORT kernel files.
Non-Claims
- This is not an RCE vulnerability.
- This is not an ACE vulnerability.
- The score tensor is not corrupted or manipulated.
- SVM kernel computation (support_vectors, coefficients, rho) is not affected.
- Model weights or computation graph are not altered in any meaningful sense.
- This is not a scanner bypass finding.
Recommendation
Validate classlabels_strings at model load time in onnxruntime/core/providers/cpu/ml/svmclassifier.cc: verify that the number of entries equals n_classes and, where a reference label set is available, that entries match expected values. Emit a warning or error when the attribute is empty or its length does not match vectors_per_class count.
References
- ONNX ML Operators spec:
ai.onnx.ml.SVMClassifierhttps://onnx.ai/onnx-ml-ops - ONNX Runtime source:
onnxruntime/core/providers/cpu/ml/svmclassifier.cc