[PATCH v2 2/2] Input: synaptics-rmi4 - reject a PDT that grows between scans
From: Wei Jie Law <hidden>
Date: 2026-08-24 12:27:47
Also in:
lkml, stable
Subsystem:
input (keyboard, mouse, joystick, touchscreen) drivers, the rest · Maintainers:
Dmitry Torokhov, Linus Torvalds
rmi_driver_probe() walks the Page Description Table three times, and each
walk reads the table back from the device:
1. rmi_initial_reset - issue the reset command in the F01 entry
2. rmi_count_irqs - total the interrupt sources
3. rmi_create_function - create the functions and set their irq bits
Scan 2 fixes data->irq_count, data->num_of_irq_regs and the size of every
per-function irq_mask[]. Scan 3 then accumulates fn->irq_pos and does
for (i = 0; i < fn->num_of_irqs; i++)
set_bit(fn->irq_pos + i, fn->irq_mask);
without checking the result against the count that sized the bitmap.
Nothing makes the device answer the third scan the way it answered the
second, so a device that reports one function with one interrupt source
on scan 2 and a long list of functions on scan 3 walks set_bit() past the
end of the flexible array at the tail of every struct rmi_function:
BUG: KASAN: slab-out-of-bounds in rmi_create_function+0x560/0x930 [rmi_core]
Write of size 8 at addr ffff888110c92b58 by task kworker/1:2/129
Workqueue: events uhid_device_add_worker
kasan_report+0xc6/0x100
kasan_check_range+0x105/0x1b0
rmi_create_function+0x560/0x930 [rmi_core]
rmi_scan_pdt+0x190/0x3f0 [rmi_core]
rmi_init_functions+0xb8/0x320 [rmi_core]
rmi_driver_probe+0x31e/0xbf0 [rmi_core]
one report per corrupted function object. The same unvalidated
fn->irq_pos is used again by the set_bit() and irq_create_mapping() in
rmi_create_function_irq().
Validate the position before using it and fail the probe instead. The
check is exact, not conservative: when both scans see the same table,
fn->irq_pos + fn->num_of_irqs is the running total that produced
data->irq_count, so it never fires for a device that behaves.
Reproduced with an emulated RMI4 device driven over /dev/uhid, and again
over dummy_hcd plus raw-gadget, on v6.12.69 booted slub_debug=FZPU and on
v6.12.105 built with CONFIG_KASAN=y. After this change the same device
gets
rmi4_physical rmi4-03: F40: interrupt count changed between PDT
scans (pos 1 + 6 > 1)
rmi4_physical rmi4-03: Function creation failed with code -22.
and a device that answers both scans consistently still probes normally.
Fixes: 2b6a321da9a2 ("Input: synaptics-rmi4 - add support for Synaptics RMI4 devices")
Cc: stable@vger.kernel.org
The rejection happens before rmi_register_function(), i.e. before
device_initialize() has touched fn->dev, so the partially built function
is disposed of with kfree(): put_device() on a kobject that was never
initialised warns and then leaves the object behind, because the
saturated refcount stops the release from ever running.
Signed-off-by: Wei Jie Law <redacted>
---
Changes in v2:
- Dispose of the rejected function with kfree() instead of
put_device(). See the cover letter.
drivers/input/rmi4/rmi_driver.c | 18 ++++++++++++++++++
1 file changed, 18 insertions(+)
diff --git a/drivers/input/rmi4/rmi_driver.c b/drivers/input/rmi4/rmi_driver.c
index 5d49a9021c7d..8dcf088a7a81 100644
--- a/drivers/input/rmi4/rmi_driver.c
+++ b/drivers/input/rmi4/rmi_driver.c@@ -899,6 +899,24 @@ static int rmi_create_function(struct rmi_device *rmi_dev, fn->irq_pos = *current_irq_count; *current_irq_count += fn->num_of_irqs; + /* + * irq_mask[] was sized from the interrupt count collected by the + * earlier rmi_count_irqs() scan of the PDT. Nothing guarantees that + * this scan sees the same table -- the PDT is read back from the + * device every time -- so a device that grows its interrupt counts + * between the two scans would push these set_bit() calls past the end + * of the flexible array. Refuse the function instead. + */ + if (fn->num_of_irqs > RMI_FN_MAX_IRQS || + fn->irq_pos + fn->num_of_irqs > data->irq_count) { + dev_err(dev, + "F%02X: interrupt count changed between PDT scans (pos %u + %u > %d)\n", + pdt->function_number, fn->irq_pos, fn->num_of_irqs, + data->irq_count); + kfree(fn); + return -EINVAL; + } + for (i = 0; i < fn->num_of_irqs; i++) set_bit(fn->irq_pos + i, fn->irq_mask);
--
2.43.0