catalinv-ncc opened a new issue, #19473:
URL: https://github.com/apache/nuttx/issues/19473

   ### 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`:
   
   ~~~c
   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:
   
   ~~~c
   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, and the pointer resides in user space.
   
   ### 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
   
   - [x] I have verified before submitting the report.


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to