CVE-2025-32434 is the PyTorch vulnerability that broke a load-bearing security assumption underpinning much of the ML ecosystem. For years, the defender guidance for handling untrusted model files was: "use torch.load(file, weights_only=True) — the safe path." That advice was based on PyTorch's documented behavior that weights_only=True would only deserialize a restricted set of safe types, blocking the pickle-based code-execution gadgets that make naive torch.load() dangerous. CVE-2025-32434, disclosed in April 2025 and patched in PyTorch 2.6.0, demonstrated that the guarantee was incomplete. An attacker can construct a model file that triggers arbitrary code execution even with weights_only=True. This post analyzes the bug, the patch, and the right downstream response.
What did weights_only=True promise and how did it break?
torch.load() uses Python's pickle module to deserialize tensors and arbitrary objects. Pickle is famously unsafe — pickle.loads() can execute arbitrary code via the __reduce__ method and other gadgets. The weights_only=True parameter was introduced in PyTorch 1.13 to restrict deserialization to a whitelist of safe types: tensors, basic Python primitives, and a handful of explicitly allowed classes. The promise: even an attacker-supplied pickle file could not achieve code execution under weights_only=True because the unpickler would reject any class outside the whitelist.
CVE-2025-32434 is the discovery that the whitelist enforcement had a bypass. The exact mechanism involves an interaction between the safe-unpickler logic and certain torch internal classes that were on the whitelist but whose construction paths allowed user-controlled callable invocation. An attacker who knew the bypass could craft a model file that, when loaded with weights_only=True, executed arbitrary code in the loader's process. The CVSS score is 9.3 and the affected versions are PyTorch ≤ 2.5.1. The patch in 2.6.0 closes the specific bypass and tightens the safe-unpickler more broadly.
Why is this worse than a normal RCE bug?
Because the affected guarantee was load-bearing for ecosystem hygiene. Every guidance document, every security tool, every static analyzer that flagged unsafe torch.load() calls used the same advice: "set weights_only=True." Defender automation around torch.load() calls trusted the flag to be sufficient. CVE-2025-32434 invalidates that automation retroactively: any system that processed untrusted model files between PyTorch 1.13 (when weights_only landed) and PyTorch 2.6.0 (when the bypass was patched) is potentially exposed. The remediation is not just "upgrade PyTorch" — it is "audit your handling of untrusted model files across that historical window."
What is the migration to safetensors and why does it matter now?
Safetensors is a model serialization format introduced by Hugging Face specifically to avoid the pickle-class issues. Unlike pickle, safetensors strictly separates raw tensor data from any executable code. The format is read-only: there is no deserialization step that can invoke a callable. Loading a safetensors file is a memory-mapped read of bytes, not a function invocation. As of 2025-2026, safetensors is the default format on Hugging Face Hub for new uploads, and major frameworks (PyTorch via safetensors.torch.load_file, Transformers, Diffusers) support it natively. The right defender posture in 2026 is: refuse to load any model in .bin, .pt, or pickle-based formats from untrusted sources, period. Convert internally if you must, but only after explicit human vetting.
# Safe model-loading pattern post-CVE-2025-32434
import os
import hashlib
from safetensors.torch import load_file
import torch
ALLOWED_EXTENSIONS = {".safetensors"}
SIGNED_REGISTRY = "https://models.internal.example-corp.local/signed/"
PINNED_HASHES = {
"Llama-4-Scout-17B-16E": "a3f1c2e...", # 64-hex sha256
}
def load_trusted_model(model_name: str, path: str) -> dict:
ext = os.path.splitext(path)[1]
if ext not in ALLOWED_EXTENSIONS:
raise ValueError(f"Refusing to load {ext}; only safetensors permitted")
with open(path, "rb") as f:
actual = hashlib.sha256(f.read()).hexdigest()
expected = PINNED_HASHES.get(model_name)
if expected is None or actual != expected:
raise ValueError(f"Hash mismatch for {model_name}; refusing to load")
return load_file(path)
What about lmdeploy and other downstream consumers?
CVE-2025-67729, disclosed shortly after CVE-2025-32434, affects InternLM's lmdeploy where torch.load() is called without weights_only=True. The vulnerability is the textbook pickle-deserialization RCE that the broader PyTorch weights_only guarantee was supposed to make obsolete — except that, as CVE-2025-32434 demonstrated, the guarantee was incomplete to begin with. The lesson generalizes: any project that calls torch.load() on untrusted input is a candidate for the same class of disclosure, and the right audit pattern is to grep your codebase and your dependencies for torch.load( calls and ensure each one either reads trusted-and-pinned input, uses safetensors, or runs in an isolated sandbox.
What did Anthropic, OpenAI, and Meta change after the disclosure?
All three labs operate enough infrastructure that they had to respond. The public observation: Hugging Face accelerated the deprecation timeline for non-safetensors uploads, and major model releases since mid-2025 ship exclusively in safetensors format. Meta's Llama 4 (April 2025), Anthropic's published evaluation harnesses, and OpenAI's open releases all default to safetensors. Within enterprise deployments, the right posture mirror this: ban pickle-based model formats at the registry and at the deployment-policy layer, not just in code-review style guides.
How does this affect CI and training-job security?
The CVE-2025-32434 exposure is not limited to inference. Training pipelines load model checkpoints during fine-tuning, evaluation runs load models for benchmarking, and CI workflows that exercise model-loading code are all candidates for the same RCE pattern. The audit pass should include: every torch.load call in your training code (most will be loading your own pinned checkpoints, but verify), every evaluation harness that loads competitor or public models for comparison, and every CI workflow that pulls a model into the build environment. The latter is the highest-risk surface because CI runners typically have credentials for your registry, your model storage, and often broader infrastructure access. A torch.load RCE in a CI runner is effectively a supply-chain compromise of the artifacts that CI builds. Move CI model-loading to safetensors-only and pin to internal mirrors before the next disclosure forces the issue under incident-response pressure.
What is the lifecycle exposure window?
The most uncomfortable number is the size of the historical exposure window. weights_only=True was promoted as the safe path from PyTorch 1.13 (October 2022) through the patch in 2.6.0 (early 2025). For ~2.5 years, defender automation that relied on the flag for safety was building on incomplete assumptions. Any incident response or forensic analysis covering that window needs to revisit the assumption that "the model files we loaded were safe because we set the flag." The honest answer is that they were probably safe in the overwhelming majority of cases — but probably is not the same as provably, and security tooling needs to assume the worse posture going forward.
What broader posture should defender teams take on serialization?
The pattern that produced CVE-2025-32434 — a documented safety property of a serialization API turning out to have edge-case bypasses — is general. Any framework that promises "safe deserialization" of an untrusted input should be assumed to have at least one unpatched bypass until proven otherwise. The defender posture is to design as if no serialization safety guarantee can be fully trusted: deserialize untrusted input in sandboxes, prefer formats (safetensors, MessagePack with strict typing, FlatBuffers) that do not have code-execution semantics by design, and instrument every deserialization call with logging that captures source, content hash, and outcome. The PyTorch case is unusual only in the scale of impact; the underlying lesson — that safety flags are useful but not load-bearing — should inform every framework choice. For 2026, the practical recommendation for any ML team is: standardize on safetensors for new artifacts, plan migration of legacy pickle artifacts to safetensors with content-equivalence verification, and treat any code path that still calls torch.load() on untrusted input as a tracked technical-debt item with an explicit owner.
How Safeguard Helps
Safeguard tracks PyTorch versions across all your inference deployments, training nodes, and CI pipelines, with continuous matching against CVE-2025-32434 and downstream advisories (lmdeploy CVE-2025-67729, ShadowMQ-family bugs). The platform identifies any code path that calls torch.load() on untrusted input and proposes a safetensors-based migration with the equivalent loading pattern. Griffin AI scans your model registry for non-safetensors artifacts and flags them for either conversion or quarantine, and the model-signing integration verifies signatures on every safetensors pull. Policy gates block any deployment that imports pickle, cloudpickle, or dill in a model-loading code path, and CI checks refuse to merge PRs that introduce torch.load() without weights_only=True and a safetensors source. The result: the broken safety guarantee at the PyTorch layer is compensated by defender controls at the platform layer.