diff mbox series

[v3] media: uvc: don't do DMA on stack

Message ID 6832dffafd54a6a95b287c4a1ef30250d6b9237a.1624282817.git.mchehab+huawei@kernel.org
State Superseded
Headers show
Series [v3] media: uvc: don't do DMA on stack | expand

Commit Message

Mauro Carvalho Chehab June 21, 2021, 1:40 p.m. UTC
As warned by smatch:
	drivers/media/usb/uvc/uvc_v4l2.c:911 uvc_ioctl_g_input() error: doing dma on the stack (&i)
	drivers/media/usb/uvc/uvc_v4l2.c:943 uvc_ioctl_s_input() error: doing dma on the stack (&i)

those two functions call uvc_query_ctrl passing a pointer to
a data at the DMA stack. those are used to send URBs via
usb_control_msg(). Using DMA stack is not supported and should
not work anymore on modern Linux versions.

So, use a kmalloc'ed buffer.

Cc: stable@vger.kernel.org	# Kernel 4.9 and upper
Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
---
 drivers/media/usb/uvc/uvc_v4l2.c | 30 ++++++++++++++++++++++--------
 1 file changed, 22 insertions(+), 8 deletions(-)

Comments

Laurent Pinchart June 21, 2021, 2:11 p.m. UTC | #1
Hi Mauro,

Thank you for the patch.

On Mon, Jun 21, 2021 at 03:40:19PM +0200, Mauro Carvalho Chehab wrote:
> As warned by smatch:
> 	drivers/media/usb/uvc/uvc_v4l2.c:911 uvc_ioctl_g_input() error: doing dma on the stack (&i)
> 	drivers/media/usb/uvc/uvc_v4l2.c:943 uvc_ioctl_s_input() error: doing dma on the stack (&i)
> 
> those two functions call uvc_query_ctrl passing a pointer to
> a data at the DMA stack. those are used to send URBs via
> usb_control_msg(). Using DMA stack is not supported and should
> not work anymore on modern Linux versions.
> 
> So, use a kmalloc'ed buffer.
> 
> Cc: stable@vger.kernel.org	# Kernel 4.9 and upper
> Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
> ---
>  drivers/media/usb/uvc/uvc_v4l2.c | 30 ++++++++++++++++++++++--------
>  1 file changed, 22 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
> index 252136cc885c..a95bf7318848 100644
> --- a/drivers/media/usb/uvc/uvc_v4l2.c
> +++ b/drivers/media/usb/uvc/uvc_v4l2.c
> @@ -899,8 +899,8 @@ static int uvc_ioctl_g_input(struct file *file, void *fh, unsigned int *input)
>  {
>  	struct uvc_fh *handle = fh;
>  	struct uvc_video_chain *chain = handle->chain;
> +	u8 *buf;
>  	int ret;
> -	u8 i;
>  
>  	if (chain->selector == NULL ||
>  	    (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
> @@ -908,13 +908,20 @@ static int uvc_ioctl_g_input(struct file *file, void *fh, unsigned int *input)
>  		return 0;
>  	}
>  
> +	buf = kmalloc(1, GFP_KERNEL);
> +	if (!buf)
> +		return -ENOMEM;
> +
>  	ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, chain->selector->id,
>  			     chain->dev->intfnum,  UVC_SU_INPUT_SELECT_CONTROL,
> -			     &i, 1);
> +			     buf, 1);
>  	if (ret < 0)
>  		return ret;

Memory leak :-)

	if (!ret)
		*input = *buf - 1;

	kfree(buf);

	return ret;

>  
> -	*input = i - 1;
> +	*input = *buf - 1;
> +
> +	kfree(buf);
> +
>  	return 0;
>  }
>  
> @@ -922,8 +929,8 @@ static int uvc_ioctl_s_input(struct file *file, void *fh, unsigned int input)
>  {
>  	struct uvc_fh *handle = fh;
>  	struct uvc_video_chain *chain = handle->chain;
> +	char *buf;

	u8 *buf;

With these two changes,

Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

Do I need to take the patch in my tree ?

>  	int ret;
> -	u32 i;
>  
>  	ret = uvc_acquire_privileges(handle);
>  	if (ret < 0)
> @@ -939,10 +946,17 @@ static int uvc_ioctl_s_input(struct file *file, void *fh, unsigned int input)
>  	if (input >= chain->selector->bNrInPins)
>  		return -EINVAL;
>  
> -	i = input + 1;
> -	return uvc_query_ctrl(chain->dev, UVC_SET_CUR, chain->selector->id,
> -			      chain->dev->intfnum, UVC_SU_INPUT_SELECT_CONTROL,
> -			      &i, 1);
> +	buf = kmalloc(1, GFP_KERNEL);
> +	if (!buf)
> +		return -ENOMEM;
> +
> +	*buf = input + 1;
> +	ret = uvc_query_ctrl(chain->dev, UVC_SET_CUR, chain->selector->id,
> +			     chain->dev->intfnum, UVC_SU_INPUT_SELECT_CONTROL,
> +			     buf, 1);
> +	kfree(buf);
> +
> +	return ret;
>  }
>  
>  static int uvc_ioctl_queryctrl(struct file *file, void *fh,
David Laight June 22, 2021, 8:07 a.m. UTC | #2
From: Mauro Carvalho Chehab

> Sent: 21 June 2021 14:40

> 

> As warned by smatch:

> 	drivers/media/usb/uvc/uvc_v4l2.c:911 uvc_ioctl_g_input() error: doing dma on the stack (&i)

> 	drivers/media/usb/uvc/uvc_v4l2.c:943 uvc_ioctl_s_input() error: doing dma on the stack (&i)

> 

> those two functions call uvc_query_ctrl passing a pointer to

> a data at the DMA stack. those are used to send URBs via

> usb_control_msg(). Using DMA stack is not supported and should

> not work anymore on modern Linux versions.

> 

> So, use a kmalloc'ed buffer.

...
> +	buf = kmalloc(1, GFP_KERNEL);

> +	if (!buf)

> +		return -ENOMEM;

> +

>  	ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, chain->selector->id,

>  			     chain->dev->intfnum,  UVC_SU_INPUT_SELECT_CONTROL,

> -			     &i, 1);

> +			     buf, 1);


Thought...

Is kmalloc(1, GFP_KERNEL) guaranteed to return a pointer into
a cache line that will not be accessed by any other code?
(This is slightly weaker than requiring a cache-line aligned
pointer - but very similar.)

Without that guarantee you can't use the returned buffer for
read dma unless the memory accesses are coherent.

	David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
Greg KH June 22, 2021, 10:12 a.m. UTC | #3
On Tue, Jun 22, 2021 at 08:07:12AM +0000, David Laight wrote:
> From: Mauro Carvalho Chehab
> > Sent: 21 June 2021 14:40
> > 
> > As warned by smatch:
> > 	drivers/media/usb/uvc/uvc_v4l2.c:911 uvc_ioctl_g_input() error: doing dma on the stack (&i)
> > 	drivers/media/usb/uvc/uvc_v4l2.c:943 uvc_ioctl_s_input() error: doing dma on the stack (&i)
> > 
> > those two functions call uvc_query_ctrl passing a pointer to
> > a data at the DMA stack. those are used to send URBs via
> > usb_control_msg(). Using DMA stack is not supported and should
> > not work anymore on modern Linux versions.
> > 
> > So, use a kmalloc'ed buffer.
> ...
> > +	buf = kmalloc(1, GFP_KERNEL);
> > +	if (!buf)
> > +		return -ENOMEM;
> > +
> >  	ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, chain->selector->id,
> >  			     chain->dev->intfnum,  UVC_SU_INPUT_SELECT_CONTROL,
> > -			     &i, 1);
> > +			     buf, 1);
> 
> Thought...
> 
> Is kmalloc(1, GFP_KERNEL) guaranteed to return a pointer into
> a cache line that will not be accessed by any other code?
> 
> (This is slightly weaker than requiring a cache-line aligned
> pointer - but very similar.)
> 
> Without that guarantee you can't use the returned buffer for
> read dma unless the memory accesses are coherent.

For USB buffers, that should be fine, we have been doing this for
decades now...

thanks,

greg k-h
Laurent Pinchart June 22, 2021, 10:22 a.m. UTC | #4
Hi Mauro,

On Mon, Jun 21, 2021 at 04:34:08PM +0200, Mauro Carvalho Chehab wrote:
> Em Mon, 21 Jun 2021 17:11:46 +0300 Laurent Pinchart escreveu:

> > On Mon, Jun 21, 2021 at 03:40:19PM +0200, Mauro Carvalho Chehab wrote:

> > > As warned by smatch:

> > > 	drivers/media/usb/uvc/uvc_v4l2.c:911 uvc_ioctl_g_input() error: doing dma on the stack (&i)

> > > 	drivers/media/usb/uvc/uvc_v4l2.c:943 uvc_ioctl_s_input() error: doing dma on the stack (&i)

> > > 

> > > those two functions call uvc_query_ctrl passing a pointer to

> > > a data at the DMA stack. those are used to send URBs via

> > > usb_control_msg(). Using DMA stack is not supported and should

> > > not work anymore on modern Linux versions.

> > > 

> > > So, use a kmalloc'ed buffer.

> > > 

> > > Cc: stable@vger.kernel.org	# Kernel 4.9 and upper

> > > Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>

> > > ---

> > >  drivers/media/usb/uvc/uvc_v4l2.c | 30 ++++++++++++++++++++++--------

> > >  1 file changed, 22 insertions(+), 8 deletions(-)

> > > 

> > > diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c

> > > index 252136cc885c..a95bf7318848 100644

> > > --- a/drivers/media/usb/uvc/uvc_v4l2.c

> > > +++ b/drivers/media/usb/uvc/uvc_v4l2.c

> > > @@ -899,8 +899,8 @@ static int uvc_ioctl_g_input(struct file *file, void *fh, unsigned int *input)

> > >  {

> > >  	struct uvc_fh *handle = fh;

> > >  	struct uvc_video_chain *chain = handle->chain;

> > > +	u8 *buf;

> > >  	int ret;

> > > -	u8 i;

> > >  

> > >  	if (chain->selector == NULL ||

> > >  	    (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {

> > > @@ -908,13 +908,20 @@ static int uvc_ioctl_g_input(struct file *file, void *fh, unsigned int *input)

> > >  		return 0;

> > >  	}

> > >  

> > > +	buf = kmalloc(1, GFP_KERNEL);

> > > +	if (!buf)

> > > +		return -ENOMEM;

> > > +

> > >  	ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, chain->selector->id,

> > >  			     chain->dev->intfnum,  UVC_SU_INPUT_SELECT_CONTROL,

> > > -			     &i, 1);

> > > +			     buf, 1);

> > >  	if (ret < 0)

> > >  		return ret;  

> > 

> > Memory leak :-)

> 

> Argh ;-)

> 

> Clearly, I'm needing more caffeine today, but it is too damn hot

> here...

> 

> > 

> > 	if (!ret)

> > 		*input = *buf - 1;

> > 

> > 	kfree(buf);

> > 

> > 	return ret;

> > 

> > >  

> > > -	*input = i - 1;

> > > +	*input = *buf - 1;

> > > +

> > > +	kfree(buf);

> > > +

> > >  	return 0;

> > >  }

> > >  

> > > @@ -922,8 +929,8 @@ static int uvc_ioctl_s_input(struct file *file, void *fh, unsigned int input)

> > >  {

> > >  	struct uvc_fh *handle = fh;

> > >  	struct uvc_video_chain *chain = handle->chain;

> > > +	char *buf;  

> > 

> > 	u8 *buf;

> > 

> > With these two changes,

> > 

> > Reviewed-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>

> 

> Thanks!

> 

> > Do I need to take the patch in my tree ?

> 

> It is up to you.

> 

> I suspect that it would be easier to just merge it at media_stage,

> together with the other patches from the smatch series, but it is

> up to you.

> 

> Just let me know if you prefer to merge it via your tree, and I'll drop

> it from my queue, or otherwise I'll merge directly at media_stage,

> after waiting for a while on feedbacks on the remaining patches.


Please merge it directly, it's less work for me :-)

-- 
Regards,

Laurent Pinchart
David Laight June 22, 2021, 2:21 p.m. UTC | #5
From: Alan Stern

> Sent: 22 June 2021 14:29

...
> > Thought...

> >

> > Is kmalloc(1, GFP_KERNEL) guaranteed to return a pointer into

> > a cache line that will not be accessed by any other code?

> > (This is slightly weaker than requiring a cache-line aligned

> > pointer - but very similar.)

> 

> As I understand it, on architectures that do not have cache-coherent

> I/O, kmalloc is guaranteed to return a buffer that is

> cacheline-aligned and whose length is a multiple of the cacheline

> size.

> 

> Now, whether that buffer ends up being accessed by any other code

> depends on what your driver does with the pointer it gets from

> kmalloc.  :-)


Thanks for the clarification.

Most of the small allocates in the usb stack are for transmits
where it is only necessary to ensure a cache write-back.

I know there has been some confusion because one of the
allocators can add a small header to every allocation.
This can lead to unexpectedly inadequately aligned pointers.
If it is updated when the preceding block is freed (as some
user-space mallocs do) then it would need to be in a
completely separate cache line.

	David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)
Alan Stern June 22, 2021, 7:58 p.m. UTC | #6
On Tue, Jun 22, 2021 at 02:21:27PM +0000, David Laight wrote:
> From: Alan Stern

> > Sent: 22 June 2021 14:29

> ...

> > > Thought...

> > >

> > > Is kmalloc(1, GFP_KERNEL) guaranteed to return a pointer into

> > > a cache line that will not be accessed by any other code?

> > > (This is slightly weaker than requiring a cache-line aligned

> > > pointer - but very similar.)

> > 

> > As I understand it, on architectures that do not have cache-coherent

> > I/O, kmalloc is guaranteed to return a buffer that is

> > cacheline-aligned and whose length is a multiple of the cacheline

> > size.

> > 

> > Now, whether that buffer ends up being accessed by any other code

> > depends on what your driver does with the pointer it gets from

> > kmalloc.  :-)

> 

> Thanks for the clarification.

> 

> Most of the small allocates in the usb stack are for transmits

> where it is only necessary to ensure a cache write-back.

> 

> I know there has been some confusion because one of the

> allocators can add a small header to every allocation.

> This can lead to unexpectedly inadequately aligned pointers.

> If it is updated when the preceding block is freed (as some

> user-space mallocs do) then it would need to be in a

> completely separate cache line.


If you really want to find out what the true story is, you should ask 
on the linux-mm mailing list.  The rest of us are not experts on this 
stuff.

Alan Stern
diff mbox series

Patch

diff --git a/drivers/media/usb/uvc/uvc_v4l2.c b/drivers/media/usb/uvc/uvc_v4l2.c
index 252136cc885c..a95bf7318848 100644
--- a/drivers/media/usb/uvc/uvc_v4l2.c
+++ b/drivers/media/usb/uvc/uvc_v4l2.c
@@ -899,8 +899,8 @@  static int uvc_ioctl_g_input(struct file *file, void *fh, unsigned int *input)
 {
 	struct uvc_fh *handle = fh;
 	struct uvc_video_chain *chain = handle->chain;
+	u8 *buf;
 	int ret;
-	u8 i;
 
 	if (chain->selector == NULL ||
 	    (chain->dev->quirks & UVC_QUIRK_IGNORE_SELECTOR_UNIT)) {
@@ -908,13 +908,20 @@  static int uvc_ioctl_g_input(struct file *file, void *fh, unsigned int *input)
 		return 0;
 	}
 
+	buf = kmalloc(1, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
 	ret = uvc_query_ctrl(chain->dev, UVC_GET_CUR, chain->selector->id,
 			     chain->dev->intfnum,  UVC_SU_INPUT_SELECT_CONTROL,
-			     &i, 1);
+			     buf, 1);
 	if (ret < 0)
 		return ret;
 
-	*input = i - 1;
+	*input = *buf - 1;
+
+	kfree(buf);
+
 	return 0;
 }
 
@@ -922,8 +929,8 @@  static int uvc_ioctl_s_input(struct file *file, void *fh, unsigned int input)
 {
 	struct uvc_fh *handle = fh;
 	struct uvc_video_chain *chain = handle->chain;
+	char *buf;
 	int ret;
-	u32 i;
 
 	ret = uvc_acquire_privileges(handle);
 	if (ret < 0)
@@ -939,10 +946,17 @@  static int uvc_ioctl_s_input(struct file *file, void *fh, unsigned int input)
 	if (input >= chain->selector->bNrInPins)
 		return -EINVAL;
 
-	i = input + 1;
-	return uvc_query_ctrl(chain->dev, UVC_SET_CUR, chain->selector->id,
-			      chain->dev->intfnum, UVC_SU_INPUT_SELECT_CONTROL,
-			      &i, 1);
+	buf = kmalloc(1, GFP_KERNEL);
+	if (!buf)
+		return -ENOMEM;
+
+	*buf = input + 1;
+	ret = uvc_query_ctrl(chain->dev, UVC_SET_CUR, chain->selector->id,
+			     chain->dev->intfnum, UVC_SU_INPUT_SELECT_CONTROL,
+			     buf, 1);
+	kfree(buf);
+
+	return ret;
 }
 
 static int uvc_ioctl_queryctrl(struct file *file, void *fh,