PyTorch before 3.7.0 has a bernoulli_p decompose function in decompositions.py even though it lacks full consistency with the eager CPU implementation, negatively affecting nn.Dropout1d, nn.Dropout2d,
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
PyTorch before 3.7.0 has a bernoulli_p decompose function in decompositions.py even though it lacks full consistency with the eager CPU implementation, negatively affecting nn.Dropout1d, nn.Dropout2d, and nn.Dropout3d for fallback_random=True.
The fix
Update
test/inductor/test_cpu_repro.py+21 −0
@@ -4946,6 +4946,27 @@ def forward(self, context_layer, hidden_states):torch.compile(converted_model)(*example_batch)check_metrics_vec_kernel_count(3)+def test_dropout(self):+class Model(nn.Module):+def __init__(self, dim):+super().__init__()+self.dropout = eval(f"nn.Dropout{dim}d(p=0.5)")++def forward(self, x):+torch.manual_seed(0)+x = self.dropout(x)+return x++for dim in [1, 2, 3]:+model = Model(dim)+torch.manual_seed(0)+shape = [1, 3] + [256] * dim+x = torch.randn(*shape)+output = model(x)+c_model = torch.compile(model)+c_output = c_model(x)+self.assertTrue(torch.allclose(output, c_output))+if __name__ == "__main__":from torch._inductor.test_case import run_tests
test/test_decomp.py+2 −0
@@ -22,6 +22,7 @@onlyCUDA,onlyNativeDeviceTypes,ops,+skipCPUIf,)from torch.testing._internal.common_methods_invocations import (op_db,@@ -608,6 +609,7 @@ def test_uniform(self, device):res = torch._decomp.decompositions.uniform(x, low=low, high=high)self.assertEqual(ref, res)+@skipCPUIf(True, "skip CPU device for testing bernoulli_p decomposition")def test_bernoulli_p(self, device):p = 0.3input_t = torch.rand(100, 100)
torch/_decomp/decompositions.py+3 −1
@@ -5119,13 +5119,15 @@ def bernoulli(@register_decomposition(aten.bernoulli.p)def bernoulli_p(self, p, *, generator: Optional[torch.Generator] = None):+if self.device.type == "cpu":+return NotImplementedif generator is None:raw_p = torch.rand(self.size(), dtype=torch.float32, device=self.device)else:raw_p = torch.rand(self.size(),generator=generator,-dtype=self.float32,+dtype=torch.float32,device=self.device,)p = (raw_p < p).to(self.dtype)test/test_decomp.py | 2 +-1 file changed, 1 insertion(+), 1 deletion(-)
test/test_decomp.py+1 −1
@@ -612,7 +612,7 @@ def test_uniform(self, device):@skipCPUIf(True, "skip CPU device for testing bernoulli_p decomposition")def test_bernoulli_p(self, device):p = 0.3-input_t = torch.rand(100, 100)+input_t = torch.rand(100, 100).to(device)torch.manual_seed(123)ref = torch.ops.aten.bernoulli.p(input_t, p)torch.manual_seed(123)test/test_decomp.py | 12 ------------torch/_decomp/decompositions.py | 17 -----------------2 files changed, 29 deletions(-)
test/test_decomp.py+0 −12
@@ -22,7 +22,6 @@onlyCUDA,onlyNativeDeviceTypes,ops,-skipCPUIf,)from torch.testing._internal.common_methods_invocations import (op_db,@@ -609,17 +608,6 @@ def test_uniform(self, device):res = torch._decomp.decompositions.uniform(x, low=low, high=high)self.assertEqual(ref, res)-@skipCPUIf(True, "skip CPU device for testing bernoulli_p decomposition")-def test_bernoulli_p(self, device):-p = 0.3-input_t = torch.rand(100, 100).to(device)-torch.manual_seed(123)-ref = torch.ops.aten.bernoulli.p(input_t, p)-torch.manual_seed(123)-res = torch._decomp.decompositions.bernoulli_p(input_t, p)-ref_p = ref.sum() / torch.prod(torch.tensor(ref.size()))-res_p = res.sum() / torch.prod(torch.tensor(res.size()))-self.assertEqual(ref_p, res_p, atol=0.06 * p, rtol=0.06)def test_bernoulli_default(self, device):p = 0.3
torch/_decomp/decompositions.py+0 −17
@@ -5117,23 +5117,6 @@ def bernoulli(return p-@register_decomposition(aten.bernoulli.p)-def bernoulli_p(self, p, *, generator: Optional[torch.Generator] = None):-if self.device.type == "cpu":-return NotImplemented-if generator is None:-raw_p = torch.rand(self.size(), dtype=torch.float32, device=self.device)-else:-raw_p = torch.rand(-self.size(),-generator=generator,-dtype=torch.float32,-device=self.device,-)-p = (raw_p < p).to(self.dtype)-return p--def isin_default(elements, test_elements, *, invert=False):if elements.numel() == 0:return torch.empty_like(elements, dtype=torch.bool)test/test_decomp.py | 1 -1 file changed, 1 deletion(-)
test/test_decomp.py+0 −1
@@ -608,7 +608,6 @@ def test_uniform(self, device):res = torch._decomp.decompositions.uniform(x, low=low, high=high)self.assertEqual(ref, res)-def test_bernoulli_default(self, device):p = 0.3p_t = p * torch.ones(5, 5)test/expect/HasDecompTest.test_has_decomposition.expect | 1 +1 file changed, 1 insertion(+)
test/expect/HasDecompTest.test_has_decomposition.expect+1 −0
@@ -704,6 +704,7 @@ aten::bernoulli.Tensoraten::bernoulli.Tensor_outaten::bernoulli.float_outaten::bernoulli.out+aten::bernoulli.paten::bernoulli_.Tensoraten::bernoulli_.floataten::bincount
References
- WEBhttps://github.com/pytorch/pytorch/compare/v2.6.0...v2.7.0
- ADVISORYhttps://gist.github.com/shaoyuyoung/4bcefba4004f8271e64b5185c95a248a
- ADVISORYhttps://gist.github.com/shaoyuyoung/e636f2e7a306105b7e96809e2b85c28a
- REPORThttps://github.com/pytorch/pytorch/issues/142853
- FIXhttps://github.com/pytorch/pytorch/pull/143460