Repository navigation
media: uvcvideo: reject zero-length extension unit control queries - #153
Conversation
An extension unit control may answer UVC_GET_LEN with 0. The camera of
the Apple Studio Display XDR (05ac:1116, bcdDevice 18.01) does so for
selector 1 of its vendor extension unit when the device is switched to
its third USB configuration.
uvc_ctrl_fill_xu_info() stores the zero as ctrl->info.size, and
uvc_xu_ctrl_query() then accepts a UVCIOC_CTRL_QUERY for UVC_GET_CUR,
UVC_GET_MIN, UVC_GET_MAX, UVC_GET_RES, UVC_GET_DEF or UVC_SET_CUR with
xqry->size == 0: kmalloc(0) returns ZERO_SIZE_PTR and uvc_query_ctrl()
is called with a zero-length buffer. usb_submit_urb() rejects the
zero-length IN control transfer ("BOGUS control dir, pipe 80000380
doesn't match bRequestType a1") with -EPIPE, which uvc_query_ctrl()
takes for a protocol STALL, and it dereferences the buffer to request
the error code:
Unable to handle kernel NULL pointer dereference at virtual address 0x10
pc : uvc_query_ctrl+0xe8/0x28c [uvcvideo]
lr : uvc_query_ctrl+0x94/0x28c [uvcvideo]
Call trace:
uvc_query_ctrl+0xe8/0x28c [uvcvideo] (P)
uvc_xu_ctrl_query+0x20c/0x420 [uvcvideo]
uvc_ioctl_default+0xe8/0x3e0 [uvcvideo]
__video_do_ioctl+0x154/0x3e0 [videodev]
video_usercopy+0x220/0x74c [videodev]
video_ioctl2+0x18/0x74 [videodev]
uvc_v4l2_unlocked_ioctl+0xd0/0x1e4 [uvcvideo]
v4l2_ioctl+0x40/0x60 [videodev]
__arm64_sys_ioctl+0x2ec/0xad4
The oops kills the calling task while it holds chain->ctrl_mutex. Its
exit path then blocks forever in uvc_ctrl_cleanup_fh(), every later
release of the video node blocks on the same mutex, and when the
display is unplugged the hub worker blocks in
vb2_video_unregister_device(), so the Thunderbolt port cannot enumerate
anything until a reboot. Any process with access to the video node can
trigger this.
Refuse value queries for a control whose length is 0 in
uvc_xu_ctrl_query(); UVC_GET_LEN and UVC_GET_INFO keep working so that
user space can still see the reported length. Also make uvc_query_ctrl()
return -EPIPE instead of borrowing the first byte of a zero-length
buffer for the error code request, so that no other caller can reach
the dereference.
Tested on a MacBook Pro 14" M1 Max (j314c) with the Studio Display XDR
on the Aurora release 20 kernel: the query that oopsed returns -ENODATA
and the process exits normally; the display's hotplug, 5K60 tiled mode,
audio and MJPEG camera are unaffected.
Same bug in mainline (checked 2026-10-19, 7.3-rc); the error code
request dates from 8e7a1db ("media: uvcvideo: Handle control pipe
protocol STALLs").
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01QhYiD43tUYf8SqrkTnG5LQ
Signed-off-by: Brian Toll <[email protected]>
chriskearney
left a comment
There was a problem hiding this comment.
Source approval for 31f6cbb, including the effective merge onto integration/aurora 12be047. Both changed files and the relevant query/cleanup/error paths were reviewed; the three-way merge is clean.
The value-query guard returns -ENODATA for a zero-size control through the existing cleanup path, releasing the mutex before any zero-size allocation/transfer. GET_LEN and GET_INFO remain available. The lower-level guard avoids borrowing data[0] when the buffer size is zero. Nonzero transfers and the 7.2.11 privacy check are preserved.
Independent host validation extracted the exact functions with mocked USB/allocator/kernel interfaces: the effective merge and PR-head variants each passed 16 cases. Before/one-guard controls exposed the missing guards, including seven zero-size crash witnesses in the pre-change fixture. This is host control-flow evidence, not a kernel/device run. The author's hardware evidence is on 7.1.12; no 7.2.11 hardware run or new kernel compile is claimed. Zero-size controls mapped through CTRL_MAP can still cause a zero-length transfer warning; that preexisting path does not invalidate these guards.
Required CI has never run on this head because this PR predates the workflow. I am reopening the unchanged PR solely to trigger the required checks. This approval is not a merge-ready claim: ci / result and the normal combined-tree merge queue must pass. No contributor branch changes or CI bypass.
|
Required CI run 41093190437 now passes on unchanged head 31f6cbb. #166 fixed the compile checkout; the fresh run was verified against test-merge fad2bbe and the corrected workflow before approval. Its compile log confirms the named event-ref fetch and compilation of both the 7.1.12 base and this commit (two objects each). The original missing-tool failures remain failures; no contributor rebase or source change was needed. The pull-request run is checks-only: package, KUnit and boot were skipped, not passed. Normal merge-queue CI must validate the combined current 7.2.11 tree with its full tier. The independent effective-merge source/host review and its hardware limitations remain as recorded above. |
a86ce7a
uvcvideo: an extension unit control with length 0 oopses the kernel (Studio Display XDR camera)
A control query from user space on the Studio Display XDR's camera crashes the kernel, and the aftermath takes the Thunderbolt port with it: the crashed task keeps the camera's control mutex, the camera's video node can never be released, the next unplug blocks the USB hub worker, and the port does not enumerate anything until a reboot (which itself hangs in
device_shutdown()and needs the power button). Any process in thevideogroup can trigger it; no special privileges beyond the camera node.Mechanism, from the commit message: selector 1 of the camera's vendor extension unit answers
UVC_GET_LENwith 0 (seen with the device in its third USB configuration, the one macOS uses).uvc_xu_ctrl_query()accepts a value query with size 0,kmalloc(0)returnsZERO_SIZE_PTR, the USB core rejects the zero-length IN control transfer with-EPIPE, anduvc_query_ctrl()treats that as a STALL and readsdata[0]from the bogus pointer.The fix is two guards: value queries on a zero-length control return
-ENODATA(UVC_GET_LENandUVC_GET_INFOstill work), anduvc_query_ctrl()never borrows a zero-length buffer for the error-code request. The same code is in mainline as of 7.3-rc; a submission to linux-media is planned with this as the basis.Tested
MacBook Pro 14" M1 Max (j314c), Omarchy, Aurora release 20 (
7.1.12), Studio Display XDR on an Apple Thunderbolt 5 cable:UVCIOC_CTRL_QUERYsequence that oopsed (GET_LEN, GET_INFO, then GET_CUR/MIN/MAX/RES/DEF on selector 1 of XU 4) now completes: selector 1 returns-ENODATAfor every value request, the process exits, no kernel message.Oops (before the fix)
Stuck hub worker after the unplug:
🤖 Generated with Claude Code
https://claude.ai/code/session_01QhYiD43tUYf8SqrkTnG5LQ