Re: [RFC v2.1 1/1] media: Support variable size IOCTL arguments

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Mauro,

On Mon, Mar 26, 2018 at 02:28:34PM -0300, Mauro Carvalho Chehab wrote:
> Em Mon, 26 Mar 2018 16:23:24 +0300
> Sakari Ailus <sakari.ailus@xxxxxxxxxxxxxxx> escreveu:
> 
> > Maintain a list of supported IOCTL argument sizes and allow only those in
> > the list.
> > 
> > As an additional bonus, IOCTL handlers will be able to check whether the
> > caller actually set (using the argument size) the field vs. assigning it
> > to zero. Separate macro can be provided for that.
> > 
> > This will be easier for applications as well since there is no longer the
> > problem of setting the reserved fields zero, or at least it is a lesser
> > problem.
> > 
> > Signed-off-by: Sakari Ailus <sakari.ailus@xxxxxxxxxxxxxxx>
> > Acked-by: Hans Verkuil <hans.verkuil@xxxxxxxxx>
> > ---
> > Hi folks,
> > 
> > I've essentially addressed Mauro's comments on v2.
> > 
> > The code is only compile tested so far but the changes from the last
> > tested version are not that big. There's still some uncertainty though.
> 
> You should test it... I guess there is a bug on this version :-)
> (see below)
> 
> > 
> > since v2:
> > 
> > - Rework is_valid_ioctl based on the comments
> > 
> > 	- Improved comments,
> > 	
> > 	- Rename cmd as user_cmd, as this comes from the user
> > 	
> > 	- Check whether there are alternative argument sizes before any
> > 	  checks on IOCTL command if there is no exact match
> > 	  
> > 	- Use IOCSIZE_MASK macro instead of creating our own
> > 
> > - Add documentation for macros declaring IOCTLs
> > 
> > 
> >  drivers/media/media-device.c | 98 +++++++++++++++++++++++++++++++++++++++++---
> >  1 file changed, 92 insertions(+), 6 deletions(-)
> > 
> > diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c
> > index 35e81f7..279d740 100644
> > --- a/drivers/media/media-device.c
> > +++ b/drivers/media/media-device.c
> > @@ -387,22 +387,65 @@ static long copy_arg_to_user(void __user *uarg, void *karg, unsigned int cmd)
> >  /* Do acquire the graph mutex */
> >  #define MEDIA_IOC_FL_GRAPH_MUTEX	BIT(0)
> >  
> > -#define MEDIA_IOC_ARG(__cmd, func, fl, from_user, to_user)		\
> > +/**
> > + * MEDIA_IOC_SZ_ARG - Declare a Media device IOCTL with alternative size and
> > + *		      to_user/from_user callbacks
> > + *
> > + * @__cmd:	The IOCTL command suffix (without "MEDIA_IOC_")
> > + * @func:	The handler function
> > + * @fl:		Flags from @enum media_ioc_flags
> > + * @alt_sz:	A 0-terminated list of alternative argument struct sizes.
> > + * @from_user:	Function to copy argument struct from the user to the kernel
> > + * @to_user:	Function to copy argument struct to the user from the kernel
> > + */
> > +#define MEDIA_IOC_SZ_ARG(__cmd, func, fl, alt_sz, from_user, to_user)	\
> >  	[_IOC_NR(MEDIA_IOC_##__cmd)] = {				\
> >  		.cmd = MEDIA_IOC_##__cmd,				\
> >  		.fn = (long (*)(struct media_device *, void *))func,	\
> >  		.flags = fl,						\
> > +		.alt_arg_sizes = alt_sz,				\
> >  		.arg_from_user = from_user,				\
> >  		.arg_to_user = to_user,					\
> >  	}
> >  
> > -#define MEDIA_IOC(__cmd, func, fl)					\
> > -	MEDIA_IOC_ARG(__cmd, func, fl, copy_arg_from_user, copy_arg_to_user)
> > +/**
> > + * MEDIA_IOC_ARG - Declare a Media device IOCTL with to_user/from_user callbacks
> > + *
> > + * Just as MEDIA_IOC_SZ_ARG but without the alternative size list.
> > + */
> 
> Nitpick: either use:
> 	/*
> 	 *...
> 	 */
> 
> or add the arguments to the macro there, as /** ... */ expects
> the arguments. Same for other comments below.

I think a regular comment would do. It's only used below.

> 
> > +#define MEDIA_IOC_ARG(__cmd, func, fl, from_user, to_user)		\
> > +	MEDIA_IOC_SZ_ARG(__cmd, func, fl, NULL, from_user, to_user)
> > +
> > +/**
> > + * MEDIA_IOC_ARG - Declare a Media device IOCTL with alternative argument struct
> > + *		   sizes
> > + *
> > + * Just as MEDIA_IOC_SZ_ARG but without the callbacks to copy the data from the
> > + * user space and back to user space.
> > + */
> > +#define MEDIA_IOC_SZ(__cmd, func, fl, alt_sz)			\
> > +	MEDIA_IOC_SZ_ARG(__cmd, func, fl, alt_sz,		\
> > +			 copy_arg_from_user, copy_arg_to_user)
> > +
> > +/**
> > + * MEDIA_IOC_ARG - Declare a Media device IOCTL
> > + *
> > + * Just as MEDIA_IOC_SZ_ARG but without the alternative size list or the
> > + * callbacks to copy the data from the user space and back to user space.
> > + */
> > +#define MEDIA_IOC(__cmd, func, fl)				\
> > +	MEDIA_IOC_ARG(__cmd, func, fl,				\
> > +		      copy_arg_from_user, copy_arg_to_user)
> >  
> >  /* the table is indexed by _IOC_NR(cmd) */
> >  struct media_ioctl_info {
> >  	unsigned int cmd;
> >  	unsigned short flags;
> > +	/*
> > +	 * Sizes of the alternative arguments. If there are none, this
> > +	 * pointer is NULL.
> > +	 */
> > +	const unsigned short *alt_arg_sizes;
> >  	long (*fn)(struct media_device *dev, void *arg);
> >  	long (*arg_from_user)(void *karg, void __user *uarg, unsigned int cmd);
> >  	long (*arg_to_user)(void __user *uarg, void *karg, unsigned int cmd);
> > @@ -416,6 +459,46 @@ static const struct media_ioctl_info ioctl_info[] = {
> >  	MEDIA_IOC(G_TOPOLOGY, media_device_get_topology, MEDIA_IOC_FL_GRAPH_MUTEX),
> >  };
> >  
> > +static inline long is_valid_ioctl(unsigned int user_cmd)
> > +{
> > +	const struct media_ioctl_info *info = ioctl_info;
> > +	const unsigned short *alt_arg_sizes;
> > +
> > +	if (_IOC_NR(user_cmd) >= ARRAY_SIZE(ioctl_info))
> > +		return -ENOIOCTLCMD;
> > +
> > +	info += _IOC_NR(user_cmd);
> > +
> > +	if (user_cmd == info->cmd)
> > +		return 0;
> > +
> > +	/*
> > +	 * There was no exact match between the user-passed IOCTL command and
> > +	 * the definition. Are there earlier revisions of the argument struct
> > +	 * available?
> > +	 */
> > +	if (!info->alt_arg_sizes)
> > +		return -ENOIOCTLCMD;
> > +
> > +	/*
> > +	 * Variable size IOCTL argument support allows using either the latest
> > +	 * revision of the IOCTL argument struct or an earlier version. Check
> > +	 * that the size-independent portions of the IOCTL command match and
> > +	 * that the size matches with one of the alternative sizes that
> > +	 * represent earlier revisions of the argument struct.
> > +	 */
> > +	if ((user_cmd & ~IOCSIZE_MASK) != (info->cmd & ~IOCSIZE_MASK)
> > +	    || _IOC_SIZE(user_cmd) < _IOC_SIZE(info->cmd))
> > +		return -ENOIOCTLCMD;
> 
> I guess it should be, instead:
> 
> 	    || _IOC_SIZE(user_cmd) > _IOC_SIZE(info->cmd))
> 
> The hole idea is that the struct sizes used by ioctls can monotonically
> increase as newer fields become needed, but never decrease.

Oops, indeed. I'll send a new version...

> 
> Assuming that, _IOC_SIZE(MEDIA_IOC_foo) will give the size of the
> latest version of an ioctl supported by a given Kernel version,
> while alt_arg_sizes will list smaller sizes from previous
> Kernel versions that will also be accepted, in order to make it
> backward-compatible with apps compiled against older Kernel headers.
> 
> However, if an application is compiled with a kernel newer than
> the current one, it should fail, as an older Kernel doesn't know
> how to handle the newer fields. So, it should be up to the userspace
> app to add backward-compatible code if it needs to support older
> Kernels.
> 
> (perhaps it should be worth adding a comment like the above
> somewhere).

Good point. I wonder if it'd be better to just handle this in the kernel
and allow larges arguments as well. This would effectively be the same as
we have right now, with a very large number of reserved fields.

We could not assume an application knowingly set a field that is present in
a struct of which an older revision exists: all it takes is to compile the
application in an environment which has the new definitions. Unless... we
put the version to the struct name. But I don't like that, it makes IOCTL
calls (and the documentation) quite ugly.

That'd also suggest the list of alternative sizes isn't very useful: even
when we have reserved fields, we don't have any way of knowing whether an
application intentionally set a field to zero or just left out initialising
that particular field.

I wonder what Hans thinks...

> 
> > +
> > +	for (alt_arg_sizes = info->alt_arg_sizes; *alt_arg_sizes;
> > +	     alt_arg_sizes++)
> > +		if (_IOC_SIZE(user_cmd) == *alt_arg_sizes)
> > +			return 0;
> > +
> > +	return -ENOIOCTLCMD;
> > +}
> > +
> >  static long media_device_ioctl(struct file *filp, unsigned int cmd,
> >  			       unsigned long __arg)
> >  {
> > @@ -426,9 +509,9 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd,
> >  	char __karg[256], *karg = __karg;
> >  	long ret;
> >  
> > -	if (_IOC_NR(cmd) >= ARRAY_SIZE(ioctl_info)
> > -	    || ioctl_info[_IOC_NR(cmd)].cmd != cmd)
> > -		return -ENOIOCTLCMD;
> > +	ret = is_valid_ioctl(cmd);
> > +	if (ret)
> > +		return ret;
> >  
> >  	info = &ioctl_info[_IOC_NR(cmd)];
> >  
> > @@ -444,6 +527,9 @@ static long media_device_ioctl(struct file *filp, unsigned int cmd,
> >  			goto out_free;
> >  	}
> >  
> > +	/* Set the rest of the argument struct to zero */
> > +	memset(karg + _IOC_SIZE(cmd), 0, _IOC_SIZE(info->cmd) - _IOC_SIZE(cmd));
> > +
> >  	if (info->flags & MEDIA_IOC_FL_GRAPH_MUTEX)
> >  		mutex_lock(&dev->graph_mutex);
> >  
> 
> Regards,
> Mauro

-- 
Sakari Ailus
sakari.ailus@xxxxxxxxxxxxxxx



[Index of Archives]     [Linux Input]     [Video for Linux]     [Gstreamer Embedded]     [Mplayer Users]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]
  Powered by Linux