Skip to content

fix(ncm): prevent HardFault on Linux USB-NCM alt-setting flap (T-312)#6

Merged
chipsoft merged 1 commit into
masterfrom
312-fix-ncm-linux-hardfault-alt-race
Jul 3, 2026
Merged

fix(ncm): prevent HardFault on Linux USB-NCM alt-setting flap (T-312)#6
chipsoft merged 1 commit into
masterfrom
312-fix-ncm-linux-hardfault-alt-race

Conversation

@chipsoft

@chipsoft chipsoft commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Problem

Connecting the FRDM-RW612 to a Linux host hard-faulted the device during CDC-NCM enumeration (device hang).

Root cause

Linux's CDC-NCM driver bounces the data interface alt-setting (alt=1 → alt=0 → alt=1) during bring-up. On the alt=0 edge, ncm_flush_data_paths() freed recv_tinyusb_ntb and set it to NULL while the bulk-OUT transfer was still queued in the DCD. The stale transfer then completed, netd_xfer_cb() read the now-NULL owner pointer and passed it to recv_validate_datagram(), which dereferenced ntb->nth before any NULL check → BusFault → HardFault.

Confirmed via non-invasive SWD RTT capture: the log froze at recv_validate_datagram(00000000, 376) and a halt showed IPSR=003 (HardFault). Same interface-toggle race family as the macOS NCM wedge (T-094); on Linux the alt-bounce turns it into a hard crash.

Fix

  1. Primary — in ncm_flush_data_paths(), do not reclaim a buffer whose bulk transfer is still in flight. Guard the release of both recv_tinyusb_ntb (ep_out) and xmit_tinyusb_ntb (ep_in) with usbd_edpt_busy(ncm_interface.rhport, ep). Ownership stays with the pending transfer, which netd_xfer_cb() reclaims normally on completion. (This fork has no usbd_edpt_abort(), so the in-flight buffer must not be released.)
  2. Defensive — NULL-guard recv_tinyusb_ntb in netd_xfer_cb(), and NULL-check ntb inside recv_validate_datagram() before the header deref.

Why it's safe

The TinyUSB device stack is single-threaded via the tud_task event queue; usbd.c clears the endpoint busy flag before invoking netd_xfer_cb. SET_INTERFACE processing and xfer-completion are serialized, so the busy check is race-free. Skipping the free when busy leaves the buffer owned (no leak, never double-freed); the glue/ready lists only hold already-completed transfers, so freeing those in flush stays safe.

Verification (real hardware, FRDM-RW612)

  • scripts/verify.sh mcuboot → PASS (build / flash / RTT live / anti-tamper).
  • Live Linux USB reconnect: the alt=1/0/1 flap now completes to strict_configured=1; NCM state machine reaches NETIF_READY/DHCP; [ALIVE] heartbeat continues across the flap. No freeze, no fault. The defensive path ("no owning NTB") never triggered — the primary fix alone resolves the race.

Fixes T-312.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Ndogj9bsRUroeAvzMMPhYq

Connecting the device to a Linux host hard-faulted during CDC-NCM
enumeration. Linux's CDC-NCM driver bounces the data interface
(alt=1 -> alt=0 -> alt=1) during bring-up. On the alt=0 edge,
ncm_flush_data_paths() freed recv_tinyusb_ntb and set it to NULL while
the bulk-OUT transfer was still queued in the DCD. The stale transfer
then completed against a NULL owner pointer and netd_xfer_cb() passed
NULL to recv_validate_datagram(), which dereferenced ntb->nth before any
NULL check -> BusFault -> HardFault (device hang).

Fix (primary): in ncm_flush_data_paths(), do not reclaim a buffer whose
bulk transfer is still in flight — guard the release of both
recv_tinyusb_ntb (ep_out) and xmit_tinyusb_ntb (ep_in) with
usbd_edpt_busy(). Ownership stays with the pending transfer, which
netd_xfer_cb() reclaims normally on completion. (This fork lacks
usbd_edpt_abort(), so the in-flight buffer must not be released.)

Fix (defensive): guard netd_xfer_cb() against a NULL recv_tinyusb_ntb and
NULL-check ntb inside recv_validate_datagram() before the header deref.

Verified on FRDM-RW612: the Linux alt=1/0/1 flap now completes to
strict_configured=1; NCM reaches NETIF_READY/DHCP and [ALIVE] heartbeat
continues across the flap. No fault, no freeze. (T-312)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ndogj9bsRUroeAvzMMPhYq
@chipsoft chipsoft merged commit fca8135 into master Jul 3, 2026
41 of 92 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant