Skip to content

Commit a841178

Browse files
Javier Martinez CanillasLee Jones
authored andcommitted
mfd: cros_ec: Use a zero-length array for command data
Commit 1b84f2a ("mfd: cros_ec: Use fixed size arrays to transfer data with the EC") modified the struct cros_ec_command fields to not use pointers for the input and output buffers and use fixed length arrays instead. This change was made because the cros_ec ioctl API uses that struct cros_ec_command to allow user-space to send commands to the EC and to get data from the EC. So using pointers made the API not 64-bit safe. Unfortunately this approach was not flexible enough for all the use-cases since there may be a need to send larger commands on newer versions of the EC command protocol. So to avoid to choose a constant length that it may be too big for most commands and thus wasting memory and CPU cycles on copy from and to user-space or having a size that is too small for some big commands, use a zero-length array that is both 64-bit safe and flexible. The same buffer is used for both output and input data so the maximum of these values should be used to allocate it. Suggested-by: Gwendal Grignou <gwendal@chromium.org> Signed-off-by: Javier Martinez Canillas <javier.martinez@collabora.co.uk> Tested-by: Heiko Stuebner <heiko@sntech.de> Acked-by: Lee Jones <lee.jones@linaro.org> Acked-by: Olof Johansson <olof@lixom.net> Signed-off-by: Lee Jones <lee.jones@linaro.org>
1 parent bb03ffb commit a841178

File tree

10 files changed

+318
-175
lines changed

10 files changed

+318
-175
lines changed

drivers/i2c/busses/i2c-cros-ec-tunnel.c

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,9 @@ static int ec_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg i2c_msgs[],
182182
const u16 bus_num = bus->remote_bus;
183183
int request_len;
184184
int response_len;
185+
int alloc_size;
185186
int result;
186-
struct cros_ec_command msg = { };
187+
struct cros_ec_command *msg;
187188

188189
request_len = ec_i2c_count_message(i2c_msgs, num);
189190
if (request_len < 0) {
@@ -198,25 +199,39 @@ static int ec_i2c_xfer(struct i2c_adapter *adap, struct i2c_msg i2c_msgs[],
198199
return response_len;
199200
}
200201

201-
result = ec_i2c_construct_message(msg.outdata, i2c_msgs, num, bus_num);
202-
if (result)
203-
return result;
202+
alloc_size = max(request_len, response_len);
203+
msg = kmalloc(sizeof(*msg) + alloc_size, GFP_KERNEL);
204+
if (!msg)
205+
return -ENOMEM;
204206

205-
msg.version = 0;
206-
msg.command = EC_CMD_I2C_PASSTHRU;
207-
msg.outsize = request_len;
208-
msg.insize = response_len;
207+
result = ec_i2c_construct_message(msg->data, i2c_msgs, num, bus_num);
208+
if (result) {
209+
dev_err(dev, "Error constructing EC i2c message %d\n", result);
210+
goto exit;
211+
}
209212

210-
result = cros_ec_cmd_xfer(bus->ec, &msg);
211-
if (result < 0)
212-
return result;
213+
msg->version = 0;
214+
msg->command = EC_CMD_I2C_PASSTHRU;
215+
msg->outsize = request_len;
216+
msg->insize = response_len;
213217

214-
result = ec_i2c_parse_response(msg.indata, i2c_msgs, &num);
215-
if (result < 0)
216-
return result;
218+
result = cros_ec_cmd_xfer(bus->ec, msg);
219+
if (result < 0) {
220+
dev_err(dev, "Error transferring EC i2c message %d\n", result);
221+
goto exit;
222+
}
223+
224+
result = ec_i2c_parse_response(msg->data, i2c_msgs, &num);
225+
if (result < 0) {
226+
dev_err(dev, "Error parsing EC i2c message %d\n", result);
227+
goto exit;
228+
}
217229

218230
/* Indicate success by saying how many messages were sent */
219-
return num;
231+
result = num;
232+
exit:
233+
kfree(msg);
234+
return result;
220235
}
221236

222237
static u32 ec_i2c_functionality(struct i2c_adapter *adap)

drivers/input/keyboard/cros_ec_keyb.c

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -148,19 +148,28 @@ static void cros_ec_keyb_process(struct cros_ec_keyb *ckdev,
148148

149149
static int cros_ec_keyb_get_state(struct cros_ec_keyb *ckdev, uint8_t *kb_state)
150150
{
151-
int ret;
152-
struct cros_ec_command msg = {
153-
.command = EC_CMD_MKBP_STATE,
154-
.insize = ckdev->cols,
155-
};
151+
int ret = 0;
152+
struct cros_ec_command *msg;
156153

157-
ret = cros_ec_cmd_xfer(ckdev->ec, &msg);
158-
if (ret < 0)
159-
return ret;
154+
msg = kmalloc(sizeof(*msg) + ckdev->cols, GFP_KERNEL);
155+
if (!msg)
156+
return -ENOMEM;
160157

161-
memcpy(kb_state, msg.indata, ckdev->cols);
158+
msg->version = 0;
159+
msg->command = EC_CMD_MKBP_STATE;
160+
msg->insize = ckdev->cols;
161+
msg->outsize = 0;
162162

163-
return 0;
163+
ret = cros_ec_cmd_xfer(ckdev->ec, msg);
164+
if (ret < 0) {
165+
dev_err(ckdev->dev, "Error transferring EC message %d\n", ret);
166+
goto exit;
167+
}
168+
169+
memcpy(kb_state, msg->data, ckdev->cols);
170+
exit:
171+
kfree(msg);
172+
return ret;
164173
}
165174

166175
static irqreturn_t cros_ec_keyb_irq(int irq, void *data)

drivers/mfd/cros_ec.c

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ int cros_ec_prepare_tx(struct cros_ec_device *ec_dev,
4141
out[2] = msg->outsize;
4242
csum = out[0] + out[1] + out[2];
4343
for (i = 0; i < msg->outsize; i++)
44-
csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->outdata[i];
44+
csum += out[EC_MSG_TX_HEADER_BYTES + i] = msg->data[i];
4545
out[EC_MSG_TX_HEADER_BYTES + msg->outsize] = (uint8_t)(csum & 0xff);
4646

4747
return EC_MSG_TX_PROTO_BYTES + msg->outsize;
@@ -75,11 +75,20 @@ int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev,
7575
ret = ec_dev->cmd_xfer(ec_dev, msg);
7676
if (msg->result == EC_RES_IN_PROGRESS) {
7777
int i;
78-
struct cros_ec_command status_msg = { };
78+
struct cros_ec_command *status_msg;
7979
struct ec_response_get_comms_status *status;
8080

81-
status_msg.command = EC_CMD_GET_COMMS_STATUS;
82-
status_msg.insize = sizeof(*status);
81+
status_msg = kmalloc(sizeof(*status_msg) + sizeof(*status),
82+
GFP_KERNEL);
83+
if (!status_msg) {
84+
ret = -ENOMEM;
85+
goto exit;
86+
}
87+
88+
status_msg->version = 0;
89+
status_msg->command = EC_CMD_GET_COMMS_STATUS;
90+
status_msg->insize = sizeof(*status);
91+
status_msg->outsize = 0;
8392

8493
/*
8594
* Query the EC's status until it's no longer busy or
@@ -88,20 +97,23 @@ int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev,
8897
for (i = 0; i < EC_COMMAND_RETRIES; i++) {
8998
usleep_range(10000, 11000);
9099

91-
ret = ec_dev->cmd_xfer(ec_dev, &status_msg);
100+
ret = ec_dev->cmd_xfer(ec_dev, status_msg);
92101
if (ret < 0)
93102
break;
94103

95-
msg->result = status_msg.result;
96-
if (status_msg.result != EC_RES_SUCCESS)
104+
msg->result = status_msg->result;
105+
if (status_msg->result != EC_RES_SUCCESS)
97106
break;
98107

99108
status = (struct ec_response_get_comms_status *)
100-
status_msg.indata;
109+
status_msg->data;
101110
if (!(status->flags & EC_COMMS_STATUS_PROCESSING))
102111
break;
103112
}
113+
114+
kfree(status_msg);
104115
}
116+
exit:
105117
mutex_unlock(&ec_dev->lock);
106118

107119
return ret;

drivers/mfd/cros_ec_i2c.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ static int cros_ec_cmd_xfer_i2c(struct cros_ec_device *ec_dev,
7676
/* copy message payload and compute checksum */
7777
sum = out_buf[0] + out_buf[1] + out_buf[2];
7878
for (i = 0; i < msg->outsize; i++) {
79-
out_buf[3 + i] = msg->outdata[i];
79+
out_buf[3 + i] = msg->data[i];
8080
sum += out_buf[3 + i];
8181
}
8282
out_buf[3 + msg->outsize] = sum;
@@ -109,7 +109,7 @@ static int cros_ec_cmd_xfer_i2c(struct cros_ec_device *ec_dev,
109109
/* copy response packet payload and compute checksum */
110110
sum = in_buf[0] + in_buf[1];
111111
for (i = 0; i < len; i++) {
112-
msg->indata[i] = in_buf[2 + i];
112+
msg->data[i] = in_buf[2 + i];
113113
sum += in_buf[2 + i];
114114
}
115115
dev_dbg(ec_dev->dev, "packet: %*ph, sum = %02x\n",

drivers/mfd/cros_ec_spi.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ static int cros_ec_cmd_xfer_spi(struct cros_ec_device *ec_dev,
299299
for (i = 0; i < len; i++) {
300300
sum += ptr[i + 2];
301301
if (ec_msg->insize)
302-
ec_msg->indata[i] = ptr[i + 2];
302+
ec_msg->data[i] = ptr[i + 2];
303303
}
304304
sum &= 0xff;
305305

drivers/platform/chrome/cros_ec_dev.c

Lines changed: 42 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <linux/fs.h>
2121
#include <linux/module.h>
2222
#include <linux/platform_device.h>
23+
#include <linux/slab.h>
2324
#include <linux/uaccess.h>
2425

2526
#include "cros_ec_dev.h"
@@ -36,36 +37,42 @@ static int ec_get_version(struct cros_ec_device *ec, char *str, int maxlen)
3637
static const char * const current_image_name[] = {
3738
"unknown", "read-only", "read-write", "invalid",
3839
};
39-
struct cros_ec_command msg = {
40-
.version = 0,
41-
.command = EC_CMD_GET_VERSION,
42-
.outdata = { 0 },
43-
.outsize = 0,
44-
.indata = { 0 },
45-
.insize = sizeof(*resp),
46-
};
40+
struct cros_ec_command *msg;
4741
int ret;
4842

49-
ret = cros_ec_cmd_xfer(ec, &msg);
43+
msg = kmalloc(sizeof(*msg) + sizeof(*resp), GFP_KERNEL);
44+
if (!msg)
45+
return -ENOMEM;
46+
47+
msg->version = 0;
48+
msg->command = EC_CMD_GET_VERSION;
49+
msg->insize = sizeof(*resp);
50+
msg->outsize = 0;
51+
52+
ret = cros_ec_cmd_xfer(ec, msg);
5053
if (ret < 0)
51-
return ret;
54+
goto exit;
5255

53-
if (msg.result != EC_RES_SUCCESS) {
56+
if (msg->result != EC_RES_SUCCESS) {
5457
snprintf(str, maxlen,
5558
"%s\nUnknown EC version: EC returned %d\n",
56-
CROS_EC_DEV_VERSION, msg.result);
57-
return 0;
59+
CROS_EC_DEV_VERSION, msg->result);
60+
ret = -EINVAL;
61+
goto exit;
5862
}
5963

60-
resp = (struct ec_response_get_version *)msg.indata;
64+
resp = (struct ec_response_get_version *)msg->data;
6165
if (resp->current_image >= ARRAY_SIZE(current_image_name))
6266
resp->current_image = 3; /* invalid */
6367

6468
snprintf(str, maxlen, "%s\n%s\n%s\n%s\n", CROS_EC_DEV_VERSION,
6569
resp->version_string_ro, resp->version_string_rw,
6670
current_image_name[resp->current_image]);
6771

68-
return 0;
72+
ret = 0;
73+
exit:
74+
kfree(msg);
75+
return ret;
6976
}
7077

7178
/* Device file ops */
@@ -110,20 +117,32 @@ static ssize_t ec_device_read(struct file *filp, char __user *buffer,
110117
static long ec_device_ioctl_xcmd(struct cros_ec_device *ec, void __user *arg)
111118
{
112119
long ret;
113-
struct cros_ec_command s_cmd = { };
120+
struct cros_ec_command u_cmd;
121+
struct cros_ec_command *s_cmd;
114122

115-
if (copy_from_user(&s_cmd, arg, sizeof(s_cmd)))
123+
if (copy_from_user(&u_cmd, arg, sizeof(u_cmd)))
116124
return -EFAULT;
117125

118-
ret = cros_ec_cmd_xfer(ec, &s_cmd);
126+
s_cmd = kmalloc(sizeof(*s_cmd) + max(u_cmd.outsize, u_cmd.insize),
127+
GFP_KERNEL);
128+
if (!s_cmd)
129+
return -ENOMEM;
130+
131+
if (copy_from_user(s_cmd, arg, sizeof(*s_cmd) + u_cmd.outsize)) {
132+
ret = -EFAULT;
133+
goto exit;
134+
}
135+
136+
ret = cros_ec_cmd_xfer(ec, s_cmd);
119137
/* Only copy data to userland if data was received. */
120138
if (ret < 0)
121-
return ret;
139+
goto exit;
122140

123-
if (copy_to_user(arg, &s_cmd, sizeof(s_cmd)))
124-
return -EFAULT;
125-
126-
return 0;
141+
if (copy_to_user(arg, s_cmd, sizeof(*s_cmd) + u_cmd.insize))
142+
ret = -EFAULT;
143+
exit:
144+
kfree(s_cmd);
145+
return ret;
127146
}
128147

129148
static long ec_device_ioctl_readmem(struct cros_ec_device *ec, void __user *arg)

0 commit comments

Comments
 (0)