Definition · AI security
Pickle
Pickle is Python's built-in format for saving objects as bytes and loading them back, and the default format behind PyTorch's torch.save and torch.load. A pickle file is a list of instructions that the loader carries out, and those instructions can import and call Python functions, so loading an untrusted pickle can run an attacker's code.
Last reviewed
Key points
- Pickle is Python's format for turning objects into bytes and back. PyTorch's torch.save and torch.load use it by default.
- A pickle file is a list of instructions, not just data. Loading it carries them out, and they can import a function such as os.system and call it.
- Python's own documentation warns that malicious pickle data can execute arbitrary code during unpickling, and says never to unpickle data from an untrusted source.
- Scanners read the instructions without running them and flag dangerous imports. They miss what their lists and parsers do not expect, and malicious models on Hugging Face got past Picklescan that way.
- PyTorch's weights_only=True, the default since version 2.6, loads only what a plain weights file needs. PyTorch says it narrows the attack surface rather than closing it.
Pickle saves a Python object as a list of instructions for rebuilding it, and loading the file carries those instructions out.
How it works
Pickling turns a Python object into a stream of bytes, and unpickling turns the bytes back into the object. Hugging Face’s documentation describes a pickle as “basically a stack of instructions or opcodes”, read one after another when the file is loaded.
Two kinds of instruction make that dangerous. A GLOBAL or STACK_GLOBAL instruction tells the loader to import a function by name. A REDUCE instruction tells it to call that function with the given arguments. By default, Python’s documentation says, unpickling “will import any class or function that it finds in the pickle data”. Its own example is a short hand-written pickle that imports os.system and runs a shell command on load.
A pickle can only name functions that already exist, so an attacker names exec or eval and passes code as a string.
Why it matters
PyTorch’s torch.save and torch.load use pickle by default. Since PyTorch 1.6, torch.save writes a ZIP archive, usually ending in .pt or .pth, whose data.pkl entry is a pickle of the saved object. A downloaded model checkpoint therefore usually carries a pickle, and loading it without restrictions follows whatever the pickle says.
Python’s documentation is blunt: “It is possible to construct malicious pickle data which will execute arbitrary code during unpickling. Never unpickle data that could have come from an untrusted source, or that could have been tampered with.” MITRE ATLAS says “pickle files are unsafe to deserialize because they can contain unsafe calls such as exec”. In its case study of malicious models on Hugging Face, ATLAS files the loading of such a model under unsafe AI artifacts. ATLAS adds that “Models with embedded malware may still operate as expected”, so a working model is no sign of a clean one.
Where the defences fall short
Scanners read the instructions without running them. Hugging Face’s Hub lists the imports in every uploaded pickle and highlights suspicious ones, and Picklescan flags pickles that import functions such as eval. Both work from lists. ReversingLabs reported in February 2025 that Picklescan was “based on a blacklist”, and that Checkmarx had found code-running functions missing from it.
The same report found two malicious models on Hugging Face that opened a reverse shell when loaded, which ATLAS says “grants the threat actor command and control capabilities on the victim’s system”. Picklescan did not flag them. ReversingLabs said the likely reason was that the files were packed with 7z instead of PyTorch’s usual ZIP, and also found that Picklescan could not properly scan broken pickle files. ATLAS, recording the incident as AML.CS0031, gives the second: the files were “seemingly purposefully corrupted in a way that the malicious payload is executed before the model ultimately fails to de-serialize fully”. Hugging Face removed the models and changed Picklescan, and ATLAS still warns that “there may be other types of malicious pickles that Picklescan cannot detect”. Hugging Face calls its own scan “not 100% foolproof”.
weights_only narrows the loader, not the risk. Since version 2.6, torch.load uses weights_only=True unless a pickle_module argument is passed. That setting allows only the functions and classes a plain weights file needs, and the loader may not import anything while it runs. PyTorch says it “narrows the surface of remote code execution attacks”, does not guard against denial of service (a file built to stall or crash the loader), and cannot rule out memory-corruption bugs. In April 2025 a CVE entry, CVE-2025-32434, showed a crafted file could still run code through weights_only=True in versions before 2.6.0.
The escape hatches widen the attack surface again. PyTorch recommends weights_only=False for old checkpoints that contain a whole model, and its own error message warns that doing so “can result in arbitrary code execution”. Allowlisting a class lets that class through, and PyTorch says to do it only “if you trust this class/function”.
A format that holds no instructions, such as safetensors, avoids the problem for the weights. It does not tell you whether the weights themselves can be trusted; see verify AI artifacts.
Questions and answers
Is it safe to load a .pt or .pth file from the internet?
Usually. Since PyTorch 1.6, torch.save writes a ZIP archive by default, and the object structure inside it, data.pkl, is a pickle. Older files skip the ZIP wrapper but still use pickle. Loading an untrusted pickle without restrictions can run any code the file names. Since version 2.6, torch.load uses weights_only=True by default, which PyTorch says narrows that risk but does not remove it. Load files only from sources you trust.
Does weights_only=True make torch.load safe?
No. PyTorch says weights_only=True narrows the surface of remote code execution but does not guard against denial of service, and memory corruption might still be possible. CVE-2025-32434 let a crafted file run code through weights_only=True in PyTorch before 2.6.0. Turning weights_only off brings back the full risk, which PyTorch warns "can result in arbitrary code execution".
Does a clean scan prove a pickle file is safe?
No. Scanners such as Picklescan read a pickle's imports without running them and flag dangerous ones from a list. In 2025 ReversingLabs found malicious models on Hugging Face that Picklescan did not flag. It named the files' unusual 7z packing as the likely reason, and found that Picklescan could not properly scan broken pickle files. Hugging Face's own documentation says its pickle scan is "not 100% foolproof".
Sources
- pickle — Python object serialization (Python documentation)Python Software Foundation
- Pickle Scanning (Hugging Face Hub documentation)Hugging Face
- Serialization semantics (PyTorch documentation)PyTorch
- GHSA-53q9-r3pm-6pq6: torch.load with weights_only=True leads to remote code executionGitHub Advisory Database, 18 Apr 2025
- picklescan READMEpicklescan project
- MITRE ATLAS 2026.09, AML.T0018.002 Embed Malware and AML.CS0031 Malicious Models on Hugging FaceMITRE
- Malicious ML models discovered on Hugging Face platformReversingLabs, 6 Feb 2025