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.

np.load(allow_pickle=False) leaks an uncaught IndexError on a .npy/.npz header whose dtype descr is a <2-element tuple β€” out-of-contract exception / parsing DoS

Summary

numpy.load(), used in its safe default configuration (allow_pickle=False, no pickle involved), raises an uncaught IndexError when parsing a crafted .npy (or .npz) file whose header descr field is a 1-element tuple ('|u1',) or an empty tuple (). IndexError is not in numpy's documented Raises contract for load (OSError / UnpicklingError / ValueError / EOFError). Any consumer that follows that contract β€” e.g. an ingest / deserialization path wrapping np.load in except (OSError, ValueError, EOFError): β€” is not protected and crashes.

This is a header-parsing bug that fires before any array data is read and does not require allow_pickle=True, so it is reachable on completely untrusted input that an application deliberately loads with the safe default.

  • Target: numpy β€” numpy/lib/_format_impl.py
  • Reproduced on: numpy 2.5.1 (pip venv, Python 3.13.12) and numpy 2.3.5 (system, Python 3.13.12) β€” identical behavior.
  • Class: out-of-contract exception / parsing denial-of-service (CWE-248 uncaught exception / CWE-20 improper input validation).
  • Impact: DoS of any service/library that ingests untrusted .npy/.npz and relies on numpy's documented exception set to sandbox parse failures.

Root cause

descr_to_dtype() in numpy/lib/_format_impl.py (lines 335–338):

elif isinstance(descr, tuple):
    # subtype, will always have a shape descr[1]
    dt = descr_to_dtype(descr[0])
    return numpy.dtype((dt, descr[1]))

The inline comment literally asserts "subtype, will always have a shape descr[1]" β€” but descr originates from the attacker-controlled .npy header, decoded via ast.literal_eval, and is never validated to have >= 2 elements. A header whose descr is a 1-element tuple ('|u1',) or an empty tuple () makes descr[1] raise IndexError: tuple index out of range.

The caller, _read_array_header() (lines 693–697), guards this call with try/except TypeError only β€” it deliberately converts TypeError into an in-contract ValueError:

try:
    dtype = descr_to_dtype(d['descr'])
except TypeError as e:
    msg = "descr is not a valid dtype descriptor: {!r}"
    raise ValueError(msg.format(d['descr'])) from e

IndexError is not a TypeError (nor a subclass of any contract exception), so it escapes _read_array_header -> read_array -> np.load unmodified.

PoC

A 130-byte NPY v1.0 file with header:

{'descr': ('|u1',), 'fortran_order': False, 'shape': (0,), }
import numpy
numpy.load('poc_descr_tuple_indexerror.npy', allow_pickle=False)  # safe default
# -> IndexError: tuple index out of range   (OUT OF CONTRACT)

A smaller 66-byte variant uses descr = ().

.npz variant (the malformed member stored as arr.npy inside a zip) is a deferred crash: NpzFile opens cleanly and reports files=['arr']; the IndexError fires only at member access z['arr'], defeating any open-time validation.

Generator / reproducer: poc_descr_indexerror.py. PoC file: poc_descr_tuple_indexerror.npy.

Captured evidence (verbatim)

numpy 2.5.1, Python 3.13.12

[1-tuple descr ('|u1',)] *** OUT-OF-CONTRACT *** builtins.IndexError: tuple index out of range  (file 130B)
[empty-tuple descr ()]   *** OUT-OF-CONTRACT *** builtins.IndexError: tuple index out of range  (file 66B)
[NEG normal str '|u1']            LOADED ok -> array([], dtype=uint8)
[NEG normal subarray ('|u1',(2,))] loads / in-contract (no crash)
[NEG structured [('a','|u1')]]    IN-CONTRACT ValueError: Failed to read all data for array...

Only the malformed <2-element tuple descr yields the out-of-contract IndexError.

Full traceback through np.load

  File ".../numpy/lib/_npyio_impl.py", line 483, in load
    return format.read_array(fid, allow_pickle=allow_pickle, ...)
  File ".../numpy/lib/_format_impl.py", line 822, in read_array
    shape, fortran_order, dtype = _read_array_header(fp, version, max_header_size=max_header_size)
  File ".../numpy/lib/_format_impl.py", line 694, in _read_array_header
    dtype = descr_to_dtype(d['descr'])
  File ".../numpy/lib/_format_impl.py", line 338, in descr_to_dtype
    return numpy.dtype((dt, descr[1]))
IndexError: tuple index out of range

.npz deferred behavior

NpzFile opened; files=['arr']
member access: IndexError tuple index out of range

System numpy 2.3.5, Python 3.13.12

[1-tuple descr ('|u1',)] *** OUT-OF-CONTRACT *** builtins.IndexError: tuple index out of range  (same)

Suggested fix

In descr_to_dtype, validate the tuple length before indexing descr[1] and raise a ValueError (in-contract) on a malformed descriptor; or broaden the caller's except TypeError in _read_array_header to also catch IndexError and re-raise it as the same ValueError("descr is not a valid dtype descriptor: ...").

Dedup note

This is distinct from other numpy .npy/.npz header findings:

  • Not the .npy shape integer-overflow DoS (crafted shape, allocation).
  • Not the .npy bool/shape TypeError contract bug (different field, different exception origin).
  • Not the .npz BadZipFile contract leak (zip container level).
  • Not the "big header" / header-size RCE-adjacent issue.

The defect here is specifically the unvalidated descr[1] index in the tuple "subtype" branch of descr_to_dtype, producing an IndexError that numpy's own caller does not normalize. No public CVE was found for this specific IndexError path at 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