]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
Input: rmi4 - refactor register descriptor parsing
authorDmitry Torokhov <dmitry.torokhov@gmail.com>
Tue, 5 May 2026 04:59:32 +0000 (21:59 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Fri, 12 Jun 2026 05:39:27 +0000 (22:39 -0700)
Factor out parsing a register descriptor item from
rmi_read_register_desc() and ensure there are no out-of-bounds accesses.

Use get_unaligned_le16() and get_unaligned_le32() for reading multi-byte
values.

Reported-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Fixes: 2b6a321da9a2 ("Input: synaptics-rmi4 - add support for Synaptics RMI4 devices")
Cc: stable@vger.kernel.org
Assisted-by: Gemini:gemini-3.1-pro
Link: https://patch.msgid.link/20260505045952.1570713-2-dmitry.torokhov@gmail.com
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
drivers/input/rmi4/rmi_driver.c

index 06f5e3000cf00ef9c605c8252593285aa886ed72..75949fb1a9223ce78df3ade160c7a259c8b3680a 100644 (file)
@@ -22,6 +22,7 @@
 #include <uapi/linux/input.h>
 #include <linux/rmi.h>
 #include <linux/export.h>
+#include <linux/unaligned.h>
 #include "rmi_bus.h"
 #include "rmi_driver.h"
 
@@ -558,30 +559,74 @@ int rmi_scan_pdt(struct rmi_device *rmi_dev, void *ctx,
        return retval < 0 ? retval : 0;
 }
 
+static int rmi_parse_register_desc_item(struct rmi_register_desc_item *item,
+                                       const u8 *buf, size_t size)
+{
+       unsigned int offset = 0;
+       unsigned int map_offset = 0;
+       int b;
+
+       if (offset >= size)
+               return -EIO;
+
+       item->reg_size = buf[offset++];
+       if (item->reg_size == 0) {
+               if (size - offset < 2)
+                       return -EIO;
+               item->reg_size = get_unaligned_le16(&buf[offset]);
+               offset += 2;
+       }
+
+       if (item->reg_size == 0) {
+               if (size - offset < 4)
+                       return -EIO;
+               item->reg_size = get_unaligned_le32(&buf[offset]);
+               offset += 4;
+       }
+
+       do {
+               if (offset >= size)
+                       return -EIO;
+
+               for (b = 0; b < 7; b++) {
+                       if (buf[offset] & BIT(b)) {
+                               if (map_offset >= RMI_REG_DESC_SUBPACKET_BITS)
+                                       return -EIO;
+                               __set_bit(map_offset, item->subpacket_map);
+                       }
+                       ++map_offset;
+               }
+       } while (buf[offset++] & BIT(7));
+
+       item->num_subpackets = bitmap_weight(item->subpacket_map,
+                                            RMI_REG_DESC_SUBPACKET_BITS);
+
+       return offset;
+}
+
 int rmi_read_register_desc(struct rmi_device *d, u16 addr,
-                               struct rmi_register_descriptor *rdesc)
+                          struct rmi_register_descriptor *rdesc)
 {
        int ret;
        u8 size_presence_reg;
        u8 buf[35];
-       int presense_offset = 1;
-       u8 *struct_buf;
-       int reg;
-       int offset = 0;
-       int map_offset = 0;
+       unsigned int presence_offset;
+       unsigned int map_offset;
+       unsigned int offset;
+       unsigned int reg;
        int i;
        int b;
 
        /*
         * The first register of the register descriptor is the size of
-        * the register descriptor's presense register.
+        * the register descriptor's presence register.
         */
        ret = rmi_read(d, addr, &size_presence_reg);
        if (ret)
                return ret;
        ++addr;
 
-       if (size_presence_reg < 0 || size_presence_reg > 35)
+       if (size_presence_reg < 1 || size_presence_reg > 35)
                return -EIO;
 
        memset(buf, 0, sizeof(buf));
@@ -597,16 +642,23 @@ int rmi_read_register_desc(struct rmi_device *d, u16 addr,
        addr += size_presence_reg;
 
        if (buf[0] == 0) {
-               presense_offset = 3;
-               rdesc->struct_size = buf[1] | (buf[2] << 8);
+               if (size_presence_reg < 3)
+                       return -EIO;
+               presence_offset = 3;
+               rdesc->struct_size = get_unaligned_le16(&buf[1]);
        } else {
+               presence_offset = 1;
                rdesc->struct_size = buf[0];
        }
 
-       for (i = presense_offset; i < size_presence_reg; i++) {
+       map_offset = 0;
+       for (i = presence_offset; i < size_presence_reg; i++) {
                for (b = 0; b < 8; b++) {
-                       if (buf[i] & (0x1 << b))
+                       if (buf[i] & BIT(b)) {
+                               if (map_offset >= RMI_REG_DESC_PRESENSE_BITS)
+                                       return -EIO;
                                bitmap_set(rdesc->presense_map, map_offset, 1);
+                       }
                        ++map_offset;
                }
        }
@@ -626,7 +678,7 @@ int rmi_read_register_desc(struct rmi_device *d, u16 addr,
         * I'm not using devm_kzalloc here since it will not be retained
         * after exiting this function
         */
-       struct_buf = kzalloc(rdesc->struct_size, GFP_KERNEL);
+       u8 *struct_buf __free(kfree) = kzalloc(rdesc->struct_size, GFP_KERNEL);
        if (!struct_buf)
                return -ENOMEM;
 
@@ -638,56 +690,32 @@ int rmi_read_register_desc(struct rmi_device *d, u16 addr,
         */
        ret = rmi_read_block(d, addr, struct_buf, rdesc->struct_size);
        if (ret)
-               goto free_struct_buff;
+               return ret;
 
        reg = find_first_bit(rdesc->presense_map, RMI_REG_DESC_PRESENSE_BITS);
+       offset = 0;
        for (i = 0; i < rdesc->num_registers; i++) {
                struct rmi_register_desc_item *item = &rdesc->registers[i];
-               int reg_size = struct_buf[offset];
-
-               ++offset;
-               if (reg_size == 0) {
-                       reg_size = struct_buf[offset] |
-                                       (struct_buf[offset + 1] << 8);
-                       offset += 2;
-               }
+               int item_size;
 
-               if (reg_size == 0) {
-                       reg_size = struct_buf[offset] |
-                                       (struct_buf[offset + 1] << 8) |
-                                       (struct_buf[offset + 2] << 16) |
-                                       (struct_buf[offset + 3] << 24);
-                       offset += 4;
-               }
+               item_size = rmi_parse_register_desc_item(item,
+                                                        &struct_buf[offset],
+                                                        rdesc->struct_size - offset);
+               if (item_size < 0)
+                       return item_size;
 
                item->reg = reg;
-               item->reg_size = reg_size;
-
-               map_offset = 0;
-
-               do {
-                       for (b = 0; b < 7; b++) {
-                               if (struct_buf[offset] & (0x1 << b))
-                                       bitmap_set(item->subpacket_map,
-                                               map_offset, 1);
-                               ++map_offset;
-                       }
-               } while (struct_buf[offset++] & 0x80);
-
-               item->num_subpackets = bitmap_weight(item->subpacket_map,
-                                               RMI_REG_DESC_SUBPACKET_BITS);
+               offset += item_size;
 
                rmi_dbg(RMI_DEBUG_CORE, &d->dev,
                        "%s: reg: %d reg size: %ld subpackets: %d\n", __func__,
                        item->reg, item->reg_size, item->num_subpackets);
 
                reg = find_next_bit(rdesc->presense_map,
-                               RMI_REG_DESC_PRESENSE_BITS, reg + 1);
+                                   RMI_REG_DESC_PRESENSE_BITS, reg + 1);
        }
 
-free_struct_buff:
-       kfree(struct_buf);
-       return ret;
+       return 0;
 }
 
 const struct rmi_register_desc_item *rmi_get_register_desc_item(