Skip to content

NetAcceptAction::cancel() use-after-free fix: part 2#12874

Merged
bneradt merged 1 commit intoapache:masterfrom
bneradt:fix-net-accept-cancel-2
Feb 13, 2026
Merged

NetAcceptAction::cancel() use-after-free fix: part 2#12874
bneradt merged 1 commit intoapache:masterfrom
bneradt:fix-net-accept-cancel-2

Conversation

@bneradt
Copy link
Contributor

@bneradt bneradt commented Feb 10, 2026

This is a revised version of PR #12803 which was reverted in PR #12841 because it lacked the idempotent cancel guard, causing its own set of crashes.

There is a race between NetAcceptAction::cancel() and NetAccept::acceptEvent() where the server pointer could be dereferenced after the NetAccept object was deleted. Thread A calls cancel(), which sets cancelled=true via Action::cancel(). Thread B running acceptEvent() sees cancelled==true and deletes the NetAccept (including the embedded Server). Thread A then calls server->close() on freed memory.

This is fixed by making the server pointer in NetAcceptAction atomic and using exchange(nullptr) so only one thread can obtain and close the server. Additionally, cancel() is made idempotent by checking !cancelled before calling Action::cancel(), which prevents ink_assert(!cancelled) assertion failures when cancel is called from both external callers (TSActionCancel) and internal error paths (acceptEvent, acceptFastEvent, acceptLoopEvent).

This is a revised version of PR apache#12803 which was reverted in PR apache#12841
because it lacked the idempotent cancel guard, causing its own set of
crashes.

There is a race between NetAcceptAction::cancel() and
NetAccept::acceptEvent() where the server pointer could be dereferenced
after the NetAccept object was deleted. Thread A calls cancel(), which
sets cancelled=true via Action::cancel(). Thread B running acceptEvent()
sees cancelled==true and deletes the NetAccept (including the embedded
Server). Thread A then calls server->close() on freed memory.

This is fixed by making the server pointer in NetAcceptAction atomic and
using exchange(nullptr) so only one thread can obtain and close the
server. Additionally, cancel() is made idempotent by checking !cancelled
before calling Action::cancel(), which prevents ink_assert(!cancelled)
assertion failures when cancel is called from both external callers
(TSActionCancel) and internal error paths (acceptEvent, acceptFastEvent,
acceptLoopEvent).
@bneradt bneradt added this to the 11.0.0 milestone Feb 10, 2026
@bneradt bneradt self-assigned this Feb 10, 2026
@bneradt bneradt added the Crash label Feb 10, 2026
@bneradt bneradt requested a review from cmcfarlen February 10, 2026 23:33
Comment on lines +85 to +87
if (!cancelled) {
Action::cancel(cont);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This !canceled check is the only change made on top of the reverted #12803 PR. That should hopefully address the double-cancel issue (in addition to addressing the use after free issue, which the rest of the patch intends to fix).

@bneradt
Copy link
Contributor Author

bneradt commented Feb 11, 2026

[approve ci autest 0 rocky]

@bneradt
Copy link
Contributor Author

bneradt commented Feb 11, 2026

[approve ci rocky]

@bneradt
Copy link
Contributor Author

bneradt commented Feb 11, 2026

[approve ci autest rocky]

@bneradt bneradt merged commit 6112a78 into apache:master Feb 13, 2026
15 checks passed
@bneradt bneradt deleted the fix-net-accept-cancel-2 branch February 13, 2026 23:14
@github-project-automation github-project-automation bot moved this to For v10.2.0 in ATS v10.2.x Feb 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: For v10.2.0

Development

Successfully merging this pull request may close these issues.

2 participants