Commit 7c39f485 authored by Ricardo Ribalda's avatar Ricardo Ribalda Committed by Hans Verkuil
Browse files

media: uvcvideo: Fix bug in error path of uvc_alloc_urb_buffers



Recent cleanup introduced a bug in the error path of
uvc_alloc_urb_buffers(). If there is not enough memory for the
allocation the following error will be triggered:

[  739.196672] UBSAN: shift-out-of-bounds in mm/page_alloc.c:1403:22
[  739.196710] shift exponent 52 is too large for 32-bit type 'int'

Resulting in:
[  740.464422] BUG: unable to handle page fault for address: fffffac1c0800000

The reason for the bug is that usb_free_noncoherent is called with an
invalid size (0) instead of the actual size of the urb.

This patch takes care of that.

Reported-by: default avatarMarek Marczykowski-Górecki <marmarek@invisiblethingslab.com>
Closes: https://lore.kernel.org/linux-media/abycbXzYupZpGkvR@hyeyoo/T/#t


Tested-by: default avatarMarek Marczykowski-Górecki <marmarek@invisiblethingslab.com>
Fixes: c8243452 ("media: uvcvideo: Pass allocation size directly to uvc_alloc_urb_buffer")
Signed-off-by: default avatarRicardo Ribalda <ribalda@chromium.org>
Reviewed-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Link: https://patch.msgid.link/20260320-uvc-urb-free-error-v1-1-b12cc3762a19@chromium.org


Signed-off-by: default avatarLaurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: default avatarHans Verkuil <hverkuil+cisco@kernel.org>
parent e8d97c27
Loading
Loading
Loading
Loading
+5 −4
Original line number Diff line number Diff line
@@ -1751,7 +1751,8 @@ static void uvc_video_complete(struct urb *urb)
/*
 * Free transfer buffers.
 */
static void uvc_free_urb_buffers(struct uvc_streaming *stream)
static void uvc_free_urb_buffers(struct uvc_streaming *stream,
				 unsigned int size)
{
	struct usb_device *udev = stream->dev->udev;
	struct uvc_urb *uvc_urb;
@@ -1760,7 +1761,7 @@ static void uvc_free_urb_buffers(struct uvc_streaming *stream)
		if (!uvc_urb->buffer)
			continue;

		usb_free_noncoherent(udev, stream->urb_size, uvc_urb->buffer,
		usb_free_noncoherent(udev, size, uvc_urb->buffer,
				     uvc_stream_dir(stream), uvc_urb->sgt);
		uvc_urb->buffer = NULL;
		uvc_urb->sgt = NULL;
@@ -1820,7 +1821,7 @@ static int uvc_alloc_urb_buffers(struct uvc_streaming *stream,

			if (!uvc_alloc_urb_buffer(stream, uvc_urb, urb_size,
						  gfp_flags)) {
				uvc_free_urb_buffers(stream);
				uvc_free_urb_buffers(stream, urb_size);
				break;
			}

@@ -1868,7 +1869,7 @@ static void uvc_video_stop_transfer(struct uvc_streaming *stream,
	}

	if (free_buffers)
		uvc_free_urb_buffers(stream);
		uvc_free_urb_buffers(stream, stream->urb_size);
}

/*