tensorizer-dos-poc / README.md
alexschose's picture
PoC: tensorizer .tensors deserializer DoS (unbounded alloc + int overflow)
ec8e6b0 verified
|
Raw
History Blame Contribute Delete
3.38 kB
metadata
license: mit

tensorizer .tensors deserializer β€” DoS PoC (unbounded allocation + integer overflow)

Proof-of-concept malicious model files for two denial-of-service flaws in the tensorizer .tensors binary deserializer (tensorizer==2.12.1, the CoreWeave/vLLM fast model-load format).

Both fire at TensorDeserializer(...) construction (default lazy_load=False), i.e. merely opening an attacker-supplied .tensors file β€” no .load() call required.

Affected

  • Package: tensorizer==2.12.1 (PyPI latest, 2026-06-20); HEAD 652d3c016c84836bba97153e108821c11428ac40.
  • File: tensorizer/serialization.py.

Reproduce

python -m venv venv && . venv/bin/activate
pip install tensorizer==2.12.1

# Finding A β€” unbounded allocation (CWE-789): 73-byte file declares a 256 TiB metadata blob
python -c "import tensorizer; tensorizer.TensorDeserializer('poc_a_meta_281tib.tensors')"
#   -> MemoryError at tensorizer/serialization.py:841  (encoded_metadata = reader.read(total_len))

# Finding B β€” numpy.prod int64 silent overflow (CWE-190): meta-tensor shape wraps the declared size
python -c "import tensorizer; tensorizer.TensorDeserializer('poc_b_neg_assert.tensors')"
#   -> AssertionError at tensorizer/serialization.py:3137  (assert is_meta or needed_buffer_size == header.data_length)

# Control β€” a well-formed file loads cleanly (1 tensor)
python -c "import tensorizer; print(len(list(tensorizer.TensorDeserializer('valid.tensors').keys())), 'tensor')"

Files

File Bytes Trigger
poc_a_meta_281tib.tensors 73 Finding A β€” metadata total_len = 0xFFFFFFFFFFFF (256 TiB) β†’ MemoryError @ serialization.py:841 (main thread, no try). Amplification β‰ˆ 3.86e12Γ—.
poc_a_header_1gib.tensors 262 KB Finding A β€” per-tensor header_len = 1 GiB β†’ bytearray(header_len) allocates 1 GiB with no bound (process RSS 176 β†’ 1200 MiB), proving the missing length check.
poc_b_neg_assert.tensors 262 KB Finding B β€” v5 long-shape meta-tensor, numpy.prod((2**62, 2)) wraps int64 to -2**63 β†’ AssertionError @ serialization.py:3137.
valid.tensors 262 KB Control β€” well-formed single-tensor file (loads clean).
generate_poc_a.py, generate_poc_b.py β€” Scripts that synthesize the files from a valid baseline.

Root cause (summary)

  • A: file-declared length prefixes (_MetadataDeserializer.from_io total_len @ :841; _TensorHeaderDeserializer.from_io header_len @ :646) drive allocations with no upper bound and no check against remaining file size.
  • B: TensorEntry.deserialized_length (:231) computes num_elements = numpy.prod(self.shape) in fixed-width int64, which silently wraps; the wrapped size is summed at :1964 and reused as the buffer size, defeating the :3137 assert.

Memory-corruption was investigated and refuted β€” numpy's PyArray_NewFromDescr performs overflow-safe size arithmetic and a hard buffer-length check, so the impact is process-termination DoS, not OOB read/write.

Proposed fix

  • A: bound each file-declared length against the remaining input size (or a configurable cap) before allocating.
  • B: compute the element count with arbitrary-precision Python ints (math.prod) and explicitly overflow-check before allocating.