mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
HID: wacom: Fix Use-After-Free in wacom_intuos_pad
wacom_intuos_pad() accesses wacom->shared->touch_input locklessly
inside the interrupt handler context. If the Touch sibling device
is disconnected, wacom_remove_shared_data() clears 'touch_input'
outside any lock, creating a Time-of-Check to Time-of-Use (TOCTOU)
race condition where a preempted reader in interrupt context
dereferences the freed pointer, leading to a Use-After-Free.
Resolve this by introducing RCU protection for the touch_input
pointer:
- Annotate 'touch_input' in wacom_shared struct with __rcu
- Wrap all lockless readers in wacom_wac.c with guard(rcu)() and
rcu_dereference() using a unified wacom_report_touch_mute()
helper
- Update writers in wacom_sys.c using rcu_assign_pointer()
- Call synchronize_rcu() in wacom_remove_shared_data() to ensure
all active RCU readers have finished before the input device is
freed
Also wrap wacom_set_shared_values() and touch/pen assignments in
wacom_add_shared_data() inside the wacom_udev_list_lock to serialize
concurrent probe assignments, and verify that 'shared->touch == hdev'
before setting touch_input to prevent concurrent sibling probe state
desynchronization.
Cc: stable@vger.kernel.org
Fixes: 961794a00e ("Input: wacom - add reporting of SW_MUTE_DEVICE events")
Signed-off-by: Lee Jones <lee@kernel.org>
Acked-by: Ping Cheng <ping.cheng@wacom.com>
Tested-by: Ping Cheng <ping.cheng@wacom.com>
Signed-off-by: Jiri Kosina <jkosina@suse.com>
This commit is contained in:
parent
ba16e5ffba
commit
ef32877368
|
|
@ -875,10 +875,16 @@ static void wacom_remove_shared_data(void *res)
|
|||
data = container_of(wacom_wac->shared, struct wacom_hdev_data,
|
||||
shared);
|
||||
|
||||
if (wacom_wac->shared->touch == wacom->hdev)
|
||||
wacom_wac->shared->touch = NULL;
|
||||
else if (wacom_wac->shared->pen == wacom->hdev)
|
||||
wacom_wac->shared->pen = NULL;
|
||||
scoped_guard(mutex, &wacom_udev_list_lock) {
|
||||
if (wacom_wac->shared->touch == wacom->hdev) {
|
||||
wacom_wac->shared->touch = NULL;
|
||||
rcu_assign_pointer(wacom_wac->shared->touch_input, NULL);
|
||||
} else if (wacom_wac->shared->pen == wacom->hdev) {
|
||||
wacom_wac->shared->pen = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
synchronize_rcu();
|
||||
|
||||
kref_put(&data->kref, wacom_release_shared_data);
|
||||
wacom_wac->shared = NULL;
|
||||
|
|
@ -915,6 +921,7 @@ static int wacom_add_shared_data(struct hid_device *hdev)
|
|||
mutex_unlock(&wacom_udev_list_lock);
|
||||
|
||||
retval = devm_add_action_or_reset(&hdev->dev, wacom_remove_shared_data, wacom);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
|
@ -2354,9 +2361,11 @@ static void wacom_set_shared_values(struct wacom_wac *wacom_wac)
|
|||
}
|
||||
|
||||
if (wacom_wac->features.device_type & WACOM_DEVICETYPE_TOUCH) {
|
||||
wacom_wac->shared->type = wacom_wac->features.type;
|
||||
wacom_wac->shared->touch_input = wacom_wac->touch_input;
|
||||
wacom_wac->shared->touch = wacom->hdev;
|
||||
if (wacom_wac->shared->touch == wacom->hdev || !wacom_wac->shared->touch) {
|
||||
wacom_wac->shared->type = wacom_wac->features.type;
|
||||
wacom_wac->shared->touch = wacom->hdev;
|
||||
rcu_assign_pointer(wacom_wac->shared->touch_input, wacom_wac->touch_input);
|
||||
}
|
||||
} else if (wacom_wac->features.device_type & WACOM_DEVICETYPE_PEN) {
|
||||
/* Pairs with smp_load_acquire() in wacom_sibling_pending() */
|
||||
smp_store_release(&wacom_wac->shared->pen, wacom->hdev);
|
||||
|
|
|
|||
|
|
@ -510,6 +510,18 @@ static void wacom_intuos_schedule_prox_event(struct wacom_wac *wacom_wac)
|
|||
}
|
||||
}
|
||||
|
||||
static void wacom_report_touch_mute(struct wacom_wac *wacom_wac, bool mute)
|
||||
{
|
||||
struct input_dev *touch_input;
|
||||
|
||||
guard(rcu)();
|
||||
touch_input = rcu_dereference(wacom_wac->shared->touch_input);
|
||||
if (touch_input) {
|
||||
input_report_switch(touch_input, SW_MUTE_DEVICE, mute);
|
||||
input_sync(touch_input);
|
||||
}
|
||||
}
|
||||
|
||||
static int wacom_intuos_pad(struct wacom_wac *wacom)
|
||||
{
|
||||
struct wacom_features *features = &wacom->features;
|
||||
|
|
@ -650,12 +662,8 @@ static int wacom_intuos_pad(struct wacom_wac *wacom)
|
|||
input_report_key(input, KEY_CONTROLPANEL, menu);
|
||||
input_report_key(input, KEY_INFO, info);
|
||||
|
||||
if (wacom->shared && wacom->shared->touch_input) {
|
||||
input_report_switch(wacom->shared->touch_input,
|
||||
SW_MUTE_DEVICE,
|
||||
!wacom->shared->is_touch_on);
|
||||
input_sync(wacom->shared->touch_input);
|
||||
}
|
||||
if (wacom->shared)
|
||||
wacom_report_touch_mute(wacom, !wacom->shared->is_touch_on);
|
||||
|
||||
input_report_abs(input, ABS_RX, strip1);
|
||||
input_report_abs(input, ABS_RY, strip2);
|
||||
|
|
@ -2168,7 +2176,7 @@ static void wacom_wac_pad_event(struct hid_device *hdev, struct hid_field *field
|
|||
*/
|
||||
if ((equivalent_usage == WACOM_HID_WD_MUTE_DEVICE) ||
|
||||
(equivalent_usage == WACOM_HID_WD_TOUCHONOFF)) {
|
||||
if (wacom_wac->shared->touch_input) {
|
||||
if (wacom_wac->shared) {
|
||||
bool *is_touch_on = &wacom_wac->shared->is_touch_on;
|
||||
|
||||
if (equivalent_usage == WACOM_HID_WD_MUTE_DEVICE && value)
|
||||
|
|
@ -2176,9 +2184,7 @@ static void wacom_wac_pad_event(struct hid_device *hdev, struct hid_field *field
|
|||
else if (equivalent_usage == WACOM_HID_WD_TOUCHONOFF)
|
||||
*is_touch_on = value;
|
||||
|
||||
input_report_switch(wacom_wac->shared->touch_input,
|
||||
SW_MUTE_DEVICE, !(*is_touch_on));
|
||||
input_sync(wacom_wac->shared->touch_input);
|
||||
wacom_report_touch_mute(wacom_wac, !(*is_touch_on));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
|
@ -3396,11 +3402,8 @@ static int wacom_wireless_irq(struct wacom_wac *wacom, size_t len)
|
|||
|
||||
if ((wacom->shared->type == INTUOSHT ||
|
||||
wacom->shared->type == INTUOSHT2) &&
|
||||
wacom->shared->touch_input &&
|
||||
wacom->shared->touch_max) {
|
||||
input_report_switch(wacom->shared->touch_input,
|
||||
SW_MUTE_DEVICE, data[5] & 0x40);
|
||||
input_sync(wacom->shared->touch_input);
|
||||
wacom_report_touch_mute(wacom, data[5] & 0x40);
|
||||
}
|
||||
|
||||
pid = get_unaligned_be16(&data[6]);
|
||||
|
|
@ -3435,11 +3438,8 @@ static int wacom_status_irq(struct wacom_wac *wacom_wac, size_t len)
|
|||
|
||||
if ((features->type == INTUOSHT ||
|
||||
features->type == INTUOSHT2) &&
|
||||
wacom_wac->shared->touch_input &&
|
||||
features->touch_max) {
|
||||
input_report_switch(wacom_wac->shared->touch_input,
|
||||
SW_MUTE_DEVICE, data[8] & 0x40);
|
||||
input_sync(wacom_wac->shared->touch_input);
|
||||
wacom_report_touch_mute(wacom_wac, data[8] & 0x40);
|
||||
}
|
||||
|
||||
if (data[9] & 0x02) { /* wireless module is attached */
|
||||
|
|
|
|||
|
|
@ -285,7 +285,7 @@ struct wacom_shared {
|
|||
/* for wireless device to access USB interfaces */
|
||||
unsigned touch_max;
|
||||
int type;
|
||||
struct input_dev *touch_input;
|
||||
struct input_dev __rcu *touch_input;
|
||||
struct hid_device *pen;
|
||||
struct hid_device *touch;
|
||||
bool has_mute_touch_switch;
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user