mirror of
https://github.com/torvalds/linux.git
synced 2026-07-28 10:09:10 +02: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:
parent
a98518e724
commit
0adb483fbf
|
|
@ -22,6 +22,7 @@
|
||||||
#include <uapi/linux/input.h>
|
#include <uapi/linux/input.h>
|
||||||
#include <linux/rmi.h>
|
#include <linux/rmi.h>
|
||||||
#include <linux/export.h>
|
#include <linux/export.h>
|
||||||
|
#include <linux/unaligned.h>
|
||||||
#include "rmi_bus.h"
|
#include "rmi_bus.h"
|
||||||
#include "rmi_driver.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;
|
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,
|
int rmi_read_register_desc(struct rmi_device *d, u16 addr,
|
||||||
struct rmi_register_descriptor *rdesc)
|
struct rmi_register_descriptor *rdesc)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
u8 size_presence_reg;
|
u8 size_presence_reg;
|
||||||
u8 buf[35];
|
u8 buf[35];
|
||||||
int presense_offset = 1;
|
unsigned int presence_offset;
|
||||||
u8 *struct_buf;
|
unsigned int map_offset;
|
||||||
int reg;
|
unsigned int offset;
|
||||||
int offset = 0;
|
unsigned int reg;
|
||||||
int map_offset = 0;
|
|
||||||
int i;
|
int i;
|
||||||
int b;
|
int b;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The first register of the register descriptor is the size of
|
* 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);
|
ret = rmi_read(d, addr, &size_presence_reg);
|
||||||
if (ret)
|
if (ret)
|
||||||
return ret;
|
return ret;
|
||||||
++addr;
|
++addr;
|
||||||
|
|
||||||
if (size_presence_reg < 0 || size_presence_reg > 35)
|
if (size_presence_reg < 1 || size_presence_reg > 35)
|
||||||
return -EIO;
|
return -EIO;
|
||||||
|
|
||||||
memset(buf, 0, sizeof(buf));
|
memset(buf, 0, sizeof(buf));
|
||||||
|
|
@ -597,16 +642,23 @@ int rmi_read_register_desc(struct rmi_device *d, u16 addr,
|
||||||
addr += size_presence_reg;
|
addr += size_presence_reg;
|
||||||
|
|
||||||
if (buf[0] == 0) {
|
if (buf[0] == 0) {
|
||||||
presense_offset = 3;
|
if (size_presence_reg < 3)
|
||||||
rdesc->struct_size = buf[1] | (buf[2] << 8);
|
return -EIO;
|
||||||
|
presence_offset = 3;
|
||||||
|
rdesc->struct_size = get_unaligned_le16(&buf[1]);
|
||||||
} else {
|
} else {
|
||||||
|
presence_offset = 1;
|
||||||
rdesc->struct_size = buf[0];
|
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++) {
|
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);
|
bitmap_set(rdesc->presense_map, map_offset, 1);
|
||||||
|
}
|
||||||
++map_offset;
|
++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
|
* I'm not using devm_kzalloc here since it will not be retained
|
||||||
* after exiting this function
|
* 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)
|
if (!struct_buf)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
|
|
||||||
|
|
@ -638,44 +690,22 @@ int rmi_read_register_desc(struct rmi_device *d, u16 addr,
|
||||||
*/
|
*/
|
||||||
ret = rmi_read_block(d, addr, struct_buf, rdesc->struct_size);
|
ret = rmi_read_block(d, addr, struct_buf, rdesc->struct_size);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto free_struct_buff;
|
return ret;
|
||||||
|
|
||||||
reg = find_first_bit(rdesc->presense_map, RMI_REG_DESC_PRESENSE_BITS);
|
reg = find_first_bit(rdesc->presense_map, RMI_REG_DESC_PRESENSE_BITS);
|
||||||
|
offset = 0;
|
||||||
for (i = 0; i < rdesc->num_registers; i++) {
|
for (i = 0; i < rdesc->num_registers; i++) {
|
||||||
struct rmi_register_desc_item *item = &rdesc->registers[i];
|
struct rmi_register_desc_item *item = &rdesc->registers[i];
|
||||||
int reg_size = struct_buf[offset];
|
int item_size;
|
||||||
|
|
||||||
++offset;
|
item_size = rmi_parse_register_desc_item(item,
|
||||||
if (reg_size == 0) {
|
&struct_buf[offset],
|
||||||
reg_size = struct_buf[offset] |
|
rdesc->struct_size - offset);
|
||||||
(struct_buf[offset + 1] << 8);
|
if (item_size < 0)
|
||||||
offset += 2;
|
return 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->reg = reg;
|
item->reg = reg;
|
||||||
item->reg_size = reg_size;
|
offset += item_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);
|
|
||||||
|
|
||||||
rmi_dbg(RMI_DEBUG_CORE, &d->dev,
|
rmi_dbg(RMI_DEBUG_CORE, &d->dev,
|
||||||
"%s: reg: %d reg size: %ld subpackets: %d\n", __func__,
|
"%s: reg: %d reg size: %ld subpackets: %d\n", __func__,
|
||||||
|
|
@ -685,9 +715,7 @@ int rmi_read_register_desc(struct rmi_device *d, u16 addr,
|
||||||
RMI_REG_DESC_PRESENSE_BITS, reg + 1);
|
RMI_REG_DESC_PRESENSE_BITS, reg + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
free_struct_buff:
|
return 0;
|
||||||
kfree(struct_buf);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct rmi_register_desc_item *rmi_get_register_desc_item(
|
const struct rmi_register_desc_item *rmi_get_register_desc_item(
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user