]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
Input: rmi4 - tolerate short register descriptor structure
authorDmitry Torokhov <dmitry.torokhov@gmail.com>
Fri, 26 Jun 2026 23:33:21 +0000 (16:33 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Sat, 27 Jun 2026 05:23:40 +0000 (22:23 -0700)
Some touchpads (e.g. ThinkPad T14 Gen 1) have buggy firmware that reports
a register descriptor structure size that is too small for the number of
registers it claims to have in the presence map. The remaining bytes in
the structure are 0, which with the new strict bounds checking causes the
parser to fail with -EIO, aborting the device probe.

Tolerate such short reads by dropping the remaining (unparseable or
0-size) registers from the list instead of failing the probe,
preventing the driver from trying to use them.

Fixes: 0adb483fbf2d ("Input: rmi4 - refactor register descriptor parsing")
Reported-by: Barry K. Nathan <barryn@pobox.com>
Tested-by: Barry K. Nathan <barryn@pobox.com>
Cc: stable@vger.kernel.org
Assisted-by: Antigravity:gemini-3.5-flash
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
drivers/input/rmi4/rmi_driver.c

index a28eef1b765e53098313feff34f2967356344285..5d49a9021c7d05fe7be19136ae4089aeeafadec4 100644 (file)
@@ -616,8 +616,8 @@ int rmi_read_register_desc(struct rmi_device *d, u16 addr,
        unsigned int presence_offset;
        unsigned int map_offset;
        unsigned int offset;
+       unsigned int num_registers;
        unsigned int reg;
-       int i;
        int b;
        int ret;
 
@@ -657,7 +657,7 @@ int rmi_read_register_desc(struct rmi_device *d, u16 addr,
 
        memset(presence_map, 0, sizeof(presence_map));
        map_offset = 0;
-       for (i = presence_offset; i < size_presence_reg; i++) {
+       for (int i = presence_offset; i < size_presence_reg; i++) {
                for (b = 0; b < 8; b++) {
                        if (buf[i] & BIT(b)) {
                                if (map_offset >= RMI_REG_DESC_PRESENCE_BITS)
@@ -697,28 +697,41 @@ int rmi_read_register_desc(struct rmi_device *d, u16 addr,
        if (ret)
                return ret;
 
-       reg = find_first_bit(presence_map, RMI_REG_DESC_PRESENCE_BITS);
        offset = 0;
-       for (i = 0; i < rdesc->num_registers; i++) {
-               struct rmi_register_desc_item *item = &rdesc->registers[i];
+       num_registers = 0;
+       for_each_set_bit(reg, presence_map, RMI_REG_DESC_PRESENCE_BITS) {
+               struct rmi_register_desc_item *item = &rdesc->registers[num_registers];
                int item_size;
 
+               if (offset >= rdesc->struct_size)
+                       break;
+
                item_size = rmi_parse_register_desc_item(item,
                                                         &struct_buf[offset],
                                                         rdesc->struct_size - offset);
-               if (item_size < 0)
-                       return item_size;
+               if (item_size < 0) {
+                       dev_warn(&d->dev,
+                                "%s: Failed to parse register %d descriptor, ignoring it\n",
+                                __func__, reg);
+                       break;
+               }
 
                item->reg = reg;
                offset += item_size;
 
-               rmi_dbg(RMI_DEBUG_CORE, &d->dev,
-                       "%s: reg: %d reg size: %u subpackets: %d\n", __func__,
-                       item->reg, item->reg_size, item->num_subpackets);
+               if (item->reg_size == 0) {
+                       dev_warn(&d->dev,
+                                "%s: Register %d has 0 size, ignoring it\n",
+                                __func__, item->reg);
+               } else {
+                       rmi_dbg(RMI_DEBUG_CORE, &d->dev,
+                               "%s: reg: %d reg size: %u subpackets: %d\n", __func__,
+                               item->reg, item->reg_size, item->num_subpackets);
 
-               reg = find_next_bit(presence_map,
-                                   RMI_REG_DESC_PRESENCE_BITS, reg + 1);
+                       num_registers++;
+               }
        }
+       rdesc->num_registers = num_registers;
 
        return 0;
 }