β¬’ DragonFlyBSD Kernel Audit
← triage Β· dashboard
DF-5428

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)

No comments yet.

PoC verification

Evidence pack

findings/poc/DF-5428 Β· 5 files
FileTypeDescriptionSize
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 β€” the UVCIOC_CTRL_QUERY case does copyin((void *)data, &qry, sizeof(qry)).
  • But data in a DragonFly d_ioctl is the kernel buffer that mapped_ioctl() already staged (sys/kern/sys_generic.c:674-697: user payload copied into ubuf.stkbuf/kmalloc'd memp before dispatch).
  • std_copyin rejects 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_testable
baseline reproduced→ patch + rebuild →patched clean

Functional 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)
per-fix-DF-5428

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.