twe_done indexes twe_lookup[TWE_Q_LENGTH=255] with the controller-supplied 8-bit response_id β id 255 reads one pointer past the array (into twe_drive[0]) and dereferences it as a request (wild read + write)
Summary
TWE_Q_LENGTH=255 and twe_setup fills only lookup[0..254], but the response_id is an 8-bit field (0..255). twe_done does tr = twe_lookup[response_id] with no range check: id 255 reads the first 8 bytes of twe_drive[0] (controller/drive-groomable) treated as a request pointer, then reads tr_status at that wild address and writes TWE_CMD_COMPLETE there. Firmware-malfunction class; no userland trigger. Bounds-check-and-drop fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-4180 Β· 4 files| File | Type | Description | Size | |
|---|---|---|---|---|
| VERDICT.md | verdict | verification verdict + full path:line trace | 2.6 KB | β raw |
| env.txt | env | guest environment | 621 B | view raw |
| fix.diff | suggested-fix | response-id bounds check (git-apply-able) | 889 B | view raw |
| verdict.json | verdict | machine verdict | 2.4 KB | view raw |
DF-4180 β Verification Verdict
Status: inconclusive (hardware-gated: no 3ware controller in the QEMU guest; trigger input comes from the controller response queue, not from userland) Impact: none on this guest (wild pointer read+write via HW-controlled index on affected hardware) Confidence: certain (bounds violation provable from source)
Mechanism (source trace)
struct twe_softclayout (twevar.h:135-136):struct twe_request *twe_lookup[TWE_Q_LENGTH];struct twe_drive twe_drive[TWE_MAX_UNITS];βtwe_drivebegins exactly wheretwe_lookupends (both pointer-aligned).TWE_Q_LENGTH == TWE_INIT_MESSAGE_CREDITS == 0xff == 255(twereg.h:146,152).twe_setup()fillssc->twe_lookup[i]for i = 0..254 only (i < TWE_Q_LENGTH, twe.c:140-157); legal request ids are 0..254.TWE_Response_Queue.u.response_idis an 8-bit field (twereg.h:331-340) β the controller can return 0..255.twe_done()(twe.c:1196-1210):rq.value = TWE_RESPONSE_QUEUE(sc);tr = sc->twe_lookup[rq.u.response_id];β no range check. response_id == 255 readstwe_lookup[255], i.e. the first 8 bytes oftwe_drive[0]=td_size(controller-reported capacity, u32) andtd_cylinders(derived, int) β a 64-bit, partially groomable bogus pointer.if (tr->tr_status != TWE_CMD_BUSY)(twe.c:1203) β wild read;tr->tr_status = TWE_CMD_COMPLETE(twe.c:1205) β wild write of the constant 2 at offset offsetof(struct twe_request, tr_status); thentwe_remove_busy/twe_enqueue_completechase the bogus pointer further (twe.c:1209-1210).
Preconditions: a 3ware Escalade controller that emits a response id of 255
(firmware malfunction, malicious device, or hostile DMA β e.g.
Thunderbolt-attached FPGA). Realistically a robustness/panic bug; the
groomed-pointer variant requires drive-size grooming of twe_drive[0].
Why it cannot be reproduced on this guest
No 3ware PCI device; twe never attaches; the response queue is only
readable from the controller itself. There is no userland-reachable
trigger by construction.
Fix validation
Bounds-check rq.u.response_id >= TWE_Q_LENGTH β consume + drop
(fix.diff); applies cleanly, batch-compiled into X86_64_GENERIC with the
rest of this batch (fix_build.log).
Fix status (batch validation)
Applied with the 36 sibling fix.diffs of this batch in findings/poc/_batch41/all_fixes.patch; single in-guest make -j6 nativekernel KERNCONF=X86_64_GENERIC completed RC=0 with zero errors under -Werror (full log: findings/poc/_batch41/fix_build8.log). Behavior A/B intentionally skipped for this Info-tier item.
Fix verification
inconclusiveCompile-validated only: fix.diff applied cleanly and batch-built into X86_64_GENERIC with -Werror, rc=0 (fix_build.log). Behavioral test impossible (requires a controller that emits id 255).
fix_build.log excerpt (twe.c -Werror in-kernel + module).
Confirmed kernel references
Detail
Evidence (decisive lines)
VERDICT.md (array layout + id-width trace); no runnable trigger by construction; fix.diff + fix_build.log
PoC changes
n/a (no userland trigger - response ids are produced by the controller)
Verified recommended fix
Bounds-check rq.u.response_id >= TWE_Q_LENGTH in twe_done and consume-and-drop such responses.
Verdict
Source-certain OOB index: twe_done uses the controller-supplied 8-bit response_id (TWE_Response_Queue.u.response_id, twereg.h:336) to index sc->twe_lookup[TWE_Q_LENGTH] (twe.c:1199) but TWE_Q_LENGTH==255 (twereg.h:152) and twe_setup fills only ids 0..254 (twe.c:140); a response_id of 255 reads twe_lookup[255] = the first 8 bytes of twe_drive[0] (td_size|td_cylinders, controller-groomable) as a struct twe_request *, then reads tr->tr_status and writes TWE_CMD_COMPLETE (twe.c:1203-1205) through that wild pointer. No userland trigger exists (the id comes from the controller response queue); not reproducible on this guest (no 3ware device at all). Robustness/panic-class on affected hardware; requires firmware malfunction or a malicious DMA device.
No comments yet.