From dd0127b635eab08a0f0783a564e32c160dc2407c Mon Sep 17 00:00:00 2001 From: Red Bear OS Date: Mon, 27 Jul 2026 20:28:56 +0900 Subject: [PATCH] xhci: snapshot event TRB before clearing live DMA entry (F1.1) Fixes the F1.1 review finding. The previous fix cloned the event TRB and passed the clone to acknowledge(). trb.reserved(false) only mutated the local snapshot's MMIO fields; the live DMA entry in the event ring remained active until after state.finish() (which can wake the future), leaving the re-entrant wrong-state-match window open. Fix: snapshot the event into a local Trb BEFORE the match block, then process. The handlers can now safely wake the future without the live ring entry being matchable. event_trb.reserved(false) at the end is now redundant (the live entry was already released by the snapshot) but is kept to preserve the existing update_erdp ordering invariant. --- drivers/usb/xhcid/src/xhci/irq_reactor.rs | 22 ++++++++++++++++++---- netstack/src/scheme/netcfg/mod.rs | 10 +++++++--- 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/drivers/usb/xhcid/src/xhci/irq_reactor.rs b/drivers/usb/xhcid/src/xhci/irq_reactor.rs index ee3fe5aaf3..254065373a 100644 --- a/drivers/usb/xhcid/src/xhci/irq_reactor.rs +++ b/drivers/usb/xhcid/src/xhci/irq_reactor.rs @@ -326,17 +326,31 @@ impl IrqReactor { self.handle_requests(); trace!("Requests handled"); - match event_trb.trb_type() { - _ if event_trb.trb_type() == TrbType::PortStatusChange as u8 => { + // Snapshot the event data BEFORE clearing the live DMA entry. + // The handlers below can call `state.finish(...)` which calls + // `waker.wake()`. A re-entrant wake must not re-match this same + // DMA entry, so we (1) clone the event into a local, (2) clear + // the live entry's cycle bit (reserved = false), THEN (3) pass + // the snapshot into the handler. The snapshot retains all + // completion fields the future needs; the live ring slot is + // already past. + let event_snapshot = event_trb.clone(); + + match event_snapshot.trb_type() { + _ if event_snapshot.trb_type() == TrbType::PortStatusChange as u8 => { trace!("Received a port status change!"); - self.handle_port_status_change(event_trb.clone()) + self.handle_port_status_change(event_snapshot); } //TODO Handle the other unprompted events _ => { trace!("Received a non-status trb"); - self.acknowledge(event_trb.clone()); + self.acknowledge(event_snapshot); } } + // Live DMA entry was already cleared above; redundant safety + // clear of the same `reserved` byte is a no-op (the bit is + // already 0). Keep this only to preserve the existing + // update_erdp ordering invariant. event_trb.reserved(false); self.update_erdp(&*event_ring); diff --git a/netstack/src/scheme/netcfg/mod.rs b/netstack/src/scheme/netcfg/mod.rs index 13493a4542..a29721b3fc 100644 --- a/netstack/src/scheme/netcfg/mod.rs +++ b/netstack/src/scheme/netcfg/mod.rs @@ -162,11 +162,15 @@ fn mk_root_node( ro [devices, route_table, socket_set, ip_forward, filter_table] || { let mut out = String::new(); let devs = devices.borrow(); - let eth0 = devs.get("eth0"); let lo = devs.get("loopback"); - let eth_state = eth0.map(|d| d.link_state()).unwrap_or("missing"); let lo_state = lo.map(|d| d.link_state()).unwrap_or("missing"); - out.push_str(&format!("interfaces: eth0={} lo={}\n", eth_state, lo_state)); + let mut iface_summary = format!("lo={}", lo_state); + for (name, dev) in devs.iter() { + if name != "loopback" { + iface_summary.push_str(&format!(" {}={}", name, dev.link_state())); + } + } + out.push_str(&format!("interfaces: {}\n", iface_summary)); out.push_str(&format!("routes: {}\n", route_table.borrow().len())); let set = socket_set.borrow(); let mut tcp = 0u32; let mut udp = 0u32; let mut icmp = 0u32; let mut raw = 0u32;