CVE-2022-34835: Out-of-bounds Write (CWE-787)
value: In Das U-Boot through 2022.07-rc5, an integer signedness error and resultant stack-based buffer overflow in the "i2c md" command enables the corruption of the return address pointer of the do_i2c_md function.
CWE description: The product writes data past the end, or before the beginning, of the intended buffer.
CVSS v3 base score: 9.8
Published: 2022-06-30T00:15Z
Repository: https://github.com/u-boot/u-boot
Language: C
Files changed: cmd/i2c.c
Commit date: 2022-06-28 15:51:56 -0400
Commit message: i2c: fix stack buffer overflow vulnerability in i2c md command

When running "i2c md 0 0 80000100", the function do_i2c_md parses the
length into an unsigned int variable named length. The value is then
moved to a signed variable:

    int nbytes = length;
    #define DISP_LINE_LEN 16
    int linebytes = (nbytes > DISP_LINE_LEN) ? DISP_LINE_LEN : nbytes;
    ret = dm_i2c_read(dev, addr, linebuf, linebytes);

On systems where integers are 32 bits wide, 0x80000100 is a negative
value to "nbytes > DISP_LINE_LEN" is false and linebytes gets assigned
0x80000100 instead of 16.

The consequence is that the function which reads from the i2c device
(dm_i2c_read or i2c_read) is called with a 16-byte stack buffer to fill
but with a size parameter which is too large. In some cases, this could
trigger a crash. But with some i2c drivers, such as drivers/i2c/nx_i2c.c
(used with "nexell,s5pxx18-i2c" bus), the size is actually truncated to
a 16-bit integer. This is because function i2c_transfer expects an
unsigned short length. In such a case, an attacker who can control the
response of an i2c device can overwrite the return address of a function
and execute arbitrary code through Return-Oriented Programming.

Fix this issue by using unsigned integers types in do_i2c_md. While at
it, make also alen unsigned, as signed sizes can cause vulnerabilities
when people forgot to check that they can be negative.

Signed-off-by: Nicolas Iooss <nicolas.iooss+uboot@ledger.fr>
Reviewed-by: Heiko Schocher <hs@denx.de>

Diff: diff --git a/cmd/i2c.c b/cmd/i2c.c
index 9050b2b8d27..bd04b14024b 100644
--- a/cmd/i2c.c
+++ b/cmd/i2c.c
@@ -198,14 +198,14 @@ void i2c_init_board(void)
 /**
  * get_alen() - Small parser helper function to get address length
  *
  * Returns the address length.
  */
-static uint get_alen(char *arg, int default_len)
+static uint get_alen(char *arg, uint default_len)
 {
-	int	j;
-	int	alen;
+	uint	j;
+	uint	alen;
 
 	alen = default_len;
 	for (j = 0; j < 8; j++) {
 		if (arg[j] == '.') {
 			alen = arg[j+1] - '0';
@@ -245,11 +245,11 @@ static int i2c_report_err(int ret, enum i2c_err_op op)
 static int do_i2c_read(struct cmd_tbl *cmdtp, int flag, int argc,
 		       char *const argv[])
 {
 	uint	chip;
 	uint	devaddr, length;
-	int alen;
+	uint	alen;
 	u_char  *memaddr;
 	int ret;
 #if CONFIG_IS_ENABLED(DM_I2C)
 	struct udevice *dev;
 #endif
@@ -299,11 +299,11 @@ static int do_i2c_read(struct cmd_tbl *cmdtp, int flag, int argc,
 static int do_i2c_write(struct cmd_tbl *cmdtp, int flag, int argc,
 			char *const argv[])
 {
 	uint	chip;
 	uint	devaddr, length;
-	int alen;
+	uint	alen;
 	u_char  *memaddr;
 	int ret;
 #if CONFIG_IS_ENABLED(DM_I2C)
 	struct udevice *dev;
 	struct dm_i2c_chip *i2c_chip;
@@ -467,12 +467,12 @@ static int do_i2c_olen(struct cmd_tbl *cmdtp, int flag, int argc,
 static int do_i2c_md(struct cmd_tbl *cmdtp, int flag, int argc,
 		     char *const argv[])
 {
 	uint	chip;
 	uint	addr, length;
-	int alen;
-	int	j, nbytes, linebytes;
+	uint	alen;
+	uint	j, nbytes, linebytes;
 	int ret;
 #if CONFIG_IS_ENABLED(DM_I2C)
 	struct udevice *dev;
 #endif
 
@@ -587,13 +587,13 @@ static int do_i2c_md(struct cmd_tbl *cmdtp, int flag, int argc,
 static int do_i2c_mw(struct cmd_tbl *cmdtp, int flag, int argc,
 		     char *const argv[])
 {
 	uint	chip;
 	ulong	addr;
-	int	alen;
+	uint	alen;
 	uchar	byte;
-	int	count;
+	uint	count;
 	int ret;
 #if CONFIG_IS_ENABLED(DM_I2C)
 	struct udevice *dev;
 #endif
 
@@ -674,12 +674,12 @@ static int do_i2c_mw(struct cmd_tbl *cmdtp, int flag, int argc,
 static int do_i2c_crc(struct cmd_tbl *cmdtp, int flag, int argc,
 		      char *const argv[])
 {
 	uint	chip;
 	ulong	addr;
-	int	alen;
-	int	count;
+	uint	alen;
+	uint	count;
 	uchar	byte;
 	ulong	crc;
 	ulong	err;
 	int ret = 0;
 #if CONFIG_IS_ENABLED(DM_I2C)
@@ -983,11 +983,11 @@ static int do_i2c_probe(struct cmd_tbl *cmdtp, int flag, int argc,
  */
 static int do_i2c_loop(struct cmd_tbl *cmdtp, int flag, int argc,
 		       char *const argv[])
 {
 	uint	chip;
-	int alen;
+	uint	alen;
 	uint	addr;
 	uint	length;
 	u_char	bytes[16];
 	int	delay;
 	int ret;

Vulnerable code: static uint get_alen(char *arg, int default_len)
	int	j;
	int	alen;
	int alen;
	int alen;
	int alen;
	int	j, nbytes, linebytes;
	int	alen;
	int	count;
	int	alen;
	int	count;
	int alen;
Fixed code: static uint get_alen(char *arg, uint default_len)
	uint	j;
	uint	alen;
	uint	alen;
	uint	alen;
	uint	alen;
	uint	j, nbytes, linebytes;
	uint	alen;
	uint	count;
	uint	alen;
	uint	count;
	uint	alen;