[RESEND PATCH V4] staging: vchiq_arm: Add compatibility wrappers for ioctls

Eric Anholt eric at anholt.net
Thu Mar 2 10:45:41 PST 2017


Michael Zoran <mzoran at crowfest.net> writes:

> This patch adds compatibility wrappers for the ioctls
> exposed by vchiq/vc04_services.  The compat ioctls are
> completely implemented on top of the native ioctls.  No
> existing lines are modified.
>
> While the ideal approach would be to cleanup the existing
> code, this path is simplier and easier to review. While
> it does have a small runtime performance penality vs
> seperating the existing code into wrapper+worker functions,
> the penality is small since only the metadata is copied
> back onto the 32 bit user mode stack.
>
> The on top of approach is the approach used by several
> existing performance critical subsystems of Linux such
> as the DRM 3D graphics subsystem.
>
> Testing:
>
> 1. A 32 bit chroot was created on a RPI 3 and vchiq_test
> was built for armhf.  The usual tests were run such as
> vchiq_test -f 10 and vchiq_test -p.
>
> 2. This patch was applied to the shipping version of
> the Linux kernel used for the RPI and that kernel was
> built for arm64. That kernel was used to boot Raspbian.
> Many of the builtin features are now functional such
> as the "hello_pi" examples, and minecraft_pi.

> Signed-off-by: Michael Zoran <mzoran at crowfest.net>
> ---
>  .../vc04_services/interface/vchiq_arm/vchiq_arm.c  | 446 +++++++++++++++++++++
>  1 file changed, 446 insertions(+)
>
> diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
> index 19bd4ac6e855..90dfa79089d3 100644
> --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
> +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
> @@ -47,6 +47,7 @@
>  #include <linux/list.h>
>  #include <linux/of.h>
>  #include <linux/platform_device.h>
> +#include <linux/compat.h>
>  #include <soc/bcm2835/raspberrypi-firmware.h>
>  
>  #include "vchiq_core.h"
> @@ -1227,6 +1228,448 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
>  	return ret;
>  }
>  
> +#if defined(CONFIG_COMPAT)

> +static long
> +vchiq_compat_ioctl_queue_message(struct file *file,
> +				 unsigned int cmd,
> +				 unsigned long arg)
> +{
> +	VCHIQ_QUEUE_MESSAGE_T *args;
> +	VCHIQ_ELEMENT_T *elements;
> +	struct vchiq_queue_message32 args32;
> +	unsigned int count;
> +
> +	if (copy_from_user(&args32,
> +			   (struct vchiq_queue_message32 __user *)arg,
> +			   sizeof(args32)))
> +		return -EFAULT;
> +
> +	args = compat_alloc_user_space(sizeof(*args) +
> +				       (sizeof(*elements) * MAX_ELEMENTS));
> +
> +	if (!args)
> +		return -EFAULT;
> +
> +	if (put_user(args32.handle, &args->handle) ||
> +	    put_user(args32.count, &args->count) ||
> +	    put_user(compat_ptr(args32.elements), &args->elements))
> +		return -EFAULT;
> +
> +	if (args32.elements && args32.count && !(args32.count > MAX_ELEMENTS)) {
> +		struct vchiq_element32 tempelement32[MAX_ELEMENTS];
> +
> +		elements = (VCHIQ_ELEMENT_T __user *)(args + 1);
> +
> +		if (copy_from_user(&tempelement32,
> +				   compat_ptr(args32.elements),
> +				   sizeof(tempelement32)))
> +			return -EFAULT;
> +
> +		for (count = 0; count < args32.count; count++) {
> +			if (put_user(compat_ptr(tempelement32[count].data),
> +				     &elements[count].data) ||
> +			    put_user(tempelement32[count].size,
> +				     &elements[count].size))
> +				return -EFAULT;
> +		}
> +
> +		if (put_user(elements, &args->elements))
> +			return -EFAULT;
> +	}

I think inside of this block you should just check args32.count >
MAX_ELEMENTS and return -EINVAL in that case, rather than silently not
copying the elements.

> +
> +	return vchiq_ioctl(file, VCHIQ_IOC_QUEUE_MESSAGE, (unsigned long)args);
> +}


> +
> +struct vchiq_completion_data32 {
> +	VCHIQ_REASON_T reason;
> +	compat_uptr_t header;
> +	compat_uptr_t service_userdata;
> +	compat_uptr_t bulk_userdata;
> +};
> +
> +struct vchiq_await_completion32 {
> +	unsigned int count;
> +	compat_uptr_t buf;
> +	unsigned int msgbufsize;
> +	unsigned int msgbufcount; /* IN/OUT */
> +	compat_uptr_t msgbufs;
> +};
> +
> +#define VCHIQ_IOC_AWAIT_COMPLETION32 \
> +	_IOWR(VCHIQ_IOC_MAGIC, 7, struct vchiq_await_completion32)
> +
> +static long
> +vchiq_compat_ioctl_await_completion(struct file *file,
> +				    unsigned int cmd,
> +				    unsigned long arg)
> +{
> +	VCHIQ_AWAIT_COMPLETION_T *args;
> +	VCHIQ_COMPLETION_DATA_T *completion;
> +	VCHIQ_COMPLETION_DATA_T completiontemp;
> +	struct vchiq_await_completion32 args32;
> +	struct vchiq_completion_data32 completion32;
> +	unsigned int *msgbufcount32;
> +	compat_uptr_t msgbuf32;
> +	void *msgbuf;
> +	void **msgbufptr;
> +	long ret;
> +
> +	args = compat_alloc_user_space(sizeof(*args) +
> +				       sizeof(*completion) +
> +				       sizeof(*msgbufptr));
> +	if (!args)
> +		return -EFAULT;
> +
> +	completion = (VCHIQ_COMPLETION_DATA_T *)(args + 1);
> +	msgbufptr = (void __user **)(completion + 1);
> +
> +	if (copy_from_user(&args32,
> +			   (struct vchiq_completion_data32 *)arg,
> +			   sizeof(args32)))
> +		return -EFAULT;
> +
> +	if (put_user(args32.count, &args->count) ||
> +	    put_user(compat_ptr(args32.buf), &args->buf) ||
> +	    put_user(args32.msgbufsize, &args->msgbufsize) ||
> +	    put_user(args32.msgbufcount, &args->msgbufcount) ||
> +	    put_user(compat_ptr(args32.msgbufs), &args->msgbufs))
> +		return -EFAULT;
> +
> +	if (!args32.count || !args32.buf || !args32.msgbufcount)
> +		return vchiq_ioctl(file,
> +				   VCHIQ_IOC_AWAIT_COMPLETION,
> +				   (unsigned long)args);
> +
> +	if (copy_from_user(&msgbuf32,
> +			   compat_ptr(args32.msgbufs) +
> +			   (sizeof(compat_uptr_t) *
> +			   (args32.msgbufcount - 1)),
> +			   sizeof(msgbuf32)))
> +		return -EFAULT;
> +
> +	msgbuf = compat_ptr(msgbuf32);
> +
> +	if (copy_to_user(msgbufptr,
> +			 &msgbuf,
> +			 sizeof(msgbuf)))
> +		return -EFAULT;
> +
> +	if (copy_to_user(&args->msgbufs,
> +			 &msgbufptr,
> +			 sizeof(msgbufptr)))
> +		return -EFAULT;
> +
> +	if (put_user(1U, &args->count) ||
> +	    put_user(completion, &args->buf) ||
> +	    put_user(1U, &args->msgbufcount))
> +		return -EFAULT;

Seems like instead of treating the user ioctl as having specified a
count of 1 / msgbufcount of 1, we should throw -EINVAL if they specified
something other than what we support for the compat path.

(this also means we don't need the weird bumping of msgbuf by
msgbufcount - 1 in the copy_from_user above, right?)

> +
> +	ret = vchiq_ioctl(file,
> +			  VCHIQ_IOC_AWAIT_COMPLETION,
> +			  (unsigned long)args);
> +
> +	if (ret <= 0)
> +		return ret;
> +
> +	if (copy_from_user(&completiontemp, completion, sizeof(*completion)))
> +		return -EFAULT;
> +
> +	completion32.reason = completiontemp.reason;
> +	completion32.header = ptr_to_compat(completiontemp.header);
> +	completion32.service_userdata =
> +		ptr_to_compat(completiontemp.service_userdata);
> +	completion32.bulk_userdata =
> +		ptr_to_compat(completiontemp.bulk_userdata);
> +
> +	if (copy_to_user(compat_ptr(args32.buf),
> +			 &completion32,
> +			 sizeof(completion32)))
> +		return -EFAULT;
> +
> +	args32.msgbufcount--;
> +
> +	msgbufcount32 =
> +		&((struct vchiq_await_completion32 __user *)arg)->msgbufcount;

There seem to be conditions in the real ioctl where msgbufcount doesn't
get decremented.  Could we just get_user() the args->msgbufcount and
copy that back out?

With these 3 fixes,

Reviewed-by: Eric Anholt <eric at anholt.net>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 832 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-rpi-kernel/attachments/20170302/ff2ffa87/attachment.sig>


More information about the linux-rpi-kernel mailing list