Skip to content

[BUG] I2C EEPROM Read/Write Kernel Operations May Cause a Device Crash #19473

Description

@catalinv-ncc

Description / Steps to reproduce the issue

Impact

If user passes NULL as buffer, the driver may crash. This is problematic for NuttX protected and kernel builds.

Description

The attacker from user space can pass a null pointer (or a pointer that is not in user space) to the EEPROM read operation. As seen from the code below, there are no checks for buffer. The I2C_TRANSFER operation is called with msgs[1].buffer that could be NULL:

static ssize_t ee24xx_read(FAR struct file *filep, [[[[FAR char *buffer]]]],
                           size_t len)
{
  FAR struct ee24xx_dev_s *eedev;
  FAR struct inode        *inode = filep->f_inode;
  [[[[struct i2c_msg_s         msgs[2];]]]]
  uint8_t                  addr[2];
  uint32_t                 addr_hi;
  int                      ret;
....

  /* Write data address */
  finfo("READ %zu bytes at pos %" PRIdOFF "\n", len, filep->f_pos);
  addr_hi           = (filep->f_pos >> (eedev->addrlen << 3));
  addr[0]           = (filep->f_pos) >> 8;
  addr[1]           = (filep->f_pos) &  0xff;
  msgs[0].frequency = eedev->freq;
  msgs[0].addr      = eedev->addr |
                      (addr_hi & ((1 << eedev->haddrbits) - 1));
  msgs[0].flags     = 0;
  msgs[0].buffer    = eedev->addrlen == 2 ? &addr[0] : &addr[1];
  msgs[0].length    = eedev->addrlen;

  /* Read data */
  msgs[1].frequency = msgs[0].frequency;
  msgs[1].addr      = msgs[0].addr;
  msgs[1].flags     = I2C_M_READ;
  [[[[msgs[1].buffer    = (FAR uint8_t *)buffer;]]]]
  msgs[1].length    = len;

  ret = I2C_TRANSFER(eedev->i2c, [[[[msgs]]]], 2);
  if (ret < 0)
    {
      goto done;
    }
...
}

For brevity the call stack (which is quite complex) is not presented in this finding. The I2C_TRANSFER call maps to different functions, depending on the hardware device. We were able to identify 15 such instances:

Filename Function Name
arch/arm/src/stm32/stm32_i2c.c stm32_i2c_transfer()
arch/arm/src/stm32f7/stm32_i2c.c stm32_i2c_transfer()
arch/arm/src/stm32h7/stm32_i2c.c stm32_i2c_transfer()
arch/arm/src/stm32l4/stm32_i2c.c stm32_i2c_transfer()
arch/arm/src/rp2040/rp2040_i2c.c rp2040_i2c_transfer()
arch/arm/src/nrf52/nrf52_i2c.c nrf52_i2c_transfer()
arch/sparc/src/cxd56xx/cxd56_i2c.c cxd56_i2c_transfer()
arch/riscv/src/bl602/bl602_i2c.c bl602_i2c_transfer()
arch/arm/src/efm32/efm32_i2c.c efm32_i2c_transfer()
arch/arm/src/lpc17xx_40xx/lpc17_40_i2c.c lpc17_40_i2c_transfer()
arch/arm/src/tiva/tiva_i2c.c tiva_i2c_transfer()
arch/arm/src/max326xx/max326_i2c.c max326_i2c_transfer()
arch/arm/src/s32k1xx/s32k1xx_lpi2c.c s32k1xx_lpi2c_transfer()
arch/arm/src/samv7/sam_twi.c sam_twi_transfer()
arch/arm/src/sam34/sam_twi.c sam_twi_transfer()
arch/arm/src/imxrt/imxrt_lpi2c.c imxrt_lpi2c_transfer()

Looking at the last example, function imxrt_lpi2c_transfer(), we can see that an invalid pointer can cause of crash:

struct stm32_i2c_priv_s
{
...
  uint8_t msgc;                /* Message count */
  [[[[struct i2c_msg_s *msgv;      /* Message list */]]]]
...
  uint32_t status;             /* End of transfer SR2|SR1 status */
};

static int imxrt_lpi2c_transfer(struct i2c_master_s *dev, struct i2c_msg_s *[[[[msgs]]]], int count)
{
...
  priv->msgv  = [[[[msgs]]]];
...
      for (m = 0; m < count; m++)
        {
          if (msgs[m].flags & I2C_M_READ)
            {
            up_invalidate_dcache([[[[(uintptr_t)msgs[m].buffer]]]],
                                [[[[(uintptr_t)msgs[m].buffer + msgs[m].length]]]]);
            }
        }
...
}

Subsequently, up_invalidate_dcache() calls xcache_op_by_range() which will write at destination address 0, likely causing a crash due to segmentation fault.

Recommendation

Verify that the buffer value passed by the user in the read() operation is not NULL, done in fs/vfs code to cover multiple code paths.

On which OS does this issue occur?

[OS: Linux]

What is the version of your OS?

Ubuntu 24.04

NuttX Version

master

Issue Architecture

[Arch: all]

Issue Area

[Area: Drivers]

Host information

No response

Verification

  • I have verified before submitting the report.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Arch: allIssues that apply to all architecturesArea: DriversDrivers issuesOS: LinuxIssues related to Linux (building system, etc)Type: BugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions