# 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:

```diff
--- 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()`.)
