CVE-2026-31250 Overview
CVE-2026-31250 is an insecure deserialization vulnerability (CWE-502) in CosyVoice, an open-source voice generation framework. The flaw resides in the average_model.py model averaging tool, which loads PyTorch checkpoint files (epoch_*.pt) using torch.load() without setting the weights_only=True security parameter. This causes the pickle module to deserialize arbitrary Python objects from the checkpoint files. An attacker who supplies a crafted checkpoint achieves arbitrary code execution on the victim's system when the averaging tool processes the directory. The issue is present through commit 6e01309e01bc93bbeb83bdd996b1182a81aaf11e.
Critical Impact
Arbitrary code execution on systems that average CosyVoice model checkpoints sourced from untrusted locations.
Affected Products
- CosyVoice through commit 6e01309e01bc93bbeb83bdd996b1182a81aaf11e
- The average_model.py model averaging utility
- PyTorch checkpoint loading workflows that omit weights_only=True
Discovery Timeline
- 2026-05-11 - CVE-2026-31250 published to NVD
- 2026-05-12 - Last updated in NVD database
Technical Details for CVE-2026-31250
Vulnerability Analysis
The vulnerability is an insecure deserialization flaw in the model averaging utility shipped with CosyVoice. The average_model.py script enumerates checkpoint files matching the epoch_*.pt pattern in a user-supplied directory and loads them with torch.load(). Because torch.load() defaults to using Python's pickle module for object reconstruction, any callable referenced by a __reduce__ method inside the checkpoint executes during deserialization. A checkpoint is therefore a code-bearing artifact, not just data.
Exploitation runs in the victim's Python process with the privileges of the user executing the averaging tool. In machine learning pipelines that frequently process community-supplied weights, this provides a practical path to host compromise, credential theft, and downstream supply-chain attacks against trained models.
Root Cause
The root cause is the omission of the weights_only=True parameter when invoking torch.load(). PyTorch introduced this flag to restrict deserialization to safe tensor types and reject arbitrary Python object reconstruction. Without it, pickle opcodes such as REDUCE and GLOBAL are honored, allowing any importable callable to be invoked with attacker-controlled arguments.
Attack Vector
An attacker publishes or otherwise delivers a malicious epoch_*.pt file to a directory that a victim later passes to average_model.py. When the script iterates the directory and loads each checkpoint, the embedded pickle payload executes attacker code. Delivery channels include model hubs, shared training directories, mounted network storage, and downloads bundled with datasets or pretrained models. No authentication or user interaction with the malicious file is required beyond running the averaging tool against the directory.
Refer to the CosyVoice GitHub Repository for the affected source.
Detection Methods for CVE-2026-31250
Indicators of Compromise
- Unexpected epoch_*.pt files in model directories that did not originate from local training runs
- Python processes spawning shells, network connections, or unexpected child processes shortly after average_model.py execution
- Checkpoint files whose pickle stream contains references to os.system, subprocess, builtins.exec, or posix.system
Detection Strategies
- Scan .pt files with a pickle disassembler such as pickletools to identify GLOBAL opcodes referencing non-tensor modules
- Monitor invocations of torch.load() in code review and CI to require weights_only=True
- Hash and inventory trusted checkpoints, and alert on unsigned or unknown files appearing in averaging directories
Monitoring Recommendations
- Log child process creation from Python interpreters running ML tooling and alert on shells, curl, wget, or compiler invocations
- Capture outbound network connections from training and inference hosts, baselining expected destinations
- Track file integrity on model directories and flag new epoch_*.pt writes from non-training user accounts
How to Mitigate CVE-2026-31250
Immediate Actions Required
- Audit all callers of torch.load() in CosyVoice deployments and forks, and add weights_only=True where feasible
- Restrict write access to checkpoint directories to trusted training accounts only
- Quarantine any epoch_*.pt files received from external sources until they are verified
Patch Information
No official fixed version is identified in the NVD record at publication. Track the CosyVoice GitHub Repository for commits addressing average_model.py and apply the upstream fix when available. Additional notes are referenced in the CVE-2026-31250 disclosure details.
Workarounds
- Modify local copies of average_model.py to call torch.load(path, weights_only=True, map_location='cpu')
- Only average checkpoints produced by your own training pipeline and stored on access-controlled storage
- Execute averaging in a sandboxed environment such as a container with no network egress and a non-privileged user
# Configuration example: enforce safe checkpoint loading
# Patch average_model.py to require weights_only deserialization
sed -i 's/torch.load(\(.*\))/torch.load(\1, weights_only=True)/' average_model.py
# Run the averaging tool inside a restricted container
docker run --rm --network=none --read-only \
-v "$PWD/checkpoints:/data:ro" \
-u 1000:1000 cosyvoice:hardened \
python average_model.py --src_path /data
Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.


