In PyTorch before 2.7.0, when torch.compile is used, FractionalMaxPool2d has inconsistent results.
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
2.6.0 → fixed in 2.7.0
Details
In PyTorch before 2.7.0, when torch.compile is used, FractionalMaxPool2d has inconsistent results.
The fix
Update
test/inductor/test_torchinductor.py+8 −0
@@ -4626,6 +4626,14 @@ def fn(x):self.common(fn, (torch.randn(1, 4, 16, 16),), check_lowp=False)+def test_fractional_max_pool2d5(self):+def fn(x, samples):+return aten.fractional_max_pool2d(x, (1, 1), (8, 8), samples)++self.common(+fn, (torch.randn(2, 4, 16, 16), torch.rand(2, 4, 2)), check_lowp=False+)+def test_multi_threading(self):model = torch.nn.Linear(2, 3).eval()inp = torch.randn(4, 2)
torch/_inductor/lowering.py+4 −3
@@ -4741,7 +4741,7 @@ def inner_fn_max_idx(idx):)-def _fractional_pooling_offsets(samples, in_sz, out_sz, kernel_sz, dim):+def _fractional_pooling_offsets(samples, in_sz, out_sz, kernel_sz, dim, ndims):out_sz = out_sz[dim]in_sz = in_sz[dim]kernel_sz = kernel_sz[dim]@@ -4749,10 +4749,10 @@ def _fractional_pooling_offsets(samples, in_sz, out_sz, kernel_sz, dim):samples_loader = samples.make_loader()def load(prefix, i):-sample = samples_loader([*prefix, dim])+sample = samples_loader([*prefix, ndims - 1 - dim])i_expr = ops.index_expr(i, samples.get_dtype())alpha_expr = ops.index_expr(alpha, samples.get_dtype())-seq_i = ops.floor((i_expr + sample) * alpha_expr) - ops.floor(+seq_i = ops.trunc((i_expr + sample) * alpha_expr) - ops.trunc(sample * alpha_expr)seq_i = ops.to_dtype(seq_i, torch.int64)@@ -4784,6 +4784,7 @@ def fractional_max_pool2d(x, kernel_size, output_size, random_samples):in_sz=[inp_h, inp_w],out_sz=output_size,kernel_sz=kernel_size,+ndims=2,)h_index_fn = gen_offsets_for_dim(dim=0)test/inductor/test_torchinductor.py | 8 --------.../_internal/common_methods_invocations.py | 16 ++++++++++++++++2 files changed, 16 insertions(+), 8 deletions(-)
test/inductor/test_torchinductor.py+0 −8
@@ -4626,14 +4626,6 @@ def fn(x):self.common(fn, (torch.randn(1, 4, 16, 16),), check_lowp=False)-def test_fractional_max_pool2d5(self):-def fn(x, samples):-return aten.fractional_max_pool2d(x, (1, 1), (8, 8), samples)--self.common(-fn, (torch.randn(2, 4, 16, 16), torch.rand(2, 4, 2)), check_lowp=False-)-def test_multi_threading(self):model = torch.nn.Linear(2, 3).eval()inp = torch.randn(4, 2)
torch/testing/_internal/common_methods_invocations.py+16 −0
@@ -5003,6 +5003,14 @@ def sample_inputs_fractional_max_pool2d(op_info, device, dtype, requires_grad, *return_indices=return_indices,)+yield SampleInput(+make_arg((1, 1, 16, 16)),+(1, 1),+output_ratio=(0.5, 0.5),+return_indices=True,+_random_samples=make_tensor((1, 1, 2), device=device, dtype=dtype, requires_grad=False),+)+def sample_inputs_fractional_max_pool3d(op_info, device, dtype, requires_grad, **kwargs):make_arg = partial(make_tensor, device=device, dtype=dtype, requires_grad=requires_grad)@@ -5042,6 +5050,14 @@ def sample_inputs_fractional_max_pool3d(op_info, device, dtype, requires_grad, *return_indices=return_indices,)+yield SampleInput(+make_arg((1, 1, 16, 16, 16)),+(1, 1, 1),+output_ratio=(0.5, 0.5, 0.5),+return_indices=True,+_random_samples=make_tensor((1, 1, 3), device=device, dtype=dtype, requires_grad=False),+)+def sample_inputs_avgpool2d(op_info, device, dtype, requires_grad, **kwargs):make_arg = partial(make_tensor, device=device, dtype=dtype, requires_grad=requires_grad)torch/testing/_internal/common_methods_invocations.py | 8 --------1 file changed, 8 deletions(-)
torch/testing/_internal/common_methods_invocations.py+0 −8
@@ -5050,14 +5050,6 @@ def sample_inputs_fractional_max_pool3d(op_info, device, dtype, requires_grad, *return_indices=return_indices,)-yield SampleInput(-make_arg((1, 1, 16, 16, 16)),-(1, 1, 1),-output_ratio=(0.5, 0.5, 0.5),-return_indices=True,-_random_samples=make_tensor((1, 1, 3), device=device, dtype=dtype, requires_grad=False),-)-def sample_inputs_avgpool2d(op_info, device, dtype, requires_grad, **kwargs):make_arg = partial(make_tensor, device=device, dtype=dtype, requires_grad=requires_grad)