network/tc: Fix stack overflow/concurrent modification when deleting TC qdisc trees (backport for v255) - #2
Conversation
…drop() With the current algorithm, we can end up removing entries from the qdisc/tclass sets while having multiple open iterators over the sets at various positions which leads to assertion failures in the hashmap logic as it's only safe to remove the "current" entry. To avoid the problem, let's split up marking and dropping of tclasses and qdiscs. First, we recursively iterate tclasses/qdiscs and mark all that need to be removed. Next, we iterate once over tclasses and qdiscs and remove all marked entries. Fixes 632d321
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0d6df438b9
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| continue; | ||
|
|
||
| tclass_drop(tclass); | ||
| tclass_mark_recursive(tclass); |
There was a problem hiding this comment.
Use iterative marking to prevent deep-tree stack overflow
When deleting a sufficiently deep alternating qdisc/class hierarchy, this call still mutually recurses through tclass_mark_recursive() and back into qdisc_mark_recursive(), consuming one or more stack frames per tree level and potentially crashing networkd before the sweep begins. The two-phase approach prevents concurrent set modification, but it does not address the stated stack-overflow scenario; the marking traversal needs an explicit work queue or stack.
Useful? React with 👍 / 👎.
A UKI might have a version from .osrelease which is not tied to the
kernel version. Pretty print the .uname section too, if present.
type: Boot Loader Specification Type #2 (UKI, .efi)
title: Debian GNU/Linux 13 (trixie) (34.186) (selected)
id: debian-trixie_34.186_x86-64.efi
source: /boot/efi//EFI/Linux/debian-trixie_34.186_x86-64.efi (on the EFI System Partition)
sort-key: debian-trixie
version: 34.186
uname: 6.12.100+deb13-amd64
linux: /boot/efi//EFI/Linux/debian-trixie_34.186_x86-64.efi
profile: 0
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Explore these optional code suggestions:
|
User description
Backport of upstream commit ee8f605 to v255.
Original issue: systemd#32247
This fixes a stack overflow SIGSEGV (or hashmap assertion ABRT) when deleting deep TC qdisc trees, which caused mass machine disconnection in production when applying traffic control rules.
The fix:
Original patch by Daan De Meyer
<daan.j.demeyer@gmail.com>.Backport to v255 by Rocker Zhang
<zhang.rocker.liyuan@gmail.com>.Closes systemd#32247.
PR Type
Bug fix
Description
Fix stack overflow and hashmap assertion when deleting deep TC qdisc trees
Replace mutually recursive qdisc/tclass deletion with two-phase mark-and-sweep
First recursively mark nodes, then drop all marked entries in single passes
Backport of upstream fix ee8f605 for issue networkd segfault/stack overflow when dropping tclass/qdisc systemd/systemd#32247 to v255
Diagram Walkthrough
File Walkthrough
qdisc.c
Two-phase mark-and-sweep qdisc deletionsrc/network/tc/qdisc.c
qdisc_drop()into two-phase mark-and-sweep:qdisc_mark_recursive()marks qdisc and child tclasseslink_qdisc_drop_marked()that iterates once overlink->qdiscsandfrees marked entries
qdisc_drop()now marks recursively, then callslink_tclass_drop_marked()beforelink_qdisc_drop_marked()and returnsNULL
tclass_drop()that caused stackoverflow and unsafe set modification
tclass.c
Two-phase mark-and-sweep tclass deletionsrc/network/tc/tclass.c
tclass_drop()into two-phase mark-and-sweep:tclass_mark_recursive()marks tclass and child qdiscslink_tclass_drop_marked()that iterates once overlink->tclassesand frees marked entries
tclass_drop()now marks recursively, then callslink_qdisc_drop_marked()beforelink_tclass_drop_marked()and returnsNULL
qdisc_drop()that caused stackoverflow and unsafe set modification
qdisc.h
Declare qdisc mark-and-sweep functionssrc/network/tc/qdisc.h
qdisc_mark_recursive()andlink_qdisc_drop_marked()tclass.h
Declare tclass mark-and-sweep functionssrc/network/tc/tclass.h
tclass_mark_recursive()andlink_tclass_drop_marked()