DF-5555 / verdict.json
{ "finding_id": "DF-5555", "status": "reproduced", "reproduced": true, "impact": "none", "confidence": "certain", "verdict": "Structural (pass-2 family class, guest-run table replay): usb_make_raw_desc()s CDC renumbering heuristic (usb_template.c:135-146,162-171) discriminates CDC union/call-management descriptors only by (bLength>=5, type==0x24, subtype==6/1), which collides with USB-Audio CS_INTERFACE subtypes; nine of the audio templates 22 raw descriptors are misclassified. Two AS_GENERAL descriptors in non-zero interfaces get real deltas: audio_raw_desc_20 byte[4] (bDelay) 3->4 at interface 1 (usb_template_audio.c:205-208) and audio_raw_desc_30 byte[4] (bDelay) 1->3 at interface 2 (usb_template_audio.c:273-276). Seven interface-0 hits (AC header subtype 1 patching bcdADC-hi; six feature units subtype 6 patching bUnitID/bSourceID) get delta 0 today but are the rebase hazard: any nonzero-interface embedding of these tables turns them into silent audio-graph identity corruption. All writes land in-bounds inside the generated heap config buffer (offsets 3-4 < bLength 7/10; the const source tables are never written), so this is deterministic descriptor corruption, not memory corruption. Impact ceiling is a malformed bDelay hint presented to a USB host by a root-configured gadget (activation is root-only: USB_SET_TEMPLATE needs SYSCAP_NODRIVER, usb_dev.c:1716-1721; the hw.usb.template default is MSC; generation only runs on gadget-side attach, usb_device.c:1727 \u2014 USB_TEMP_AUDIO does have a by-index case at usb_template.c:1359, unlike PHONE), and the guest has no device-side USB controller, hence impact=none. Everything else the family checklist hunts is verified negative by the same harness (S1-S8: bLength==sizeof for all 22 raw tables, all 9 pointer tables NULL-terminated without holes, all string descriptors self-sized, 256-index callback sweep clean, no vendor/HID surface (getVendorDesc==NULL -> core returns NULL, usb_template.c:1050-1060), AC-header hardcodes bInCollection=2/baInterfaceNr={1,2}/wTotalLength=169 match the numbering walk exactly, speed tables in-bounds and fail-closed on unconfigured speeds).", "exploit_chain": "", "evidence": "run.log / run.2.log (decisive: 9 HIT census lines with the two <-- REAL DELTA entries, 2 MISFIRE lines with <-- CORRUPTED bDelay 0x03->0x04 and 0x01->0x03, ALL STRUCTURAL CHECKS PASSED, MISFIRE lines under FIX_CLASSGATE: 0; logs byte-identical across runs); build.log (guest /usr/src source sha256 dd3d65e1... == audited tree; 339 lines extracted verbatim into audio_body.inc, BUILD_OK); VERDICT.md (full path:line narrative incl. census table and byte layouts); fix.diff; table_check.c (harness: verbatim-extracted body + faithful core predicate/numbering-walk replay + S1-S8)", "kernel_refs": [ "sys/bus/u4b/template/usb_template.c:135", "sys/bus/u4b/template/usb_template.c:162", "sys/bus/u4b/template/usb_template.c:209", "sys/bus/u4b/template/usb_template.c:307", "sys/bus/u4b/template/usb_template.c:379", "sys/bus/u4b/template/usb_template.c:1050", "sys/bus/u4b/template/usb_template.c:1359", "sys/bus/u4b/usb_cdc.h:38", "sys/bus/u4b/usb_cdc.h:43", "sys/bus/u4b/usb_dev.c:1716", "sys/bus/u4b/usb_device.c:1727", "sys/bus/u4b/template/usb_template_audio.c:96", "sys/bus/u4b/template/usb_template_audio.c:141", "sys/bus/u4b/template/usb_template_audio.c:205", "sys/bus/u4b/template/usb_template_audio.c:273", "sys/bus/u4b/template/usb_template_audio.c:375" ], "poc_changes": "No seed PoC existed (pass 2, no prior finding). Built the evidence from scratch, adapted from the DF-5553 family harness: verbatim #include of the audited files body (sed from ^enum { at line 58 to EOF, 339 lines) against a minimal shim of usb.h/usb_cdc.h/usb_template.h constants, plus (a) a byte-exact two-pass generator replay (memcpy + CDC union/CM/IAD patching + numbering walk incl. endpoint ppRawDesc scan order, usb_template.c:209-215,318-338) with a generated-vs-authored diff naming the corrupted field, (b) a full predicate census listing all 9 hits with field names and deltas (the DF-5553 harness printed hits but not the byte diff; the diff plus census together answer what bytes get corrupted exactly), and (c) eight structural check families S1-S8 with audio-specific S5 (no vendor surface), S6 (AC-header bInCollection/baInterfaceNr/wTotalLength consistency with the numbering walk) and S8 (audio-graph ID resolution) replacing the phones HID-specific checks. One harness iteration bug fixed en route (census printf format/argument mismatch -> segfault; corrected with per-branch printfs).", "attempts": 3, "guest_uname": "DragonFly dfbsd 6.5-DEVELOPMENT DragonFly 6.5-DEVELOPMENT #0: Thu Jul 2 06:02:54 UTC 2026 root@dfbsd:/usr/obj/usr/src/sys/X86_64_GENERIC x86_64", "runtime_sec": 240, "guest_dirty": 0, "build_cmd": "in-guest: cd /tmp/df5555 && ./build.sh (sed-extract /usr/src/.../usb_template_audio.c lines 58-396 -> audio_body.inc; cc -O2 -Wall -o table_check table_check.c; cc -O2 -Wall -DFIX_CLASSGATE -o table_check_fixed table_check.c)", "run_cmd": "in-guest: cd /tmp/df5555 && ./run.sh (baseline replay + census + S1-S8 + fixed-logic check; run twice: run.log, run.2.log)", "code_hash": "4d7cf91d169e6b5fd2b2bbc04538dfaa94fb88eac4841f882f00a73f2b78680e", "notes": "Evidence class per family standard: gadget-mode activation of this template is root-only in-tree (USB_SET_TEMPLATE requires SYSCAP_NODRIVER, usb_dev.c:1716-1721; hw.usb.template default observed 0 = MSC on the guest) and needs a device-side USB controller the guest lacks, so the structural replay against the guests own sha-verified /usr/src is the honest maximum. Misfire census worth triage: the seven zero-delta hits patch bUnitID/bSourceID (feature units) and bcdADC-hi (AC header) \u2014 under any nonzero-interface rebase or composite embedding these become silent audio-graph corruption while AS_GENERAL bDelay is corrupted today; the family class gate (fix.diff, same core change as DF-5553) closes all nine. Also verified negative this pass: file has no dynamic state/locks/refcounts (const tables + pure callback) so race and lifetime classes are structurally N/A; endpoint CS_ENDPOINT (0x25) descriptors never match the predicate; usb_handle_request.c:744-748 clamps every template-supplied length to the EP0 wLength remainder for the family.", "recommended_fix": "Thread the containing interfaces bInterfaceClass into usb_make_raw_desc() and gate the CDC union/call-management renumbering on UICLASS_CDC/UICLASS_CDC_DATA interfaces so audio CS_INTERFACE descriptors are emitted byte-exact (fix.diff, same family fix as DF-5553).", "fix_status": "not_testable", "fix_baseline_reproduced": true, "fix_patched_reproduced": false, "fix_verdict": "Fix LOGIC validated: table_check_fixed (identical harness with fix.diffs class gate compiled in) drops misfires from 9 (two with nonzero bDelay deltas) to 0 while all S1-S8 checks still pass. A kernel-level A/B build is not honestly testable: observing the generated descriptors at runtime requires a device-side USB controller plus root template selection (no such controller exists on the guest and none is configured in-tree by default), so no observable kernel behavior exists to compare baseline vs patched; with the class gate the generated descriptors equal the authored bytes by construction.", "fix_evidence": "run.log (baseline: 9 HIT census lines, two <-- REAL DELTA, MISFIRE bDelay 0x03 -> 0x04 / 0x01 -> 0x03 with <-- CORRUPTED, then MISFIRE lines under FIX_CLASSGATE: 0 (expected 0)); fix.diff; VERDICT.md Fix and validation" } |