Fix xHCI heap corruption: allocate the Output Device Context full size at Address Device, never swap it (found at O-C)
xhci_cmd_address_device() sized the Output Device Context for Slot + EP0 (64 bytes); xhci_cmd_configure_endpoint() then allocated a bigger copy and repointed DCBAA[slot_id]. The controller keeps the address it was given at Address Device (QEMU caches it), so every bulk Endpoint Context was written past the 64-byte block into the next kmalloc header -- on every USB mass-storage attach, every ISA. After O-C's smoke campaign reshuffled the heap, a kfree at thumbdrive unplug walked into it: #GP in coalesce_neighbors(). Found with temporary instrumentation (load base, code bytes at RIP, event crumbs, a heap-list checker): the corrupt word was an EP Context (Bulk IN, Running, MPS 1024) exactly DCI 3 x 32 bytes into the 64-byte context. Now: XHCI_DEVICE_CTX_BYTES (32 contexts x 32 bytes) allocated once per slot at Address Device, zeroed whole, never reallocated; Configure Endpoint just fills it. Also ends the per-attach leak of the old context. Verified: heap checker clean from boot; campaign -> O-A -> MINT -> unplug -> re-attach passes with 0 exceptions; 3-ISA acceptance passes, parity and dict_hash set unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
ee4c401d1c
commit
df150e8626
@@ -738,3 +738,64 @@ Evidence is named **before** each stage runs.
|
|||||||
- The relay and `BLK-ATTACH` flows are unchanged: mint → WIREBIND → `USE` → `3`, with the
|
- The relay and `BLK-ATTACH` flows are unchanged: mint → WIREBIND → `USE` → `3`, with the
|
||||||
ledger at the standard `held=2048 pulled=258048 returned=255263 consumed=737`.
|
ledger at the standard `held=2048 pulled=258048 returned=255263 consumed=737`.
|
||||||
- Idle log growth is 0, there are no faults, and QEMU exits cleanly.
|
- Idle log growth is 0, there are no faults, and QEMU exits cleanly.
|
||||||
|
- [x] **Fix, found at O-C: the xHCI driver corrupted the kernel heap on every USB mass-storage
|
||||||
|
attach** (2026-09-25). Captain Bob: *"1, investigate the crash first"*, then *"go ahead with
|
||||||
|
the xHCI fix"*.
|
||||||
|
|
||||||
|
**The symptom.** After O-C's 1-rep smoke campaign, the standard sequence halted with a `#GP`
|
||||||
|
(vector 13) right after `MINT`, when Bob's thumbdrive is unplugged:
|
||||||
|
- `logs/20260925-013706/amd64/`;
|
||||||
|
- reproduced as `-043257`, `-055533` and `-071858`.
|
||||||
|
|
||||||
|
The kernel was unchanged since O-B; O-C is capsule-only. A fresh boot passed
|
||||||
|
(`-025933`), and so did 28 births and kills with trivial jobs (`-041213`). Only a real
|
||||||
|
campaign followed by the unplug crashed.
|
||||||
|
|
||||||
|
**How it was found** (temporary instrumentation, all reverted):
|
||||||
|
- **Mapping the fault.** Printing the load base showed that the amd64 kernel is relocated by
|
||||||
|
a whole number of pages plus 0x440, and differently on every boot. RIP-to-symbol mapping
|
||||||
|
against the wrong base was the earlier dead end.
|
||||||
|
- **Code bytes at RIP.** Dumping them and matching them in the ELF located the fault exactly:
|
||||||
|
`kfree` → `coalesce_neighbors()`, at `mov %rdx,0x20(%rax)`, writing a free-list neighbour's
|
||||||
|
`prev` through a garbage pointer.
|
||||||
|
- **Event breadcrumbs** put it inside `blk_subsys_detach_device()`, between `blkio_close()`
|
||||||
|
and the end of the two `free()`s.
|
||||||
|
- **A heap-list checker, run at every event,** found the corruption already at boot, during
|
||||||
|
the first USB attach, Zuse's thumbdrive (`-084150`, `-084728`). One allocated block's
|
||||||
|
header `prev` field held `0x0400003600000001`.
|
||||||
|
|
||||||
|
**The defect.** That value is the first 8 bytes of an xHCI **Endpoint Context**: EP State
|
||||||
|
Running (1); EP Type 6 (Bulk IN), CErr 3, Max Packet Size 1024.
|
||||||
|
- The controller writes it at DCI 3 × 32 = 0x60 bytes into the device context.
|
||||||
|
- 0x3253060 − 0x60 = 0x3253000 is the start of the **64-byte Output Device Context** allocated
|
||||||
|
at Address Device, whose heap block ends at the damaged header, 0x3253040.
|
||||||
|
- `xhci_cmd_address_device()` sized that context for Slot + EP0 only.
|
||||||
|
- `xhci_cmd_configure_endpoint()` then allocated a bigger copy and repointed `DCBAA[slot_id]`.
|
||||||
|
- But the controller takes the Output Device Context address at Address Device and keeps it.
|
||||||
|
QEMU caches it and never re-reads DCBAA. So every bulk EP Context was written past the end
|
||||||
|
of the 64-byte block, into the next heap header, on every mass-storage attach, on every ISA.
|
||||||
|
- It is usually harmless in practice: the damaged header is never walked. The campaign's
|
||||||
|
churn moves the heap so that a later `kfree` at unplug walks straight into it.
|
||||||
|
- The old context also leaked on every attach.
|
||||||
|
|
||||||
|
**The fix** (`xhci.c`, `xhci.h`, `xhci_driver.h`):
|
||||||
|
- The Output Device Context is allocated **full size** at Address Device, once per slot:
|
||||||
|
`XHCI_DEVICE_CTX_BYTES` = 32 contexts × 32 bytes (CSZ = 0, the only mode this driver
|
||||||
|
implements).
|
||||||
|
- It is zeroed across its whole length and never swapped.
|
||||||
|
- Configure Endpoint no longer grows, copies or repoints it. It refuses a DCI past the context.
|
||||||
|
- That also ends the per-attach leak.
|
||||||
|
|
||||||
|
**Verification:**
|
||||||
|
- Instrumented, fixed kernel (`-090302`): **0** heap-checker reports at boot attach and
|
||||||
|
throughout. Then the full reproduction (smoke campaign, 6/6 trials `fail=0`, O-A, `MINT` →
|
||||||
|
unplug → re-attach → `WIREBIND` → `3`) passes with **0** exceptions.
|
||||||
|
- Clean tree, instrumentation reverted, 3-ISA acceptance (`logs/20260925-101854/amd64/`,
|
||||||
|
`-103145/aarch64/`, `-104448/riscv64/`): boot parity and the `dict_hash` set unchanged (546
|
||||||
|
words, `0x99b01134c3d3049c`), the O-A checks pass, mint → WIREBIND → `USE` → `3` with the
|
||||||
|
standard ledger, idle growth 0, no faults.
|
||||||
|
- These boots also carry O-C's capsules and serve as its standard sequence (next entry).
|
||||||
|
|
||||||
|
**Process note.** During the investigation, a `make clean` for amd64 was run while that
|
||||||
|
ISA's QEMU was up. It deleted the running QEMU's control sockets; that QEMU was stopped with
|
||||||
|
SIGTERM, since no QMP socket was left, and its log was discarded as flawed.
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
vm_name,vm_id_hex,tick_number,elapsed_ns,tick_interval_ns,cache_hits_delta,bucket_hits_delta,word_executions_delta,hot_word_count,avg_word_heat_q48,window_width,actual_window_size,predicted_label_hits,jitter_bits,apic_ticks,time_trust_q48,variance_q48,vm_call_depth_max,hera_heat_q48,hermes_heat_q48,artemis_heat_q48,fleet_k_q48,fleet_conserved,switch_count_cumulative,switch_current_slot,switch_hera_readiness,switch_hermes_readiness,switch_artemis_readiness,switch_ticks_since
|
||||||
|
Hera,0000000000000000-0000000000000000,76,760000,10000,0,0,256,61,35,4096,4096,0,0,20011,65533,3,1,65536,0,0,65536,1,0,2,0,0,0,9
|
||||||
|
Hera,0000000000000000-0000000000000000,77,770000,10000,0,0,256,62,36,2048,2048,0,0,20012,65533,3,1,65536,0,0,65536,1,0,2,0,0,0,10
|
||||||
|
Hera,0000000000000000-0000000000000000,78,780000,10000,0,0,256,62,37,2048,2048,0,0,20014,65533,3,1,65536,0,0,65536,1,0,2,0,0,0,11
|
||||||
|
,3a3611885b52444c-91aeb2c971167037,1,10000,18446744073708781616,0,0,93,4,4,4096,0,0,4895412794951728771,20125,65533,3,0,65536,0,0,65536,1,0,2,0,0,0,11
|
||||||
|
Hera,0000000000000000-0000000000000000,79,790000,780000,0,0,15572,67,38,1024,1024,0,4694861461728526336,20132,65533,3,1,65536,0,0,65536,1,0,2,0,0,0,12
|
||||||
|
,edaab92527ee4080-ac1e0c431056e4e7,1,10000,18446744073708771616,0,0,98,7,8,4096,0,0,4895412794951728766,20253,65533,3,0,65536,0,0,65536,1,0,2,0,0,0,12
|
||||||
|
Hera,0000000000000000-0000000000000000,80,800000,790000,0,0,15823,69,39,1024,1024,0,4694947361074446336,20260,65533,3,1,65536,0,0,65536,1,0,2,0,0,0,13
|
||||||
|
Hera,0000000000000000-0000000000000000,81,810000,10000,0,0,256,71,40,512,512,0,0,20309,65533,3,1,65536,0,0,65536,1,0,2,0,0,0,14
|
||||||
|
@@ -0,0 +1,9 @@
|
|||||||
|
vm_name,vm_id_hex,tick_number,elapsed_ns,tick_interval_ns,cache_hits_delta,bucket_hits_delta,word_executions_delta,hot_word_count,avg_word_heat_q48,window_width,actual_window_size,predicted_label_hits,jitter_bits,apic_ticks,time_trust_q48,variance_q48,vm_call_depth_max,hera_heat_q48,hermes_heat_q48,artemis_heat_q48,fleet_k_q48,fleet_conserved,switch_count_cumulative,switch_current_slot,switch_hera_readiness,switch_hermes_readiness,switch_artemis_readiness,switch_ticks_since
|
||||||
|
Hera,0000000000000000-0000000000000000,76,760000,10000,0,0,256,61,35,4096,4096,0,0,19515,65535,1,1,65536,0,0,65536,1,0,2,0,0,0,9
|
||||||
|
Hera,0000000000000000-0000000000000000,77,770000,10000,0,0,256,62,36,2048,2048,0,0,19517,65535,1,1,65536,0,0,65536,1,0,2,0,0,0,10
|
||||||
|
Hera,0000000000000000-0000000000000000,78,780000,10000,0,0,256,62,37,2048,2048,0,0,19519,65535,1,1,65536,0,0,65536,1,0,2,0,0,0,11
|
||||||
|
,3a3611885b52444c-91aeb2c971167037,1,10000,18446744073708781616,0,0,93,4,4,4096,0,0,4895412794951728771,19634,65535,1,0,65536,0,0,65536,1,0,2,0,0,0,11
|
||||||
|
Hera,0000000000000000-0000000000000000,79,790000,780000,0,0,15572,67,38,1024,1024,0,4694861461728526336,19639,65535,1,1,65536,0,0,65536,1,0,2,0,0,0,12
|
||||||
|
,edaab92527ee4080-ac1e0c431056e4e7,1,10000,18446744073708771616,0,0,98,7,8,4096,0,0,4895412794951728766,19763,65535,1,0,65536,0,0,65536,1,0,2,0,0,0,12
|
||||||
|
Hera,0000000000000000-0000000000000000,80,800000,790000,0,0,15823,69,39,1024,1024,0,4694947361074446336,19770,65535,1,1,65536,0,0,65536,1,0,2,0,0,0,13
|
||||||
|
Hera,0000000000000000-0000000000000000,81,810000,10000,0,0,256,71,40,512,512,0,0,19820,65535,1,1,65536,0,0,65536,1,0,2,0,0,0,14
|
||||||
|
@@ -0,0 +1,9 @@
|
|||||||
|
vm_name,vm_id_hex,tick_number,elapsed_ns,tick_interval_ns,cache_hits_delta,bucket_hits_delta,word_executions_delta,hot_word_count,avg_word_heat_q48,window_width,actual_window_size,predicted_label_hits,jitter_bits,apic_ticks,time_trust_q48,variance_q48,vm_call_depth_max,hera_heat_q48,hermes_heat_q48,artemis_heat_q48,fleet_k_q48,fleet_conserved,switch_count_cumulative,switch_current_slot,switch_hera_readiness,switch_hermes_readiness,switch_artemis_readiness,switch_ticks_since
|
||||||
|
Hera,0000000000000000-0000000000000000,76,760000,10000,0,0,256,61,35,4096,4096,0,0,20858,65534,2,1,65536,0,0,65536,1,0,2,0,0,0,9
|
||||||
|
Hera,0000000000000000-0000000000000000,77,770000,10000,0,0,256,62,36,2048,2048,0,0,20860,65534,2,1,65536,0,0,65536,1,0,2,0,0,0,10
|
||||||
|
Hera,0000000000000000-0000000000000000,78,780000,10000,0,0,256,62,37,2048,2048,0,0,20862,65534,2,1,65536,0,0,65536,1,0,2,0,0,0,11
|
||||||
|
,3a3611885b52444c-91aeb2c971167037,1,10000,18446744073708781616,0,0,93,4,4,4096,0,0,4895412794951728771,20971,65534,2,0,65536,0,0,65536,1,0,2,0,0,0,11
|
||||||
|
Hera,0000000000000000-0000000000000000,79,790000,780000,0,0,15572,67,38,1024,1024,0,4694861461728526336,20976,65534,2,1,65536,0,0,65536,1,0,2,0,0,0,12
|
||||||
|
,edaab92527ee4080-ac1e0c431056e4e7,1,10000,18446744073708771616,0,0,98,7,8,4096,0,0,4895412794951728766,21095,65534,2,0,65536,0,0,65536,1,0,2,0,0,0,12
|
||||||
|
Hera,0000000000000000-0000000000000000,80,800000,790000,0,0,15823,69,39,1024,1024,0,4694947361074446336,21104,65534,2,1,65536,0,0,65536,1,0,2,0,0,0,13
|
||||||
|
Hera,0000000000000000-0000000000000000,81,810000,10000,0,0,256,71,40,512,512,0,0,21152,65534,2,1,65536,0,0,65536,1,0,2,0,0,0,14
|
||||||
|
@@ -0,0 +1,9 @@
|
|||||||
|
vm_name,vm_id_hex,tick_number,elapsed_ns,tick_interval_ns,cache_hits_delta,bucket_hits_delta,word_executions_delta,hot_word_count,avg_word_heat_q48,window_width,actual_window_size,predicted_label_hits,jitter_bits,apic_ticks,time_trust_q48,variance_q48,vm_call_depth_max,hera_heat_q48,hermes_heat_q48,artemis_heat_q48,fleet_k_q48,fleet_conserved,switch_count_cumulative,switch_current_slot,switch_hera_readiness,switch_hermes_readiness,switch_artemis_readiness,switch_ticks_since
|
||||||
|
Hera,0000000000000000-0000000000000000,76,760000,10000,0,0,256,61,35,4096,4096,0,0,20795,65536,0,1,65536,0,0,65536,1,0,2,0,0,0,9
|
||||||
|
Hera,0000000000000000-0000000000000000,77,770000,10000,0,0,256,62,36,2048,2048,0,0,20799,65536,0,1,65536,0,0,65536,1,0,2,0,0,0,10
|
||||||
|
Hera,0000000000000000-0000000000000000,78,780000,10000,0,0,256,62,37,2048,2048,0,0,20801,65536,0,1,65536,0,0,65536,1,0,2,0,0,0,11
|
||||||
|
,3a3611885b52444c-91aeb2c971167037,1,10000,18446744073708781616,0,0,93,4,4,4096,0,0,4895412794951728771,20912,65536,0,0,65536,0,0,65536,1,0,2,0,0,0,11
|
||||||
|
Hera,0000000000000000-0000000000000000,79,790000,780000,0,0,15572,67,38,1024,1024,0,4694861461728526336,20917,65536,0,1,65536,0,0,65536,1,0,2,0,0,0,12
|
||||||
|
,edaab92527ee4080-ac1e0c431056e4e7,1,10000,18446744073708771616,0,0,98,7,8,4096,0,0,4895412794951728766,21037,65536,0,0,65536,0,0,65536,1,0,2,0,0,0,12
|
||||||
|
Hera,0000000000000000-0000000000000000,80,800000,790000,0,0,15823,69,39,1024,1024,0,4694947361074446336,21045,65536,0,1,65536,0,0,65536,1,0,2,0,0,0,13
|
||||||
|
Hera,0000000000000000-0000000000000000,81,810000,10000,0,0,256,71,40,512,512,0,0,21093,65536,0,1,65536,0,0,65536,1,0,2,0,0,0,14
|
||||||
|
@@ -0,0 +1,9 @@
|
|||||||
|
vm_name,vm_id_hex,tick_number,elapsed_ns,tick_interval_ns,cache_hits_delta,bucket_hits_delta,word_executions_delta,hot_word_count,avg_word_heat_q48,window_width,actual_window_size,predicted_label_hits,jitter_bits,apic_ticks,time_trust_q48,variance_q48,vm_call_depth_max,hera_heat_q48,hermes_heat_q48,artemis_heat_q48,fleet_k_q48,fleet_conserved,switch_count_cumulative,switch_current_slot,switch_hera_readiness,switch_hermes_readiness,switch_artemis_readiness,switch_ticks_since
|
||||||
|
Hera,0000000000000000-0000000000000000,76,760000,10000,0,0,256,61,35,4096,4096,0,0,20870,65535,1,1,65536,0,0,65536,1,0,2,0,0,0,9
|
||||||
|
Hera,0000000000000000-0000000000000000,77,770000,10000,0,0,256,62,36,2048,2048,0,0,20872,65535,1,1,65536,0,0,65536,1,0,2,0,0,0,10
|
||||||
|
Hera,0000000000000000-0000000000000000,78,780000,10000,0,0,256,62,37,2048,2048,0,0,20874,65535,1,1,65536,0,0,65536,1,0,2,0,0,0,11
|
||||||
|
,3a3611885b52444c-91aeb2c971167037,1,10000,18446744073708781616,0,0,93,4,4,4096,0,0,4895412794951728771,20982,65535,1,0,65536,0,0,65536,1,0,2,0,0,0,11
|
||||||
|
Hera,0000000000000000-0000000000000000,79,790000,780000,0,0,15572,67,38,1024,1024,0,4694861461728526336,20988,65535,1,1,65536,0,0,65536,1,0,2,0,0,0,12
|
||||||
|
,edaab92527ee4080-ac1e0c431056e4e7,1,10000,18446744073708771616,0,0,98,7,8,4096,0,0,4895412794951728766,21109,65535,1,0,65536,0,0,65536,1,0,2,0,0,0,12
|
||||||
|
Hera,0000000000000000-0000000000000000,80,800000,790000,0,0,15823,69,39,1024,1024,0,4694947361074446336,21116,65535,1,1,65536,0,0,65536,1,0,2,0,0,0,13
|
||||||
|
Hera,0000000000000000-0000000000000000,81,810000,10000,0,0,256,71,40,512,512,0,0,21165,65535,1,1,65536,0,0,65536,1,0,2,0,0,0,14
|
||||||
|
@@ -0,0 +1,9 @@
|
|||||||
|
vm_name,vm_id_hex,tick_number,elapsed_ns,tick_interval_ns,cache_hits_delta,bucket_hits_delta,word_executions_delta,hot_word_count,avg_word_heat_q48,window_width,actual_window_size,predicted_label_hits,jitter_bits,apic_ticks,time_trust_q48,variance_q48,vm_call_depth_max,hera_heat_q48,hermes_heat_q48,artemis_heat_q48,fleet_k_q48,fleet_conserved,switch_count_cumulative,switch_current_slot,switch_hera_readiness,switch_hermes_readiness,switch_artemis_readiness,switch_ticks_since
|
||||||
|
Hera,0000000000000000-0000000000000000,76,760000,10000,0,0,256,61,35,4096,4096,0,0,22526,65536,0,1,65536,0,0,65536,1,0,2,0,0,0,9
|
||||||
|
Hera,0000000000000000-0000000000000000,77,770000,10000,0,0,256,62,36,2048,2048,0,0,22528,65536,0,1,65536,0,0,65536,1,0,2,0,0,0,10
|
||||||
|
Hera,0000000000000000-0000000000000000,78,780000,10000,0,0,256,62,37,2048,2048,0,0,22531,65536,0,1,65536,0,0,65536,1,0,2,0,0,0,11
|
||||||
|
,3a3611885b52444c-91aeb2c971167037,1,10000,18446744073708781616,0,0,93,4,4,4096,0,0,4895412794951728771,22646,65536,0,0,65536,0,0,65536,1,0,2,0,0,0,11
|
||||||
|
Hera,0000000000000000-0000000000000000,79,790000,780000,0,0,15572,67,38,1024,1024,0,4694861461728526336,22654,65536,0,1,65536,0,0,65536,1,0,2,0,0,0,12
|
||||||
|
,edaab92527ee4080-ac1e0c431056e4e7,1,10000,18446744073708771616,0,0,98,7,8,4096,0,0,4895412794951728766,22778,65536,0,0,65536,0,0,65536,1,0,2,0,0,0,12
|
||||||
|
Hera,0000000000000000-0000000000000000,80,800000,790000,0,0,15823,69,39,1024,1024,0,4694947361074446336,22787,65536,0,1,65536,0,0,65536,1,0,2,0,0,0,13
|
||||||
|
Hera,0000000000000000-0000000000000000,81,810000,10000,0,0,256,71,40,512,512,0,0,22857,65536,0,1,65536,0,0,65536,1,0,2,0,0,0,14
|
||||||
|
@@ -443,6 +443,13 @@ typedef struct {
|
|||||||
volatile uint32_t reserved[3];
|
volatile uint32_t reserved[3];
|
||||||
} xhci_ep_ctx32_t;
|
} xhci_ep_ctx32_t;
|
||||||
|
|
||||||
|
/* The whole Output Device Context (xHCI 1.2 spec section 6.2.1): the Slot
|
||||||
|
* Context plus 31 Endpoint Contexts, 32-byte each (CSZ=0, the only mode
|
||||||
|
* this driver implements). Allocated at this size once, at Address Device
|
||||||
|
* -- the controller keeps the address it was given there. */
|
||||||
|
#define XHCI_DEVICE_CTX_ENTRIES 32u
|
||||||
|
#define XHCI_DEVICE_CTX_BYTES (XHCI_DEVICE_CTX_ENTRIES * sizeof(xhci_ep_ctx32_t))
|
||||||
|
|
||||||
#define XHCI_EP_CTX_TYPE_SHIFT 3
|
#define XHCI_EP_CTX_TYPE_SHIFT 3
|
||||||
#define XHCI_EP_CTX_TYPE_CONTROL_BIDI 4u /* EP0 */
|
#define XHCI_EP_CTX_TYPE_CONTROL_BIDI 4u /* EP0 */
|
||||||
#define XHCI_EP_CTX_TYPE_BULK_OUT 2u /* xHCI 1.2 spec table 6-9 */
|
#define XHCI_EP_CTX_TYPE_BULK_OUT 2u /* xHCI 1.2 spec table 6-9 */
|
||||||
|
|||||||
@@ -98,7 +98,8 @@ typedef struct {
|
|||||||
* device_ctx/ep0_ring* before this fix; single-drive-at-a-time scope
|
* device_ctx/ep0_ring* before this fix; single-drive-at-a-time scope
|
||||||
* removed, each attached device now gets its own. */
|
* removed, each attached device now gets its own. */
|
||||||
void *input_ctx; /* Input Control Ctx + Slot Ctx + EP0 Ctx (96 bytes, 32-byte contexts) */
|
void *input_ctx; /* Input Control Ctx + Slot Ctx + EP0 Ctx (96 bytes, 32-byte contexts) */
|
||||||
void *device_ctx; /* Slot Ctx + EP0 Ctx (64 bytes) -- DCBAA[slot_id] points here */
|
void *device_ctx; /* full Output Device Context (XHCI_DEVICE_CTX_BYTES, all 32
|
||||||
|
* contexts) -- DCBAA[slot_id] points here, never swapped */
|
||||||
xhci_trb_t *ep0_ring; /* EP0 Transfer Ring, XHCI_RING_TRB_COUNT TRBs */
|
xhci_trb_t *ep0_ring; /* EP0 Transfer Ring, XHCI_RING_TRB_COUNT TRBs */
|
||||||
uint32_t ep0_ring_cycle;
|
uint32_t ep0_ring_cycle;
|
||||||
uint32_t ep0_ring_enq;
|
uint32_t ep0_ring_enq;
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+22
-23
@@ -489,9 +489,18 @@ int xhci_cmd_address_device(xhci_dev_t *dev, uint32_t slot_id,
|
|||||||
sizeof(xhci_ep_ctx32_t), 64);
|
sizeof(xhci_ep_ctx32_t), 64);
|
||||||
if (!ms->input_ctx) return -1;
|
if (!ms->input_ctx) return -1;
|
||||||
}
|
}
|
||||||
|
/* FABRIC-3.9.8 (found at Stage O-C, 2026-09-25): the Output Device
|
||||||
|
* Context is allocated FULL SIZE here, once -- Slot Context plus all 31
|
||||||
|
* Endpoint Contexts (XHCI_DEVICE_CTX_BYTES). It used to be sized for
|
||||||
|
* Slot + EP0 only (64 bytes) and swapped for a bigger one in
|
||||||
|
* xhci_cmd_configure_endpoint(), repointing DCBAA[slot_id]. But the
|
||||||
|
* controller takes the Output Device Context's address at Address
|
||||||
|
* Device and keeps writing there (QEMU caches it; it does not re-read
|
||||||
|
* DCBAA), so the bulk endpoints' contexts landed past the end of the
|
||||||
|
* 64-byte block -- corrupting the next kmalloc header on every mass
|
||||||
|
* storage attach, until a later kfree walked into it and #GP'd. */
|
||||||
if (!ms->device_ctx) {
|
if (!ms->device_ctx) {
|
||||||
ms->device_ctx = kmalloc_aligned(
|
ms->device_ctx = kmalloc_aligned(XHCI_DEVICE_CTX_BYTES, 64);
|
||||||
sizeof(xhci_slot_ctx32_t) + sizeof(xhci_ep_ctx32_t), 64);
|
|
||||||
if (!ms->device_ctx) return -1;
|
if (!ms->device_ctx) return -1;
|
||||||
}
|
}
|
||||||
if (!ms->ep0_ring) {
|
if (!ms->ep0_ring) {
|
||||||
@@ -522,7 +531,7 @@ int xhci_cmd_address_device(xhci_dev_t *dev, uint32_t slot_id,
|
|||||||
* controller writes this on Address Device success, software must not
|
* controller writes this on Address Device success, software must not
|
||||||
* pre-fill it. */
|
* pre-fill it. */
|
||||||
uint8_t *dctx = (uint8_t *)ms->device_ctx;
|
uint8_t *dctx = (uint8_t *)ms->device_ctx;
|
||||||
for (size_t i = 0; i < sizeof(xhci_slot_ctx32_t) + sizeof(xhci_ep_ctx32_t); i++) dctx[i] = 0;
|
for (size_t i = 0; i < XHCI_DEVICE_CTX_BYTES; i++) dctx[i] = 0;
|
||||||
((uint64_t *)dev->dcbaa)[slot_id] = (uint64_t)(uintptr_t)ms->device_ctx;
|
((uint64_t *)dev->dcbaa)[slot_id] = (uint64_t)(uintptr_t)ms->device_ctx;
|
||||||
|
|
||||||
/* Input Context: Input Control Context, then Slot Context, then EP0
|
/* Input Context: Input Control Context, then Slot Context, then EP0
|
||||||
@@ -602,26 +611,16 @@ int xhci_cmd_configure_endpoint(xhci_dev_t *dev, uint32_t slot_id)
|
|||||||
uint32_t out_dci = XHCI_EP_ADDR_TO_DCI(ms->bulk_out_ep_addr);
|
uint32_t out_dci = XHCI_EP_ADDR_TO_DCI(ms->bulk_out_ep_addr);
|
||||||
uint32_t max_dci = (in_dci > out_dci) ? in_dci : out_dci;
|
uint32_t max_dci = (in_dci > out_dci) ? in_dci : out_dci;
|
||||||
|
|
||||||
/* DCBAA[slot_id]'s Device Context also needs to grow to hold the new
|
/* The Output Device Context is NOT grown or swapped here (FABRIC-3.9.8,
|
||||||
* EP Contexts -- it's currently sized for Slot+EP0 only (from Address
|
* found at Stage O-C): xhci_cmd_address_device() allocated it full size
|
||||||
* Device). Unlike the Input Context below, this one's *existing*
|
* (XHCI_DEVICE_CTX_BYTES, every DCI) and pointed DCBAA[slot_id] at it,
|
||||||
* content must be preserved, not zeroed: xHCI 1.2 spec section 4.6.6
|
* and the controller keeps using that address. This used to allocate a
|
||||||
* only has the controller write the DCIs actually named in this
|
* bigger copy and repoint DCBAA -- the controller never saw the swap,
|
||||||
* command's Add/Drop flags (EP0's entry here is neither), so it
|
* wrote the new bulk EP Contexts past the end of the original 64-byte
|
||||||
* expects to find EP0's live output state (its current TR Dequeue
|
* block, and corrupted the heap. Every DCI up to max_dci already has
|
||||||
* Pointer in particular) still intact in the Device Context it reads
|
* room; EP0's live output state is left exactly where the controller
|
||||||
* -- swapping in a freshly zeroed buffer would hand the controller a
|
* put it. */
|
||||||
* blank EP0 out from under an endpoint it isn't being asked to touch. */
|
if (max_dci >= XHCI_DEVICE_CTX_ENTRIES) return -1;
|
||||||
size_t old_device_ctx_bytes = sizeof(xhci_slot_ctx32_t) + sizeof(xhci_ep_ctx32_t);
|
|
||||||
size_t new_device_ctx_bytes = (1 + (size_t)max_dci) * sizeof(xhci_ep_ctx32_t);
|
|
||||||
void *new_device_ctx = kmalloc_aligned(new_device_ctx_bytes, 64);
|
|
||||||
if (!new_device_ctx) return -1;
|
|
||||||
uint8_t *ndctx = (uint8_t *)new_device_ctx;
|
|
||||||
for (size_t i = 0; i < new_device_ctx_bytes; i++) ndctx[i] = 0;
|
|
||||||
const uint8_t *odctx = (const uint8_t *)ms->device_ctx;
|
|
||||||
for (size_t i = 0; i < old_device_ctx_bytes; i++) ndctx[i] = odctx[i];
|
|
||||||
ms->device_ctx = new_device_ctx;
|
|
||||||
((uint64_t *)dev->dcbaa)[slot_id] = (uint64_t)(uintptr_t)ms->device_ctx;
|
|
||||||
|
|
||||||
/* ms->input_ctx is reused from Address Device -- same allocation
|
/* ms->input_ctx is reused from Address Device -- same allocation
|
||||||
* (96 bytes: Input Control Ctx + Slot Ctx + one EP Ctx) is too small
|
* (96 bytes: Input Control Ctx + Slot Ctx + one EP Ctx) is too small
|
||||||
|
|||||||
Reference in New Issue
Block a user