* [PATCH v10 1/5] HID: wacom: Use hdev->product in wacom_setup_touch_input_capabilities
@ 2026-09-09 11:12 Lee Jones
2026-09-09 11:12 ` [PATCH v10 2/5] HID: wacom: Advertise SW_MUTE_DEVICE capability prior to registration Lee Jones
` (7 more replies)
0 siblings, 8 replies; 11+ messages in thread
From: Lee Jones @ 2026-09-09 11:12 UTC (permalink / raw)
To: lee, Ping Cheng, Jason Gerecke, Jiri Kosina, Benjamin Tissoires,
Aaron Skomra, Dmitry Torokhov, Peter Hutterer, linux-input,
linux-kernel
Cc: stable
Replace the lookup-dependent 'wacom_wac->shared->touch->product' references
with 'hdev->product' inside wacom_setup_touch_input_capabilities() since
'hdev' is already available (via container_of) and represents the touch
device itself.
Cc: stable@vger.kernel.org
Signed-off-by: Lee Jones <lee@kernel.org>
---
v7 -> v8: New patch
v8 -> v9: No change
v9 -> v10: No change
drivers/hid/wacom_wac.c | 18 +++++++++---------
1 file changed, 9 insertions(+), 9 deletions(-)
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index 8feb8027be95..7cf2b4de52be 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -3966,8 +3966,8 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev,
int wacom_setup_touch_input_capabilities(struct input_dev *input_dev,
struct wacom_wac *wacom_wac)
{
+ struct hid_device *hdev = container_of(wacom_wac, struct wacom, wacom_wac)->hdev;
struct wacom_features *features = &wacom_wac->features;
-
if (!(features->device_type & WACOM_DEVICETYPE_TOUCH))
return -ENODEV;
@@ -3976,9 +3976,11 @@ int wacom_setup_touch_input_capabilities(struct input_dev *input_dev,
else
__set_bit(INPUT_PROP_POINTER, input_dev->propbit);
- if (features->type == HID_GENERIC)
+ if (features->type == HID_GENERIC) {
+ hid_dbg(hdev, "generic touch setup\n");
/* setup has already been done */
return 0;
+ }
input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
__set_bit(BTN_TOUCH, input_dev->keybit);
@@ -4010,19 +4012,17 @@ int wacom_setup_touch_input_capabilities(struct input_dev *input_dev,
input_dev->evbit[0] |= BIT_MASK(EV_SW);
__set_bit(SW_MUTE_DEVICE, input_dev->swbit);
- if (wacom_wac->shared->touch->product == 0x361) {
+ if (hdev->product == 0x361) {
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
0, 12440, 4, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
0, 8640, 4, 0);
- }
- else if (wacom_wac->shared->touch->product == 0x360) {
+ } else if (hdev->product == 0x360) {
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
0, 8960, 4, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
0, 5920, 4, 0);
- }
- else if (wacom_wac->shared->touch->product == 0x393) {
+ } else if (hdev->product == 0x393) {
input_set_abs_params(input_dev, ABS_MT_POSITION_X,
0, 6400, 4, 0);
input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
@@ -4052,8 +4052,8 @@ int wacom_setup_touch_input_capabilities(struct input_dev *input_dev,
fallthrough;
case WACOM_27QHDT:
- if (wacom_wac->shared->touch->product == 0x32C ||
- wacom_wac->shared->touch->product == 0xF6) {
+ if (hdev->product == 0x32C ||
+ hdev->product == 0xF6) {
input_dev->evbit[0] |= BIT_MASK(EV_SW);
__set_bit(SW_MUTE_DEVICE, input_dev->swbit);
wacom_wac->has_mute_touch_switch = true;
--
2.55.0.979.g7e5102b832-goog
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v10 2/5] HID: wacom: Advertise SW_MUTE_DEVICE capability prior to registration
2026-09-09 11:12 [PATCH v10 1/5] HID: wacom: Use hdev->product in wacom_setup_touch_input_capabilities Lee Jones
@ 2026-09-09 11:12 ` Lee Jones
2026-09-09 11:12 ` [PATCH v10 3/5] HID: wacom: Fix Use-After-Free in wacom_intuos_pad Lee Jones
` (6 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Lee Jones @ 2026-09-09 11:12 UTC (permalink / raw)
To: lee, Ping Cheng, Jason Gerecke, Jiri Kosina, Benjamin Tissoires,
Aaron Skomra, Peter Hutterer, Dmitry Torokhov, linux-input,
linux-kernel
Cc: stable
Input subsystem guidelines require that device capabilities are advertised
before the input device is registered. The Wacom driver was violating
this by advertising the SW_MUTE_DEVICE capability post-registration in
wacom_set_shared_values() (and duplicating it in device-specific setup
cases).
Resolve this by moving the SW_MUTE_DEVICE capability setup to
wacom_setup_touch_input_capabilities() for all touch devices that support
it.
For generic touch devices whose capabilities depend on mute switch
usages parsed from a sibling Pen/Pad interface, defer registration
with -EPROBE_DEFER until the sibling has parsed its descriptors and
initialized shared capabilities.
Cc: stable@vger.kernel.org
Fixes: d2ec58aee8b1 ("HID: wacom: generic: support generic touch switch")
Signed-off-by: Lee Jones <lee@kernel.org>
---
v4 -> v5: New patch used to split out SW_MUTE_DEVICE as per Jason's request
v5 -> v6: Unconditionally advertise SW_MUTE_DEVICE on generic touch devices
v6 -> v7: Only advertise SW_MUTE_DEVICE on composite USB generic touch devices
v7 -> v8: Replace heuristic with probe deferral until sibling Pen/Pad is parsed
Split out 'hdev->product' cleanups into a separate standalone patch
v8 -> v9: Fix TOCTOU race by assigning shared sibling pointers in wacom_set_shared_values()
Support Pad interfaces in sibling deferral logic
v9 -> v10: Defer probe if unprobed sibling HID interface exists on composite
USB device
Ensure standalone generic touch devices advertise SW_MUTE_DEVICE
Use acquire memory barrier (smp_load_acquire) when reading shared
sibling state
Cancel pending init_work in fail_hw_stop error path after stopping
hardware
Move has_mute_touch_switch write inside wacom_udev_list_lock
Only assign shared->pen for Pen devices to prevent overwrite by Pad
drivers/hid/wacom_sys.c | 95 ++++++++++++++++++++++++++++++++---------
drivers/hid/wacom_wac.c | 5 +++
2 files changed, 80 insertions(+), 20 deletions(-)
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index 0eafa483b7f7..4eed2c189017 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -907,19 +907,14 @@ static int wacom_add_shared_data(struct hid_device *hdev)
list_add_tail(&data->list, &wacom_udev_list);
}
- mutex_unlock(&wacom_udev_list_lock);
-
wacom_wac->shared = &data->shared;
+ if (wacom_wac->has_mute_touch_switch)
+ WRITE_ONCE(wacom_wac->shared->has_mute_touch_switch, true);
+
+ mutex_unlock(&wacom_udev_list_lock);
+
retval = devm_add_action_or_reset(&hdev->dev, wacom_remove_shared_data, wacom);
- if (retval)
- return retval;
-
- if (wacom_wac->features.device_type & WACOM_DEVICETYPE_TOUCH)
- wacom_wac->shared->touch = hdev;
- else if (wacom_wac->features.device_type & WACOM_DEVICETYPE_PEN)
- wacom_wac->shared->pen = hdev;
-
return retval;
}
@@ -2343,13 +2338,12 @@ static void wacom_release_resources(struct wacom *wacom)
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;
- }
+ struct wacom *wacom = container_of(wacom_wac, struct wacom, wacom_wac);
+
+ guard(mutex)(&wacom_udev_list_lock);
if (wacom_wac->has_mute_touch_switch) {
- wacom_wac->shared->has_mute_touch_switch = true;
+ WRITE_ONCE(wacom_wac->shared->has_mute_touch_switch, true);
/* Hardware touch switch may be off. Wait until
* we know the switch state to decide is_touch_on.
* Softkey state should be initialized to "on" to
@@ -2359,14 +2353,69 @@ static void wacom_set_shared_values(struct wacom_wac *wacom_wac)
wacom_wac->shared->is_touch_on = true;
}
- if (wacom_wac->shared->has_mute_touch_switch &&
- wacom_wac->shared->touch_input) {
- set_bit(EV_SW, wacom_wac->shared->touch_input->evbit);
- input_set_capability(wacom_wac->shared->touch_input, EV_SW,
- SW_MUTE_DEVICE);
+ 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;
+ } 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);
}
}
+static bool wacom_sibling_pending(struct wacom *wacom)
+{
+ const struct wacom_features *features = &wacom->wacom_wac.features;
+ struct hid_device *hdev = wacom->hdev;
+ struct usb_interface *sibling_intf;
+ int ifnum;
+
+ if (features->type != HID_GENERIC ||
+ !(features->device_type & WACOM_DEVICETYPE_TOUCH))
+ return false;
+
+ if (wacom->wacom_wac.shared) {
+ /* Pairs with smp_store_release() in wacom_set_shared_values() */
+ if (smp_load_acquire(&wacom->wacom_wac.shared->pen))
+ return false;
+ }
+
+ if (!hid_is_usb(hdev) || !wacom->usbdev || !wacom->intf ||
+ !wacom->intf->cur_altsetting)
+ return false;
+
+ ifnum = wacom->intf->cur_altsetting->desc.bInterfaceNumber;
+
+ /*
+ * On composite Wacom devices, the Pen interface is always interface 0.
+ * If the Touch interface is interface 0, there is no sibling Pen
+ * interface on this device (standalone touch device).
+ */
+ if (ifnum == 0)
+ return false;
+
+ /* Look for the sibling Pen interface at interface 0 */
+ sibling_intf = usb_ifnum_to_if(wacom->usbdev, 0);
+ if (!sibling_intf || !sibling_intf->cur_altsetting)
+ return false;
+
+ if (sibling_intf->cur_altsetting->desc.bInterfaceClass !=
+ USB_INTERFACE_CLASS_HID)
+ return false;
+
+ if (sibling_intf->cur_altsetting->desc.bInterfaceSubClass == 1 &&
+ (sibling_intf->cur_altsetting->desc.bInterfaceProtocol == 1 ||
+ sibling_intf->cur_altsetting->desc.bInterfaceProtocol == 2))
+ return false;
+
+ /*
+ * Interface 0 is a candidate HID interface on this composite device
+ * whose probe has not completed yet (shared->pen is NULL). Defer until
+ * interface 0 finishes probing and registers shared values.
+ */
+ return true;
+}
+
static int wacom_parse_and_register(struct wacom *wacom, bool wireless)
{
struct wacom_wac *wacom_wac = &wacom->wacom_wac;
@@ -2444,6 +2493,11 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless)
if (error)
goto fail;
+ if (wacom_sibling_pending(wacom)) {
+ error = -EPROBE_DEFER;
+ goto fail;
+ }
+
error = wacom_setup_inputs(wacom);
if (error)
goto fail;
@@ -2501,6 +2555,7 @@ static int wacom_parse_and_register(struct wacom *wacom, bool wireless)
fail_hw_stop:
hid_hw_stop(hdev);
+ cancel_delayed_work_sync(&wacom->init_work);
fail:
wacom_release_resources(wacom);
return error;
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index 7cf2b4de52be..30df866feac8 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -3978,6 +3978,11 @@ int wacom_setup_touch_input_capabilities(struct input_dev *input_dev,
if (features->type == HID_GENERIC) {
hid_dbg(hdev, "generic touch setup\n");
+ if (wacom_wac->has_mute_touch_switch ||
+ (wacom_wac->shared && READ_ONCE(wacom_wac->shared->has_mute_touch_switch))) {
+ input_set_capability(input_dev, EV_SW, SW_MUTE_DEVICE);
+ wacom_wac->has_mute_touch_switch = true;
+ }
/* setup has already been done */
return 0;
}
--
2.55.0.979.g7e5102b832-goog
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v10 3/5] HID: wacom: Fix Use-After-Free in wacom_intuos_pad
2026-09-09 11:12 [PATCH v10 1/5] HID: wacom: Use hdev->product in wacom_setup_touch_input_capabilities Lee Jones
2026-09-09 11:12 ` [PATCH v10 2/5] HID: wacom: Advertise SW_MUTE_DEVICE capability prior to registration Lee Jones
@ 2026-09-09 11:12 ` Lee Jones
2026-09-09 11:32 ` sashiko-bot
2026-09-09 11:12 ` [PATCH v10 4/5] HID: wacom: Fix Use-After-Free in wacom_bamboo_pad Lee Jones
` (5 subsequent siblings)
7 siblings, 1 reply; 11+ messages in thread
From: Lee Jones @ 2026-09-09 11:12 UTC (permalink / raw)
To: lee, Ping Cheng, Jason Gerecke, Jiri Kosina, Benjamin Tissoires,
Aaron Skomra, Peter Hutterer, Dmitry Torokhov, linux-input,
linux-kernel
Cc: stable
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: 961794a00eab ("Input: wacom - add reporting of SW_MUTE_DEVICE events")
Signed-off-by: Lee Jones <lee@kernel.org>
---
v1 -> v2: Split and use RCU as per Dmitry's review
v2 -> v3: Sashiko fixes
v3 -> v4: Dmitry's review [redundant check and guard()]
v4 -> v5: Jason's review [split, remove "awkward if"]
v5 -> v6: No change
v6 -> v7: No change
v7 -> v8: No functional change - cleaned up a stray whitespace/blank line deletion hunk
v8 -> v9: No change
v9 -> v10: No change
drivers/hid/wacom_sys.c | 23 ++++++++++++++++-------
drivers/hid/wacom_wac.c | 36 ++++++++++++++++++------------------
drivers/hid/wacom_wac.h | 2 +-
3 files changed, 35 insertions(+), 26 deletions(-)
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index 4eed2c189017..10f7de515ea2 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -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);
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index 30df866feac8..c5227530f291 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -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 */
diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h
index 126bec6e5c0c..a8bbba4a6f37 100644
--- a/drivers/hid/wacom_wac.h
+++ b/drivers/hid/wacom_wac.h
@@ -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;
--
2.55.0.979.g7e5102b832-goog
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v10 4/5] HID: wacom: Fix Use-After-Free in wacom_bamboo_pad
2026-09-09 11:12 [PATCH v10 1/5] HID: wacom: Use hdev->product in wacom_setup_touch_input_capabilities Lee Jones
2026-09-09 11:12 ` [PATCH v10 2/5] HID: wacom: Advertise SW_MUTE_DEVICE capability prior to registration Lee Jones
2026-09-09 11:12 ` [PATCH v10 3/5] HID: wacom: Fix Use-After-Free in wacom_intuos_pad Lee Jones
@ 2026-09-09 11:12 ` Lee Jones
2026-09-09 11:31 ` sashiko-bot
2026-09-09 11:12 ` [PATCH v10 5/5] HID: wacom: Redesign shared sibling data lifecycle Lee Jones
` (4 subsequent siblings)
7 siblings, 1 reply; 11+ messages in thread
From: Lee Jones @ 2026-09-09 11:12 UTC (permalink / raw)
To: lee, Ping Cheng, Jason Gerecke, Jiri Kosina, Benjamin Tissoires,
Aaron Skomra, Dmitry Torokhov, Peter Hutterer, linux-input,
linux-kernel
Cc: stable
wacom_bamboo_pad_pen_event() accesses wacom->shared->pen locklessly
relative to wacom_remove_shared_data() which nullifies it. This
can lead to a Use-After-Free if the sibling device is removed while
events are being processed.
Resolve this by introducing RCU protection for pen and touch
pointers:
- Annotate 'pen' and 'touch' in wacom_shared struct with __rcu.
- Wrap lockless readers in wacom_bamboo_pad_pen_event() with
rcu_read_lock() and rcu_dereference().
- Update writers in wacom_sys.c using rcu_assign_pointer().
- Use rcu_dereference_protected for comparisons under
wacom_udev_list_lock.
- Also use rcu_access_pointer in wacom_sibling_pending() and
wacom_mode_change_work() to avoid warnings (while lockless access in
the latter remains a pre-existing issue).
Cc: stable@vger.kernel.org
Fixes: 8c97a765467c ("HID: wacom: add full support of the Wacom Bamboo PAD")
Signed-off-by: Lee Jones <lee@kernel.org>
---
v1 -> v2: Split and use RCU as per Dmitry's review
v2 -> v3: Sashiko fixes
v3 -> v4: Dmitry's review [guard()]
v4 -> v5: No change
v5 -> v6: No change
v6 -> v7: No change
v7 -> v8: No change
v8 -> v9: No change
v9 -> v10: Use rcu_access_pointer() for __rcu annotated shared->pen in
wacom_sibling_pending()
drivers/hid/wacom_sys.c | 40 ++++++++++++++++++++++++++--------------
drivers/hid/wacom_wac.c | 13 ++++++-------
drivers/hid/wacom_wac.h | 4 ++--
3 files changed, 34 insertions(+), 23 deletions(-)
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index 10f7de515ea2..307eda9bbdd0 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -876,11 +876,18 @@ static void wacom_remove_shared_data(void *res)
shared);
scoped_guard(mutex, &wacom_udev_list_lock) {
- if (wacom_wac->shared->touch == wacom->hdev) {
- wacom_wac->shared->touch = NULL;
+ struct hid_device *touch =
+ rcu_dereference_protected(wacom_wac->shared->touch,
+ lockdep_is_held(&wacom_udev_list_lock));
+ struct hid_device *pen =
+ rcu_dereference_protected(wacom_wac->shared->pen,
+ lockdep_is_held(&wacom_udev_list_lock));
+
+ if (touch == wacom->hdev) {
+ rcu_assign_pointer(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;
+ } else if (pen == wacom->hdev) {
+ rcu_assign_pointer(wacom_wac->shared->pen, NULL);
}
}
@@ -2361,14 +2368,17 @@ static void wacom_set_shared_values(struct wacom_wac *wacom_wac)
}
if (wacom_wac->features.device_type & WACOM_DEVICETYPE_TOUCH) {
- if (wacom_wac->shared->touch == wacom->hdev || !wacom_wac->shared->touch) {
+ struct hid_device *touch =
+ rcu_dereference_protected(wacom_wac->shared->touch,
+ lockdep_is_held(&wacom_udev_list_lock));
+
+ if (touch == wacom->hdev || !touch) {
wacom_wac->shared->type = wacom_wac->features.type;
- wacom_wac->shared->touch = wacom->hdev;
+ rcu_assign_pointer(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);
+ rcu_assign_pointer(wacom_wac->shared->pen, wacom->hdev);
}
}
@@ -2384,8 +2394,7 @@ static bool wacom_sibling_pending(struct wacom *wacom)
return false;
if (wacom->wacom_wac.shared) {
- /* Pairs with smp_store_release() in wacom_set_shared_values() */
- if (smp_load_acquire(&wacom->wacom_wac.shared->pen))
+ if (rcu_access_pointer(wacom->wacom_wac.shared->pen))
return false;
}
@@ -2852,16 +2861,19 @@ static void wacom_mode_change_work(struct work_struct *work)
bool is_direct = wacom->wacom_wac.is_direct_mode;
int error = 0;
- if (shared->pen) {
- wacom1 = hid_get_drvdata(shared->pen);
+ struct hid_device *pen = rcu_access_pointer(shared->pen);
+ struct hid_device *touch = rcu_access_pointer(shared->touch);
+
+ if (pen) {
+ wacom1 = hid_get_drvdata(pen);
wacom_release_resources(wacom1);
hid_hw_stop(wacom1->hdev);
wacom1->wacom_wac.has_mode_change = true;
wacom1->wacom_wac.is_direct_mode = is_direct;
}
- if (shared->touch) {
- wacom2 = hid_get_drvdata(shared->touch);
+ if (touch) {
+ wacom2 = hid_get_drvdata(touch);
wacom_release_resources(wacom2);
hid_hw_stop(wacom2->hdev);
wacom2->wacom_wac.has_mode_change = true;
diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
index c5227530f291..c4cd87b781c4 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -3307,6 +3307,7 @@ static int wacom_bpt_irq(struct wacom_wac *wacom, size_t len)
static void wacom_bamboo_pad_pen_event(struct wacom_wac *wacom,
unsigned char *data)
{
+ struct hid_device *pen;
unsigned char prefix;
/*
@@ -3319,13 +3320,11 @@ static void wacom_bamboo_pad_pen_event(struct wacom_wac *wacom,
prefix = data[0];
data[0] = WACOM_REPORT_BPAD_PEN;
- /*
- * actually reroute the event.
- * No need to check if wacom->shared->pen is valid, hid_input_report()
- * will check for us.
- */
- hid_input_report(wacom->shared->pen, HID_INPUT_REPORT, data,
- WACOM_PKGLEN_PENABLED, 1);
+ guard(rcu)();
+ pen = rcu_dereference(wacom->shared->pen);
+ if (pen)
+ hid_input_report(pen, HID_INPUT_REPORT, data,
+ WACOM_PKGLEN_PENABLED, 1);
data[0] = prefix;
}
diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h
index a8bbba4a6f37..170d6adbe02a 100644
--- a/drivers/hid/wacom_wac.h
+++ b/drivers/hid/wacom_wac.h
@@ -286,8 +286,8 @@ struct wacom_shared {
unsigned touch_max;
int type;
struct input_dev __rcu *touch_input;
- struct hid_device *pen;
- struct hid_device *touch;
+ struct hid_device __rcu *pen;
+ struct hid_device __rcu *touch;
bool has_mute_touch_switch;
bool is_touch_on;
};
--
2.55.0.979.g7e5102b832-goog
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH v10 5/5] HID: wacom: Redesign shared sibling data lifecycle
2026-09-09 11:12 [PATCH v10 1/5] HID: wacom: Use hdev->product in wacom_setup_touch_input_capabilities Lee Jones
` (2 preceding siblings ...)
2026-09-09 11:12 ` [PATCH v10 4/5] HID: wacom: Fix Use-After-Free in wacom_bamboo_pad Lee Jones
@ 2026-09-09 11:12 ` Lee Jones
2026-09-09 11:31 ` [PATCH v10 1/5] HID: wacom: Use hdev->product in wacom_setup_touch_input_capabilities sashiko-bot
` (3 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: Lee Jones @ 2026-09-09 11:12 UTC (permalink / raw)
To: lee, Ping Cheng, Jason Gerecke, Jiri Kosina, Benjamin Tissoires,
Aaron Skomra, Dmitry Torokhov, Peter Hutterer, linux-input,
linux-kernel
Cc: stable
The Wacom driver coordinates state between sibling interfaces of
the same physical device using a shared structure 'wacom_shared'
inside 'wacom_hdev_data'. The driver kept a volatile representative
pointer 'data->dev' pointing to a sibling 'hid_device' for physical
path comparisons during sibling matching.
This pointer management is fragile. When the representative device
is disconnected, wacom_remove_shared_data() failed to clear/update
'data->dev', leading to a Use-After-Free vulnerability when
subsequent sibling probes dereference the dangling 'data->dev'
pointer.
Resolve this issue by redesigning the sibling data lifecycle:
- Eliminate the volatile 'data->dev' representative pointer
completely
- Redesign 'wacom_hdev_data' to store stable static copies of the
required attributes upon first allocation: 'phys' path string,
'vendor', 'product' IDs and the sibling's 'device_type'
- Use these static attributes for stable sibling matching in
wacom_are_sibling() and wacom_get_hdev_data()
This ensures sibling matching remains safe and stable even if
individual siblings are dynamically added or removed.
To secure the lifecycle against concurrent probe/disconnect races:
- Switch kref_put() to kref_put_mutex() in
wacom_remove_shared_data() to serialize refcount drops with list
traversal and lookup
- Modify wacom_release_shared_data() to assume the list lock is
already held
Also, do not accumulate the 'device_type' capability flag during
subsequent sibling probes. Keeping only the first probed sibling's
device_type exactly preserves the original sibling matching behavior
without introducing side effects.
Cc: stable@vger.kernel.org
Fixes: 4492efffffeb ("Input: wacom - share pen info with touch of the same ID")
Signed-off-by: Lee Jones <lee@kernel.org>
---
v1 -> v2: Split and use RCU as per Dmitry's review
v2 -> v3: Sashiko fixes
v3 -> v4: No change
v4 -> v5: No change
v5 -> v6: No change
v6 -> v7: No change
v7 -> v8: No change
v8 -> v9: No change
v9 -> v10: No change
drivers/hid/wacom_sys.c | 70 +++++++++++++++++++++++++++--------------
1 file changed, 46 insertions(+), 24 deletions(-)
diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index 307eda9bbdd0..8dc7aa1c0a26 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -757,27 +757,47 @@ static void wacom_retrieve_hid_descriptor(struct hid_device *hdev,
struct wacom_hdev_data {
struct list_head list;
struct kref kref;
- struct hid_device *dev;
+ char phys[64];
+ __u32 vendor;
+ __u32 product;
+ __u32 device_type;
struct wacom_shared shared;
};
+static bool wacom_compare_device_paths(struct hid_device *hdev_a,
+ const char *phys_b, char separator)
+{
+ const char *p1 = strrchr(hdev_a->phys, separator);
+ const char *p2 = strrchr(phys_b, separator);
+ int n1, n2;
+
+ if (!p1 || !p2)
+ return false;
+
+ n1 = p1 - hdev_a->phys;
+ n2 = p2 - phys_b;
+
+ if (n1 != n2 || n1 <= 0 || n2 <= 0)
+ return false;
+
+ return !strncmp(hdev_a->phys, phys_b, n1);
+}
+
static LIST_HEAD(wacom_udev_list);
static DEFINE_MUTEX(wacom_udev_list_lock);
static bool wacom_are_sibling(struct hid_device *hdev,
- struct hid_device *sibling)
+ struct wacom_hdev_data *data)
{
struct wacom *wacom = hid_get_drvdata(hdev);
struct wacom_features *features = &wacom->wacom_wac.features;
- struct wacom *sibling_wacom = hid_get_drvdata(sibling);
- struct wacom_features *sibling_features = &sibling_wacom->wacom_wac.features;
__u32 oVid = features->oVid ? features->oVid : hdev->vendor;
__u32 oPid = features->oPid ? features->oPid : hdev->product;
/* The defined oVid/oPid must match that of the sibling */
- if (features->oVid != HID_ANY_ID && sibling->vendor != oVid)
+ if (features->oVid != HID_ANY_ID && data->vendor != oVid)
return false;
- if (features->oPid != HID_ANY_ID && sibling->product != oPid)
+ if (features->oPid != HID_ANY_ID && data->product != oPid)
return false;
/*
@@ -785,11 +805,11 @@ static bool wacom_are_sibling(struct hid_device *hdev,
* device path, while those with different VID/PID must share
* the same physical parent device path.
*/
- if (hdev->vendor == sibling->vendor && hdev->product == sibling->product) {
- if (!hid_compare_device_paths(hdev, sibling, '/'))
+ if (hdev->vendor == data->vendor && hdev->product == data->product) {
+ if (!wacom_compare_device_paths(hdev, data->phys, '/'))
return false;
} else {
- if (!hid_compare_device_paths(hdev, sibling, '.'))
+ if (!wacom_compare_device_paths(hdev, data->phys, '.'))
return false;
}
@@ -802,7 +822,7 @@ static bool wacom_are_sibling(struct hid_device *hdev,
* devices.
*/
if ((features->device_type & WACOM_DEVICETYPE_DIRECT) &&
- !(sibling_features->device_type & WACOM_DEVICETYPE_DIRECT))
+ !(data->device_type & WACOM_DEVICETYPE_DIRECT))
return false;
/*
@@ -810,17 +830,17 @@ static bool wacom_are_sibling(struct hid_device *hdev,
* devices.
*/
if (!(features->device_type & WACOM_DEVICETYPE_DIRECT) &&
- (sibling_features->device_type & WACOM_DEVICETYPE_DIRECT))
+ (data->device_type & WACOM_DEVICETYPE_DIRECT))
return false;
/* Pen devices may only be siblings of touch devices */
if ((features->device_type & WACOM_DEVICETYPE_PEN) &&
- !(sibling_features->device_type & WACOM_DEVICETYPE_TOUCH))
+ !(data->device_type & WACOM_DEVICETYPE_TOUCH))
return false;
/* Touch devices may only be siblings of pen devices */
if ((features->device_type & WACOM_DEVICETYPE_TOUCH) &&
- !(sibling_features->device_type & WACOM_DEVICETYPE_PEN))
+ !(data->device_type & WACOM_DEVICETYPE_PEN))
return false;
/*
@@ -836,7 +856,7 @@ static struct wacom_hdev_data *wacom_get_hdev_data(struct hid_device *hdev)
/* Try to find an already-probed interface from the same device */
list_for_each_entry(data, &wacom_udev_list, list) {
- if (hid_compare_device_paths(hdev, data->dev, '/')) {
+ if (wacom_compare_device_paths(hdev, data->phys, '/')) {
kref_get(&data->kref);
return data;
}
@@ -844,7 +864,7 @@ static struct wacom_hdev_data *wacom_get_hdev_data(struct hid_device *hdev)
/* Fallback to finding devices that appear to be "siblings" */
list_for_each_entry(data, &wacom_udev_list, list) {
- if (wacom_are_sibling(hdev, data->dev)) {
+ if (wacom_are_sibling(hdev, data)) {
kref_get(&data->kref);
return data;
}
@@ -858,18 +878,15 @@ static void wacom_release_shared_data(struct kref *kref)
struct wacom_hdev_data *data =
container_of(kref, struct wacom_hdev_data, kref);
- mutex_lock(&wacom_udev_list_lock);
list_del(&data->list);
- mutex_unlock(&wacom_udev_list_lock);
-
kfree(data);
}
static void wacom_remove_shared_data(void *res)
{
- struct wacom *wacom = res;
+ struct wacom *res_wacom = res;
struct wacom_hdev_data *data;
- struct wacom_wac *wacom_wac = &wacom->wacom_wac;
+ struct wacom_wac *wacom_wac = &res_wacom->wacom_wac;
if (wacom_wac->shared) {
data = container_of(wacom_wac->shared, struct wacom_hdev_data,
@@ -883,17 +900,19 @@ static void wacom_remove_shared_data(void *res)
rcu_dereference_protected(wacom_wac->shared->pen,
lockdep_is_held(&wacom_udev_list_lock));
- if (touch == wacom->hdev) {
+ if (touch == res_wacom->hdev) {
rcu_assign_pointer(wacom_wac->shared->touch, NULL);
rcu_assign_pointer(wacom_wac->shared->touch_input, NULL);
- } else if (pen == wacom->hdev) {
+ } else if (pen == res_wacom->hdev) {
rcu_assign_pointer(wacom_wac->shared->pen, NULL);
}
}
synchronize_rcu();
- kref_put(&data->kref, wacom_release_shared_data);
+ if (kref_put_mutex(&data->kref, wacom_release_shared_data, &wacom_udev_list_lock))
+ mutex_unlock(&wacom_udev_list_lock);
+
wacom_wac->shared = NULL;
}
}
@@ -916,7 +935,10 @@ static int wacom_add_shared_data(struct hid_device *hdev)
}
kref_init(&data->kref);
- data->dev = hdev;
+ strscpy(data->phys, hdev->phys, sizeof(data->phys));
+ data->vendor = hdev->vendor;
+ data->product = hdev->product;
+ data->device_type = wacom_wac->features.device_type;
list_add_tail(&data->list, &wacom_udev_list);
}
--
2.55.0.979.g7e5102b832-goog
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH v10 4/5] HID: wacom: Fix Use-After-Free in wacom_bamboo_pad
2026-09-09 11:12 ` [PATCH v10 4/5] HID: wacom: Fix Use-After-Free in wacom_bamboo_pad Lee Jones
@ 2026-09-09 11:31 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-09-09 11:31 UTC (permalink / raw)
To: Lee Jones; +Cc: dmitry.torokhov, linux-input
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [Critical] Lockless access to `shared->pen` and `shared->touch` in `wacom_mode_change_work()` leads to a Use-After-Free.
--
commit 128e0f492fb46bf1508b21d7d9f05ef9e57ed2be
Author: Lee Jones <lee@kernel.org>
HID: wacom: Fix Use-After-Free in wacom_bamboo_pad
This commit introduces RCU protection for the pen and touch pointers in the
wacom_shared structure to resolve a Use-After-Free during sibling device
removal. It updates lockless readers with rcu_read_lock() and uses
rcu_assign_pointer() in writers.
> diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
> index 10f7de515ea28..307eda9bbdd07 100644
> --- a/drivers/hid/wacom_sys.c
> +++ b/drivers/hid/wacom_sys.c
[ ... ]
> @@ -2852,16 +2861,19 @@ static void wacom_mode_change_work(struct work_struct *work)
> bool is_direct = wacom->wacom_wac.is_direct_mode;
> int error = 0;
>
> - if (shared->pen) {
> - wacom1 = hid_get_drvdata(shared->pen);
> + struct hid_device *pen = rcu_access_pointer(shared->pen);
> + struct hid_device *touch = rcu_access_pointer(shared->touch);
> +
> + if (pen) {
> + wacom1 = hid_get_drvdata(pen);
> wacom_release_resources(wacom1);
> hid_hw_stop(wacom1->hdev);
[Severity: Critical]
This is a pre-existing issue, but could this lockless access to shared->pen
and shared->touch lead to a Use-After-Free here?
The commit message explicitly notes that lockless access in this work item
remains a pre-existing issue. The removal path nullifies the shared pointer
and calls synchronize_rcu(). However, wacom_mode_change_work() accesses the
pointers using rcu_access_pointer() without establishing an RCU read-side
critical section using rcu_read_lock().
Because there is no active RCU reader, synchronize_rcu() does not wait for
this work item to finish. If a device report schedules this work, and the
sibling device is concurrently unplugged, won't the sibling device's
resources be freed while wacom_mode_change_work() is actively dereferencing
the stale pointer via hid_get_drvdata(pen) and hid_hw_stop()?
--
Sashiko AI review · https://sashiko.dev/#/patchset/fa3cb42f7b58c1b3022d8208969a81882783a5ec.1788868602.git.lee@kernel.org?part=4
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v10 1/5] HID: wacom: Use hdev->product in wacom_setup_touch_input_capabilities
2026-09-09 11:12 [PATCH v10 1/5] HID: wacom: Use hdev->product in wacom_setup_touch_input_capabilities Lee Jones
` (3 preceding siblings ...)
2026-09-09 11:12 ` [PATCH v10 5/5] HID: wacom: Redesign shared sibling data lifecycle Lee Jones
@ 2026-09-09 11:31 ` sashiko-bot
2026-09-10 18:53 ` Ping Cheng
` (2 subsequent siblings)
7 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-09-09 11:31 UTC (permalink / raw)
To: Lee Jones; +Cc: dmitry.torokhov, linux-input
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
Pre-existing issues:
- [Critical] Missing cancellation of asynchronous works and timers in the probe error path leads to use-after-free.
--
commit ca8b53c76679ad840d06b68ac883d22ba58fe317
Author: Lee Jones <lee@kernel.org>
Subject: HID: wacom: Use hdev->product in wacom_setup_touch_input_capabilities
This commit replaces lookup-dependent touch product references with
hdev->product inside wacom_setup_touch_input_capabilities(). This
simplifies the code since hdev is already available via container_of
and represents the touch device itself.
> diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
> index 8feb8027be95e..7cf2b4de52be7 100644
> --- a/drivers/hid/wacom_wac.c
> +++ b/drivers/hid/wacom_wac.c
[ ... ]
While reviewing the wacom driver lifecycle, I noticed a potential issue in
a related file during the probe error path:
drivers/hid/wacom_sys.c:wacom_parse_and_register() {
...
if (features->device_type & WACOM_DEVICETYPE_WL_MONITOR) {
error = hid_hw_open(hdev);
if (error) {
hid_err(hdev, "hw open failed\n");
goto fail_hw_stop;
}
}
...
fail_hw_stop:
hid_hw_stop(hdev);
fail:
wacom_release_resources(wacom);
return error;
}
[Severity: Critical]
This is a pre-existing issue, but does this error path leave asynchronous
works and timers running after freeing the wacom structure?
The driver allocates the wacom structure using devm_kzalloc(). If an
initialization step fails after hid_hw_start() is called, such as
hid_hw_open() failing, the code jumps to the fail_hw_stop label.
While hid_hw_stop() is called, it appears to omit cancelling other
asynchronous resources like wireless_work, battery_work, remote_work,
mode_change_work, aes_battery_work, and idleprox_timer.
Since hid_hw_start() enables hardware interrupts, an incoming interrupt can
trigger wacom_raw_event() -> wacom_wac_irq() -> wacom_schedule_work(),
which queues these works.
If the probe function returns an error, devres will automatically free the
wacom structure. Could an untrusted device triggering rapid reports cause
these pending works to execute later and dereference the freed memory?
--
Sashiko AI review · https://sashiko.dev/#/patchset/fa3cb42f7b58c1b3022d8208969a81882783a5ec.1788868602.git.lee@kernel.org?part=1
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v10 3/5] HID: wacom: Fix Use-After-Free in wacom_intuos_pad
2026-09-09 11:12 ` [PATCH v10 3/5] HID: wacom: Fix Use-After-Free in wacom_intuos_pad Lee Jones
@ 2026-09-09 11:32 ` sashiko-bot
0 siblings, 0 replies; 11+ messages in thread
From: sashiko-bot @ 2026-09-09 11:32 UTC (permalink / raw)
To: Lee Jones; +Cc: linux-input, dmitry.torokhov
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
Pre-existing issues:
- [High] Lockless access to `shared->pen` and `shared->touch` leaves sibling `hid_device` pointers vulnerable to Use-After-Free during concurrent hot-unplug.
- [High] Sysfs callbacks access `wacom->led.groups` without NULL checks, racing with `wacom_remove()` cleanup.
--
commit f9df2fb1ac73b67929633c2e25c6e475a7abd240
Author: Lee Jones <lee@kernel.org>
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.
> diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
> index 4eed2c189017d..10f7de515ea28 100644
> --- a/drivers/hid/wacom_sys.c
> +++ b/drivers/hid/wacom_sys.c
> @@ -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;
[Severity: High]
This is a pre-existing issue, but since this patch introduces RCU protection
for touch_input, is it possible that lockless access to shared->pen and
shared->touch in wacom_mode_change_work() is still vulnerable to a
Use-After-Free during concurrent hot-unplug?
When a sibling interface is unbound, devres automatically frees its driver
data. If a WACOM_HID_WD_MODE_CHANGE event concurrently schedules
wacom_mode_change_work(), it accesses these pointers locklessly:
drivers/hid/wacom_sys.c:wacom_mode_change_work() {
...
if (shared->pen) {
wacom1 = hid_get_drvdata(shared->pen);
wacom_release_resources(wacom1);
hid_hw_stop(wacom1->hdev);
...
if (shared->touch) {
wacom2 = hid_get_drvdata(shared->touch);
wacom_release_resources(wacom2);
hid_hw_stop(wacom2->hdev);
...
}
While Patch 5/5 in this series attempts to fix this by replacing the raw
accesses with rcu_access_pointer(), it appears to omit wrapping the worker
in an RCU read-side critical section and doesn't use rcu_dereference().
Because of this, would the synchronize_rcu() call in the unbind path fail to
wait for this worker, leaving the Use-After-Free vulnerability unresolved
at the end of the series?
[ ... ]
[Severity: High]
This is a pre-existing issue, but could wacom_show_remote_mode() race with
wacom_remove() and cause a NULL pointer dereference?
During unbind, wacom_remove() explicitly nullifies the groups pointer before
devres removes the sysfs attributes:
drivers/hid/wacom_sys.c:wacom_remove() {
...
/* make sure we don't trigger the LEDs */
wacom_led_groups_release(wacom);
if (wacom->wacom_wac.features.type != REMOTE)
wacom_release_resources(wacom);
}
If an unprivileged local user continuously reads the world-readable sysfs
files during a hot-unplug, the sysfs callback dereferences wacom->led.groups
without a NULL check:
drivers/hid/wacom_sys.c:wacom_show_remote_mode() {
...
mode = wacom->led.groups[index].select;
return sprintf(buf, "%d\n", mode < 3 ? mode : -1);
}
Could this crash the kernel if the sysfs read callback executes after
wacom_led_groups_release() clears the pointer, but before devres cleanup
finishes?
--
Sashiko AI review · https://sashiko.dev/#/patchset/fa3cb42f7b58c1b3022d8208969a81882783a5ec.1788868602.git.lee@kernel.org?part=3
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v10 1/5] HID: wacom: Use hdev->product in wacom_setup_touch_input_capabilities
2026-09-09 11:12 [PATCH v10 1/5] HID: wacom: Use hdev->product in wacom_setup_touch_input_capabilities Lee Jones
` (4 preceding siblings ...)
2026-09-09 11:31 ` [PATCH v10 1/5] HID: wacom: Use hdev->product in wacom_setup_touch_input_capabilities sashiko-bot
@ 2026-09-10 18:53 ` Ping Cheng
2026-09-11 13:47 ` Jiri Kosina
2026-09-11 14:04 ` Jiri Kosina
7 siblings, 0 replies; 11+ messages in thread
From: Ping Cheng @ 2026-09-10 18:53 UTC (permalink / raw)
To: Lee Jones
Cc: Ping Cheng, Jason Gerecke, Jiri Kosina, Benjamin Tissoires,
Aaron Skomra, Dmitry Torokhov, Peter Hutterer, linux-input,
linux-kernel, stable
Tested the whole set of v10. Status stays the same:
Acked-by: Ping Cheng <ping.cheng@wacom.com>
Tested-by: Ping Cheng <ping.cheng@wacom.com>
Cheers,
Ping
On Wed, Sep 9, 2026 at 4:27 AM Lee Jones <lee@kernel.org> wrote:
>
> Replace the lookup-dependent 'wacom_wac->shared->touch->product' references
> with 'hdev->product' inside wacom_setup_touch_input_capabilities() since
> 'hdev' is already available (via container_of) and represents the touch
> device itself.
>
> Cc: stable@vger.kernel.org
> Signed-off-by: Lee Jones <lee@kernel.org>
> ---
> v7 -> v8: New patch
> v8 -> v9: No change
> v9 -> v10: No change
>
> drivers/hid/wacom_wac.c | 18 +++++++++---------
> 1 file changed, 9 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c
> index 8feb8027be95..7cf2b4de52be 100644
> --- a/drivers/hid/wacom_wac.c
> +++ b/drivers/hid/wacom_wac.c
> @@ -3966,8 +3966,8 @@ int wacom_setup_pen_input_capabilities(struct input_dev *input_dev,
> int wacom_setup_touch_input_capabilities(struct input_dev *input_dev,
> struct wacom_wac *wacom_wac)
> {
> + struct hid_device *hdev = container_of(wacom_wac, struct wacom, wacom_wac)->hdev;
> struct wacom_features *features = &wacom_wac->features;
> -
> if (!(features->device_type & WACOM_DEVICETYPE_TOUCH))
> return -ENODEV;
>
> @@ -3976,9 +3976,11 @@ int wacom_setup_touch_input_capabilities(struct input_dev *input_dev,
> else
> __set_bit(INPUT_PROP_POINTER, input_dev->propbit);
>
> - if (features->type == HID_GENERIC)
> + if (features->type == HID_GENERIC) {
> + hid_dbg(hdev, "generic touch setup\n");
> /* setup has already been done */
> return 0;
> + }
>
> input_dev->evbit[0] |= BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
> __set_bit(BTN_TOUCH, input_dev->keybit);
> @@ -4010,19 +4012,17 @@ int wacom_setup_touch_input_capabilities(struct input_dev *input_dev,
> input_dev->evbit[0] |= BIT_MASK(EV_SW);
> __set_bit(SW_MUTE_DEVICE, input_dev->swbit);
>
> - if (wacom_wac->shared->touch->product == 0x361) {
> + if (hdev->product == 0x361) {
> input_set_abs_params(input_dev, ABS_MT_POSITION_X,
> 0, 12440, 4, 0);
> input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
> 0, 8640, 4, 0);
> - }
> - else if (wacom_wac->shared->touch->product == 0x360) {
> + } else if (hdev->product == 0x360) {
> input_set_abs_params(input_dev, ABS_MT_POSITION_X,
> 0, 8960, 4, 0);
> input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
> 0, 5920, 4, 0);
> - }
> - else if (wacom_wac->shared->touch->product == 0x393) {
> + } else if (hdev->product == 0x393) {
> input_set_abs_params(input_dev, ABS_MT_POSITION_X,
> 0, 6400, 4, 0);
> input_set_abs_params(input_dev, ABS_MT_POSITION_Y,
> @@ -4052,8 +4052,8 @@ int wacom_setup_touch_input_capabilities(struct input_dev *input_dev,
> fallthrough;
>
> case WACOM_27QHDT:
> - if (wacom_wac->shared->touch->product == 0x32C ||
> - wacom_wac->shared->touch->product == 0xF6) {
> + if (hdev->product == 0x32C ||
> + hdev->product == 0xF6) {
> input_dev->evbit[0] |= BIT_MASK(EV_SW);
> __set_bit(SW_MUTE_DEVICE, input_dev->swbit);
> wacom_wac->has_mute_touch_switch = true;
> --
> 2.55.0.979.g7e5102b832-goog
>
>
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v10 1/5] HID: wacom: Use hdev->product in wacom_setup_touch_input_capabilities
2026-09-09 11:12 [PATCH v10 1/5] HID: wacom: Use hdev->product in wacom_setup_touch_input_capabilities Lee Jones
` (5 preceding siblings ...)
2026-09-10 18:53 ` Ping Cheng
@ 2026-09-11 13:47 ` Jiri Kosina
2026-09-11 14:04 ` Jiri Kosina
7 siblings, 0 replies; 11+ messages in thread
From: Jiri Kosina @ 2026-09-11 13:47 UTC (permalink / raw)
To: Lee Jones
Cc: Ping Cheng, Jason Gerecke, Benjamin Tissoires, Aaron Skomra,
Dmitry Torokhov, Peter Hutterer, linux-input, linux-kernel,
stable
On Wed, 9 Sep 2026, Lee Jones wrote:
> Replace the lookup-dependent 'wacom_wac->shared->touch->product' references
> with 'hdev->product' inside wacom_setup_touch_input_capabilities() since
> 'hdev' is already available (via container_of) and represents the touch
> device itself.
>
> Cc: stable@vger.kernel.org
> Signed-off-by: Lee Jones <lee@kernel.org>
Applied, thanks.
--
Jiri Kosina
SUSE Labs
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH v10 1/5] HID: wacom: Use hdev->product in wacom_setup_touch_input_capabilities
2026-09-09 11:12 [PATCH v10 1/5] HID: wacom: Use hdev->product in wacom_setup_touch_input_capabilities Lee Jones
` (6 preceding siblings ...)
2026-09-11 13:47 ` Jiri Kosina
@ 2026-09-11 14:04 ` Jiri Kosina
7 siblings, 0 replies; 11+ messages in thread
From: Jiri Kosina @ 2026-09-11 14:04 UTC (permalink / raw)
To: Lee Jones
Cc: Ping Cheng, Jason Gerecke, Benjamin Tissoires, Aaron Skomra,
Dmitry Torokhov, Peter Hutterer, linux-input, linux-kernel,
stable
On Wed, 9 Sep 2026, Lee Jones wrote:
> Replace the lookup-dependent 'wacom_wac->shared->touch->product' references
> with 'hdev->product' inside wacom_setup_touch_input_capabilities() since
> 'hdev' is already available (via container_of) and represents the touch
> device itself.
>
> Cc: stable@vger.kernel.org
> Signed-off-by: Lee Jones <lee@kernel.org>
Applied, thanks.
--
Jiri Kosina
SUSE Labs
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2026-09-11 14:04 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-09 11:12 [PATCH v10 1/5] HID: wacom: Use hdev->product in wacom_setup_touch_input_capabilities Lee Jones
2026-09-09 11:12 ` [PATCH v10 2/5] HID: wacom: Advertise SW_MUTE_DEVICE capability prior to registration Lee Jones
2026-09-09 11:12 ` [PATCH v10 3/5] HID: wacom: Fix Use-After-Free in wacom_intuos_pad Lee Jones
2026-09-09 11:32 ` sashiko-bot
2026-09-09 11:12 ` [PATCH v10 4/5] HID: wacom: Fix Use-After-Free in wacom_bamboo_pad Lee Jones
2026-09-09 11:31 ` sashiko-bot
2026-09-09 11:12 ` [PATCH v10 5/5] HID: wacom: Redesign shared sibling data lifecycle Lee Jones
2026-09-09 11:31 ` [PATCH v10 1/5] HID: wacom: Use hdev->product in wacom_setup_touch_input_capabilities sashiko-bot
2026-09-10 18:53 ` Ping Cheng
2026-09-11 13:47 ` Jiri Kosina
2026-09-11 14:04 ` Jiri Kosina
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox