Skip to contentSkip to conversation

HID: ft260: add usb hid to i2c host bridge driver

bug report Dan Carpenter 2 participants 6 messages Apr 9, 2021 – Apr 13, 2021

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

Hello Michael Zaidman,

The patch 6a82582d9fa4: "HID: ft260: add usb hid to i2c host bridge driver" from Feb 19, 2021, leads to the following static checker warning:

	drivers/hid/hid-ft260.c:441 ft260_smbus_write()
	error: '__memcpy()' '&rep->data[1]' too small (59 vs 255)

drivers/hid/hid-ft260.c
423 static int ft260_smbus_write(struct ft260_device *dev, u8 addr, u8 cmd,
424 u8 *data, u8 data_len, u8 flag)
425 {
426 int ret = 0;
427 int len = 4;
428
429 struct ft260_i2c_write_request_report *rep =
430 (struct ft260_i2c_write_request_report *)dev->write_buf;
431

   432          rep->address = addr;
   433          rep->data[0] = cmd;
   434          rep->length = data_len + 1;
   435          rep->flag = flag;
   436          len += rep->length;
   437  
   438          rep->report = FT260_I2C_DATA_REPORT_ID(len);
   439  
   440          if (data_len > 0)
   441                  memcpy(&rep->data[1], data, data_len);
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Smatch says that this can be called from the i2cdev_ioctl_smbus() function.

i2cdev_ioctl_smbus()
--> i2c_smbus_xfer

      --> __i2c_smbus_xfer
          --> ft260_smbus_xfer
              --> ft260_smbus_write

442
443 ft260_dbg("rep %#02x addr %#02x cmd %#02x datlen %d replen %d\n",
444 rep->report, addr, cmd, rep->length, len);
445
446 ret = ft260_hid_output_report_check_status(dev, (u8 *)rep, len);
447
448 return ret;
449 }

regards,
dan carpenter

#1 lore raw
Quoted 30 lines · Dan Carpenter

Hello Michael Zaidman,

The patch 6a82582d9fa4: "HID: ft260: add usb hid to i2c host bridge driver" from Feb 19, 2021, leads to the following static checker warning:

	drivers/hid/hid-ft260.c:441 ft260_smbus_write()
	error: '__memcpy()' '&rep->data[1]' too small (59 vs 255)

drivers/hid/hid-ft260.c
423 static int ft260_smbus_write(struct ft260_device *dev, u8 addr, u8 cmd,
424 u8 *data, u8 data_len, u8 flag)

   425  {
   426          int ret = 0;
   427          int len = 4;
   428  
   429          struct ft260_i2c_write_request_report *rep =
   430                  (struct ft260_i2c_write_request_report *)dev->write_buf;
   431  
   432          rep->address = addr;
   433          rep->data[0] = cmd;
   434          rep->length = data_len + 1;
   435          rep->flag = flag;
   436          len += rep->length;
   437  
   438          rep->report = FT260_I2C_DATA_REPORT_ID(len);
   439  
   440          if (data_len > 0)
   441                  memcpy(&rep->data[1], data, data_len);
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Quoted 2 lines

Smatch says that this can be called from the i2cdev_ioctl_smbus() function.

Hi Dan,

This is an example of a false-positive static checker warning.

The maximum data size that the i2cdev_ioctl_smbus() can pass to the i2c_smbus_xfer() is sizeof(data->block) which is (I2C_SMBUS_BLOCK_MAX + 2) or 34 bytes. Thus, no need to check the data_len against 59 here.

Regrads,
Michael

Quoted 18 lines

i2cdev_ioctl_smbus()
--> i2c_smbus_xfer

      --> __i2c_smbus_xfer
          --> ft260_smbus_xfer
              --> ft260_smbus_write
   442  
   443          ft260_dbg("rep %#02x addr %#02x cmd %#02x datlen %d replen %d\n",
   444                    rep->report, addr, cmd, rep->length, len);
   445  
   446          ret = ft260_hid_output_report_check_status(dev, (u8 *)rep, len);
   447  
   448          return ret;
   449  }

regards,
dan carpenter

#2 lore raw
Quoted 34 lines · Michael Zaidman
Quoted 30 lines · Dan Carpenter

Hello Michael Zaidman,

The patch 6a82582d9fa4: "HID: ft260: add usb hid to i2c host bridge driver" from Feb 19, 2021, leads to the following static checker warning:

	drivers/hid/hid-ft260.c:441 ft260_smbus_write()
	error: '__memcpy()' '&rep->data[1]' too small (59 vs 255)

drivers/hid/hid-ft260.c
423 static int ft260_smbus_write(struct ft260_device *dev, u8 addr, u8 cmd,
424 u8 *data, u8 data_len, u8 flag)

   425  {
   426          int ret = 0;
   427          int len = 4;
   428  
   429          struct ft260_i2c_write_request_report *rep =
   430                  (struct ft260_i2c_write_request_report *)dev->write_buf;
   431  
   432          rep->address = addr;
   433          rep->data[0] = cmd;
   434          rep->length = data_len + 1;
   435          rep->flag = flag;
   436          len += rep->length;
   437  
   438          rep->report = FT260_I2C_DATA_REPORT_ID(len);
   439  
   440          if (data_len > 0)
   441                  memcpy(&rep->data[1], data, data_len);
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Quoted 2 lines

Smatch says that this can be called from the i2cdev_ioctl_smbus() function.

Quoted 14 lines

Hi Dan,

This is an example of a false-positive static checker warning.

The maximum data size that the i2cdev_ioctl_smbus() can pass to the i2c_smbus_xfer() is sizeof(data->block) which is (I2C_SMBUS_BLOCK_MAX + 2) or 34 bytes. Thus, no need to check the data_len against 59 here.

Quoted 6 lines

i2cdev_ioctl_smbus()
--> i2c_smbus_xfer

      --> __i2c_smbus_xfer
          --> ft260_smbus_xfer
              --> ft260_smbus_write

It's actually me who misunderstood the Smatch warning. Smatch is not complaining about data_len, it's data->block[0] which is user controlled and only for the I2C_SMBUS_I2C_BLOCK_DATA command.

The call tree is the same. I've looked at it again. Here is how i2cdev_ioctl_smbus() looks like:

drivers/i2c/i2c-dev.c
355 return -EINVAL;
356 }
357
358 if ((size == I2C_SMBUS_BYTE_DATA) ||
359 (size == I2C_SMBUS_BYTE))
360 datasize = sizeof(data->byte);
361 else if ((size == I2C_SMBUS_WORD_DATA) ||
362 (size == I2C_SMBUS_PROC_CALL))
363 datasize = sizeof(data->word);
364 else /* size == smbus block, i2c block, or block proc. call */

   365                  datasize = sizeof(data->block);
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

366
367 if ((size == I2C_SMBUS_PROC_CALL) ||

   368              (size == I2C_SMBUS_BLOCK_PROC_CALL) ||
   369              (size == I2C_SMBUS_I2C_BLOCK_DATA) ||
                             ^^^^^^^^^^^^^^^^^^^^^^^^
   370              (read_write == I2C_SMBUS_WRITE)) {
   371                  if (copy_from_user(&temp, data, datasize))
                                            ^^^^

temp.block[0] is user controlled.

   372                          return -EFAULT;
   373          }
   374          if (size == I2C_SMBUS_I2C_BLOCK_BROKEN) {
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

375 /* Convert old I2C block commands to the new
376 convention. This preserves binary compatibility. */
377 size = I2C_SMBUS_I2C_BLOCK_DATA;
378 if (read_write == I2C_SMBUS_READ)

   379                          temp.block[0] = I2C_SMBUS_BLOCK_MAX;
                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Except for size BROKEN

380 }
381 res = i2c_smbus_xfer(client->adapter, client->addr, client->flags,

   382                read_write, command, size, &temp);
                                                 ^^^^^
   383          if (!res && ((size == I2C_SMBUS_PROC_CALL) ||
   384                       (size == I2C_SMBUS_BLOCK_PROC_CALL) ||
   385                       (read_write == I2C_SMBUS_READ))) {
   386                  if (copy_to_user(data, &temp, datasize))
   387                          return -EFAULT;
   388          }

The rest of the call tree seems straight forward but it's possible I have missed somewhere that checks data[0]. Here is how ft260_smbus_xfer() looks like.

drivers/hid/hid-ft260.c
655 case I2C_SMBUS_BLOCK_DATA:
656 if (read_write == I2C_SMBUS_READ) {
657 ret = ft260_smbus_write(dev, addr, cmd, NULL, 0,
658 FT260_FLAG_START);
659 if (ret)
660 goto smbus_exit;
661
662 ret = ft260_i2c_read(dev, addr, data->block,
663 data->block[0] + 1,
664 FT260_FLAG_START_STOP_REPEATED);
665 } else {
666 ret = ft260_smbus_write(dev, addr, cmd, data->block,
667 data->block[0] + 1,
668 FT260_FLAG_START_STOP);
669 }
670 break;
671 case I2C_SMBUS_I2C_BLOCK_DATA:
672 if (read_write == I2C_SMBUS_READ) {
673 ret = ft260_smbus_write(dev, addr, cmd, NULL, 0,
674 FT260_FLAG_START);
675 if (ret)
676 goto smbus_exit;
677
678 ret = ft260_i2c_read(dev, addr, data->block + 1,
679 data->block[0],
680 FT260_FLAG_START_STOP_REPEATED);
681 } else {
682 ret = ft260_smbus_write(dev, addr, cmd, data->block + 1,
683 data->block[0],

                                                        ^^^^^^^^^^^^^^

Boom. Dead.

684 FT260_FLAG_START_STOP);
685 }
686 break;
687 default:
688 hid_err(hdev, "unsupported smbus transaction size %d\n", size);
689 ret = -EOPNOTSUPP;
690 }

regards,
dan carpenter

#3 lore raw
Quoted 106 lines · Dan Carpenter
Quoted 34 lines · Michael Zaidman
Quoted 30 lines · Dan Carpenter

Hello Michael Zaidman,

The patch 6a82582d9fa4: "HID: ft260: add usb hid to i2c host bridge driver" from Feb 19, 2021, leads to the following static checker warning:

	drivers/hid/hid-ft260.c:441 ft260_smbus_write()
	error: '__memcpy()' '&rep->data[1]' too small (59 vs 255)

drivers/hid/hid-ft260.c
423 static int ft260_smbus_write(struct ft260_device *dev, u8 addr, u8 cmd,
424 u8 *data, u8 data_len, u8 flag)

   425  {
   426          int ret = 0;
   427          int len = 4;
   428  
   429          struct ft260_i2c_write_request_report *rep =
   430                  (struct ft260_i2c_write_request_report *)dev->write_buf;
   431  
   432          rep->address = addr;
   433          rep->data[0] = cmd;
   434          rep->length = data_len + 1;
   435          rep->flag = flag;
   436          len += rep->length;
   437  
   438          rep->report = FT260_I2C_DATA_REPORT_ID(len);
   439  
   440          if (data_len > 0)
   441                  memcpy(&rep->data[1], data, data_len);
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Quoted 2 lines

Smatch says that this can be called from the i2cdev_ioctl_smbus() function.

Quoted 14 lines

Hi Dan,

This is an example of a false-positive static checker warning.

The maximum data size that the i2cdev_ioctl_smbus() can pass to the i2c_smbus_xfer() is sizeof(data->block) which is (I2C_SMBUS_BLOCK_MAX + 2) or 34 bytes. Thus, no need to check the data_len against 59 here.

Quoted 6 lines

i2cdev_ioctl_smbus()
--> i2c_smbus_xfer

      --> __i2c_smbus_xfer
          --> ft260_smbus_xfer
              --> ft260_smbus_write

It's actually me who misunderstood the Smatch warning. Smatch is not complaining about data_len, it's data->block[0] which is user controlled and only for the I2C_SMBUS_I2C_BLOCK_DATA command.

The call tree is the same. I've looked at it again. Here is how i2cdev_ioctl_smbus() looks like:

drivers/i2c/i2c-dev.c

   355                  return -EINVAL;
   356          }
   357  
   358          if ((size == I2C_SMBUS_BYTE_DATA) ||
   359              (size == I2C_SMBUS_BYTE))
   360                  datasize = sizeof(data->byte);
   361          else if ((size == I2C_SMBUS_WORD_DATA) ||
   362                   (size == I2C_SMBUS_PROC_CALL))
   363                  datasize = sizeof(data->word);
   364          else /* size == smbus block, i2c block, or block proc. call */
   365                  datasize = sizeof(data->block);
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

   366  
   367          if ((size == I2C_SMBUS_PROC_CALL) ||
   368              (size == I2C_SMBUS_BLOCK_PROC_CALL) ||
   369              (size == I2C_SMBUS_I2C_BLOCK_DATA) ||
                             ^^^^^^^^^^^^^^^^^^^^^^^^
   370              (read_write == I2C_SMBUS_WRITE)) {
   371                  if (copy_from_user(&temp, data, datasize))
                                            ^^^^

temp.block[0] is user controlled.

   372                          return -EFAULT;
   373          }
   374          if (size == I2C_SMBUS_I2C_BLOCK_BROKEN) {
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   375                  /* Convert old I2C block commands to the new
   376                     convention. This preserves binary compatibility. */
   377                  size = I2C_SMBUS_I2C_BLOCK_DATA;
   378                  if (read_write == I2C_SMBUS_READ)
   379                          temp.block[0] = I2C_SMBUS_BLOCK_MAX;
                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Except for size BROKEN

   380          }
   381          res = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
   382                read_write, command, size, &temp);
                                                 ^^^^^

   383          if (!res && ((size == I2C_SMBUS_PROC_CALL) ||
   384                       (size == I2C_SMBUS_BLOCK_PROC_CALL) ||
   385                       (read_write == I2C_SMBUS_READ))) {
   386                  if (copy_to_user(data, &temp, datasize))
   387                          return -EFAULT;
   388          }
Quoted 3 lines

The rest of the call tree seems straight forward but it's possible I have missed somewhere that checks data[0]. Here is how ft260_smbus_xfer() looks like.

Oh, you are right. Despite that the SMbus block transaction limits the maximum number of bytes to 32, nothing prevents a user from specifying via ioctl a larger data size than the ft260 can handle in a single transfer.

I am going to fix it in the ft260_smbus_write (with your Signed-off-by), but perhaps we should fix it in the first place, in the i2cdev_ioctl_smbus routine? What do you think?

Quoted 46 lines

drivers/hid/hid-ft260.c
655 case I2C_SMBUS_BLOCK_DATA:

   656                  if (read_write == I2C_SMBUS_READ) {
   657                          ret = ft260_smbus_write(dev, addr, cmd, NULL, 0,
   658                                                  FT260_FLAG_START);
   659                          if (ret)
   660                                  goto smbus_exit;
   661  
   662                          ret = ft260_i2c_read(dev, addr, data->block,
   663                                               data->block[0] + 1,
   664                                               FT260_FLAG_START_STOP_REPEATED);
   665                  } else {
   666                          ret = ft260_smbus_write(dev, addr, cmd, data->block,
   667                                                  data->block[0] + 1,
   668                                                  FT260_FLAG_START_STOP);
   669                  }
   670                  break;
   671          case I2C_SMBUS_I2C_BLOCK_DATA:
   672                  if (read_write == I2C_SMBUS_READ) {
   673                          ret = ft260_smbus_write(dev, addr, cmd, NULL, 0,
   674                                                  FT260_FLAG_START);
   675                          if (ret)
   676                                  goto smbus_exit;
   677  
   678                          ret = ft260_i2c_read(dev, addr, data->block + 1,
   679                                               data->block[0],
   680                                               FT260_FLAG_START_STOP_REPEATED);
   681                  } else {
   682                          ret = ft260_smbus_write(dev, addr, cmd, data->block + 1,
   683                                                  data->block[0],
                                                        ^^^^^^^^^^^^^^

Boom. Dead.

   684                                                  FT260_FLAG_START_STOP);
   685                  }
   686                  break;
   687          default:
   688                  hid_err(hdev, "unsupported smbus transaction size %d\n", size);
   689                  ret = -EOPNOTSUPP;
   690          }

regards,
dan carpenter

#4 lore raw
Quoted 111 lines · Michael Zaidman
Quoted 106 lines · Dan Carpenter
Quoted 34 lines · Michael Zaidman
Quoted 30 lines · Dan Carpenter

Hello Michael Zaidman,

The patch 6a82582d9fa4: "HID: ft260: add usb hid to i2c host bridge driver" from Feb 19, 2021, leads to the following static checker warning:

	drivers/hid/hid-ft260.c:441 ft260_smbus_write()
	error: '__memcpy()' '&rep->data[1]' too small (59 vs 255)

drivers/hid/hid-ft260.c
423 static int ft260_smbus_write(struct ft260_device *dev, u8 addr, u8 cmd,
424 u8 *data, u8 data_len, u8 flag)

   425  {
   426          int ret = 0;
   427          int len = 4;
   428  
   429          struct ft260_i2c_write_request_report *rep =
   430                  (struct ft260_i2c_write_request_report *)dev->write_buf;
   431  
   432          rep->address = addr;
   433          rep->data[0] = cmd;
   434          rep->length = data_len + 1;
   435          rep->flag = flag;
   436          len += rep->length;
   437  
   438          rep->report = FT260_I2C_DATA_REPORT_ID(len);
   439  
   440          if (data_len > 0)
   441                  memcpy(&rep->data[1], data, data_len);
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Quoted 2 lines

Smatch says that this can be called from the i2cdev_ioctl_smbus() function.

Quoted 14 lines

Hi Dan,

This is an example of a false-positive static checker warning.

The maximum data size that the i2cdev_ioctl_smbus() can pass to the i2c_smbus_xfer() is sizeof(data->block) which is (I2C_SMBUS_BLOCK_MAX + 2) or 34 bytes. Thus, no need to check the data_len against 59 here.

Quoted 6 lines

i2cdev_ioctl_smbus()
--> i2c_smbus_xfer

      --> __i2c_smbus_xfer
          --> ft260_smbus_xfer
              --> ft260_smbus_write

It's actually me who misunderstood the Smatch warning. Smatch is not complaining about data_len, it's data->block[0] which is user controlled and only for the I2C_SMBUS_I2C_BLOCK_DATA command.

The call tree is the same. I've looked at it again. Here is how i2cdev_ioctl_smbus() looks like:

drivers/i2c/i2c-dev.c

   355                  return -EINVAL;
   356          }
   357  
   358          if ((size == I2C_SMBUS_BYTE_DATA) ||
   359              (size == I2C_SMBUS_BYTE))
   360                  datasize = sizeof(data->byte);
   361          else if ((size == I2C_SMBUS_WORD_DATA) ||
   362                   (size == I2C_SMBUS_PROC_CALL))
   363                  datasize = sizeof(data->word);
   364          else /* size == smbus block, i2c block, or block proc. call */
   365                  datasize = sizeof(data->block);
                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

   366  
   367          if ((size == I2C_SMBUS_PROC_CALL) ||
   368              (size == I2C_SMBUS_BLOCK_PROC_CALL) ||
   369              (size == I2C_SMBUS_I2C_BLOCK_DATA) ||
                             ^^^^^^^^^^^^^^^^^^^^^^^^
   370              (read_write == I2C_SMBUS_WRITE)) {
   371                  if (copy_from_user(&temp, data, datasize))
                                            ^^^^

temp.block[0] is user controlled.

   372                          return -EFAULT;
   373          }
   374          if (size == I2C_SMBUS_I2C_BLOCK_BROKEN) {
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   375                  /* Convert old I2C block commands to the new
   376                     convention. This preserves binary compatibility. */
   377                  size = I2C_SMBUS_I2C_BLOCK_DATA;
   378                  if (read_write == I2C_SMBUS_READ)
   379                          temp.block[0] = I2C_SMBUS_BLOCK_MAX;
                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Except for size BROKEN

   380          }
   381          res = i2c_smbus_xfer(client->adapter, client->addr, client->flags,
   382                read_write, command, size, &temp);
                                                 ^^^^^

   383          if (!res && ((size == I2C_SMBUS_PROC_CALL) ||
   384                       (size == I2C_SMBUS_BLOCK_PROC_CALL) ||
   385                       (read_write == I2C_SMBUS_READ))) {
   386                  if (copy_to_user(data, &temp, datasize))
   387                          return -EFAULT;
   388          }
Quoted 3 lines

The rest of the call tree seems straight forward but it's possible I have missed somewhere that checks data[0]. Here is how ft260_smbus_xfer() looks like.

Quoted 7 lines

Oh, you are right. Despite that the SMbus block transaction limits the maximum number of bytes to 32, nothing prevents a user from specifying via ioctl a larger data size than the ft260 can handle in a single transfer.

I am going to fix it in the ft260_smbus_write (with your Signed-off-by), but perhaps we should fix it in the first place, in the i2cdev_ioctl_smbus routine? What do you think?

Could you just give me a Reported-by tag? Thanks!

regards,
dan carpenter

#5 lore raw
Quoted 6 lines · Dan Carpenter
Quoted 5 lines · Michael Zaidman

Oh, you are right. Despite that the SMbus block transaction limits the maximum number of bytes to 32, nothing prevents a user from specifying via ioctl a larger data size than the ft260 can handle in a single transfer.

Quoted 8 lines
Quoted 3 lines

I am going to fix it in the ft260_smbus_write (with your Signed-off-by), but perhaps we should fix it in the first place, in the i2cdev_ioctl_smbus routine? What do you think?

Could you just give me a Reported-by tag? Thanks!

regards,
dan carpenter

Done, thanks!