mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 06:57:30 -04:00
Input: rmi4 - refactor register descriptor parsing
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: 2b6a321da9 ("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>
This commit is contained in:
@@ -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];
|
||||
int item_size;
|
||||
|
||||
++offset;
|
||||
if (reg_size == 0) {
|
||||
reg_size = struct_buf[offset] |
|
||||
(struct_buf[offset + 1] << 8);
|
||||
offset += 2;
|
||||
}
|
||||
|
||||
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(
|
||||
|
||||
Reference in New Issue
Block a user