YAML Metadata Warning:empty or missing yaml metadata in repo card
Check out the documentation for more information.
PyTorch .pt duplicate storage entries β PoC
This repository contains a proof-of-concept demonstrating that a crafted .pt
state_dict file can cause torch.load() to silently load attacker-controlled
tensor values while Python-based inspection sees benign values.
This is not a pickle or code execution issue
The PoC uses a state_dict-only .pt file. archive/data.pkl is unchanged β
it contains no __reduce__, no os.system, and no dangerous callable. The issue
is a ZIP storage blob resolution differential that silently changes the tensor
values restored by torch.load().
Background
torch.save(state_dict, ...) produces a ZIP archive containing:
archive/data.pkl β pickle with ordinal storage references (unchanged)
archive/data/0 β raw float32 bytes for tensor storage 0
archive/data/1 β raw float32 bytes for tensor storage 1
...
When a ZIP archive contains two entries with the same name, different readers resolve the collision differently:
| Reader | Resolves to |
|---|---|
torch.load() β PyTorch C++ ZIP reader |
First entry |
Python zipfile.read() β central directory |
Last entry |
A crafted .pt file places malicious storage blobs first and benign blobs
last. torch.load() restores malicious weights; Python-based tools that use
zipfile.read() see benign weights.
Files
| File | Description |
|---|---|
make_pytorch_dup_storage.py |
Creates the crafted dup_storage_reverse.pt |
check_pytorch_dup_storage.py |
Verifies the differential and output manipulation |
benign_state_dict.pt |
Reference benign model (fc.weight=[[1,1]], fc.bias=[0]) |
dup_storage_reverse.pt |
Crafted file β malicious blobs first, benign blobs last |
requirements.txt |
Python dependencies |
SHA256SUMS.txt |
File hashes for verification |
Reproduction
pip install -r requirements.txt
python make_pytorch_dup_storage.py # recreates dup_storage_reverse.pt
python check_pytorch_dup_storage.py # verifies differential
Expected output:
[1] What zipfile.read() returns for duplicate entries:
zipfile.read('archive/data/0') -> (1.0, 1.0) # BENIGN (last entry)
[2] torch.load() on crafted file (weights_only=True):
fc.weight = [-1.0, -1.0] # MALICIOUS (first entry)
[3] Inference output differential:
[1.0, 0.0] benign=1.0 crafted=4.0 changed=True
[0.0, 1.0] benign=1.0 crafted=4.0 changed=True
[1.0, 1.0] benign=2.0 crafted=3.0 changed=True
[4] ModelScan 0.8.8 reports 0 issues for the tested artifact.
TORCH_READS_FIRST_STORAGE = True
ZIPFILE_READS_LAST_STORAGE = True
DIFFERENTIAL_CONFIRMED = True
OUTPUT_FLIP_CONFIRMED = True
NO_WARNING_OBSERVED = True
Environment tested
Python 3.10.12
torch 2.11.0+cpu
modelscan 0.8.8