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.

TensorFlow tf.io.parse_example / parse_single_example / parse_sequence_example β€” SIGFPE (division-by-zero) Denial of Service

Status: gated PoC, private disclosure in progress via huntr.com (protectai). Do not use against production systems.

Summary

TensorFlow's fast Example/SequenceExample proto parser (tensorflow/core/util/example_proto_fast_parsing.cc) computes an internal per-row element count (elements_per_stride) directly from the inner shape supplied for a variable-length dense feature (tf.io.FixedLenSequenceFeature(shape=..., allow_missing=True), or the equivalent dense_shapes attribute on the raw ParseExampleV2 / ParseSingleExample / ParseSequenceExample ops). If that inner shape has a 0 in it, elements_per_stride is 0, and the parser later divides/modulos by it with no zero-check, crashing the whole process with SIGFPE (signal 8) β€” not a catchable Python exception, not a tf.errors.* status, just an instant process kill.

The crash is independent of the actual serialized Example/SequenceExample bytes β€” it fires even for a syntactically empty/valid proto, and even when the feature in question is entirely absent from the data. It is purely a function of the (attacker-controlled, when the parsing graph ships inside a model file) feature-spec shape. Any model/graph that wires up ParseExampleV2/ParseSingleExample/ParseSequenceExample with such a shape turns every future invocation of that signature into an instant crash of the hosting process β€” a persistent, config-driven Denial of Service against any inference server that loads the model.

Root cause

tensorflow/core/util/example_proto_helper.cc, GetDenseShapes():

elements_per_stride->push_back(dense_shape.num_elements());

dense_shape is the caller-supplied shape with the (optional) leading variable-length dimension stripped. If the remaining static dims multiply to 0 (e.g. shape=[0]), elements_per_stride is 0. Nothing rejects this.

tensorflow/core/util/example_proto_fast_parsing.cc:

  • Batch path, FastParseExample() β†’ MergeDenseVarLenMinibatches lambda:

    const size_t stride_size = config.dense[d].elements_per_stride;
    const size_t max_num_elements = max_num_features / stride_size;   // <-- div by 0
    

    This runs unconditionally once per configured variable-length dense feature, regardless of whether that feature appears anywhere in the batch's data (confirmed by crashing on a batch of empty Example bytes with the feature entirely missing).

  • Per-example path, FastParseSerializedExample():

    if (out.int64_list.size() % num_elements != 0) { ... }   // num_elements == elements_per_stride
    

    (analogous lines for float_list / bytes_list).

  • Single-example path, FastParseSingleExample():

    if (num_elements % num_elements_divisor != 0) { ... }   // num_elements_divisor == elements_per_stride
    
  • SequenceExample dense path, ParseSequenceDenseFeatures():

    if (!c.shape.AsTensorShape(&row_shape) ||
        expected_max_elements != (expected_max_elements / row_shape.num_elements()) *
                                      row_shape.num_elements()) { ... }   // div by 0
    ...
    int64_t expected_max_rows = expected_max_elements / row_shape.num_elements();  // div by 0
    

None of the reachable call sites (tensorflow/core/kernels/example_parsing_ops.cc, tensorflow/core/ops/parsing_ops.cc) validate that a configured dense shape has a non-zero element count before it is used as a divisor.

Verified impact

Tested against a freshly pip install-ed tensorflow-cpu==2.21.0 (current stable release at the time of testing, matching a fresh git clone of tensorflow/tensorflow master). All three PoCs reproducibly kill the Python interpreter with signal 8 (SIGFPE):

$ python3 confirm_signal.py
returncode: -8
Killed by signal: 8 SIGFPE
  • poc_parse_example_minimal.py β€” tf.io.parse_example([b""], {...}) crashes immediately, no TFRecord file, no real feature data at all.
  • poc_parse_sequence_example.py β€” same crash via tf.io.parse_sequence_example.
  • poc_tfrecord_sigfpe.py β€” the realistic end-to-end path: writes one perfectly ordinary tf.train.Example ({"label": int64_list([1])}) to a real .tfrecord file with tf.io.TFRecordWriter, then reads it back with the canonical tf.data.TFRecordDataset(...).batch(1).map(tf.io.parse_example) pipeline. The data is 100% benign; only the feature-spec ("extra": tf.io.FixedLenSequenceFeature(shape=[0], dtype=tf.float32, allow_missing=True)) is malicious. The process is killed by SIGFPE the moment the first batch is materialized.

Attacker-controlled surface (malicious model file)

dense_shapes / the analogous shape attrs are ordinary attributes of the ParseExampleV2, ParseSingleExample, and ParseSequenceExample ops. A GraphDef/SavedModel is free to embed a pre-processing subgraph (e.g. an input-parsing signature) that calls one of these ops with a zero-size inner shape on a variable-length dense feature. Any consumer that loads that model and invokes the corresponding signature β€” regardless of what input tensor it passes β€” will crash on first use. This is a persistent Denial-of-Service purely from loading and calling a malicious model file; no separately malicious dataset/TFRecord is required (though, as poc_tfrecord_sigfpe.py shows, this also fires naturally in the ordinary TFRecordDataset β†’ parse_example data-loading idiom if the feature spec is ever malformed in this specific way).

Suggested fix

Reject a dense_shapes entry whose element count is 0 for a variable-length (allow_missing=True / leading -1) dense feature in GetDenseShapes() (example_proto_helper.cc), or guard every division/modulo by elements_per_stride (and the analogous row_shape.num_elements() in the SequenceExample dense path) with an explicit != 0 check that returns an InvalidArgumentError instead of dividing.

Files

  • poc_parse_example_minimal.py β€” minimal repro via tf.io.parse_example.
  • poc_parse_sequence_example.py β€” minimal repro via tf.io.parse_sequence_example.
  • poc_tfrecord_sigfpe.py β€” realistic end-to-end repro starting from a real, valid .tfrecord file.
  • confirm_signal.py β€” runs the end-to-end PoC as a subprocess and prints the confirmed signal (SIGFPE / 8).

Environment used to verify

$ pip install tensorflow-cpu
$ python3 -c "import tensorflow as tf; print(tf.__version__)"
2.21.0
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