Safeguard
AI Security•

Safe model loading: safetensors vs pickle after the peft CVE

A peft advisory published 5 August 2026 shows how one unguarded torch.load call undoes a project's own safe-loading wrapper. Here is how to load models without executing code.

Safeguard Security Team
5 min read

On 5 August 2026 an advisory was published for CVE-2026-71281 in Hugging Face peft. Two initialization modules, LoRA-GA and CorDA, call torch.load on cache and covariance files named in the config, without weights_only=True. The same codebase already has a safe-loading wrapper, and these call sites go around it.

That is the whole story, and it is a common one. The safe path exists, one code path does not use it, and a file from outside your trust boundary ends up in a full pickle deserialization.

What's affected

  • peft (Hugging Face). OSV lists every release through 0.19.1 as affected and records no fixed version. The calls are in src/peft/tuners/lora/corda.py (around lines 102 and 163) and src/peft/tuners/lora/loraga.py (around line 101). The CVSS vector is network, low complexity, no privileges, user interaction required, with high impact on confidentiality, integrity and availability.
  • PyTorch. Separately, CVE-2026-24747 (published 26 January 2026) is a flaw in the weights_only unpickler itself. A crafted .pth file loaded with torch.load(..., weights_only=True) can corrupt memory and possibly run code. Versions 2.9.1 and earlier are affected, and 2.10.0 fixes it. The advisory is GHSA-63cw-57p8-fm3p, CVSS 8.8.

The two issues teach different lessons. The peft one is "you forgot the flag." The PyTorch one is "the flag is a mitigation, not a sandbox."

How to check if you're exposed

Find the PyTorch and peft versions in every image and environment that loads checkpoints:

pip show torch peft | grep -E '^(Name|Version)'
python -c "import torch; print(torch.__version__)"

Then look for unguarded loads in your own code and in vendored training scripts:

grep -rnE "torch\.load\(" --include="*.py" . | grep -v "weights_only=True"
grep -rnE "pickle\.load|joblib\.load|dill\.load" --include="*.py" .

Every hit is a place where the file's origin decides whether you run someone else's code. Ask for each one: who can write this path? A shared cache directory, a model downloaded at startup, and a path that comes from a config file all count as untrusted until proven otherwise.

If you use LoRA-GA or CorDA initialization in peft, treat the cache and covariance files as code. Generate them yourself, in the same job, and do not accept them from other users or from a shared bucket.

What to do now

  1. Upgrade PyTorch to 2.10.0 or later everywhere you load checkpoints.
  2. Pass weights_only=True on every torch.load, and treat it as defense in depth, not as permission to load untrusted files.
  3. Prefer safetensors for anything you download or share. The safetensors project describes itself as a format for storing tensors safely, as opposed to pickle, while staying fast through zero-copy loading.
  4. In transformers, ask for safetensors explicitly so a repo with only a pickle file fails instead of loading: AutoModel.from_pretrained(name, use_safetensors=True).
  5. For peft, avoid the LoRA-GA and CorDA cache and covariance paths with files you did not create. Watch the repository for a fix, since no patched version is recorded yet.
  6. Convert legacy checkpoints once, in an isolated job with no credentials and no network, then store only the safetensors output.

How this could have been avoided

  • One loader, one policy. The peft project had a safe-loading wrapper and two modules that did not use it. A lint rule that fails the build on any direct torch.load outside the wrapper would have caught both call sites.
  • No config-controlled file paths into deserializers. The paths came from config. Validate them against an allowlisted directory the process itself writes.
  • Safe formats by default. A tensor-only format has no code path to exploit during load, so the question of a forgotten flag does not arise.
  • Pin and sign what you load. The OpenSSF model signing project reached 1.0 in April 2025. It signs models with Sigstore using a short-lived certificate tied to an OIDC identity, and you verify with model_signing verify. A signature proves the file is unchanged and who produced it. It does not prove the model is safe.
  • Isolate loaders. Run conversion and evaluation in containers without cloud credentials, so a successful load still has nowhere to go.

Why it matters

Pickle is a program, not a data format. Loading one means running whatever opcodes it contains, and weights_only tries to restrict that with an allowlist. When the allowlist has a bug, as in the January PyTorch advisory, the restriction fails quietly.

Safetensors removes the question. It stores tensors and a header, with no executable content. That is why the durable fix for model loading is a format change and a provenance check, not a better scanner for pickles.

Try it yourself

Run the grep commands above against one training repository. Count the unguarded loads, and for each one write down who controls the file. Then check whether the models you pull each have a safetensors variant.

Click through it at your own pace, or open it full screen.

You can also check the dependencies in your requirements file with the free scan at gold.safeguard.sh/scan. It runs in your browser and the file never leaves your machine. The weakness class is CWE-502.

Quick check

Does torch.load with weights_only=True make an untrusted checkpoint safe?

No. CVE-2026-24747 was a bug in that restricted unpickler, fixed in PyTorch 2.10.0. Treat the flag as one layer, and prefer safetensors for untrusted weights.

What did the peft advisory get wrong, in one sentence?

Two modules called torch.load on config-specified files without weights_only=True, bypassing the project's own safe-loading wrapper.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.