NULL-deref in wsp_intr_callback when first HID report yields ntouch=0 (always for TYPE1 devices)
Summary
wsp_intr_callback unconditionally dereferences sc->index[0]->touch_major at :966 (and index[1]/index[2] later) but sc->index[] only populated inside finger loop at :920 gated on ntouch>0. Softc zero-allocated M_ZERO so sc->index[0] is NULL until first report with ntouch>=1. For TYPE1 devices driver never reads ntouch (byte read guarded by params->tp_type>=TYPE2 at :884) so ntouch stays 0 and panic automatic on every report. For TYPE2+ malicious device sends valid-length report with ntouch byte cleared first report panics. sc->index[0]->touch_major reads address offsetof(tp_finger touch_major)=16 unmapped low address. Callback in USB interrupt thread context holding sc->sc_lock. Reliable local kernel panic DoS from USB device enumerating as Apple trackpad. wsp in GENERIC auto-attaches to all listed PIDs.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-2338 Β· 6 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | gate analysis + ntouch==0 NULL-deref trace | 3.4 KB | β raw |
| fix.diff | suggested-fix | early-skip index-deref logic when ntouch==0 | 834 B | view raw |
| build.sh | build-script | documents HW gate | 155 B | view raw |
| run.sh | run-script | prints gate proof | 265 B | view raw |
| env.txt | environment | guest env | 1.1 KB | view raw |
| usb_gate.txt | gate-proof | usbconfig empty, no /dev/ugen*/wsp* | 375 B | view raw |
DF-2338 β NULL-deref in wsp_intr_callback when ntouch==0 (sys/bus/u4b/input/wsp.c)
Verdict: NOT REPRODUCED (HW-gated); REAL BUG IN SOURCE (defense-in-depth fix warranted)
Hardware gate (why the PoC cannot run on this guest)
wsp is the Apple Wireless Trackpad / Magic Trackpad USB HID driver. It attaches
only to specific Apple USB trackpad products (VID 0x05ac PIDs 0x0221/0x0230/
0x0236/0x0307 etc. β see wsp_devs[]). The audit QEMU/KVM guest has no USB
device of any kind attached, let alone an Apple trackpad:
$ usbconfig list # No device match or lack of permissions. $ ls /dev/ugen* /dev/wsp* # No such file or directory $ ls /dev/usb* # only /dev/usbctl (operator group; maxx not in operator) $ id maxx # uid=1001(maxx) gid=1001(maxx) groups=1001(maxx)
With no matching USB device the wsp driver never probes/attaches, so its USB
interrupt callback wsp_intr_callback never executes. The unprivileged maxx
user cannot plug a USB trackpad into the QEMU guest nor inject a HID report.
Source trace β the bug is REAL (sys/bus/u4b/input/wsp.c)
Softc field (wsp.c:550): struct tp_finger *index[MAX_FINGERS]; β an array of
pointers, zero-initialised (softc allocated with M_ZERO).
The interrupt callback (wsp.c:882-966) computes ntouch:
h = (struct tp_header *)(sc->tp_data);
if (params->tp_type >= TYPE2) { /* wsp.c:884 */
ibt = sc->tp_data[params->tp_button];
ntouch = sc->tp_data[params->tp_button - 1]; /* only read for TYPE2+ */
}
if (ntouch < 0) ntouch = 0; /* wsp.c:889 */
else if (ntouch > MAX_FINGERS) ntouch = MAX_FINGERS;
for (i = 0; i != ntouch; i++) { /* wsp.c:894 */
f = (struct tp_finger *)(sc->tp_data + params->tp_offset + ...);
...
sc->index[i] = f; /* wsp.c:920 β ONLY assignment */
}
For TYPE1 devices the ntouch byte is never read (the read is gated on
params->tp_type >= TYPE2 at line 884), so ntouch stays 0, the loop body never
runs, and sc->index[0..] remain NULL. Then at line 966 (and 942/947/952/1027/
1030/1040) the code unconditionally dereferences sc->index[0]->touch_major:
if (sc->index[0]->touch_major < tun.pressure_untouch_threshold && /* wsp.c:966 */
sc->sc_status.button == 0) {
β NULL dereference: reads address offsetof(struct tp_finger, touch_major) β 16,
an unmapped low address β kernel panic in the USB interrupt thread context
holding sc->sc_lock. For TYPE2+ devices a malicious unit sending a
valid-length report with the ntouch byte cleared panics on the first report.
Trigger: a USB device enumerating as an Apple trackpad (automatic attach via
wsp_devs[] PIDs which are in the GENERIC kernel). No privilege, no user
interaction beyond plug-in. Reliable local kernel panic / DoS.
Exploit chain status
Not pursuable β primitive (NULL-deref panic) behind absent USB trackpad hardware (valid Phase-6 hard blocker: dead path at runtime on this guest). The primitive is a pure DoS panic, not a write primitive; no escalation chain.
PoC changes
None. No USB trackpad on guest; verified by source trace only.
Recommended fix
Guard the sc->index[0] dereferences against ntouch == 0 (skip the tap/untouch
logic when no fingers are present). See fix.diff (matches finding proposal: add
an ntouch == 0 early-skip / NULL guard before the index dereferences).
Fix verification
not_testablenot_testable: PoC cannot run on this guest (HW-gated, no target device). fix.diff validated by git apply --check (clean) + line-accurate source trace confirming it closes the cited path.
git apply --check findings/poc/DF-2338/fix.diff -> OK (clean apply). No runtime test possible (HW-gated).
Confirmed kernel references
Detail
Exploit chain
none β valid hard blocker (driver/device path dead at runtime on this guest: no target HW / no attached device). No unprivileged->root path.
Evidence (decisive lines)
usbconfig list -> No device match or lack of permissions.; pciconf -l -> no target controller HW; ifconfig -l -> vtnet0 lo0; kldstat -> kernel/ehci/xhci only; ls /dev/<target> -> No such file or directory; id maxx -> uid=1001 groups=1001 (not operator). Source confirmed at cited lines.
PoC changes
Created findings/poc/DF-2338/{VERDICT.md,fix.diff,build.sh,run.sh,env.txt,gate_proof.txt,manifest.json}. No PoC source (HW-gated).
Verified recommended fix
early-skip index-deref block when ntouch==0. Full git-apply-able diff in findings/poc/DF-2338/fix.diff (git apply --check OK).
Verdict
NOT REPRODUCED (HW-gated). The bug is REAL in source (traced line-by-line). wsp_intr_callback NULL-deref when first HID report has ntouch==0; no Apple USB trackpad. Gate confirmed via usbconfig list (No device match / no /dev/ugen*), pciconf -l (no target controller HW), ifconfig (vtnet0 lo0 only), kldstat (no target module), and ls /dev (no target nodes). maxx (uid 1001, not in operator) cannot reach any /dev/usbctl write path.
No comments yet.