Both reader families do `bus_if = dev_get_drvdata(dev)` and immediately
dereference bus_if->bus_priv.usb. With the ordering from the parent
commit the corresponding threads (cmd tx, bustx) are quiesced before
drvdata is cleared, so these guards should be unreachable in the current
call graph — explicit belt-and-suspenders per the #103 reader audit,
covering both CONFIG_USB_TX_AGGR txdata variants and txmsg against any
future or premature caller.
Per review: distinguish the tested hardware revision (this unit:
chip_id=7, chip_sub_id=1, chip_mcu_id=1) from any assumption that every
device with this VID/PID is MCU1. The ID-table entry only routes to the
DC path; mcu gating is runtime.
In the vif_started==0 branch rwnx_close() evaluates
usbdev->bus_if->state without checking usbdev. On the disconnect path
aicwf_bus_deinit() runs before netdev close, so close observes usbdev==NULL
and oopses (CR2=0x8, pinned by disassembly relocation rwnx_send_reset;
field symptom: host hard-hang on physical unplug, reproduced x8 in the lab
with this stack -> BUG=0 after this guard). In the unpatched tree the same
expression reads a still-allocated DOWN-state object in the synchronous
disconnect path — our earlier "latent UAF" phrasing is retracted (no
freed-object access was observed or proven). Covers USB and SDIO variants.
Both maintained branches ship no binding for this PID: the TP entries in
aicwf_usb_id_table cover 0x014b (Mercury) and 0x014e only, and
aicwf_usb_chipmatch only matches pid == 0x88dc for the DC family, so the
device is ignored by the interface table and rejected by chipmatch even
if force-bound.
- add USB_PRODUCT_ID_TP_AIC8800DC (0x0147)
- match it in aicwf_usb_id_table and in aicwf_usb_chipmatch (DC chipid)
Verified on legacy-mcu1 @ b7a476c (arch1-2 kernel7.2.8, VFIO lab):
clean baseline probe shows BIND=NONE (no driver claims 1-10); with this
commit the interface binds and the issue71 loader profile boots the
chip (Firmware Version: zh Aug 08 2022 ... gcf79227 -> wlan0 created).
Review-driven rework of the teardown lifetime (shenmintao/aic8800d80#103):
- aicwf_bus_deinit(): keep the entry NULL guard (sequential double-deinit
on probe-failure paths), but move dev_set_drvdata(dev, NULL) to the END
of completed cleanup — after URB cancel, platform/rx/cmd teardown and the
kthread_stop() join of bustx_thread. The previous placement (right after
bus_stop) opened a real race: aicwf_usb_bus_txdata() re-reads drvdata and
runs from bustx_thread, which is joined only later, and the TX-path state
check is not a synchronization point. With the new ordering every cleanup
callback and TX reader observes a non-NULL pointer throughout.
- aicwf_usb_bus_stop(): NULL-check bus_if before dereferencing
bus_if->bus_priv.usb (defense in depth; historical host oops
RIP aicwf_usb_bus_stop+0x11 on probe-failure rollback).
Verification (isolated VFIO lab, one 2357:0147 unit, sustained ping -f):
a test-only fault-injection build (not included) reaches the repeated-deinit
path and prints "already deinitialized, skip" with BUG=0; missing-firmware
init failure runs one clean deinit; physical unplug x8, sysfs unbind and
authorized cold cycles, disconnect during scanning and during connection
establishment, ifdown/up x5, rmmod/insmod x3 -> BUG=0 throughout (only the
pre-existing WARNs at rwnx_main.c:1529 and rwnx_cfg80211_disconnect:3328).