Re: [RFC v2 01/10] 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 07:47:42AM -0300, Mauro Carvalho Chehab wrote:
> Em Fri, 23 Mar 2018 23:17:35 +0200
> 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.
> 
> Patch makes sense to me, but I have a few comments on it.
> 
> > 
> > Signed-off-by: Sakari Ailus <sakari.ailus@xxxxxxxxxxxxxxx>
> > Acked-by: Hans Verkuil <hans.verkuil@xxxxxxxxx>
> > ---
> >  drivers/media/media-device.c | 65 ++++++++++++++++++++++++++++++++++++++++----
> >  1 file changed, 59 insertions(+), 6 deletions(-)
> > 
> > diff --git a/drivers/media/media-device.c b/drivers/media/media-device.c
> > index 35e81f7..da63da1 100644
> > --- a/drivers/media/media-device.c
> > +++ b/drivers/media/media-device.c
> > @@ -387,22 +387,36 @@ 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)		\
> > +#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)
> > +#define MEDIA_IOC_ARG(__cmd, func, fl, from_user, to_user)		\
> > +	MEDIA_IOC_SZ_ARG(__cmd, func, fl, NULL, from_user, to_user)
> > +
> > +#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)
> > +
> > +#define MEDIA_IOC(__cmd, func, fl)				\
> > +	MEDIA_IOC_ARG(__cmd, func, fl,				\
> > +		      copy_arg_from_user, copy_arg_to_user)
> 
> Please add some comments to those macros (specially the first one,
> as the names of the values are too short to help identifying what
> they are.

Ack.

> 
> >  
> >  /* 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 +430,42 @@ static const struct media_ioctl_info ioctl_info[] = {
> >  	MEDIA_IOC(G_TOPOLOGY, media_device_get_topology, MEDIA_IOC_FL_GRAPH_MUTEX),
> >  };
> >  
> > +#define MASK_IOC_SIZE(cmd) \
> > +	((cmd) & ~(_IOC_SIZEMASK << _IOC_SIZESHIFT))
> 
> This should be, instead at:
> 	include/uapi/asm-generic/ioctl.h
> 
> The patch series adding it there should also touch the other usecases
> for _IOC_SIZEMASK (evdev.c, phantom.c, v4l2-ioctl.c).

It seems there's already IOCSIZE_MASK which is already used elsewhere in
the kernel:

#define IOCSIZE_MASK    (_IOC_SIZEMASK << _IOC_SIZESHIFT)

I'll switch to that instead.

> 
> > +
> > +static inline long is_valid_ioctl(unsigned int cmd)
> 
> Please rename from "cmd" to "user_cmd", in order to make it
> clearer that it contains the value passed by userspace.

Done.

> 
> > +{
> > +	const struct media_ioctl_info *info = ioctl_info;
> > +	const unsigned short *alt_arg_sizes;
> > +
> > +	if (_IOC_NR(cmd) >= ARRAY_SIZE(ioctl_info))
> > +		return -ENOIOCTLCMD;
> > +
> > +	info += _IOC_NR(cmd);
> > +
> > +	if (info->cmd == cmd)
> > +		return 0;
> 
> Please revert it:
> 	if (user_cmd == info->cmd)
> 		return 0

Agreed.

> 
> As we're validating if the userspace ioctl code makes sense,
> and not the reverse.
> 
> Please add the check if alt_arg_sizes is defined here:
> 
> 	alt_arg_sizes = info->alt_arg_sizes;
> 	if (!alt_arg_sizes)
> 		return -ENOIOCTLCMD;

I'll move the assignment to the beginning of the loop at the same time to
make the code cleaner.

> 
> As the remaining code is not needed if user_cmd != info_cmd.
> 
> > +
> > +	/*
> > +	 * Verify that the size-dependent patch of the IOCTL command
> > +	 * matches and that the size does not exceed the principal
> > +	 * argument size.
> > +	 */
> 
> what do you mean by "principal argument size"? I guess what you're
> meaning here is the "argument size of the latest version" with is
> always bigger than the previous version. If so, make it clear.

Correct.

> 
> I would write it as something like:
> 
> 	/*
> 	 * As the ioctl passed by userspace doesn't match the current
> 	 * one, and there are alternate sizes for thsi ioctl, 
> 	 * we need to check if the ioctl code is correct.
> 	 *
> 	 * Validate that the ioctl code passed by userspace matches the
> 	 * Kernel definition with regards to its number, type and dir.
> 	 * Also checks if the size is not bigger than the one defined
> 	 * by the latest version of the ioctl.

The number has been already validated earlier. I think this is a quote
thorough explanation of what is not really that complicated. What would you
think of the following?

	/*
	 * Variable size IOCTL argument support allows using either the latest
	 * variant 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 (MASK_IOC_SIZE(info->cmd) != MASK_IOC_SIZE(cmd)
> > +	    || _IOC_SIZE(info->cmd) < _IOC_SIZE(cmd))
> > +		return -ENOIOCTLCMD;
> 
> I would invert the check: what we want do to is to check if whatever
> userspace passes is valid. So, better to place user_cmd as the first
> arguments at the check, e. g.: 
> 
> 	if (MASK_IOC_SIZE(user_cmd) != MASK_IOC_SIZE(info->cmd)
> 	    ||  _IOC_SIZE(user_cmd) > _IOC_SIZE(info->cmd))
> 		return -ENOIOCTLCMD;

Agreed.

> 
> > +
> > +	alt_arg_sizes = info->alt_arg_sizes;
> > +	if (!alt_arg_sizes)
> > +		return -ENOIOCTLCMD;
> 
> As said before, this check should happen earlier.
> 
> > +
> > +	for (; *alt_arg_sizes; alt_arg_sizes++)
> > +		if (_IOC_SIZE(cmd) == *alt_arg_sizes)
> > +			return 0;
> > +
> > +	return -ENOIOCTLCMD;
> > +}
> > +
> >  static long media_device_ioctl(struct file *filp, unsigned int cmd,
> >  			       unsigned long __arg)
> >  {
> > @@ -426,9 +476,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 +494,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);
> >  
> 
> 
> 

After the changes I've done, the function would look like this. I also
added a comment on the check for alt_arg_sizes.

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;

	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;
}


-- 
Kind regards,

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