Skip to content

[14.5-stable] usbmanager: stop self-parent assigngrp from OOMing pillar - #6255

Merged
rene merged 1 commit into
lf-edge:14.5-stablefrom
eriknordmark:usbmanager-self-parent-recursion-14.5-stable
Aug 1, 2026
Merged

[14.5-stable] usbmanager: stop self-parent assigngrp from OOMing pillar#6255
rene merged 1 commit into
lf-edge:14.5-stablefrom
eriknordmark:usbmanager-self-parent-recursion-14.5-stable

Conversation

@eriknordmark

@eriknordmark eriknordmark commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Description

Backport of #6184 to 14.5-stable.

A device model can declare a PhysicalIO whose parentassigngrp equals its own
assigngrp. When usbmanager processes such a bundle it builds a
self-referential node in its internal ioBundle tree (the group becomes its own
child), and the dependents walk then recurses on that self-loop with no visited
guard. The goroutine stack grows without bound until the pillar memory cgroup
OOM-kills zedbox, and the watchdog reboots the device — repeatedly, since the
offending config is re-applied on every boot, leaving the device stuck in a
reboot loop and unmanageable.

The existing circular-dependency check in addIOBundle cannot catch a
self-parent, because a group is never among its own descendants before it is
inserted. This change rejects a self-parent bundle in addIOBundle, and guards
both tree walks (groupDependendentsImpl and groupParents) with a visited
set so any cyclic parentassigngrp graph that reaches them terminates.

Cherry-picked with git cherry-pick -x from
00a7df8768e80a0726996e1dc84fe1434a2cae62, the merge of #6184 on master. It
applies with no adaptation: pkg/pillar/cmd/usbmanager/ioBundleTree.go and its
test file have not been touched since the file was added in 31b9c8eab
(Dec 2023), so both are byte-identical to master on this branch and the
resulting diff matches #6184 exactly.

How to test and validate this PR

Covered by unit tests in pkg/pillar/cmd/usbmanager/ioBundleTree_test.go
(TestSelfParentAssigngrpRejected, TestTreeWalksGuardCycles).

Both were run against this branch's pkg/pillar before and after the fix.
Without it, TestSelfParentAssigngrpRejected reports that the self-parent
bundle was added, and TestTreeWalksGuardCycles does not terminate — the run
either trips the test's 2 s guard or dies with fatal error: stack overflow.
With the fix, the full ./cmd/usbmanager/ package passes, along with go vet
and gofmt.

End to end: push a device model with a PhysicalIO whose parentassigngrp
equals its assigngrp. On an unfixed build the device OOM-reboots within
~2 minutes and loops; with this fix the model is reported as an error
(IOBundle cannot be its own parent) and the device stays up with steady
memory.

Changelog notes

Fixed a device reboot loop that could occur when the device model contained an
I/O adapter whose parent assignment group referred to itself.

PR Backports

All four current LTS branches carry the affected code byte-identical to master,
so all of them are affected. This PR is one of a set of four:

Checklist

  • I've provided a proper description
  • I've added the proper documentation — not needed, no user-facing
    configuration surface changes
  • I've tested my PR on amd64 device — not re-tested on this branch; the
    identical change was verified on amd64 in usbmanager: stop self-parent assigngrp from OOMing pillar #6184, and the unit tests above
    were run against this branch
  • I've tested my PR on arm64 device
  • I've written the test verification instructions
  • I've set the proper labels to this PR

For backport PRs (remove it if it's not a backport):

  • I've added a reference link to the original PR
  • PR's title follows the template

And the last but not least:

  • I've checked the boxes above, or I've provided a good reason why I didn't
    check them.

🤖 Generated with Claude Code

A PhysicalIO whose parentassigngrp equals its own assigngrp built a
self-referential node in usbmanager's ioBundleTree (children[grp] points
at the group itself), because addIOBundle's circular-dependency check
cannot catch a self-parent: a group is never among its own descendants
before it is inserted. groupDependendentsImpl then recursed on that
self-loop with no visited guard, growing the goroutine stack without
bound until the pillar memory cgroup OOM-killed zedbox and the watchdog
rebooted the device in a loop.

Reject a self-parent bundle in addIOBundle, and guard both tree walks
(groupDependendentsImpl and groupParents) against cycles with a visited
set so a malformed model can no longer wedge usbmanager.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: eriknordmark <erik@zededa.com>
(cherry picked from commit 00a7df8)
@eriknordmark
eriknordmark force-pushed the usbmanager-self-parent-recursion-14.5-stable branch from 786eac9 to 9d73b42 Compare July 31, 2026 21:03
@codecov

codecov Bot commented Jul 31, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 32.62%. Comparing base (272ffae) to head (9d73b42).
⚠️ Report is 164 commits behind head on 14.5-stable.

Additional details and impacted files
@@               Coverage Diff               @@
##           14.5-stable    #6255      +/-   ##
===============================================
+ Coverage        24.97%   32.62%   +7.64%     
===============================================
  Files                8        9       +1     
  Lines             1185     1122      -63     
===============================================
+ Hits               296      366      +70     
+ Misses             820      675     -145     
- Partials            69       81      +12     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@rene
rene merged commit 92819ed into lf-edge:14.5-stable Aug 1, 2026
44 checks passed
@eriknordmark
eriknordmark deleted the usbmanager-self-parent-recursion-14.5-stable branch August 4, 2026 15:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants