mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-30 13:23:02 -04:00
HID: uclogic: fix use-after-free of inrange_timer on remove
uclogic_remove() cancels the pen in-range timer and then stops the
device:
timer_delete_sync(&drvdata->inrange_timer);
hid_hw_stop(hdev);
timer_delete_sync() only guarantees the timer is idle at that instant.
uclogic_raw_event_pen() keeps delivering pen reports until hid_hw_stop()
stops the transport several lines later, and every report with
pen->inrange == UCLOGIC_PARAMS_PEN_INRANGE_NONE re-arms the timer:
mod_timer(&drvdata->inrange_timer, jiffies + msecs_to_jiffies(100));
A report landing between the timer_delete_sync() call and the transport
teardown in hid_hw_stop() re-arms inrange_timer after it was cancelled.
uclogic_remove() then returns and the devm drvdata is freed, while
hid_hw_stop() has already freed the input device drvdata->pen_input
points at, so when the timer fires ~100 ms later
uclogic_inrange_timeout() dereferences freed memory -- a use-after-free
in timer-softirq context.
Swapping the two calls is not a fix: stopping the device first frees
drvdata->pen_input via hidinput_disconnect() while the timer may still
be pending, so a timer already armed before removal fires on the freed
input device in the window before timer_delete_sync() runs.
Use timer_shutdown_sync() before hid_hw_stop() instead. It cancels the
timer, waits for a running callback while pen_input is still valid, and
prevents any further re-arming -- a later mod_timer() from an in-flight
report is silently ignored -- so the timer is provably dead before
hid_hw_stop() frees the inputs. This is the ordering the timer core
documents for this "timer re-armed from another path" teardown case.
Fixes: 01309e29eb ("HID: uclogic: Support in-range reporting emulation")
Cc: stable@vger.kernel.org
Signed-off-by: Ibrahim Hashimov <security@auditcode.ai>
Assisted-by: AuditCode-AI:2026.07
Signed-off-by: Jiri Kosina <jkosina@suse.com>
This commit is contained in:
committed by
Jiri Kosina
parent
e9c667395a
commit
506fd50a90
@@ -548,7 +548,17 @@ static void uclogic_remove(struct hid_device *hdev)
|
||||
{
|
||||
struct uclogic_drvdata *drvdata = hid_get_drvdata(hdev);
|
||||
|
||||
timer_delete_sync(&drvdata->inrange_timer);
|
||||
/*
|
||||
* Shut the in-range timer down before stopping the device.
|
||||
* uclogic_raw_event_pen() re-arms inrange_timer on every pen report
|
||||
* and keeps running until hid_hw_stop() stops the transport, so a
|
||||
* plain timer_delete_sync() here can be undone by a report landing in
|
||||
* the window before hid_hw_stop(). timer_shutdown_sync() cancels the
|
||||
* timer and makes any later re-arm a no-op, so it is provably dead
|
||||
* before hid_hw_stop() frees the input device drvdata->pen_input
|
||||
* points at.
|
||||
*/
|
||||
timer_shutdown_sync(&drvdata->inrange_timer);
|
||||
hid_hw_stop(hdev);
|
||||
kfree(drvdata->desc_ptr);
|
||||
uclogic_params_cleanup(&drvdata->params);
|
||||
|
||||
Reference in New Issue
Block a user