USB_SET_CONFIG / USB_SET_ALTINTERFACE ioctls perform privileged device reconfiguration (driver detach/re-attach, re-enumeration) with no privilege check, bypassing the SYSCAP_NODRIVER gate the same file enforces on the equivalent raw control requests
Summary
ugen_check_request gates raw SET_CONFIG/SET_INTERFACE behind SYSCAP_NODRIVER and USB_DEVICEENUMERATE is gated, but USB_SET_CONFIG -> ugen_set_config and USB_SET_ALTINTERFACE -> ugen_set_interface have no check (only FWRITE). Guest-reproduced as uid 1001: raw USB_REQUEST(SET_CONFIG) -> EPERM (gate works) while USB_SET_CONFIG(255) -> 0 with curr_config_index flipped to 255 (genuinely unconfigured by the unpriv user), restored with 0. Unpriv reconfiguration of arbitrary accessible USB devices (detaches umass/HID/network drivers mid-op). Mirror-the-gate fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5316 Β· 11 files| File | Type | Description | Size | |
|---|---|---|---|---|
| df5316.c | β | 4.0 KB | view raw | |
| build.sh | β | 65 B | view raw | |
| run.sh | β | 89 B | view raw | |
| run.log | β | 1.2 KB | view raw | |
| run.patched.log | β | 913 B | view raw | |
| env.txt | β | 664 B | view raw | |
| fix.diff | β | 1.1 KB | view raw | |
| VERDICT.md | β | 3.2 KB | β raw | |
| README.md | β | 846 B | β raw | |
| manifest.json | β | 868 B | view raw | |
| verdict.json | β | 3.1 KB | view raw |
DF-5316 β USB_SET_CONFIG / USB_SET_ALTINTERFACE missing privilege check
ugen_check_request() gates raw UR_SET_CONFIG/UR_SET_INTERFACE control
requests behind SYSCAP_NODRIVER, but the USB_SET_CONFIG /
USB_SET_ALTINTERFACE ioctls β which do the same thing (and more:
driver detach + re-attach + re-enumeration) β have no privilege check.
An unprivileged user with libusb-style access to a ugen control node
can unconfigure/reconfigure the device.
Build / Run (guest, as maxx)
./build.sh ./df5316
Success (unpatched): [1] USB_REQUEST(SET_CONFIG)=EPERM while [2] USB_SET_CONFIG(255)=0 and curr_config_index flips to 255. On the fix-validated kernel: [2] and [3] both EPERM, device unchanged.
Fix: fix.diff (caps_priv_check_self(SYSCAP_NODRIVER) in
ugen_set_config + ugen_set_interface), validated on the patched kernel.
VERDICT β DF-5316
Status: REPRODUCED (unprivileged local user, fresh boot) β impact
dos/policy-bypass: an unprivileged user holding an FWRITE ugen
control-node fd can change the USB device configuration (unconfigure,
reconfigure, force driver detach/re-attach + re-enumeration) although
the very same operation via the raw control-request path
(USB_REQUEST with UR_SET_CONFIG) is privilege-gated in the same
file.
Root cause
ugen_check_request() gates raw UR_SET_ADDRESS / UR_SET_CONFIG /
UR_SET_INTERFACE control requests behind
caps_priv_check_self(SYSCAP_NODRIVER) (sys/bus/u4b/usb_generic.c:874-887,
the comment even says "These requests can be useful for testing USB
drivers"), and ugen_re_enumerate() (USB_DEVICEENUMERATE) is gated the
same way (:952). But the ioctl shortcuts:
USB_SET_CONFIGβugen_set_config()(usb_generic.c:2096-2102 β :605-625)USB_SET_ALTINTERFACEβugen_set_interface()(:2114-2121 β :628-652)
perform the equivalent (stronger, even: they kick the explore thread to
re-enumerate, re-probe and re-attach drivers via
usbd_start_set_config / usbd_set_alt_interface_index +
usb_probe_and_attach) with no privilege check at all β only
FWRITE on the fd (:2097, :2115).
Reproduction (run.log)
Unprivileged maxx (uid 1001) on /dev/usb/0.2.0:
USB_REQUEST(SET_CONFIGURATION)β EPERM (gate works);USB_SET_CONFIG(255 = USB_UNCONFIG_INDEX)β 0, andUSB_GET_CONFIGthen reportscurr_config_index = 255: the device was genuinely unconfigured (driver detach cycle) by uid 1001;USB_SET_CONFIG(0)β 0, device reconfigured back.
Impact ceiling: unprivileged device reconfiguration/driver-rebinding (DoS on other users' or the system's USB devices β e.g. dropping a umass mid-I/O by unconfiguring it, or re-claiming interfaces), plus the inconsistency itself (one more way for an unprivileged process to disturb devices the SYSCAP gate was written to protect). Not a memory corruption; severity Medium (matches "requires unusual config/privilege... local DoS" only partially β it needs just the standard libusb-access device permissions, hence Medium not Low).
Fix validation (run.patched.log, fix.diff)
Two-line gate added to both functions (mirroring the
ugen_re_enumerate pattern). Patched kernel: USB_SET_CONFIG(255)
and USB_SET_ALTINTERFACE both return EPERM for uid 1001 and the
device stays configured (curr_config_index = 0); the raw-request path
is unchanged. No functional regression for privileged callers (root
still passes SYSCAP_NODRIVER).
Kernel references
- sys/bus/u4b/usb_generic.c:874-887 (raw-request gate)
- sys/bus/u4b/usb_generic.c:952 (re-enumerate gate)
- sys/bus/u4b/usb_generic.c:605-625 (ugen_set_config, no gate)
- sys/bus/u4b/usb_generic.c:628-652 (ugen_set_interface, no gate)
- sys/bus/u4b/usb_generic.c:2096-2102, :2114-2121 (ungated dispatch)
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
fixedfix.diff (2 hunks) applied with the DF-5314 fix.diff in the same validation build (kernel #1 15:25:39): USB_SET_CONFIG(255) and USB_SET_ALTINTERFACE now return EPERM for uid 1001 and curr_config_index stays 0; raw-request behaviour unchanged.
run.patched.log, ../DF-5314/fix_build.log
Confirmed kernel references
Detail
Exploit chain
unpriv maxx (libusb-access perms on /dev/usb/0.2.0) -> open O_RDWR -> ioctl(USB_SET_CONFIG, 255) -> usbd_start_set_config schedules RE_ENUM_SET_CONFIG -> explore thread unconfigures the device, detaching interface drivers -> other consumers of the device lose it mid-operation (DoS / driver rebind confusion) -> ioctl(USB_SET_CONFIG, 0) reconfigures it back. Privilege-policy bypass of the SYSCAP_NODRIVER gate, not memory corruption.
Evidence (decisive lines)
run.log (baseline: [1] EPERM vs [2] success + curr_config_index=255 + [4] restore), run.patched.log ([2]/[3] both EPERM, config unchanged)
PoC changes
Straightforward; [3] USB_SET_ALTINTERFACE demo ran while the device was unconfigured (EIO) -- ordered after the unconfig step; on a configured device it returns 0 unprivileged (same missing gate); patched run shows it gated.
Verified recommended fix
Add caps_priv_check_self(SYSCAP_NODRIVER) to ugen_set_config() and ugen_set_interface(), mirroring ugen_re_enumerate() (fix.diff in this pack)
Verdict
USB_SET_CONFIG and USB_SET_ALTINTERFACE in ugen_ioctl_post perform privileged device reconfiguration (driver detach/re-attach, re-enumeration via usbd_start_set_config / usbd_set_alt_interface_index + usb_probe_and_attach) with no caps_priv_check, while the same file gates the equivalent raw control requests (ugen_check_request, usb_generic.c:874-887) and USB_DEVICEENUMERATE (:952) behind SYSCAP_NODRIVER. Reproduced as unprivileged uid 1001: raw SET_CONFIGURATION -> EPERM, but USB_SET_CONFIG(255) -> 0 and USB_GET_CONFIG then reports 255 (device genuinely unconfigured by maxx), restored with USB_SET_CONFIG(0) -> 0.
No comments yet.