[PATCH 1/5] staging: vchiq: rework compat handling

From: Arnd Bergmann
Date: Fri Sep 18 2020 - 05:55:22 EST


The compat handlers for VCHIQ_IOC_QUEUE_MESSAGE32 and
VCHIQ_IOC_GET_CONFIG32 can simply call the underlying implementations
that are already separate functions rather than using copy_in_user to
simulate the native 64-bit interface for the full ioctl handler.

vchiq_ioc_queue_message gets a small update to the calling
conventions to simplify the compat version by directly
returning a normal errno value.

Signed-off-by: Arnd Bergmann <arnd@xxxxxxxx>
---
.../interface/vchiq_arm/vchiq_arm.c | 109 +++++++++---------
1 file changed, 56 insertions(+), 53 deletions(-)

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 d4d811884861..56a38bec848a 100644
--- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
+++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c
@@ -765,12 +765,13 @@ static ssize_t vchiq_ioc_copy_element_data(void *context, void *dest,
* vchiq_ioc_queue_message
*
**************************************************************************/
-static enum vchiq_status
+static int
vchiq_ioc_queue_message(unsigned int handle,
struct vchiq_element *elements,
unsigned long count)
{
struct vchiq_io_copy_callback_context context;
+ enum vchiq_status status = VCHIQ_SUCCESS;
unsigned long i;
size_t total_size = 0;

@@ -785,8 +786,14 @@ vchiq_ioc_queue_message(unsigned int handle,
total_size += elements[i].size;
}

- return vchiq_queue_message(handle, vchiq_ioc_copy_element_data,
- &context, total_size);
+ status = vchiq_queue_message(handle, vchiq_ioc_copy_element_data,
+ &context, total_size);
+
+ if (status == VCHIQ_ERROR)
+ return -EIO;
+ else if (status == VCHIQ_RETRY)
+ return -EINTR;
+ return 0;
}

/****************************************************************************
@@ -1020,9 +1027,8 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)

if (copy_from_user(elements, args.elements,
args.count * sizeof(struct vchiq_element)) == 0)
- status = vchiq_ioc_queue_message
- (args.handle,
- elements, args.count);
+ ret = vchiq_ioc_queue_message(args.handle, elements,
+ args.count);
else
ret = -EFAULT;
} else {
@@ -1550,55 +1556,53 @@ struct vchiq_queue_message32 {
static long
vchiq_compat_ioctl_queue_message(struct file *file,
unsigned int cmd,
- unsigned long arg)
+ struct vchiq_queue_message32 __user *arg)
{
- struct vchiq_queue_message __user *args;
- struct vchiq_element __user *elements;
+ struct vchiq_queue_message args;
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));
+ struct vchiq_service *service;
+ int ret;

- if (!args)
+ if (copy_from_user(&args32, arg, sizeof(args32)))
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;
+ args = (struct vchiq_queue_message) {
+ .handle = args32.handle,
+ .count = args32.count,
+ .elements = compat_ptr(args32.elements),
+ };

if (args32.count > MAX_ELEMENTS)
return -EINVAL;

- if (args32.elements && args32.count) {
- struct vchiq_element32 tempelement32[MAX_ELEMENTS];
+ service = find_service_for_instance(file->private_data, args.handle);
+ if (!service)
+ return -EINVAL;

- elements = (struct vchiq_element __user *)(args + 1);
+ if (args32.elements && args32.count) {
+ struct vchiq_element32 element32[MAX_ELEMENTS];
+ struct vchiq_element elements[MAX_ELEMENTS];
+ unsigned int count;

- if (copy_from_user(&tempelement32,
- compat_ptr(args32.elements),
- sizeof(tempelement32)))
+ if (copy_from_user(&element32, args.elements,
+ sizeof(element32))) {
+ unlock_service(service);
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;
+ elements[count].data =
+ compat_ptr(element32[count].data);
+ elements[count].size = element32[count].size;
}
-
- if (put_user(elements, &args->elements))
- return -EFAULT;
+ ret = vchiq_ioc_queue_message(args.handle, elements,
+ args.count);
+ } else {
+ ret = -EINVAL;
}
+ unlock_service(service);

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

struct vchiq_queue_bulk_transfer32 {
@@ -1866,35 +1870,34 @@ struct vchiq_get_config32 {
static long
vchiq_compat_ioctl_get_config(struct file *file,
unsigned int cmd,
- unsigned long arg)
+ struct vchiq_get_config32 __user *arg)
{
- struct vchiq_get_config __user *args;
struct vchiq_get_config32 args32;
+ struct vchiq_config config;
+ void __user *ptr;

- args = compat_alloc_user_space(sizeof(*args));
- if (!args)
- return -EFAULT;
-
- if (copy_from_user(&args32,
- (struct vchiq_get_config32 __user *)arg,
- sizeof(args32)))
+ if (copy_from_user(&args32, arg, sizeof(args32)))
return -EFAULT;
+ if (args32.config_size > sizeof(config))
+ return -EINVAL;

- if (put_user(args32.config_size, &args->config_size) ||
- put_user(compat_ptr(args32.pconfig), &args->pconfig))
+ vchiq_get_config(&config);
+ ptr = compat_ptr(args32.pconfig);
+ if (copy_to_user(ptr, &config, args32.config_size))
return -EFAULT;

- return vchiq_ioctl(file, VCHIQ_IOC_GET_CONFIG, (unsigned long)args);
+ return 0;
}

static long
vchiq_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
+ void __user *argp = compat_ptr(arg);
switch (cmd) {
case VCHIQ_IOC_CREATE_SERVICE32:
return vchiq_compat_ioctl_create_service(file, cmd, arg);
case VCHIQ_IOC_QUEUE_MESSAGE32:
- return vchiq_compat_ioctl_queue_message(file, cmd, arg);
+ return vchiq_compat_ioctl_queue_message(file, cmd, argp);
case VCHIQ_IOC_QUEUE_BULK_TRANSMIT32:
case VCHIQ_IOC_QUEUE_BULK_RECEIVE32:
return vchiq_compat_ioctl_queue_bulk(file, cmd, arg);
@@ -1903,9 +1906,9 @@ vchiq_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
case VCHIQ_IOC_DEQUEUE_MESSAGE32:
return vchiq_compat_ioctl_dequeue_message(file, cmd, arg);
case VCHIQ_IOC_GET_CONFIG32:
- return vchiq_compat_ioctl_get_config(file, cmd, arg);
+ return vchiq_compat_ioctl_get_config(file, cmd, argp);
default:
- return vchiq_ioctl(file, cmd, arg);
+ return vchiq_ioctl(file, cmd, (unsigned long)argp);
}
}

--
2.27.0