[PATCH] drm: Don't overwrite user ioctl arg unless requested

Christian König deathsimple at vodafone.de
Thu Jul 14 07:46:24 UTC 2016


Am 12.07.2016 um 16:59 schrieb Chris Wilson:
> Currently, we completely ignore the user when it comes to the in/out
> direction of the ioctl argument, as we simply cannot trust userspace.
> (For example, they might request a copy of the modified ioctl argument
> when the driver is not expecting such and so leak kernel stack.)
> However, blindly copying over the target address may also lead to a
> spurious EFAULT, and a failure after the ioctl was completed
> successfully. This is important in order to avoid an ABI break when
> extending an ioctl from IOR to IORW. Similar to how we only copy the
> intersection of the kernel arg size and the user arg size, we only want
> to copy back the kernel arg data iff both the kernel and userspace
> request the copy.
>
> Signed-off-by: Chris Wilson <chris at chris-wilson.co.uk>

Seems to make a lot of sense to me, patch is Reviewed-by: Christian 
König <christian.koenig at amd.com>

Regards,
Christian.

> ---
>   drivers/gpu/drm/drm_ioctl.c | 50 ++++++++++++++++++++-------------------------
>   1 file changed, 22 insertions(+), 28 deletions(-)
>
> diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
> index 2c87c1df0910..33af4a5ddca1 100644
> --- a/drivers/gpu/drm/drm_ioctl.c
> +++ b/drivers/gpu/drm/drm_ioctl.c
> @@ -648,7 +648,7 @@ long drm_ioctl(struct file *filp,
>   	int retcode = -EINVAL;
>   	char stack_kdata[128];
>   	char *kdata = NULL;
> -	unsigned int usize, asize, drv_size;
> +	unsigned int in_size, out_size, drv_size, ksize;
>   	bool is_driver_ioctl;
>   
>   	dev = file_priv->minor->dev;
> @@ -671,9 +671,12 @@ long drm_ioctl(struct file *filp,
>   	}
>   
>   	drv_size = _IOC_SIZE(ioctl->cmd);
> -	usize = _IOC_SIZE(cmd);
> -	asize = max(usize, drv_size);
> -	cmd = ioctl->cmd;
> +	out_size = in_size = _IOC_SIZE(cmd);
> +	if ((cmd & ioctl->cmd & IOC_IN) == 0)
> +		in_size = 0;
> +	if ((cmd & ioctl->cmd & IOC_OUT) == 0)
> +		out_size = 0;
> +	ksize = max(max(in_size, out_size), drv_size);
>   
>   	DRM_DEBUG("pid=%d, dev=0x%lx, auth=%d, %s\n",
>   		  task_pid_nr(current),
> @@ -693,30 +696,24 @@ long drm_ioctl(struct file *filp,
>   	if (unlikely(retcode))
>   		goto err_i1;
>   
> -	if (cmd & (IOC_IN | IOC_OUT)) {
> -		if (asize <= sizeof(stack_kdata)) {
> -			kdata = stack_kdata;
> -		} else {
> -			kdata = kmalloc(asize, GFP_KERNEL);
> -			if (!kdata) {
> -				retcode = -ENOMEM;
> -				goto err_i1;
> -			}
> +	if (ksize <= sizeof(stack_kdata)) {
> +		kdata = stack_kdata;
> +	} else {
> +		kdata = kmalloc(ksize, GFP_KERNEL);
> +		if (!kdata) {
> +			retcode = -ENOMEM;
> +			goto err_i1;
>   		}
> -		if (asize > usize)
> -			memset(kdata + usize, 0, asize - usize);
>   	}
>   
> -	if (cmd & IOC_IN) {
> -		if (copy_from_user(kdata, (void __user *)arg,
> -				   usize) != 0) {
> -			retcode = -EFAULT;
> -			goto err_i1;
> -		}
> -	} else if (cmd & IOC_OUT) {
> -		memset(kdata, 0, usize);
> +	if (copy_from_user(kdata, (void __user *)arg, in_size) != 0) {
> +		retcode = -EFAULT;
> +		goto err_i1;
>   	}
>   
> +	if (ksize > in_size)
> +		memset(kdata + in_size, 0, ksize - in_size);
> +
>   	/* Enforce sane locking for kms driver ioctls. Core ioctls are
>   	 * too messy still. */
>   	if ((drm_core_check_feature(dev, DRIVER_MODESET) && is_driver_ioctl) ||
> @@ -728,11 +725,8 @@ long drm_ioctl(struct file *filp,
>   		mutex_unlock(&drm_global_mutex);
>   	}
>   
> -	if (cmd & IOC_OUT) {
> -		if (copy_to_user((void __user *)arg, kdata,
> -				 usize) != 0)
> -			retcode = -EFAULT;
> -	}
> +	if (copy_to_user((void __user *)arg, kdata, out_size) != 0)
> +		retcode = -EFAULT;
>   
>         err_i1:
>   	if (!ioctl)



More information about the dri-devel mailing list