Security context
High· 7.5PYSEC-2025-207 CVE-2025-55557Published Sep 25, 2025

A Name Error occurs in pytorch v2.7.0 when a PyTorch model consists of torch.cummin and is compiled by Inductor, leading to a Denial of Service (DoS).

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

A Name Error occurs in pytorch v2.7.0 when a PyTorch model consists of torch.cummin and is compiled by Inductor, leading to a Denial of Service (DoS).

The fix

Update

Isuru Fernando· Apr 22, 2025, 06:54 PM+31122f3a9f0325
test/inductor/test_torchinductor.py+10 0
@@ -2543,6 +2543,16 @@ def fn(x):
self.common(fn, (torch.ones(32, 32) * 70,))
+ def test_cummin(self):
+ def fn(x):
+ return x.cummin(0)
+
+ self.common(
+ fn, (torch.rand(16, 32),), check_lowp=not is_halide_backend(self.device)
+ )
+ self.common(fn, (torch.rand(1),), check_lowp=not is_halide_backend(self.device))
+ self.common(fn, (torch.rand(0),), check_lowp=not is_halide_backend(self.device))
+
def test_cumsum(self):
def fn(x):
return x.cumsum(0), x.cumsum(1)
torch/_inductor/lowering.py+4 4
@@ -6295,9 +6295,9 @@ def log_add_exp_helper(a_tuple, b_tuple):
@register_lowering(aten.cummax, type_promotion_kind=None)
def cummax(x, axis=None):
- if len(x.get_size()) == 0:
+ if x.get_numel() <= 1:
assert axis in [0, -1]
- return clone(x), empty_like(x, dtype=torch.int64)
+ return clone(x), zeros_like(x, dtype=torch.int64)
dtype = x.get_dtype()
combine_fn = ir.get_reduction_combine_fn(
@@ -6315,9 +6315,9 @@ def cummax(x, axis=None):
@register_lowering(aten.cummin, type_promotion_kind=None)
def cummin(x, axis=None):
- if len(x.get_size()) == 0:
+ if x.get_numel() <= 1:
assert axis in [0, -1]
- return clone(x), empty_like(x, dtype=torch.int64)
+ return clone(x), zeros_like(x, dtype=torch.int64)
dtype = x.get_dtype()
combine_fn = ir.get_reduction_combine_fn(
test/inductor/test_torchinductor_codegen_dynamic_shapes.py | 1 +
torch/_inductor/lowering.py | 4 ++--
2 files changed, 3 insertions(+), 2 deletions(-)
test/inductor/test_torchinductor_codegen_dynamic_shapes.py+1 0
@@ -159,6 +159,7 @@ def run(*ex, **kwargs):
"test_conv_functional_bn_fuse_dynamic_shapes": TestFailure(("cpu",), is_skip=True),
"test_convolution2_dynamic_shapes": TestFailure(("cpu",)),
"test_cumprod_zero_dim_dynamic_shapes": TestFailure(("cpu",)),
+ "test_cummin_dynamic_shapes": TestFailure(("cpu", "cuda", "xpu")),
"test_cumsum_dynamic_shapes": TestFailure(("cpu",)),
"test_cumsum_no_mask_dynamic_shapes": TestFailure(("cpu",)),
"test_cumsum_zero_dim_dynamic_shapes": TestFailure(("cpu",)),
torch/_inductor/lowering.py+2 2
@@ -6295,7 +6295,7 @@ def log_add_exp_helper(a_tuple, b_tuple):
@register_lowering(aten.cummax, type_promotion_kind=None)
def cummax(x, axis=None):
- if x.get_numel() <= 1:
+ if V.graph.sizevars.statically_known_leq(x.get_numel(), 1):
assert axis in [0, -1]
return clone(x), zeros_like(x, dtype=torch.int64)
@@ -6315,7 +6315,7 @@ def cummax(x, axis=None):
@register_lowering(aten.cummin, type_promotion_kind=None)
def cummin(x, axis=None):
- if x.get_numel() <= 1:
+ if V.graph.sizevars.statically_known_leq(x.get_numel(), 1):
assert axis in [0, -1]
return clone(x), zeros_like(x, dtype=torch.int64)
test/inductor/test_torchinductor.py | 1 +
1 file changed, 1 insertion(+)
test/inductor/test_torchinductor.py+1 0
@@ -2543,6 +2543,7 @@ def fn(x):
self.common(fn, (torch.ones(32, 32) * 70,))
+ @skip_if_halide
def test_cummin(self):
def fn(x):
return x.cummin(0)
torch/_inductor/lowering.py | 18 ++++++++++++------
1 file changed, 12 insertions(+), 6 deletions(-)
torch/_inductor/lowering.py+12 6
@@ -6295,9 +6295,9 @@ def log_add_exp_helper(a_tuple, b_tuple):
@register_lowering(aten.cummax, type_promotion_kind=None)
def cummax(x, axis=None):
- if V.graph.sizevars.statically_known_leq(x.get_numel(), 1):
+ if len(x.get_size()) == 0:
assert axis in [0, -1]
- return clone(x), zeros_like(x, dtype=torch.int64)
+ return clone(x), empty_like(x, dtype=torch.int64)
dtype = x.get_dtype()
combine_fn = ir.get_reduction_combine_fn(
@@ -6306,7 +6306,10 @@ def cummax(x, axis=None):
kwargs = _make_scan_inner(x, axis=axis, dtype=dtype)
kwargs["dtypes"] = (dtype, torch.int64)
- kwargs["inner_fns"] = (x.make_loader(), lambda _: "rindex")
+ kwargs["inner_fns"] = (
+ x.make_loader(),
+ lambda idx: ops.index_expr(idx[axis], torch.int64),
+ )
values, indices = ir.Scan.create(**kwargs, combine_fn=combine_fn) # type: ignore[arg-type]
if values is None:
return fallback_cummax(x, dim=axis)
@@ -6315,9 +6318,9 @@ def cummax(x, axis=None):
@register_lowering(aten.cummin, type_promotion_kind=None)
def cummin(x, axis=None):
- if V.graph.sizevars.statically_known_leq(x.get_numel(), 1):
+ if len(x.get_size()) == 0:
assert axis in [0, -1]
- return clone(x), zeros_like(x, dtype=torch.int64)
+ return clone(x), empty_like(x, dtype=torch.int64)
dtype = x.get_dtype()
combine_fn = ir.get_reduction_combine_fn(
@@ -6326,7 +6329,10 @@ def cummin(x, axis=None):
kwargs = _make_scan_inner(x, axis=axis, dtype=dtype)
kwargs["dtypes"] = (dtype, torch.int64)
- kwargs["inner_fns"] = (x.make_loader(), lambda _: "rindex")
+ kwargs["inner_fns"] = (
+ x.make_loader(),
+ lambda idx: ops.index_expr(idx[axis], torch.int64),
+ )
values, indices = ir.Scan.create(**kwargs, combine_fn=combine_fn) # type: ignore[arg-type]
if values is None:
return fallback_cummin(x, dim=axis)
test/inductor/test_torchinductor.py | 1 +
1 file changed, 1 insertion(+)
test/inductor/test_torchinductor.py+1 0
@@ -2544,6 +2544,7 @@ def fn(x):
self.common(fn, (torch.ones(32, 32) * 70,))
@skip_if_halide
+ @xfail_if_mps
def test_cummin(self):
def fn(x):
return x.cummin(0)

References