Security context
High· 7.5PYSEC-2025-209 CVE-2025-55560Published Sep 25, 2025

An issue in pytorch v2.7.0 can lead to a Denial of Service (DoS) when a PyTorch model consists of torch.Tensor.to_sparse() and torch.Tensor.to_dense() and is compiled by Inductor.

Research this vulnerability

Research is free — Hunters explains how the bug works, the root-cause code pattern, how the fix addresses it, and how to test whether a target is affected, in chat. Investigate & write exploit is a paid run — the engine reads the advisory and fix commits, then builds and validates a working proof-of-concept exploit with reproduction steps.

Affected versions

0 → fixed in 2.7.1

Details

An issue in pytorch v2.7.0 can lead to a Denial of Service (DoS) when a PyTorch model consists of torch.Tensor.to_sparse() and torch.Tensor.to_dense() and is compiled by Inductor.

The fix

fix graph break for sparse

Zhiyi Zhang· Apr 22, 2025, 08:25 AM+2924a5e9329f5
torch/_dynamo/variables/builtin.py+14 0
@@ -18,6 +18,7 @@
import torch
from torch import sym_float, sym_int
from torch.utils._python_dispatch import is_traceable_wrapper_subclass
+from torch._subclasses.meta_utils import is_sparse_any
from .. import config, graph_break_hints, polyfills, variables
from ..exc import (
@@ -1996,6 +1997,19 @@ def call_getattr(
"Please report an issue to PyTorch.",
],
)
+ if isinstance(obj, TensorVariable):
+ fake_val = obj.proxy.node.meta["example_value"]
+ if (
+ isinstance(fake_val, torch.Tensor)
+ and is_sparse_any(fake_val)
+ and (not tx.export or not config.capture_sparse_compute)
+ ):
+ unimplemented_v2(
+ gb_type="Attempted to wrap sparse Tensor",
+ context="",
+ explanation="torch.compile does not support sparse Tensors",
+ hints=[*graph_break_hints.SUPPORTABLE],
+ )
try:
return obj.var_getattr(tx, name)
test/dynamo/test_compile.py | 12 ++++++++++++
1 file changed, 12 insertions(+)
test/dynamo/test_compile.py+12 0
@@ -211,7 +211,19 @@ def fn(x: torch.Tensor) -> torch.Tensor:
a = torch.randn(1, 1)
out = torch.compile(fn)(a)
self.assertEqual(out, a)
+
+ def test_to_sparse_to_dense_with_graph_break(self):
+ def fn(x):
+ x = x.to_sparse()
+ x = x.to_dense()
+ return x
+
+ x = torch.tensor([[1.0]])
+ c_fn = torch.compile(fn)
+ output = fn(x)
+ c_output = c_fn(x)
+ self.assertEqual(output, c_output)
# The private variants of the below functions are extensively tested
# So as long as the signatures match we're good
test/dynamo/test_compile.py | 2 +-
torch/_dynamo/variables/builtin.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
test/dynamo/test_compile.py+1 1
@@ -211,7 +211,7 @@ def fn(x: torch.Tensor) -> torch.Tensor:
a = torch.randn(1, 1)
out = torch.compile(fn)(a)
self.assertEqual(out, a)
-
+
def test_to_sparse_to_dense_with_graph_break(self):
def fn(x):
x = x.to_sparse()
torch/_dynamo/variables/builtin.py+1 1
@@ -17,8 +17,8 @@
import torch
from torch import sym_float, sym_int
-from torch.utils._python_dispatch import is_traceable_wrapper_subclass
from torch._subclasses.meta_utils import is_sparse_any
+from torch.utils._python_dispatch import is_traceable_wrapper_subclass
from .. import config, graph_break_hints, polyfills, variables
from ..exc import (
test/dynamo/test_compile.py | 1 +
1 file changed, 1 insertion(+)
test/dynamo/test_compile.py+1 0
@@ -225,6 +225,7 @@ def fn(x):
c_output = c_fn(x)
self.assertEqual(output, c_output)
+
# The private variants of the below functions are extensively tested
# So as long as the signatures match we're good
class PublicTorchCompilerTests(TestCase):

References