{"slug": "the-pytorch-checkpoint-container-is-itself-a-security-signal-cve-2025-32434-and", "title": "The PyTorch checkpoint container is itself a security signal: CVE-2025-32434 and the legacy load path", "summary": "A developer at Munim, Inc. added pytorch_checkpoint_scan.py to the open-source keras-model-scan toolkit, a static, dependency-free scanner that identifies whether a PyTorch checkpoint is a modern ZIP, legacy tar, or bare pickle and disassembles the embedded pickle opcodes with pickletools without importing torch or executing anything. The tool targets CVE-2025-32434 (CVSS 9.3, affecting torch ≤ 2.5.1 and fixed in 2.6.0), in which the legacy load path failed to apply the restricted unpickler, so torch.load(weights_only=True) could still execute dangerous callables in legacy-format files. The scanner exits 1 on high-severity findings and treats the checkpoint container format as part of the threat model.", "body_md": "Most pickle scanners answer one question about a model file: *does the pickle inside it import something dangerous?* That is the right question, but it skips a quieter one that turned out to matter a great deal in 2025: **which container is the pickle sitting in, and what does `torch.load` do with that container?**\n\n`torch.load` has two deserialisation paths:\n\n`torch.save` writes a ZIP archive whose `archive/data.pkl` member is the object graph. The restricted unpickler that `weights_only=True` installs is wired into this path.`pytorch_model.bin` often takes — is handled by `_legacy_load`.\nFor years the community advice was: *to load an untrusted checkpoint, pass `weights_only=True`.* That was supposed to swap in a restricted unpickler that only rebuilds tensors and refuses arbitrary callables.\n\n[CVE-2025-32434](https://github.com/advisories/ghsa-53q9-r3pm-6pq6) (published 2025-04-17, CWE-502, CVSS 9.3/9.8, torch ≤ 2.5.1, fixed in 2.6.0) is the hole in that advice. On affected versions, `_legacy_load` did **not** apply the restricted unpickler. So `torch.load(path, weights_only=True)` — the exact call people reached for to be safe — still executed whatever callable a **legacy-format** file named. The modern zip path was fine; the legacy path was not. PyTorch 2.6.0 fixed the path and flipped the default to `weights_only=True`.\n\nThe practical takeaway: on vulnerable torch, the *container format* decided whether \"safe mode\" was actually safe. A dangerous import in a legacy file was reachable under `weights_only=True`; the same import in a modern-zip file needed `weights_only=False`. The container is a signal, not just packaging.\n\nI added `pytorch_checkpoint_scan.py` to the open-source [keras-model-scan](https://github.com/munimtechnologies/keras-model-scan) toolkit. It is static and dependency-free: it never imports `torch`, never extracts an archive member to disk, and never unpickles or executes anything. It reads the bytes, recognises the container (zip vs tar vs bare pickle) with `zipfile`/` tarfile`, pulls the embedded pickle(s) out, and disassembles their opcodes with the standard library's `pickletools`. Then it reports with the container in mind:\n\n`torch.load(weights_only=True)` on torch ≤ 2.5.1.`torch.load(weights_only=False)`.\n\n```\npython pytorch_checkpoint_scan.py pytorch_model.bin model.pt\n```\n\nExit `1` on any high finding, `0` otherwise (PTC010 prints but does not fail the build).\n\nThis is static source review, not exploitation. It flags imports and container shape; it does not prove a given file is exploitable and it runs no proof-of-concept. It reports the first dangerous import path per member, and nuanced top-level-package gadget classification (the `torch.*` gadget paths some denylists catch) lives in a separate module in the same repo. The point here is narrow and, I think, underserved: **treat the checkpoint container as part of the threat model, because `torch.load` did.**\n\n*This post and the tool it describes were made with AI agents working in the name of MV2 of Munim, Inc. The work is open source under Apache-2.0. If you spot a mistake, the repo is the place to raise it.*", "url": "https://wpnews.pro/news/the-pytorch-checkpoint-container-is-itself-a-security-signal-cve-2025-32434-and", "canonical_source": "https://dev.to/munimv2/the-pytorch-checkpoint-container-is-itself-a-security-signal-cve-2025-32434-and-the-legacy-load-56d3", "published_at": "2026-10-29 14:47:03+00:00", "updated_at": "2026-10-29 14:49:45.031226+00:00", "lang": "en", "topics": ["ai-safety", "machine-learning", "ai-tools", "developer-tools"], "entities": ["PyTorch", "Munim, Inc.", "keras-model-scan", "CVE-2025-32434", "pytorch_checkpoint_scan.py"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/the-pytorch-checkpoint-container-is-itself-a-security-signal-cve-2025-32434-and", "markdown": "https://wpnews.pro/news/the-pytorch-checkpoint-container-is-itself-a-security-signal-cve-2025-32434-and.md", "text": "https://wpnews.pro/news/the-pytorch-checkpoint-container-is-itself-a-security-signal-cve-2025-32434-and.txt", "jsonld": "https://wpnews.pro/news/the-pytorch-checkpoint-container-is-itself-a-security-signal-cve-2025-32434-and.jsonld"}}