Skip to contentSkip to conversation

i2c: smbus: make i2c_smbus_read_block_data() safer

PATCH In review Dmitry Torokhov 2 participants 3 messages Jul 29 – Sep 29

Subsystems (2)
lore
Share & tools
lore
#0 lore raw

PATCH v2 1/1 i2c: smbus: make i2c_smbus_read_block_data() safer

i2c_smbus_read_block_data() is dangerous to use because it may deliver up to I2C_SMBUS_BLOCK_MAX (32) bytes, which may be surprising to the caller. Callers tend to allocate buffers of sizes big enough to hold data from a well-behaving device and do not expect that i2c_smbus_read_block_data() may attempt to write more data than expected.

To make i2c_smbus_read_block_data() safer to use, change it so that it accepts size of the supplied buffer as another argument and ensure that it will not copy more data than the size of the buffer. Signal oversized responses with -EMSGSIZE.

To allow users to gradually transition to the new API employ some macro trickery allowing calling i2c_smbus_read_block_data() with either 3 or 4 arguments. When called with 3 arguments it is assumed that the buffer size is I2C_SMBUS_BLOCK_MAX bytes. Once everyone is transitioned to the 4 argument form the macros should be removed.

---

v2:
- dropped incorrect changes to Documentation (Andi)
- signal oversized responses with -EMSGSIZE (Andi)

 drivers/i2c/i2c-core-smbus.c | 24 ++++++++++++++++--------
 include/linux/i2c.h          | 19 +++++++++++++++++--
 2 files changed, 33 insertions(+), 10 deletions(-)
drivers/i2c/i2c-core-smbus.c +16 -8
index fa63bee0b345..57f55bbab8a1 100644
@@ -208,11 +208,11 @@ s32 i2c_smbus_write_word_data(const struct i2c_client *client, u8 command,
 EXPORT_SYMBOL(i2c_smbus_write_word_data);
 
 /**
- * i2c_smbus_read_block_data - SMBus "block read" protocol
+ * __i2c_smbus_read_block_data - SMBus "block read" protocol
  * @client: Handle to slave device
  * @command: Byte interpreted by slave
- * @values: Byte array into which data will be read; big enough to hold
- *	the data returned by the slave.  SMBus allows at most 32 bytes.
+ * @length: size of the @values array. SMBus allows at most 32 bytes
+ * @values: Byte array into which data will be read
  *
  * This executes the SMBus "block read" protocol, returning negative errno
  * else the number of data bytes in the slave's response.
@@ -222,22 +222,30 @@ EXPORT_SYMBOL(i2c_smbus_write_word_data);
  * support this; its emulation through I2C messaging relies on a specific
  * mechanism (I2C_M_RECV_LEN) which may not be implemented.
  */
-s32 i2c_smbus_read_block_data(const struct i2c_client *client, u8 command,
-			      u8 *values)
+s32 __i2c_smbus_read_block_data(const struct i2c_client *client, u8 command,
+				u8 length, u8 *values)
 {
 	union i2c_smbus_data data;
+	int ret_len;
 	int status;
 
+	if (length > I2C_SMBUS_BLOCK_MAX)
+		return -EINVAL;
+
 	status = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
 				I2C_SMBUS_READ, command,
 				I2C_SMBUS_BLOCK_DATA, &data);
 	if (status)
 		return status;
 
-	memcpy(values, &data.block[1], data.block[0]);
-	return data.block[0];
+	ret_len = data.block[0];
+	if (ret_len > length)
+		return -EMSGSIZE;
+
+	memcpy(values, &data.block[1], ret_len);
+	return ret_len;
 }
-EXPORT_SYMBOL(i2c_smbus_read_block_data);
+EXPORT_SYMBOL(__i2c_smbus_read_block_data);
 
 /**
  * i2c_smbus_write_block_data - SMBus "block write" protocol
include/linux/i2c.h +17 -2
index 14ab4d3055af..3e685476fa15 100644
@@ -174,8 +174,23 @@ i2c_smbus_write_word_swapped(const struct i2c_client *client,
 }
 
 /* Returns the number of read bytes */
-s32 i2c_smbus_read_block_data(const struct i2c_client *client,
-			      u8 command, u8 *values);
+s32 __i2c_smbus_read_block_data(const struct i2c_client *client,
+				u8 command, u8 length, u8 *values);
+/*
+ * This monstrosity allows to call i2c_smbus_read_block_data() with either
+ * 3 or 4 arguments and will be removed once all users have been switched
+ * to the 4 argument version.
+ */
+#define __i2c_smbus_read_block_data_3arg(client, cmd, values)			\
+	__i2c_smbus_read_block_data(client, cmd, I2C_SMBUS_BLOCK_MAX, values)
+#define __i2c_smbus_read_block_data_4arg(client, cmd, length, values)		\
+	__i2c_smbus_read_block_data(client, cmd, length, values)
+#define __i2c_smbus_read_block_data_impl(_1, _2, _3, _4, impl, ...) impl
+#define i2c_smbus_read_block_data(client, cmd, varargs...)			\
+	__i2c_smbus_read_block_data_impl(client, cmd, varargs,			\
+					 __i2c_smbus_read_block_data_4arg,	\
+					 __i2c_smbus_read_block_data_3arg)	\
+					 (client, cmd, varargs)
 s32 i2c_smbus_write_block_data(const struct i2c_client *client,
 			       u8 command, u8 length, const u8 *values);
 /* Returns the number of read bytes */
Signature · 5 lines
2.55.0.487.gaf234c4eb3-goog


-- 
Dmitry
#1 lore raw
Quoted 18 lines · Dmitry Torokhov

i2c_smbus_read_block_data() is dangerous to use because it may deliver up to I2C_SMBUS_BLOCK_MAX (32) bytes, which may be surprising to the caller. Callers tend to allocate buffers of sizes big enough to hold data from a well-behaving device and do not expect that i2c_smbus_read_block_data() may attempt to write more data than expected.

To make i2c_smbus_read_block_data() safer to use, change it so that it accepts size of the supplied buffer as another argument and ensure that it will not copy more data than the size of the buffer. Signal oversized responses with -EMSGSIZE.

To allow users to gradually transition to the new API employ some macro trickery allowing calling i2c_smbus_read_block_data() with either 3 or 4 arguments. When called with 3 arguments it is assumed that the buffer size is I2C_SMBUS_BLOCK_MAX bytes. Once everyone is transitioned to the 4 argument form the macros should be removed.

Quoted 6 lines

---

v2:
- dropped incorrect changes to Documentation (Andi)
- signal oversized responses with -EMSGSIZE (Andi)

Andi, could this be applied or would you like me to change something else?

Thanks.

Signature · 1 line
Dmitry
maintainer
#2 lore raw
In reply to Dmitry Torokhov

Hi Dmitry,

Quoted 12 lines · Dmitry Torokhov

i2c_smbus_read_block_data() is dangerous to use because it may deliver up to I2C_SMBUS_BLOCK_MAX (32) bytes, which may be surprising to the caller. Callers tend to allocate buffers of sizes big enough to hold data from a well-behaving device and do not expect that i2c_smbus_read_block_data() may attempt to write more data than expected.

To make i2c_smbus_read_block_data() safer to use, change it so that it accepts size of the supplied buffer as another argument and ensure that it will not copy more data than the size of the buffer. Signal oversized responses with -EMSGSIZE.

Quoted 7 lines

To allow users to gradually transition to the new API employ some macro trickery allowing calling i2c_smbus_read_block_data() with either 3 or 4 arguments. When called with 3 arguments it is assumed that the buffer size is I2C_SMBUS_BLOCK_MAX bytes. Once everyone is transitioned to the 4 argument form the macros should be removed.

Sorry for the long wait. I pushed this change to i2c/i2c.

Thanks,
Andi