mfd: cros_ec: Use a zero-length array for command data
[cascardo/linux.git] / drivers / platform / chrome / cros_ec_dev.c
index 6090d0b..e91ced1 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/fs.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/slab.h>
 #include <linux/uaccess.h>
 
 #include "cros_ec_dev.h"
@@ -36,28 +37,31 @@ static int ec_get_version(struct cros_ec_device *ec, char *str, int maxlen)
        static const char * const current_image_name[] = {
                "unknown", "read-only", "read-write", "invalid",
        };
-       struct cros_ec_command msg = {
-               .version = 0,
-               .command = EC_CMD_GET_VERSION,
-               .outdata = { 0 },
-               .outsize = 0,
-               .indata = { 0 },
-               .insize = sizeof(*resp),
-       };
+       struct cros_ec_command *msg;
        int ret;
 
-       ret = cros_ec_cmd_xfer(ec, &msg);
+       msg = kmalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL);
+       if (!msg)
+               return -ENOMEM;
+
+       msg->version = 0;
+       msg->command = EC_CMD_GET_VERSION;
+       msg->insize = sizeof(*resp);
+       msg->outsize = 0;
+
+       ret = cros_ec_cmd_xfer(ec, msg);
        if (ret < 0)
-               return ret;
+               goto exit;
 
-       if (msg.result != EC_RES_SUCCESS) {
+       if (msg->result != EC_RES_SUCCESS) {
                snprintf(str, maxlen,
                         "%s\nUnknown EC version: EC returned %d\n",
-                        CROS_EC_DEV_VERSION, msg.result);
-               return 0;
+                        CROS_EC_DEV_VERSION, msg->result);
+               ret = -EINVAL;
+               goto exit;
        }
 
-       resp = (struct ec_response_get_version *)msg.indata;
+       resp = (struct ec_response_get_version *)msg->data;
        if (resp->current_image >= ARRAY_SIZE(current_image_name))
                resp->current_image = 3; /* invalid */
 
@@ -65,7 +69,10 @@ static int ec_get_version(struct cros_ec_device *ec, char *str, int maxlen)
                 resp->version_string_ro, resp->version_string_rw,
                 current_image_name[resp->current_image]);
 
-       return 0;
+       ret = 0;
+exit:
+       kfree(msg);
+       return ret;
 }
 
 /* Device file ops */
@@ -110,20 +117,32 @@ static ssize_t ec_device_read(struct file *filp, char __user *buffer,
 static long ec_device_ioctl_xcmd(struct cros_ec_device *ec, void __user *arg)
 {
        long ret;
-       struct cros_ec_command s_cmd = { };
+       struct cros_ec_command u_cmd;
+       struct cros_ec_command *s_cmd;
 
-       if (copy_from_user(&s_cmd, arg, sizeof(s_cmd)))
+       if (copy_from_user(&u_cmd, arg, sizeof(u_cmd)))
                return -EFAULT;
 
-       ret = cros_ec_cmd_xfer(ec, &s_cmd);
+       s_cmd = kmalloc(sizeof(*s_cmd) + max(u_cmd.outsize, u_cmd.insize),
+                       GFP_KERNEL);
+       if (!s_cmd)
+               return -ENOMEM;
+
+       if (copy_from_user(s_cmd, arg, sizeof(*s_cmd) + u_cmd.outsize)) {
+               ret = -EFAULT;
+               goto exit;
+       }
+
+       ret = cros_ec_cmd_xfer(ec, s_cmd);
        /* Only copy data to userland if data was received. */
        if (ret < 0)
-               return ret;
+               goto exit;
 
-       if (copy_to_user(arg, &s_cmd, sizeof(s_cmd)))
-               return -EFAULT;
-
-       return 0;
+       if (copy_to_user(arg, s_cmd, sizeof(*s_cmd) + u_cmd.insize))
+               ret = -EFAULT;
+exit:
+       kfree(s_cmd);
+       return ret;
 }
 
 static long ec_device_ioctl_readmem(struct cros_ec_device *ec, void __user *arg)