PyTorch Improper Resource Shutdown or Release 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
Details
A vulnerability, which was classified as problematic, was found in PyTorch 2.6.0. Affected is the function torch.nn.functional.ctc_loss of the file aten/src/ATen/native/LossCTC.cpp. The manipulation leads to denial of service. An attack has to be approached locally. The exploit has been disclosed to the public and may be used. The name of the patch is 46fc5d8e360127361211cb237d5f9eef0223e567. It is recommended to apply a patch to fix this issue.
The fix
Add check for ctc_loss targets param
aten/src/ATen/native/LossCTC.cpp+1 −0
@@ -126,6 +126,7 @@ std::tuple<Tensor, Tensor, size_t, std::vector<int64_t>> ctc_loss_allocate_outpu// the alphas from the user by only returning the loss.template<typename scalar_t, ScalarType target_scalar_type>std::tuple<Tensor, Tensor> ctc_loss_cpu_template(const Tensor& log_probs, const Tensor& targets, IntArrayRef input_lengths, IntArrayRef target_lengths, int64_t BLANK) {+TORCH_CHECK(targets.numel() > 0, "targets tensor must not be empty");// log_probs: input_len x batch_size x num_labels// targets [int64]: batch_size x target_length OR sum(target_lengths)constexpr scalar_t neginf = -std::numeric_limits<scalar_t>::infinity();
aten/src/ATen/native/cuda/LossCTC.cu+1 −0
@@ -219,6 +219,7 @@ ctc_loss_log_alpha_gpu_kernel(scalar_t* __restrict__ log_alpha_data,// backward. The dispatch function will only return the loss.template<typename scalar_t, ScalarType target_scalar_type>std::tuple<Tensor, Tensor> ctc_loss_gpu_template(const Tensor& log_probs, const Tensor& targets, IntArrayRef input_lengths, IntArrayRef target_lengths, int64_t BLANK) {+TORCH_CHECK(targets.numel() > 0, "targets tensor must not be empty");// log_probs: input_len x batch_size x num_labels// targets [int64]: batch_size x target_length OR sum(target_lengths)CheckedFrom c = "ctc_loss_gpu";
test/test_nn.py+8 −0
@@ -11512,6 +11512,14 @@ def test_ctc_loss_cudnn_tensor(self, device):grad_cudnn, = torch.autograd.grad(loss_cudnn, log_probs, grad_out)self.assertEqual(grad_cudnn, grad_native, atol=1e-4, rtol=0)+def test_ctc_loss_error(self, device):+log_probs = torch.rand(0, 0, 4, device=device)+targets = torch.tensor([], device=device, dtype=torch.long)+input_lengths = torch.tensor([], device=device, dtype=torch.long)+target_lengths = torch.tensor([], device=device, dtype=torch.long)+with self.assertRaisesRegex(RuntimeError, "targets tensor must not be empty"):+F.ctc_loss(log_probs, targets, input_lengths, target_lengths, reduction='none')+@expectedFailureMPS # RuntimeError: LSTM with projections is not currently supported with MPS.@dtypesIfCUDA(torch.half, torch.float, torch.double)@dtypes(torch.float)aten/src/ATen/native/LossCTC.cpp | 2 +-aten/src/ATen/native/cuda/LossCTC.cu | 2 +-test/test_nn.py | 2 +-3 files changed, 3 insertions(+), 3 deletions(-)
aten/src/ATen/native/LossCTC.cpp+1 −1
@@ -126,7 +126,7 @@ std::tuple<Tensor, Tensor, size_t, std::vector<int64_t>> ctc_loss_allocate_outpu// the alphas from the user by only returning the loss.template<typename scalar_t, ScalarType target_scalar_type>std::tuple<Tensor, Tensor> ctc_loss_cpu_template(const Tensor& log_probs, const Tensor& targets, IntArrayRef input_lengths, IntArrayRef target_lengths, int64_t BLANK) {-TORCH_CHECK(targets.numel() > 0, "targets tensor must not be empty");+TORCH_CHECK(log_probs.numel() > 0, "log_probs tensor must not be empty");// log_probs: input_len x batch_size x num_labels// targets [int64]: batch_size x target_length OR sum(target_lengths)constexpr scalar_t neginf = -std::numeric_limits<scalar_t>::infinity();
aten/src/ATen/native/cuda/LossCTC.cu+1 −1
@@ -219,7 +219,7 @@ ctc_loss_log_alpha_gpu_kernel(scalar_t* __restrict__ log_alpha_data,// backward. The dispatch function will only return the loss.template<typename scalar_t, ScalarType target_scalar_type>std::tuple<Tensor, Tensor> ctc_loss_gpu_template(const Tensor& log_probs, const Tensor& targets, IntArrayRef input_lengths, IntArrayRef target_lengths, int64_t BLANK) {-TORCH_CHECK(targets.numel() > 0, "targets tensor must not be empty");+TORCH_CHECK(log_probs.numel() > 0, "log_probs tensor must not be empty");// log_probs: input_len x batch_size x num_labels// targets [int64]: batch_size x target_length OR sum(target_lengths)CheckedFrom c = "ctc_loss_gpu";
test/test_nn.py+1 −1
@@ -11517,7 +11517,7 @@ def test_ctc_loss_error(self, device):targets = torch.tensor([], device=device, dtype=torch.long)input_lengths = torch.tensor([], device=device, dtype=torch.long)target_lengths = torch.tensor([], device=device, dtype=torch.long)-with self.assertRaisesRegex(RuntimeError, "targets tensor must not be empty"):+with self.assertRaisesRegex(RuntimeError, "log_probs tensor must not be empty"):F.ctc_loss(log_probs, targets, input_lengths, target_lengths, reduction='none')@expectedFailureMPS # RuntimeError: LSTM with projections is not currently supported with MPS.test/test_nn.py | 1 +1 file changed, 1 insertion(+)
test/test_nn.py+1 −0
@@ -11512,6 +11512,7 @@ def test_ctc_loss_cudnn_tensor(self, device):grad_cudnn, = torch.autograd.grad(loss_cudnn, log_probs, grad_out)self.assertEqual(grad_cudnn, grad_native, atol=1e-4, rtol=0)+@expectedFailureMPSdef test_ctc_loss_error(self, device):log_probs = torch.rand(0, 0, 4, device=device)targets = torch.tensor([], device=device, dtype=torch.long)
Add check for ctc_loss targets param (#150981)
aten/src/ATen/native/LossCTC.cpp+1 −0
@@ -126,6 +126,7 @@ std::tuple<Tensor, Tensor, size_t, std::vector<int64_t>> ctc_loss_allocate_outpu// the alphas from the user by only returning the loss.template<typename scalar_t, ScalarType target_scalar_type>std::tuple<Tensor, Tensor> ctc_loss_cpu_template(const Tensor& log_probs, const Tensor& targets, IntArrayRef input_lengths, IntArrayRef target_lengths, int64_t BLANK) {+TORCH_CHECK(log_probs.numel() > 0, "log_probs tensor must not be empty");// log_probs: input_len x batch_size x num_labels// targets [int64]: batch_size x target_length OR sum(target_lengths)constexpr scalar_t neginf = -std::numeric_limits<scalar_t>::infinity();
aten/src/ATen/native/cuda/LossCTC.cu+1 −0
@@ -219,6 +219,7 @@ ctc_loss_log_alpha_gpu_kernel(scalar_t* __restrict__ log_alpha_data,// backward. The dispatch function will only return the loss.template<typename scalar_t, ScalarType target_scalar_type>std::tuple<Tensor, Tensor> ctc_loss_gpu_template(const Tensor& log_probs, const Tensor& targets, IntArrayRef input_lengths, IntArrayRef target_lengths, int64_t BLANK) {+TORCH_CHECK(log_probs.numel() > 0, "log_probs tensor must not be empty");// log_probs: input_len x batch_size x num_labels// targets [int64]: batch_size x target_length OR sum(target_lengths)CheckedFrom c = "ctc_loss_gpu";
test/test_nn.py+9 −0
@@ -11532,6 +11532,15 @@ def test_ctc_loss_cudnn_tensor(self, device):grad_cudnn, = torch.autograd.grad(loss_cudnn, log_probs, grad_out)self.assertEqual(grad_cudnn, grad_native, atol=1e-4, rtol=0)+@expectedFailureMPS+def test_ctc_loss_error(self, device):+log_probs = torch.rand(0, 0, 4, device=device)+targets = torch.tensor([], device=device, dtype=torch.long)+input_lengths = torch.tensor([], device=device, dtype=torch.long)+target_lengths = torch.tensor([], device=device, dtype=torch.long)+with self.assertRaisesRegex(RuntimeError, "log_probs tensor must not be empty"):+F.ctc_loss(log_probs, targets, input_lengths, target_lengths, reduction='none')+@expectedFailureMPS # RuntimeError: LSTM with projections is not currently supported with MPS.@dtypesIfCUDA(torch.half, torch.float, torch.double)@dtypes(torch.float)
Add check for ctc_loss targets param (#150981)
aten/src/ATen/native/LossCTC.cpp+1 −0
@@ -126,6 +126,7 @@ std::tuple<Tensor, Tensor, size_t, std::vector<int64_t>> ctc_loss_allocate_outpu// the alphas from the user by only returning the loss.template<typename scalar_t, ScalarType target_scalar_type>std::tuple<Tensor, Tensor> ctc_loss_cpu_template(const Tensor& log_probs, const Tensor& targets, IntArrayRef input_lengths, IntArrayRef target_lengths, int64_t BLANK) {+TORCH_CHECK(log_probs.numel() > 0, "log_probs tensor must not be empty");// log_probs: input_len x batch_size x num_labels// targets [int64]: batch_size x target_length OR sum(target_lengths)constexpr scalar_t neginf = -std::numeric_limits<scalar_t>::infinity();
aten/src/ATen/native/cuda/LossCTC.cu+1 −0
@@ -219,6 +219,7 @@ ctc_loss_log_alpha_gpu_kernel(scalar_t* __restrict__ log_alpha_data,// backward. The dispatch function will only return the loss.template<typename scalar_t, ScalarType target_scalar_type>std::tuple<Tensor, Tensor> ctc_loss_gpu_template(const Tensor& log_probs, const Tensor& targets, IntArrayRef input_lengths, IntArrayRef target_lengths, int64_t BLANK) {+TORCH_CHECK(log_probs.numel() > 0, "log_probs tensor must not be empty");// log_probs: input_len x batch_size x num_labels// targets [int64]: batch_size x target_length OR sum(target_lengths)CheckedFrom c = "ctc_loss_gpu";
test/test_nn.py+9 −0
@@ -11532,6 +11532,15 @@ def test_ctc_loss_cudnn_tensor(self, device):grad_cudnn, = torch.autograd.grad(loss_cudnn, log_probs, grad_out)self.assertEqual(grad_cudnn, grad_native, atol=1e-4, rtol=0)+@expectedFailureMPS+def test_ctc_loss_error(self, device):+log_probs = torch.rand(0, 0, 4, device=device)+targets = torch.tensor([], device=device, dtype=torch.long)+input_lengths = torch.tensor([], device=device, dtype=torch.long)+target_lengths = torch.tensor([], device=device, dtype=torch.long)+with self.assertRaisesRegex(RuntimeError, "log_probs tensor must not be empty"):+F.ctc_loss(log_probs, targets, input_lengths, target_lengths, reduction='none')+@expectedFailureMPS # RuntimeError: LSTM with projections is not currently supported with MPS.@dtypesIfCUDA(torch.half, torch.float, torch.double)@dtypes(torch.float)
References
- ADVISORYhttps://nvd.nist.gov/vuln/detail/CVE-2025-3730
- WEBhttps://github.com/pytorch/pytorch/issues/150835
- WEBhttps://github.com/pytorch/pytorch/pull/150981
- WEBhttps://github.com/pytorch/pytorch/commit/01f226bfb8f2c343f5c614a6bbf685d91160f3af
- WEBhttps://github.com/timocafe/tewart-pytorch/commit/46fc5d8e360127361211cb237d5f9eef0223e567
- PACKAGEhttps://github.com/pytorch/pytorch
- WEBhttps://vuldb.com/?ctiid.305076
- WEBhttps://vuldb.com/?id.305076
- WEBhttps://vuldb.com/?submit.553645