[libcamera-devel,02/15] v4l2: v4l2_camera_proxy: Check for null arg values in ioctl handlers

Message ID 20200616131244.70308-3-paul.elder@ideasonboard.com
State Superseded
Headers show
Series
  • Support v4l2-compliance
Related show

Commit Message

Paul Elder June 16, 2020, 1:12 p.m. UTC
The ioctl handlers currently don't check if arg is null, so if it ever
is, it will cause a segfault. Check that arg is null and return -EFAULT
in all vidioc ioctl handlers.

Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
---
 src/v4l2/v4l2_camera_proxy.cpp | 33 +++++++++++++++++++++++++++++++--
 1 file changed, 31 insertions(+), 2 deletions(-)

Comments

Laurent Pinchart June 17, 2020, 1:06 a.m. UTC | #1
Hi Paul,

Thank you for the patch.

On Tue, Jun 16, 2020 at 10:12:31PM +0900, Paul Elder wrote:
> The ioctl handlers currently don't check if arg is null, so if it ever
> is, it will cause a segfault. Check that arg is null and return -EFAULT
> in all vidioc ioctl handlers.
> 
> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>

Can't you check that in V4L2CameraProxy::ioctl() ?

> ---
>  src/v4l2/v4l2_camera_proxy.cpp | 33 +++++++++++++++++++++++++++++++--
>  1 file changed, 31 insertions(+), 2 deletions(-)
> 
> diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp
> index 594dd13..5b74b53 100644
> --- a/src/v4l2/v4l2_camera_proxy.cpp
> +++ b/src/v4l2/v4l2_camera_proxy.cpp
> @@ -238,6 +238,9 @@ int V4L2CameraProxy::vidioc_querycap(struct v4l2_capability *arg)
>  {
>  	LOG(V4L2Compat, Debug) << "Servicing vidioc_querycap";
>  
> +	if (arg == nullptr)
> +		return -EFAULT;
> +
>  	*arg = capabilities_;
>  
>  	return 0;
> @@ -247,6 +250,8 @@ int V4L2CameraProxy::vidioc_enum_fmt(int fd, struct v4l2_fmtdesc *arg)
>  {
>  	LOG(V4L2Compat, Debug) << "Servicing vidioc_enum_fmt fd = " << fd;
>  
> +	if (arg == nullptr)
> +		return -EFAULT;
>  
>  	if (!validateBufferType(arg->type) ||
>  	    arg->index >= streamConfig_.formats().pixelformats().size())
> @@ -264,6 +269,8 @@ int V4L2CameraProxy::vidioc_g_fmt(int fd, struct v4l2_format *arg)
>  {
>  	LOG(V4L2Compat, Debug) << "Servicing vidioc_g_fmt fd = " << fd;
>  
> +	if (arg == nullptr)
> +		return -EFAULT;
>  
>  	if (!validateBufferType(arg->type))
>  		return -EINVAL;
> @@ -303,6 +310,9 @@ int V4L2CameraProxy::vidioc_s_fmt(int fd, struct v4l2_format *arg)
>  {
>  	LOG(V4L2Compat, Debug) << "Servicing vidioc_s_fmt fd = " << fd;
>  
> +	if (arg == nullptr)
> +		return -EFAULT;
> +
>  	int ret = lock(fd);
>  	if (ret < 0)
>  		return ret;
> @@ -334,6 +344,9 @@ int V4L2CameraProxy::vidioc_try_fmt(int fd, struct v4l2_format *arg)
>  {
>  	LOG(V4L2Compat, Debug) << "Servicing vidioc_try_fmt fd = " << fd;
>  
> +	if (arg == nullptr)
> +		return -EFAULT;
> +
>  	if (!validateBufferType(arg->type))
>  		return -EINVAL;
>  
> @@ -361,6 +374,8 @@ int V4L2CameraProxy::vidioc_reqbufs(int fd, struct v4l2_requestbuffers *arg)
>  {
>  	LOG(V4L2Compat, Debug) << "Servicing vidioc_reqbufs fd = " << fd;
>  
> +	if (arg == nullptr)
> +		return -EFAULT;
>  
>  	int ret = lock(fd);
>  	if (ret < 0)
> @@ -444,6 +459,9 @@ int V4L2CameraProxy::vidioc_querybuf(int fd, struct v4l2_buffer *arg)
>  {
>  	LOG(V4L2Compat, Debug) << "Servicing vidioc_querybuf fd = " << fd;
>  
> +	if (arg == nullptr)
> +		return -EFAULT;
> +
>  	int ret = lock(fd);
>  	if (ret < 0)
>  		return ret;
> @@ -461,8 +479,10 @@ int V4L2CameraProxy::vidioc_querybuf(int fd, struct v4l2_buffer *arg)
>  
>  int V4L2CameraProxy::vidioc_qbuf(int fd, struct v4l2_buffer *arg)
>  {
> -	LOG(V4L2Compat, Debug) << "Servicing vidioc_qbuf, index = "
> -			       << arg->index << " fd = " << fd;
> +	LOG(V4L2Compat, Debug) << "Servicing vidioc_qbuf fd = " << fd;
> +
> +	if (arg == nullptr)
> +		return -EFAULT;
>  
>  	int ret = lock(fd);
>  	if (ret < 0)
> @@ -487,6 +507,9 @@ int V4L2CameraProxy::vidioc_dqbuf(int fd, struct v4l2_buffer *arg)
>  {
>  	LOG(V4L2Compat, Debug) << "Servicing vidioc_dqbuf fd = " << fd;
>  
> +	if (arg == nullptr)
> +		return -EFAULT;
> +
>  	int ret = lock(fd);
>  	if (ret < 0)
>  		return ret;
> @@ -522,6 +545,9 @@ int V4L2CameraProxy::vidioc_streamon(int fd, int *arg)
>  {
>  	LOG(V4L2Compat, Debug) << "Servicing vidioc_streamon fd = " << fd;
>  
> +	if (arg == nullptr)
> +		return -EFAULT;
> +
>  	int ret = lock(fd);
>  	if (ret < 0)
>  		return ret;
> @@ -538,6 +564,9 @@ int V4L2CameraProxy::vidioc_streamoff(int fd, int *arg)
>  {
>  	LOG(V4L2Compat, Debug) << "Servicing vidioc_streamoff fd = " << fd;
>  
> +	if (arg == nullptr)
> +		return -EFAULT;
> +
>  	int ret = lock(fd);
>  	if (ret < 0)
>  		return ret;
Paul Elder June 17, 2020, 9:30 a.m. UTC | #2
On Wed, Jun 17, 2020 at 04:06:07AM +0300, Laurent Pinchart wrote:
> Hi Paul,
> 
> Thank you for the patch.
> 
> On Tue, Jun 16, 2020 at 10:12:31PM +0900, Paul Elder wrote:
> > The ioctl handlers currently don't check if arg is null, so if it ever
> > is, it will cause a segfault. Check that arg is null and return -EFAULT
> > in all vidioc ioctl handlers.
> > 
> > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
> 
> Can't you check that in V4L2CameraProxy::ioctl() ?

We can't do that. v4l2-compliance requires that 1) querycap shold not
return EFAULT when arg == NULL, and 2) invalid ioctl should return
ENOTTY. Because of the second point, we would still have to check at
every ioctl.


Paul


> > ---
> >  src/v4l2/v4l2_camera_proxy.cpp | 33 +++++++++++++++++++++++++++++++--
> >  1 file changed, 31 insertions(+), 2 deletions(-)
> > 
> > diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp
> > index 594dd13..5b74b53 100644
> > --- a/src/v4l2/v4l2_camera_proxy.cpp
> > +++ b/src/v4l2/v4l2_camera_proxy.cpp
> > @@ -238,6 +238,9 @@ int V4L2CameraProxy::vidioc_querycap(struct v4l2_capability *arg)
> >  {
> >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_querycap";
> >  
> > +	if (arg == nullptr)
> > +		return -EFAULT;
> > +
> >  	*arg = capabilities_;
> >  
> >  	return 0;
> > @@ -247,6 +250,8 @@ int V4L2CameraProxy::vidioc_enum_fmt(int fd, struct v4l2_fmtdesc *arg)
> >  {
> >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_enum_fmt fd = " << fd;
> >  
> > +	if (arg == nullptr)
> > +		return -EFAULT;
> >  
> >  	if (!validateBufferType(arg->type) ||
> >  	    arg->index >= streamConfig_.formats().pixelformats().size())
> > @@ -264,6 +269,8 @@ int V4L2CameraProxy::vidioc_g_fmt(int fd, struct v4l2_format *arg)
> >  {
> >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_g_fmt fd = " << fd;
> >  
> > +	if (arg == nullptr)
> > +		return -EFAULT;
> >  
> >  	if (!validateBufferType(arg->type))
> >  		return -EINVAL;
> > @@ -303,6 +310,9 @@ int V4L2CameraProxy::vidioc_s_fmt(int fd, struct v4l2_format *arg)
> >  {
> >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_s_fmt fd = " << fd;
> >  
> > +	if (arg == nullptr)
> > +		return -EFAULT;
> > +
> >  	int ret = lock(fd);
> >  	if (ret < 0)
> >  		return ret;
> > @@ -334,6 +344,9 @@ int V4L2CameraProxy::vidioc_try_fmt(int fd, struct v4l2_format *arg)
> >  {
> >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_try_fmt fd = " << fd;
> >  
> > +	if (arg == nullptr)
> > +		return -EFAULT;
> > +
> >  	if (!validateBufferType(arg->type))
> >  		return -EINVAL;
> >  
> > @@ -361,6 +374,8 @@ int V4L2CameraProxy::vidioc_reqbufs(int fd, struct v4l2_requestbuffers *arg)
> >  {
> >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_reqbufs fd = " << fd;
> >  
> > +	if (arg == nullptr)
> > +		return -EFAULT;
> >  
> >  	int ret = lock(fd);
> >  	if (ret < 0)
> > @@ -444,6 +459,9 @@ int V4L2CameraProxy::vidioc_querybuf(int fd, struct v4l2_buffer *arg)
> >  {
> >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_querybuf fd = " << fd;
> >  
> > +	if (arg == nullptr)
> > +		return -EFAULT;
> > +
> >  	int ret = lock(fd);
> >  	if (ret < 0)
> >  		return ret;
> > @@ -461,8 +479,10 @@ int V4L2CameraProxy::vidioc_querybuf(int fd, struct v4l2_buffer *arg)
> >  
> >  int V4L2CameraProxy::vidioc_qbuf(int fd, struct v4l2_buffer *arg)
> >  {
> > -	LOG(V4L2Compat, Debug) << "Servicing vidioc_qbuf, index = "
> > -			       << arg->index << " fd = " << fd;
> > +	LOG(V4L2Compat, Debug) << "Servicing vidioc_qbuf fd = " << fd;
> > +
> > +	if (arg == nullptr)
> > +		return -EFAULT;
> >  
> >  	int ret = lock(fd);
> >  	if (ret < 0)
> > @@ -487,6 +507,9 @@ int V4L2CameraProxy::vidioc_dqbuf(int fd, struct v4l2_buffer *arg)
> >  {
> >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_dqbuf fd = " << fd;
> >  
> > +	if (arg == nullptr)
> > +		return -EFAULT;
> > +
> >  	int ret = lock(fd);
> >  	if (ret < 0)
> >  		return ret;
> > @@ -522,6 +545,9 @@ int V4L2CameraProxy::vidioc_streamon(int fd, int *arg)
> >  {
> >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_streamon fd = " << fd;
> >  
> > +	if (arg == nullptr)
> > +		return -EFAULT;
> > +
> >  	int ret = lock(fd);
> >  	if (ret < 0)
> >  		return ret;
> > @@ -538,6 +564,9 @@ int V4L2CameraProxy::vidioc_streamoff(int fd, int *arg)
> >  {
> >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_streamoff fd = " << fd;
> >  
> > +	if (arg == nullptr)
> > +		return -EFAULT;
> > +
> >  	int ret = lock(fd);
> >  	if (ret < 0)
> >  		return ret;
> 
> -- 
> Regards,
> 
> Laurent Pinchart
Jacopo Mondi June 17, 2020, 12:20 p.m. UTC | #3
Hi Paul, Laurent

On Wed, Jun 17, 2020 at 04:06:07AM +0300, Laurent Pinchart wrote:
> Hi Paul,
>
> Thank you for the patch.
>
> On Tue, Jun 16, 2020 at 10:12:31PM +0900, Paul Elder wrote:
> > The ioctl handlers currently don't check if arg is null, so if it ever
> > is, it will cause a segfault. Check that arg is null and return -EFAULT
> > in all vidioc ioctl handlers.
> >
> > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
>
> Can't you check that in V4L2CameraProxy::ioctl() ?
>

And maybe use !arg  for the check ?

Thanks
  j

> > ---
> >  src/v4l2/v4l2_camera_proxy.cpp | 33 +++++++++++++++++++++++++++++++--
> >  1 file changed, 31 insertions(+), 2 deletions(-)
> >
> > diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp
> > index 594dd13..5b74b53 100644
> > --- a/src/v4l2/v4l2_camera_proxy.cpp
> > +++ b/src/v4l2/v4l2_camera_proxy.cpp
> > @@ -238,6 +238,9 @@ int V4L2CameraProxy::vidioc_querycap(struct v4l2_capability *arg)
> >  {
> >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_querycap";
> >
> > +	if (arg == nullptr)
> > +		return -EFAULT;
> > +
> >  	*arg = capabilities_;
> >
> >  	return 0;
> > @@ -247,6 +250,8 @@ int V4L2CameraProxy::vidioc_enum_fmt(int fd, struct v4l2_fmtdesc *arg)
> >  {
> >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_enum_fmt fd = " << fd;
> >
> > +	if (arg == nullptr)
> > +		return -EFAULT;
> >
> >  	if (!validateBufferType(arg->type) ||
> >  	    arg->index >= streamConfig_.formats().pixelformats().size())
> > @@ -264,6 +269,8 @@ int V4L2CameraProxy::vidioc_g_fmt(int fd, struct v4l2_format *arg)
> >  {
> >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_g_fmt fd = " << fd;
> >
> > +	if (arg == nullptr)
> > +		return -EFAULT;
> >
> >  	if (!validateBufferType(arg->type))
> >  		return -EINVAL;
> > @@ -303,6 +310,9 @@ int V4L2CameraProxy::vidioc_s_fmt(int fd, struct v4l2_format *arg)
> >  {
> >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_s_fmt fd = " << fd;
> >
> > +	if (arg == nullptr)
> > +		return -EFAULT;
> > +
> >  	int ret = lock(fd);
> >  	if (ret < 0)
> >  		return ret;
> > @@ -334,6 +344,9 @@ int V4L2CameraProxy::vidioc_try_fmt(int fd, struct v4l2_format *arg)
> >  {
> >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_try_fmt fd = " << fd;
> >
> > +	if (arg == nullptr)
> > +		return -EFAULT;
> > +
> >  	if (!validateBufferType(arg->type))
> >  		return -EINVAL;
> >
> > @@ -361,6 +374,8 @@ int V4L2CameraProxy::vidioc_reqbufs(int fd, struct v4l2_requestbuffers *arg)
> >  {
> >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_reqbufs fd = " << fd;
> >
> > +	if (arg == nullptr)
> > +		return -EFAULT;
> >
> >  	int ret = lock(fd);
> >  	if (ret < 0)
> > @@ -444,6 +459,9 @@ int V4L2CameraProxy::vidioc_querybuf(int fd, struct v4l2_buffer *arg)
> >  {
> >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_querybuf fd = " << fd;
> >
> > +	if (arg == nullptr)
> > +		return -EFAULT;
> > +
> >  	int ret = lock(fd);
> >  	if (ret < 0)
> >  		return ret;
> > @@ -461,8 +479,10 @@ int V4L2CameraProxy::vidioc_querybuf(int fd, struct v4l2_buffer *arg)
> >
> >  int V4L2CameraProxy::vidioc_qbuf(int fd, struct v4l2_buffer *arg)
> >  {
> > -	LOG(V4L2Compat, Debug) << "Servicing vidioc_qbuf, index = "
> > -			       << arg->index << " fd = " << fd;
> > +	LOG(V4L2Compat, Debug) << "Servicing vidioc_qbuf fd = " << fd;
> > +
> > +	if (arg == nullptr)
> > +		return -EFAULT;
> >
> >  	int ret = lock(fd);
> >  	if (ret < 0)
> > @@ -487,6 +507,9 @@ int V4L2CameraProxy::vidioc_dqbuf(int fd, struct v4l2_buffer *arg)
> >  {
> >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_dqbuf fd = " << fd;
> >
> > +	if (arg == nullptr)
> > +		return -EFAULT;
> > +
> >  	int ret = lock(fd);
> >  	if (ret < 0)
> >  		return ret;
> > @@ -522,6 +545,9 @@ int V4L2CameraProxy::vidioc_streamon(int fd, int *arg)
> >  {
> >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_streamon fd = " << fd;
> >
> > +	if (arg == nullptr)
> > +		return -EFAULT;
> > +
> >  	int ret = lock(fd);
> >  	if (ret < 0)
> >  		return ret;
> > @@ -538,6 +564,9 @@ int V4L2CameraProxy::vidioc_streamoff(int fd, int *arg)
> >  {
> >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_streamoff fd = " << fd;
> >
> > +	if (arg == nullptr)
> > +		return -EFAULT;
> > +
> >  	int ret = lock(fd);
> >  	if (ret < 0)
> >  		return ret;
>
> --
> Regards,
>
> Laurent Pinchart
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel@lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel
Jacopo Mondi June 17, 2020, 12:32 p.m. UTC | #4
Hi Paul,

On Wed, Jun 17, 2020 at 06:30:31PM +0900, Paul Elder wrote:
> On Wed, Jun 17, 2020 at 04:06:07AM +0300, Laurent Pinchart wrote:
> > Hi Paul,
> >
> > Thank you for the patch.
> >
> > On Tue, Jun 16, 2020 at 10:12:31PM +0900, Paul Elder wrote:
> > > The ioctl handlers currently don't check if arg is null, so if it ever
> > > is, it will cause a segfault. Check that arg is null and return -EFAULT
> > > in all vidioc ioctl handlers.
> > >
> > > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
> >
> > Can't you check that in V4L2CameraProxy::ioctl() ?
>
> We can't do that. v4l2-compliance requires that 1) querycap shold not
> return EFAULT when arg == NULL, and 2) invalid ioctl should return

@@ -238,6 +238,9 @@ int V4L2CameraProxy::vidioc_querycap(struct v4l2_capability *arg)
 {
        LOG(V4L2Compat, Debug) << "Servicing vidioc_querycap";

+       if (arg == nullptr)
+               return -EFAULT;
+

Doesn't this return EFAUL on querycap with !arg ?

> ENOTTY. Because of the second point, we would still have to check at
> every ioctl.

You could work it around with a
bool is_ioctl_valid(unsigned long request)
{
        switch (request) {
        case VIDIOC_QUERYCAP:
        case VIDIOC_G_FMT:
        ....
                return true;
        default:
                return false;
        }
}

If you happen to have to check for an ioctl to be supported in
multiple places, otherwise I'm not sure it's worth it.

>
>
> Paul
>
>
> > > ---
> > >  src/v4l2/v4l2_camera_proxy.cpp | 33 +++++++++++++++++++++++++++++++--
> > >  1 file changed, 31 insertions(+), 2 deletions(-)
> > >
> > > diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp
> > > index 594dd13..5b74b53 100644
> > > --- a/src/v4l2/v4l2_camera_proxy.cpp
> > > +++ b/src/v4l2/v4l2_camera_proxy.cpp
> > > @@ -238,6 +238,9 @@ int V4L2CameraProxy::vidioc_querycap(struct v4l2_capability *arg)
> > >  {
> > >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_querycap";
> > >
> > > +	if (arg == nullptr)
> > > +		return -EFAULT;
> > > +
> > >  	*arg = capabilities_;
> > >
> > >  	return 0;
> > > @@ -247,6 +250,8 @@ int V4L2CameraProxy::vidioc_enum_fmt(int fd, struct v4l2_fmtdesc *arg)
> > >  {
> > >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_enum_fmt fd = " << fd;
> > >
> > > +	if (arg == nullptr)
> > > +		return -EFAULT;
> > >
> > >  	if (!validateBufferType(arg->type) ||
> > >  	    arg->index >= streamConfig_.formats().pixelformats().size())
> > > @@ -264,6 +269,8 @@ int V4L2CameraProxy::vidioc_g_fmt(int fd, struct v4l2_format *arg)
> > >  {
> > >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_g_fmt fd = " << fd;
> > >
> > > +	if (arg == nullptr)
> > > +		return -EFAULT;
> > >
> > >  	if (!validateBufferType(arg->type))
> > >  		return -EINVAL;
> > > @@ -303,6 +310,9 @@ int V4L2CameraProxy::vidioc_s_fmt(int fd, struct v4l2_format *arg)
> > >  {
> > >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_s_fmt fd = " << fd;
> > >
> > > +	if (arg == nullptr)
> > > +		return -EFAULT;
> > > +
> > >  	int ret = lock(fd);
> > >  	if (ret < 0)
> > >  		return ret;
> > > @@ -334,6 +344,9 @@ int V4L2CameraProxy::vidioc_try_fmt(int fd, struct v4l2_format *arg)
> > >  {
> > >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_try_fmt fd = " << fd;
> > >
> > > +	if (arg == nullptr)
> > > +		return -EFAULT;
> > > +
> > >  	if (!validateBufferType(arg->type))
> > >  		return -EINVAL;
> > >
> > > @@ -361,6 +374,8 @@ int V4L2CameraProxy::vidioc_reqbufs(int fd, struct v4l2_requestbuffers *arg)
> > >  {
> > >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_reqbufs fd = " << fd;
> > >
> > > +	if (arg == nullptr)
> > > +		return -EFAULT;
> > >
> > >  	int ret = lock(fd);
> > >  	if (ret < 0)
> > > @@ -444,6 +459,9 @@ int V4L2CameraProxy::vidioc_querybuf(int fd, struct v4l2_buffer *arg)
> > >  {
> > >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_querybuf fd = " << fd;
> > >
> > > +	if (arg == nullptr)
> > > +		return -EFAULT;
> > > +
> > >  	int ret = lock(fd);
> > >  	if (ret < 0)
> > >  		return ret;
> > > @@ -461,8 +479,10 @@ int V4L2CameraProxy::vidioc_querybuf(int fd, struct v4l2_buffer *arg)
> > >
> > >  int V4L2CameraProxy::vidioc_qbuf(int fd, struct v4l2_buffer *arg)
> > >  {
> > > -	LOG(V4L2Compat, Debug) << "Servicing vidioc_qbuf, index = "
> > > -			       << arg->index << " fd = " << fd;
> > > +	LOG(V4L2Compat, Debug) << "Servicing vidioc_qbuf fd = " << fd;
> > > +
> > > +	if (arg == nullptr)
> > > +		return -EFAULT;
> > >
> > >  	int ret = lock(fd);
> > >  	if (ret < 0)
> > > @@ -487,6 +507,9 @@ int V4L2CameraProxy::vidioc_dqbuf(int fd, struct v4l2_buffer *arg)
> > >  {
> > >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_dqbuf fd = " << fd;
> > >
> > > +	if (arg == nullptr)
> > > +		return -EFAULT;
> > > +
> > >  	int ret = lock(fd);
> > >  	if (ret < 0)
> > >  		return ret;
> > > @@ -522,6 +545,9 @@ int V4L2CameraProxy::vidioc_streamon(int fd, int *arg)
> > >  {
> > >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_streamon fd = " << fd;
> > >
> > > +	if (arg == nullptr)
> > > +		return -EFAULT;
> > > +
> > >  	int ret = lock(fd);
> > >  	if (ret < 0)
> > >  		return ret;
> > > @@ -538,6 +564,9 @@ int V4L2CameraProxy::vidioc_streamoff(int fd, int *arg)
> > >  {
> > >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_streamoff fd = " << fd;
> > >
> > > +	if (arg == nullptr)
> > > +		return -EFAULT;
> > > +
> > >  	int ret = lock(fd);
> > >  	if (ret < 0)
> > >  		return ret;
> >
> > --
> > Regards,
> >
> > Laurent Pinchart
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel@lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel
Laurent Pinchart June 17, 2020, 3:21 p.m. UTC | #5
Hi Paul,

On Wed, Jun 17, 2020 at 06:30:31PM +0900, Paul Elder wrote:
> On Wed, Jun 17, 2020 at 04:06:07AM +0300, Laurent Pinchart wrote:
> > On Tue, Jun 16, 2020 at 10:12:31PM +0900, Paul Elder wrote:
> > > The ioctl handlers currently don't check if arg is null, so if it ever
> > > is, it will cause a segfault. Check that arg is null and return -EFAULT
> > > in all vidioc ioctl handlers.
> > > 
> > > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
> > 
> > Can't you check that in V4L2CameraProxy::ioctl() ?
> 
> We can't do that. v4l2-compliance requires that 1) querycap shold not
> return EFAULT when arg == NULL,

Really ? v4l2-compliance has

	fail_on_test(doioctl(node, VIDIOC_QUERYCAP, NULL) != EFAULT);

Doesn't it mean it expects EFAULT when arg == NULL ?

> and 2) invalid ioctl should return
> ENOTTY. Because of the second point, we would still have to check at
> every ioctl.

Indeed, that's an annoying one :-( We could have a static const std::set
of supported ioctls (or a switch/case as Jacopo proposed), and use that
for the initial checks. That would require keeping the set in sync with
the switch/case that dispatches the ioctls, but that would be less
error-prone than requiring a manual null check in every handler.

std::set<unsigned long> V4L2CameraProxy::supportedIoctls_ = {
...
};

...

	if (supportedIoctls_.find(request) == supportedIoctls_.end())
		return -ENOTTY;

	if (!arg)
		return -EFAULT;

Now that I've written this, I've looked at the kernel code and at
v4l2-compliance. The latter has

static int testInvalidIoctls(struct node *node, char type)
{
	unsigned ioc = _IOC(_IOC_NONE, type, 0xff, 0);
	unsigned char buf[0x4000] = {};

	fail_on_test(doioctl(node, ioc, NULL) != ENOTTY);
	ioc = _IOC(_IOC_NONE, type, 0, 0x3fff);
	fail_on_test(doioctl(node, ioc, NULL) != ENOTTY);
	ioc = _IOC(_IOC_READ, type, 0, 0x3fff);
	fail_on_test(doioctl(node, ioc, buf) != ENOTTY);
	fail_on_test(check_0(buf, sizeof(buf)));
	ioc = _IOC(_IOC_WRITE, type, 0, 0x3fff);
	fail_on_test(doioctl(node, ioc, buf) != ENOTTY);
	fail_on_test(check_0(buf, sizeof(buf)));
	ioc = _IOC(_IOC_READ | _IOC_WRITE, type, 0, 0x3fff);
	fail_on_test(doioctl(node, ioc, buf) != ENOTTY);
	fail_on_test(check_0(buf, sizeof(buf)));
	return 0;
}

Note how the _IOC_WRITE ioctls are not tested with a NULL argument.
Those would return -EFAULT. How about the following code ?

	if (!arg && _IOC_DIR(request) & _IOC_WRITE)
		return -EFAULT;

	if (supportedIoctls_.find(request) == supportedIoctls_.end())
		return -ENOTTY;

	if (!arg && _IOC_DIR(request) & _IOC_READ)
		return -EFAULT;

The only ioctl that has neither _IOC_READ nor _IOC_WRITE set is
VIDIOC_LOG_STATUS. We don't support it, so the last check could
technically be

	if (!arg)
		return -EFAULT;

Up to you.

> > > ---
> > >  src/v4l2/v4l2_camera_proxy.cpp | 33 +++++++++++++++++++++++++++++++--
> > >  1 file changed, 31 insertions(+), 2 deletions(-)
> > > 
> > > diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp
> > > index 594dd13..5b74b53 100644
> > > --- a/src/v4l2/v4l2_camera_proxy.cpp
> > > +++ b/src/v4l2/v4l2_camera_proxy.cpp
> > > @@ -238,6 +238,9 @@ int V4L2CameraProxy::vidioc_querycap(struct v4l2_capability *arg)
> > >  {
> > >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_querycap";
> > >  
> > > +	if (arg == nullptr)
> > > +		return -EFAULT;
> > > +
> > >  	*arg = capabilities_;
> > >  
> > >  	return 0;
> > > @@ -247,6 +250,8 @@ int V4L2CameraProxy::vidioc_enum_fmt(int fd, struct v4l2_fmtdesc *arg)
> > >  {
> > >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_enum_fmt fd = " << fd;
> > >  
> > > +	if (arg == nullptr)
> > > +		return -EFAULT;
> > >  
> > >  	if (!validateBufferType(arg->type) ||
> > >  	    arg->index >= streamConfig_.formats().pixelformats().size())
> > > @@ -264,6 +269,8 @@ int V4L2CameraProxy::vidioc_g_fmt(int fd, struct v4l2_format *arg)
> > >  {
> > >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_g_fmt fd = " << fd;
> > >  
> > > +	if (arg == nullptr)
> > > +		return -EFAULT;
> > >  
> > >  	if (!validateBufferType(arg->type))
> > >  		return -EINVAL;
> > > @@ -303,6 +310,9 @@ int V4L2CameraProxy::vidioc_s_fmt(int fd, struct v4l2_format *arg)
> > >  {
> > >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_s_fmt fd = " << fd;
> > >  
> > > +	if (arg == nullptr)
> > > +		return -EFAULT;
> > > +
> > >  	int ret = lock(fd);
> > >  	if (ret < 0)
> > >  		return ret;
> > > @@ -334,6 +344,9 @@ int V4L2CameraProxy::vidioc_try_fmt(int fd, struct v4l2_format *arg)
> > >  {
> > >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_try_fmt fd = " << fd;
> > >  
> > > +	if (arg == nullptr)
> > > +		return -EFAULT;
> > > +
> > >  	if (!validateBufferType(arg->type))
> > >  		return -EINVAL;
> > >  
> > > @@ -361,6 +374,8 @@ int V4L2CameraProxy::vidioc_reqbufs(int fd, struct v4l2_requestbuffers *arg)
> > >  {
> > >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_reqbufs fd = " << fd;
> > >  
> > > +	if (arg == nullptr)
> > > +		return -EFAULT;
> > >  
> > >  	int ret = lock(fd);
> > >  	if (ret < 0)
> > > @@ -444,6 +459,9 @@ int V4L2CameraProxy::vidioc_querybuf(int fd, struct v4l2_buffer *arg)
> > >  {
> > >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_querybuf fd = " << fd;
> > >  
> > > +	if (arg == nullptr)
> > > +		return -EFAULT;
> > > +
> > >  	int ret = lock(fd);
> > >  	if (ret < 0)
> > >  		return ret;
> > > @@ -461,8 +479,10 @@ int V4L2CameraProxy::vidioc_querybuf(int fd, struct v4l2_buffer *arg)
> > >  
> > >  int V4L2CameraProxy::vidioc_qbuf(int fd, struct v4l2_buffer *arg)
> > >  {
> > > -	LOG(V4L2Compat, Debug) << "Servicing vidioc_qbuf, index = "
> > > -			       << arg->index << " fd = " << fd;
> > > +	LOG(V4L2Compat, Debug) << "Servicing vidioc_qbuf fd = " << fd;
> > > +
> > > +	if (arg == nullptr)
> > > +		return -EFAULT;
> > >  
> > >  	int ret = lock(fd);
> > >  	if (ret < 0)
> > > @@ -487,6 +507,9 @@ int V4L2CameraProxy::vidioc_dqbuf(int fd, struct v4l2_buffer *arg)
> > >  {
> > >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_dqbuf fd = " << fd;
> > >  
> > > +	if (arg == nullptr)
> > > +		return -EFAULT;
> > > +
> > >  	int ret = lock(fd);
> > >  	if (ret < 0)
> > >  		return ret;
> > > @@ -522,6 +545,9 @@ int V4L2CameraProxy::vidioc_streamon(int fd, int *arg)
> > >  {
> > >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_streamon fd = " << fd;
> > >  
> > > +	if (arg == nullptr)
> > > +		return -EFAULT;
> > > +
> > >  	int ret = lock(fd);
> > >  	if (ret < 0)
> > >  		return ret;
> > > @@ -538,6 +564,9 @@ int V4L2CameraProxy::vidioc_streamoff(int fd, int *arg)
> > >  {
> > >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_streamoff fd = " << fd;
> > >  
> > > +	if (arg == nullptr)
> > > +		return -EFAULT;
> > > +
> > >  	int ret = lock(fd);
> > >  	if (ret < 0)
> > >  		return ret;

Patch

diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp
index 594dd13..5b74b53 100644
--- a/src/v4l2/v4l2_camera_proxy.cpp
+++ b/src/v4l2/v4l2_camera_proxy.cpp
@@ -238,6 +238,9 @@  int V4L2CameraProxy::vidioc_querycap(struct v4l2_capability *arg)
 {
 	LOG(V4L2Compat, Debug) << "Servicing vidioc_querycap";
 
+	if (arg == nullptr)
+		return -EFAULT;
+
 	*arg = capabilities_;
 
 	return 0;
@@ -247,6 +250,8 @@  int V4L2CameraProxy::vidioc_enum_fmt(int fd, struct v4l2_fmtdesc *arg)
 {
 	LOG(V4L2Compat, Debug) << "Servicing vidioc_enum_fmt fd = " << fd;
 
+	if (arg == nullptr)
+		return -EFAULT;
 
 	if (!validateBufferType(arg->type) ||
 	    arg->index >= streamConfig_.formats().pixelformats().size())
@@ -264,6 +269,8 @@  int V4L2CameraProxy::vidioc_g_fmt(int fd, struct v4l2_format *arg)
 {
 	LOG(V4L2Compat, Debug) << "Servicing vidioc_g_fmt fd = " << fd;
 
+	if (arg == nullptr)
+		return -EFAULT;
 
 	if (!validateBufferType(arg->type))
 		return -EINVAL;
@@ -303,6 +310,9 @@  int V4L2CameraProxy::vidioc_s_fmt(int fd, struct v4l2_format *arg)
 {
 	LOG(V4L2Compat, Debug) << "Servicing vidioc_s_fmt fd = " << fd;
 
+	if (arg == nullptr)
+		return -EFAULT;
+
 	int ret = lock(fd);
 	if (ret < 0)
 		return ret;
@@ -334,6 +344,9 @@  int V4L2CameraProxy::vidioc_try_fmt(int fd, struct v4l2_format *arg)
 {
 	LOG(V4L2Compat, Debug) << "Servicing vidioc_try_fmt fd = " << fd;
 
+	if (arg == nullptr)
+		return -EFAULT;
+
 	if (!validateBufferType(arg->type))
 		return -EINVAL;
 
@@ -361,6 +374,8 @@  int V4L2CameraProxy::vidioc_reqbufs(int fd, struct v4l2_requestbuffers *arg)
 {
 	LOG(V4L2Compat, Debug) << "Servicing vidioc_reqbufs fd = " << fd;
 
+	if (arg == nullptr)
+		return -EFAULT;
 
 	int ret = lock(fd);
 	if (ret < 0)
@@ -444,6 +459,9 @@  int V4L2CameraProxy::vidioc_querybuf(int fd, struct v4l2_buffer *arg)
 {
 	LOG(V4L2Compat, Debug) << "Servicing vidioc_querybuf fd = " << fd;
 
+	if (arg == nullptr)
+		return -EFAULT;
+
 	int ret = lock(fd);
 	if (ret < 0)
 		return ret;
@@ -461,8 +479,10 @@  int V4L2CameraProxy::vidioc_querybuf(int fd, struct v4l2_buffer *arg)
 
 int V4L2CameraProxy::vidioc_qbuf(int fd, struct v4l2_buffer *arg)
 {
-	LOG(V4L2Compat, Debug) << "Servicing vidioc_qbuf, index = "
-			       << arg->index << " fd = " << fd;
+	LOG(V4L2Compat, Debug) << "Servicing vidioc_qbuf fd = " << fd;
+
+	if (arg == nullptr)
+		return -EFAULT;
 
 	int ret = lock(fd);
 	if (ret < 0)
@@ -487,6 +507,9 @@  int V4L2CameraProxy::vidioc_dqbuf(int fd, struct v4l2_buffer *arg)
 {
 	LOG(V4L2Compat, Debug) << "Servicing vidioc_dqbuf fd = " << fd;
 
+	if (arg == nullptr)
+		return -EFAULT;
+
 	int ret = lock(fd);
 	if (ret < 0)
 		return ret;
@@ -522,6 +545,9 @@  int V4L2CameraProxy::vidioc_streamon(int fd, int *arg)
 {
 	LOG(V4L2Compat, Debug) << "Servicing vidioc_streamon fd = " << fd;
 
+	if (arg == nullptr)
+		return -EFAULT;
+
 	int ret = lock(fd);
 	if (ret < 0)
 		return ret;
@@ -538,6 +564,9 @@  int V4L2CameraProxy::vidioc_streamoff(int fd, int *arg)
 {
 	LOG(V4L2Compat, Debug) << "Servicing vidioc_streamoff fd = " << fd;
 
+	if (arg == nullptr)
+		return -EFAULT;
+
 	int ret = lock(fd);
 	if (ret < 0)
 		return ret;