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 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
torch/_dynamo/variables/builtin.py+14 −0
@@ -18,6 +18,7 @@import torchfrom torch import sym_float, sym_intfrom torch.utils._python_dispatch import is_traceable_wrapper_subclass+from torch._subclasses.meta_utils import is_sparse_anyfrom .. import config, graph_break_hints, polyfills, variablesfrom ..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 goodtest/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 torchfrom torch import sym_float, sym_int-from torch.utils._python_dispatch import is_traceable_wrapper_subclassfrom torch._subclasses.meta_utils import is_sparse_any+from torch.utils._python_dispatch import is_traceable_wrapper_subclassfrom .. import config, graph_break_hints, polyfills, variablesfrom ..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 goodclass PublicTorchCompilerTests(TestCase):