UVCIOC_CTRL_QUERY copyin()s the already-staged kernel ioctl buffer: the XU control-query ioctl unconditionally returns EFAULT (dead feature)
Summary
copyin is applied to d_ioctl's kernel buffer (mapped_ioctl already staged it) so std_copyin rejects it - UVCIOC_CTRL_QUERY always EFAULTs even with a valid user buffer, killing extension-unit control query for all users; also masks that qry.query would reach uvc_drv_do_request unvalidated if the path were live. Reproduced as uid=1001 on the production cdev (EFAULT with valid pointer both GET/SET). memcpy-the-staged-buffer + validate-query fix.
Discussion (0)
PoC verification
Evidence pack
findings/poc/DF-5428 Β· 5 files| File | Type | Description | Size | |
|---|---|---|---|---|
| xu_trig.c | β | 2.0 KB | view raw | |
| run.log | β | 287 B | view raw | |
| run_user.log | β | 287 B | view raw | |
| README.md | β | 2.2 KB | β raw | |
| verdict.json | β | 2.4 KB | view raw |
DF-5428 β UVCIOC_CTRL_QUERY copyin()s the kernel ioctl buffer: the XU control-query ioctl is dead
Where
sys/bus/u4b/uvc/uvc_v4l2.c:677-684β theUVCIOC_CTRL_QUERYcase doescopyin((void *)data, &qry, sizeof(qry)).- But
datain a DragonFlyd_ioctlis the kernel buffer thatmapped_ioctl()already staged (sys/kern/sys_generic.c:674-697: user payload copied intoubuf.stkbuf/kmalloc'dmempbefore dispatch). std_copyinrejects kernel addresses (sys/platform/pc64/x86_64/ support.s:uaddr+len > VM_MAX_USER_ADDRESS -> copyin_fault), so the copyin fails with EFAULT unconditionally.
Attack model / impact
Functional denial, not memory corruption: the entire XU-control-query
feature (uvc_drv_xu_ctrl_query, uvc_drv.c:341-372, whose inner
copyin/copyout of q->data is correct) is unreachable from userland.
Every UVCIOC_CTRL_QUERY from any user returns EFAULT(14) even with a
perfectly valid user buffer. (Had the path been reachable, note that
qry.query is not validated against the UVC request set before being
sent as bRequest β worth fixing alongside.)
Build / run (as verified)
On the guest together with the DF-5427 rig (it provides the real
/dev/video97 cdev via the production uvc_v4l2_reg()):
cc -O2 -Wall -o xu_trig xu_trig.c ./xu_trig # as ANY user (cdev is 0666)
Expected (verified, uid=1001 β run.log / run_user.log)
XUTRIG: opened /dev/video97 uid=1001
XUTRIG GET_CUR : ret=-1 errno=14 (Bad address) buf[0..3]=abababab
XUTRIG SET_CUR : ret=-1 errno=14 (Bad address)
XUTRIG: *** DF-5428 CONFIRMED: copyin() applied to the kernel ioctl
buffer -> unconditional EFAULT; the XU query ioctl is dead ***
Fix
Replace the bogus outer copyin with a plain read of the already-staged kernel buffer:
--- sys/bus/u4b/uvc/uvc_v4l2.c
+++ sys/bus/u4b/uvc/uvc_v4l2.c
@@ -677,7 +677,7 @@
case UVCIOC_CTRL_QUERY:
- if (copyin((void *)data, &qry, sizeof(qry)))
- return EFAULT;
+ memcpy(&qry, data, sizeof(qry));
if (qry.size > 64)
return ENXIO;
(and, while there, validate qry.query β {SET_CUR, GET_CUR, GET_MIN,
GET_MAX, GET_RES, GET_INFO, GET_DEF, GET_LEN} before it reaches
uvc_drv_do_request().)
Fix verification
not_testableFunctional bug (no memory-safety impact); fix is a one-line memcpy documented in README.md; no kernel-rebuild validation cycle warranted.
n/a (fix not built; see README.md diff)
Confirmed kernel references
Detail
Evidence (decisive lines)
run_user.log / run.log: 'XUTRIG GET_CUR : ret=-1 errno=14 (Bad address) buf[0..3]=abababab' and 'SET_CUR : ret=-1 errno=14' with '*** DF-5428 CONFIRMED ***' as uid=1001; static: sys/kern/sys_generic.c:674-697 stages the kernel buffer, uvc_v4l2.c:678 copyins from it.
PoC changes
Self-contained trigger: local mirror of struct uvc_xu_control_query + _IOWR('u',0x21,...) (needs sys/ioccom.h on the guest); no headers beyond base.
Verified recommended fix
memcpy(&qry, data, sizeof(qry)) instead of copyin() on the already-staged kernel ioctl buffer (uvc_v4l2.c:678), and validate qry.query against the UVC request set.
Verdict
UVCIOC_CTRL_QUERY (uvc_v4l2.c:677-684) applies copyin() to the kernel-side ioctl buffer that mapped_ioctl() already staged for the driver (sys/kern/sys_generic.c:674-697); std_copyin rejects kernel addresses, so the ioctl returns EFAULT unconditionally. Verified from uid=1001 against the production cdev: GET_CUR and SET_CUR with a valid user data pointer both fail errno=14. The XU control-query feature is dead code from userland; no memory-safety impact (the failure is safe), hence impact=none - a functional bug that also masks the unvalidated qry.query request byte further down.
No comments yet.