anndata β .h5ad HDF5 Sparse Data Decompression Bomb (PoC)
Repo: MBM7/anndata-h5ad-sparse-bomb-poc
Status: Responsible disclosure β submitted to Huntr
Severity: High / CWE-789
Packages: anndata + h5py (PyPI) β HDF5 format (.h5ad)
Summary
A crafted 68 KB .h5ad file causes anndata.read_h5ad() to allocate
2 GB of memory silently β no exception is raised, the returned
AnnData object appears completely valid.
| File size | Claimed X/data shape | Peak allocation | Amplification |
|---|---|---|---|
| 68 KB | (500,000,000,) | 2,000 MB | 1 : 29,391 |
Root Cause
anndata/_io/specs/methods.py, read_sparse():
# No size validation before reading
sparse_dataset(elem).to_memory()
β h5py: numpy.empty(ds.shape, dtype=ds.dtype) # β 2 GB allocated
# ds.shape = (500_000_000,) from HDF5 metadata
β reads actual chunks (only 3 values on disk)
HDF5 chunked datasets support sparse storage β unwritten chunks
exist only as metadata. The X/data dataset can claim
shape=(500_000_000,) float32 (= 2 GB) while storing only 3 values.
h5py pre-allocates numpy.empty(ds.shape) before reading any chunks.
Distinct from keras/h5py finding
| keras/h5py | anndata | |
|---|---|---|
| Result | OOM kill (MemoryError) | Silent successful load |
| File format | .h5 |
.h5ad |
| Code path | legacy_h5_format.py |
sparse_dataset.py |
| Detectability | Process crashes | No error β harder to detect |
Reproduce
pip install anndata h5py scipy numpy
python poc_anndata_h5ad_bomb.py
Expected output:
Malicious .h5ad : 68,048 bytes
X/data claimed : 2,000,000,000 bytes (2.0 GB)
Amplification : 1:29,391
Result : LOADED β no exception!
Peak memory : 2000 MB
Impact
Any service loading user-supplied .h5ad files (HuggingFace single-cell
genomics datasets, bioinformatics ML pipelines, spatial transcriptomics
tools) can be OOM-killed by a 68 KB payload. The silent load makes the
attack harder to detect than typical decompression bombs.
Suggested Fix
In SparseDataset.to_memory() / read_sparse(), validate before reading:
MAX_ARRAY_BYTES = 512 * 1024 * 1024
for key in ['data', 'indices', 'indptr']:
ds = elem[key]
if ds.size * ds.dtype.itemsize > MAX_ARRAY_BYTES:
raise ValueError(f"Dataset '{key}' too large β possible bomb")
Environment
| Package | Version |
|---|---|
| anndata | 0.13.2 |
| h5py | 3.14.0 |
| Python | 3.12 |
Distinct from keras HDF5 finding β different library, different code path,
different behavior (silent vs crash). anndata is used in HuggingFace Hub
for single-cell ML datasets.