Skip to content

CUDA backend: delete each output handle once when a program returns one tensor twice - #24442

Closed
pytorchbot wants to merge 1 commit into
release/1.6from
cherry-pick-24420-by-pytorch_bot_bot_
Closed

pytorchbot wants to merge 1 commit into
release/1.6from
cherry-pick-24420-by-pytorch_bot_bot_

Conversation

@pytorchbot

Copy link
Copy Markdown
Collaborator

What is wrong today

When a CUDA program returns the same tensor more than once, for example y.detach() twice, AOTI (AOTInductor, PyTorch's ahead-of-time compiler that generates the CUDA code) puts one output handle in each of those output slots:

output_handles[0] = buf0.release();
output_handles[1] = output_handles[0];

The CUDA backend copied each output into its ExecuTorch tensor and then deleted that slot's handle. So it deleted the shared handle twice, and the process crashed, for example with:

SlimTensor storage_offset must not be negative, got -9007650542493611503
Failed to D2D copy GPU output 1 into ETensor
free(): double free detected in tcache 2

What this change does

A handle is now deleted only at the last output slot that holds it, as PyTorch's own AOTI runner does. The cleanup on error paths follows the same rule. With CUDA graphs on, a successful capture keeps every output handle for the graph and deletes none of them, so nothing changes there. If ending the capture or instantiating the graph fails, the cleanup deletes the handles by the same rule.

A comment in ReplaceViewCopyWithViewPass said that a view would make two outputs share one handle. That was misleading, because two alias_copy outputs of one tensor share one handle too, so the comment no longer says it.

What was tested

test_same_tensor_returned_twice returns one tensor in two output slots that are not next to each other, with CUDA graphs on: three eager calls, a graph capture and a replay, each compared with eager. With torch 2.14 on an A100 it passes, and it crashes without this change. The other backends/cuda tests that CI runs in the same job still pass.

…ne tensor twice (#24420)

### What is wrong today

When a CUDA program returns the same tensor more than once, for example
`y.detach()` twice, AOTI (AOTInductor, PyTorch's ahead-of-time compiler
that generates the CUDA code) puts one output handle in each of those
output slots:

```
output_handles[0] = buf0.release();
output_handles[1] = output_handles[0];
```

The CUDA backend copied each output into its ExecuTorch tensor and then
deleted that slot's handle. So it deleted the shared handle twice, and
the process crashed, for example with:

```
SlimTensor storage_offset must not be negative, got -9007650542493611503
Failed to D2D copy GPU output 1 into ETensor
free(): double free detected in tcache 2
```

### What this change does

A handle is now deleted only at the last output slot that holds it, as
PyTorch's own AOTI runner does. The cleanup on error paths follows the
same rule. With CUDA graphs on, a successful capture keeps every output
handle for the graph and deletes none of them, so nothing changes there.
If ending the capture or instantiating the graph fails, the cleanup
deletes the handles by the same rule.

A comment in `ReplaceViewCopyWithViewPass` said that a view would make
two outputs share one handle. That was misleading, because two
`alias_copy` outputs of one tensor share one handle too, so the comment
no longer says it.

### What was tested

`test_same_tensor_returned_twice` returns one tensor in two output slots
that are not next to each other, with CUDA graphs on: three eager calls,
a graph capture and a replay, each compared with eager. With torch 2.14
on an A100 it passes, and it crashes without this change. The other
`backends/cuda` tests that CI runs in the same job still pass.

(cherry picked from commit 5c05ad4)
@pytorch-bot

pytorch-bot Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

🔗 Helpful Links

🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/24442

Note: Links to docs will display an error until the docs builds have been completed.

⏳ No Failures, 204 Pending

As of commit acd31c5 with merge base 4db778e (image):
💚 Looks good so far! There are no failures yet. 💚

This comment was automatically generated by Dr. CI and updates every 15 minutes.

@meta-cla meta-cla Bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Nov 2, 2026
@shoumikhin shoumikhin closed this Nov 2, 2026
@shoumikhin
shoumikhin deleted the cherry-pick-24420-by-pytorch_bot_bot_ branch November 2, 2026 17:06
@shoumikhin

Copy link
Copy Markdown
Contributor

Replaced by #24448, which is stacked on #24324 so it carries only this change.

This branch was successfully deployed

1 active and 1 inactive deployments
upload-benchmark-results — acd31c58 Deployed Nov 2, 2026 by pytorchbot via upload-benchmark-results #21731
cadence — acd31c58 Deployed Nov 2, 2026 by pytorchbot via hifi-op-test / hifi4 #35964
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants