[libcamera-devel,v2,03/17] v4l2: v4l2_camera_proxy: Check for null arg values in main ioctl handler

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

Commit Message

Paul Elder June 19, 2020, 5:41 a.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 the main vidioc ioctl handler.

Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>

---
Changes in v2:
- moved !arg check to main ioctl handler, and added a set of supported
  ioctls
- use !arg instead of arg == nullptr
---
 src/v4l2/v4l2_camera_proxy.cpp | 27 +++++++++++++++++++++++++--
 src/v4l2/v4l2_camera_proxy.h   |  3 +++
 2 files changed, 28 insertions(+), 2 deletions(-)

Comments

Jacopo Mondi June 19, 2020, 10:22 a.m. UTC | #1
Hi Paul,

On Fri, Jun 19, 2020 at 02:41:09PM +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 the main vidioc ioctl handler.
>
> Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
>
> ---
> Changes in v2:
> - moved !arg check to main ioctl handler, and added a set of supported
>   ioctls
> - use !arg instead of arg == nullptr
> ---
>  src/v4l2/v4l2_camera_proxy.cpp | 27 +++++++++++++++++++++++++--
>  src/v4l2/v4l2_camera_proxy.h   |  3 +++
>  2 files changed, 28 insertions(+), 2 deletions(-)
>
> diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp
> index f06f58d..cff6562 100644
> --- a/src/v4l2/v4l2_camera_proxy.cpp
> +++ b/src/v4l2/v4l2_camera_proxy.cpp
> @@ -11,6 +11,7 @@
>  #include <array>
>  #include <errno.h>
>  #include <linux/videodev2.h>
> +#include <set>
>  #include <string.h>
>  #include <sys/mman.h>
>  #include <unistd.h>
> @@ -238,7 +239,6 @@ int V4L2CameraProxy::vidioc_enum_fmt(V4L2CameraFile *cf, struct v4l2_fmtdesc *ar
>  {
>  	LOG(V4L2Compat, Debug) << "Servicing vidioc_enum_fmt fd = " << cf->efd();
>
> -

Where does these spurious empty lines come from ?

>  	if (!validateBufferType(arg->type) ||
>  	    arg->index >= streamConfig_.formats().pixelformats().size())
>  		return -EINVAL;
> @@ -255,7 +255,6 @@ int V4L2CameraProxy::vidioc_g_fmt(V4L2CameraFile *cf, struct v4l2_format *arg)
>  {
>  	LOG(V4L2Compat, Debug) << "Servicing vidioc_g_fmt fd = " << cf->efd();
>
> -
>  	if (!validateBufferType(arg->type))
>  		return -EINVAL;
>
> @@ -543,8 +542,32 @@ int V4L2CameraProxy::vidioc_streamoff(V4L2CameraFile *cf, int *arg)
>  	return ret;
>  }
>
> +std::set<unsigned long> V4L2CameraProxy::supportedIoctls_ = {
> +	VIDIOC_QUERYCAP,
> +	VIDIOC_ENUM_FMT,
> +	VIDIOC_G_FMT,
> +	VIDIOC_S_FMT,
> +	VIDIOC_TRY_FMT,
> +	VIDIOC_REQBUFS,
> +	VIDIOC_QUERYBUF,
> +	VIDIOC_QBUF,
> +	VIDIOC_DQBUF,
> +	VIDIOC_STREAMON,
> +	VIDIOC_STREAMOFF,
> +};

I understand why an std::set(), as it provides a ::find() function
which ease lookup. I'm wondering if it's worth it though, as a
function in an anonymous namespace which simply switch on the
supported requests might be more efficient.

Anyway, have you considered having this in an anonymous namespace
instead of making a static class member out of it ? Is it accessed
from any other class (don't think so, as it's a private static class
member)

> +
>  int V4L2CameraProxy::ioctl(V4L2CameraFile *cf, unsigned long request, void *arg)
>  {
> +	if (supportedIoctls_.find(request) == supportedIoctls_.end()) {
> +		errno = ENOTTY;
> +		return -1;
> +	}
> +
> +	if (!arg) {
> +		errno = EFAULT;
> +		return -1;
> +	}
> +
>  	int ret;
>  	switch (request) {
>  	case VIDIOC_QUERYCAP:
> diff --git a/src/v4l2/v4l2_camera_proxy.h b/src/v4l2/v4l2_camera_proxy.h
> index 43290ca..dd7e793 100644
> --- a/src/v4l2/v4l2_camera_proxy.h
> +++ b/src/v4l2/v4l2_camera_proxy.h
> @@ -11,6 +11,7 @@
>  #include <linux/videodev2.h>
>  #include <map>
>  #include <memory>
> +#include <set>
>  #include <sys/mman.h>
>  #include <sys/types.h>
>  #include <vector>
> @@ -67,6 +68,8 @@ private:
>  	static PixelFormat v4l2ToDrm(uint32_t format);
>  	static uint32_t drmToV4L2(const PixelFormat &format);
>
> +	static std::set<unsigned long> supportedIoctls_;
> +
>  	unsigned int refcount_;
>  	unsigned int index_;
>
> --
> 2.27.0
>
> _______________________________________________
> libcamera-devel mailing list
> libcamera-devel@lists.libcamera.org
> https://lists.libcamera.org/listinfo/libcamera-devel
Laurent Pinchart June 20, 2020, 1:12 a.m. UTC | #2
Hello,

On Fri, Jun 19, 2020 at 12:22:30PM +0200, Jacopo Mondi wrote:
> On Fri, Jun 19, 2020 at 02:41:09PM +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 the main vidioc ioctl handler.
> >
> > Signed-off-by: Paul Elder <paul.elder@ideasonboard.com>
> >
> > ---
> > Changes in v2:
> > - moved !arg check to main ioctl handler, and added a set of supported
> >   ioctls
> > - use !arg instead of arg == nullptr
> > ---
> >  src/v4l2/v4l2_camera_proxy.cpp | 27 +++++++++++++++++++++++++--
> >  src/v4l2/v4l2_camera_proxy.h   |  3 +++
> >  2 files changed, 28 insertions(+), 2 deletions(-)
> >
> > diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp
> > index f06f58d..cff6562 100644
> > --- a/src/v4l2/v4l2_camera_proxy.cpp
> > +++ b/src/v4l2/v4l2_camera_proxy.cpp
> > @@ -11,6 +11,7 @@
> >  #include <array>
> >  #include <errno.h>
> >  #include <linux/videodev2.h>
> > +#include <set>
> >  #include <string.h>
> >  #include <sys/mman.h>
> >  #include <unistd.h>
> > @@ -238,7 +239,6 @@ int V4L2CameraProxy::vidioc_enum_fmt(V4L2CameraFile *cf, struct v4l2_fmtdesc *ar
> >  {
> >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_enum_fmt fd = " << cf->efd();
> >
> > -
> 
> Where does these spurious empty lines come from ?

I suspect a rebase mistake :-) It should indeed be fixed in the previous
patch.

> >  	if (!validateBufferType(arg->type) ||
> >  	    arg->index >= streamConfig_.formats().pixelformats().size())
> >  		return -EINVAL;
> > @@ -255,7 +255,6 @@ int V4L2CameraProxy::vidioc_g_fmt(V4L2CameraFile *cf, struct v4l2_format *arg)
> >  {
> >  	LOG(V4L2Compat, Debug) << "Servicing vidioc_g_fmt fd = " << cf->efd();
> >
> > -
> >  	if (!validateBufferType(arg->type))
> >  		return -EINVAL;
> >
> > @@ -543,8 +542,32 @@ int V4L2CameraProxy::vidioc_streamoff(V4L2CameraFile *cf, int *arg)
> >  	return ret;
> >  }
> >
> > +std::set<unsigned long> V4L2CameraProxy::supportedIoctls_ = {

This should be const.

> > +	VIDIOC_QUERYCAP,
> > +	VIDIOC_ENUM_FMT,
> > +	VIDIOC_G_FMT,
> > +	VIDIOC_S_FMT,
> > +	VIDIOC_TRY_FMT,
> > +	VIDIOC_REQBUFS,
> > +	VIDIOC_QUERYBUF,
> > +	VIDIOC_QBUF,
> > +	VIDIOC_DQBUF,
> > +	VIDIOC_STREAMON,
> > +	VIDIOC_STREAMOFF,
> > +};
> 
> I understand why an std::set(), as it provides a ::find() function
> which ease lookup. I'm wondering if it's worth it though, as a
> function in an anonymous namespace which simply switch on the
> supported requests might be more efficient.

Would it ? It's a honest question, I'm not sure how the compiler would
optimize that.

> Anyway, have you considered having this in an anonymous namespace
> instead of making a static class member out of it ? Is it accessed
> from any other class (don't think so, as it's a private static class
> member)

I have a slight preference for a set here. As for storing it in the
class or as a global variable in an anonymous namespace, I don't mind
much either way, especially as V4L2CameraProxy is an internal class. I'd
certainly go for the latter if the class was part of the public API.

> > +
> >  int V4L2CameraProxy::ioctl(V4L2CameraFile *cf, unsigned long request, void *arg)
> >  {

If we want to match V4L2, should we here add

	if (!arg && (_IOC_DIR(cmd) & _IOC_WRITE)) {
		errno = EFAULT;
		return -1;
	}

> > +	if (supportedIoctls_.find(request) == supportedIoctls_.end()) {
> > +		errno = ENOTTY;
> > +		return -1;
> > +	}
> > +
> > +	if (!arg) {

	if (!arg && (_IOC_DIR(cmd) & _IOC_READ)) {

With the small issues addressed,

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

> > +		errno = EFAULT;
> > +		return -1;
> > +	}
> > +
> >  	int ret;
> >  	switch (request) {
> >  	case VIDIOC_QUERYCAP:
> > diff --git a/src/v4l2/v4l2_camera_proxy.h b/src/v4l2/v4l2_camera_proxy.h
> > index 43290ca..dd7e793 100644
> > --- a/src/v4l2/v4l2_camera_proxy.h
> > +++ b/src/v4l2/v4l2_camera_proxy.h
> > @@ -11,6 +11,7 @@
> >  #include <linux/videodev2.h>
> >  #include <map>
> >  #include <memory>
> > +#include <set>
> >  #include <sys/mman.h>
> >  #include <sys/types.h>
> >  #include <vector>
> > @@ -67,6 +68,8 @@ private:
> >  	static PixelFormat v4l2ToDrm(uint32_t format);
> >  	static uint32_t drmToV4L2(const PixelFormat &format);
> >
> > +	static std::set<unsigned long> supportedIoctls_;
> > +
> >  	unsigned int refcount_;
> >  	unsigned int index_;
> >

Patch

diff --git a/src/v4l2/v4l2_camera_proxy.cpp b/src/v4l2/v4l2_camera_proxy.cpp
index f06f58d..cff6562 100644
--- a/src/v4l2/v4l2_camera_proxy.cpp
+++ b/src/v4l2/v4l2_camera_proxy.cpp
@@ -11,6 +11,7 @@ 
 #include <array>
 #include <errno.h>
 #include <linux/videodev2.h>
+#include <set>
 #include <string.h>
 #include <sys/mman.h>
 #include <unistd.h>
@@ -238,7 +239,6 @@  int V4L2CameraProxy::vidioc_enum_fmt(V4L2CameraFile *cf, struct v4l2_fmtdesc *ar
 {
 	LOG(V4L2Compat, Debug) << "Servicing vidioc_enum_fmt fd = " << cf->efd();
 
-
 	if (!validateBufferType(arg->type) ||
 	    arg->index >= streamConfig_.formats().pixelformats().size())
 		return -EINVAL;
@@ -255,7 +255,6 @@  int V4L2CameraProxy::vidioc_g_fmt(V4L2CameraFile *cf, struct v4l2_format *arg)
 {
 	LOG(V4L2Compat, Debug) << "Servicing vidioc_g_fmt fd = " << cf->efd();
 
-
 	if (!validateBufferType(arg->type))
 		return -EINVAL;
 
@@ -543,8 +542,32 @@  int V4L2CameraProxy::vidioc_streamoff(V4L2CameraFile *cf, int *arg)
 	return ret;
 }
 
+std::set<unsigned long> V4L2CameraProxy::supportedIoctls_ = {
+	VIDIOC_QUERYCAP,
+	VIDIOC_ENUM_FMT,
+	VIDIOC_G_FMT,
+	VIDIOC_S_FMT,
+	VIDIOC_TRY_FMT,
+	VIDIOC_REQBUFS,
+	VIDIOC_QUERYBUF,
+	VIDIOC_QBUF,
+	VIDIOC_DQBUF,
+	VIDIOC_STREAMON,
+	VIDIOC_STREAMOFF,
+};
+
 int V4L2CameraProxy::ioctl(V4L2CameraFile *cf, unsigned long request, void *arg)
 {
+	if (supportedIoctls_.find(request) == supportedIoctls_.end()) {
+		errno = ENOTTY;
+		return -1;
+	}
+
+	if (!arg) {
+		errno = EFAULT;
+		return -1;
+	}
+
 	int ret;
 	switch (request) {
 	case VIDIOC_QUERYCAP:
diff --git a/src/v4l2/v4l2_camera_proxy.h b/src/v4l2/v4l2_camera_proxy.h
index 43290ca..dd7e793 100644
--- a/src/v4l2/v4l2_camera_proxy.h
+++ b/src/v4l2/v4l2_camera_proxy.h
@@ -11,6 +11,7 @@ 
 #include <linux/videodev2.h>
 #include <map>
 #include <memory>
+#include <set>
 #include <sys/mman.h>
 #include <sys/types.h>
 #include <vector>
@@ -67,6 +68,8 @@  private:
 	static PixelFormat v4l2ToDrm(uint32_t format);
 	static uint32_t drmToV4L2(const PixelFormat &format);
 
+	static std::set<unsigned long> supportedIoctls_;
+
 	unsigned int refcount_;
 	unsigned int index_;