All of lore.kernel.org
 help / color / mirror / Atom feed
* [STABLE v5.15 0/2] HID: wacom: Fix Use-After-Free vulnerabilities in Wacom pad handling
@ 2026-11-09 13:38 Lee Jones
  2026-11-09 13:38 ` [STABLE v5.15 1/2] HID: wacom: Fix Use-After-Free in wacom_intuos_pad Lee Jones
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Lee Jones @ 2026-11-09 13:38 UTC (permalink / raw)
  To: lee
  Cc: stable, Benjamin Tissoires, Dmitry Torokhov, Jiri Kosina,
	Peter Hutterer, Ping Cheng, linux-input

Backport the remaining two Wacom Use-After-Free fixes to v5.15.y, on top
of the five Wacom sibling-lifecycle and SW_MUTE_DEVICE patches already
queued in stable-queue (queue-5.15).

Both patches have been adapted to the queued res_wacom variable naming
in wacom_remove_shared_data() and use explicit locking calls for
-std=gnu89 compatibility.

Cc: Benjamin Tissoires <benjamin.tissoires@redhat.com>
Cc: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Cc: Jiri Kosina <jikos@kernel.org>
Cc: Peter Hutterer <peter.hutterer@who-t.net>
Cc: Ping Cheng <ping.cheng@wacom.com>
Cc: linux-input@vger.kernel.org

Lee Jones (2):
  HID: wacom: Fix Use-After-Free in wacom_intuos_pad
  HID: wacom: Fix Use-After-Free in wacom_bamboo_pad

 drivers/hid/wacom_sys.c | 50 ++++++++++++++++++++++++++++-------------
 drivers/hid/wacom_wac.c | 50 ++++++++++++++++++++---------------------
 drivers/hid/wacom_wac.h |  6 ++---
 3 files changed, 63 insertions(+), 43 deletions(-)

-- 
2.56.0.506.g506a7eabc5-goog

^ permalink raw reply	[flat|nested] 4+ messages in thread

* [STABLE v5.15 1/2] HID: wacom: Fix Use-After-Free in wacom_intuos_pad
  2026-11-09 13:38 [STABLE v5.15 0/2] HID: wacom: Fix Use-After-Free vulnerabilities in Wacom pad handling Lee Jones
@ 2026-11-09 13:38 ` Lee Jones
  2026-11-09 13:38 ` [STABLE v5.15 2/2] HID: wacom: Fix Use-After-Free in wacom_bamboo_pad Lee Jones
  2026-11-10 20:59 ` [STABLE v5.15 0/2] HID: wacom: Fix Use-After-Free vulnerabilities in Wacom pad handling Sasha Levin
  2 siblings, 0 replies; 4+ messages in thread
From: Lee Jones @ 2026-11-09 13:38 UTC (permalink / raw)
  To: lee, Jiri Kosina, Benjamin Tissoires, Ping Cheng, Peter Hutterer,
	Dmitry Torokhov, linux-input, linux-kernel
  Cc: stable, Jiri Kosina

[ Upstream commit ef32877368a7013c060ef6bd03a30b6da2fe0c41 ]

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>
Acked-by: Ping Cheng <ping.cheng@wacom.com>
Tested-by: Ping Cheng <ping.cheng@wacom.com>
Signed-off-by: Jiri Kosina <jkosina@suse.com>
[Lee: Use explicit mutex_lock()/mutex_unlock() in wacom_remove_shared_data()
      for -std=gnu89 compatibility and adapt to res_wacom]
Signed-off-by: Lee Jones <lee@kernel.org>
---
 drivers/hid/wacom_sys.c | 18 +++++++++++++-----
 drivers/hid/wacom_wac.c | 36 ++++++++++++++++++------------------
 drivers/hid/wacom_wac.h |  2 +-
 3 files changed, 32 insertions(+), 24 deletions(-)

diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index b6393a806574..149d3546367f 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -885,10 +885,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 == res_wacom->hdev)
+		mutex_lock(&wacom_udev_list_lock);
+		if (wacom_wac->shared->touch == res_wacom->hdev) {
 			wacom_wac->shared->touch = NULL;
-		else if (wacom_wac->shared->pen == res_wacom->hdev)
+			rcu_assign_pointer(wacom_wac->shared->touch_input, NULL);
+		} else if (wacom_wac->shared->pen == res_wacom->hdev) {
 			wacom_wac->shared->pen = NULL;
+		}
+		mutex_unlock(&wacom_udev_list_lock);
+
+		synchronize_rcu();
 
 		if (kref_put_mutex(&data->kref, wacom_release_shared_data, &wacom_udev_list_lock))
 			mutex_unlock(&wacom_udev_list_lock);
@@ -2350,9 +2356,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 78d7fda68f20..6549439f3203 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);
@@ -2192,7 +2200,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)
@@ -2200,9 +2208,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;
 	}
@@ -3324,11 +3330,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]);
@@ -3363,11 +3366,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 0491b19430cd..4f9ef7b21de3 100644
--- a/drivers/hid/wacom_wac.h
+++ b/drivers/hid/wacom_wac.h
@@ -286,7 +286,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.56.0.506.g506a7eabc5-goog


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* [STABLE v5.15 2/2] HID: wacom: Fix Use-After-Free in wacom_bamboo_pad
  2026-11-09 13:38 [STABLE v5.15 0/2] HID: wacom: Fix Use-After-Free vulnerabilities in Wacom pad handling Lee Jones
  2026-11-09 13:38 ` [STABLE v5.15 1/2] HID: wacom: Fix Use-After-Free in wacom_intuos_pad Lee Jones
@ 2026-11-09 13:38 ` Lee Jones
  2026-11-10 20:59 ` [STABLE v5.15 0/2] HID: wacom: Fix Use-After-Free vulnerabilities in Wacom pad handling Sasha Levin
  2 siblings, 0 replies; 4+ messages in thread
From: Lee Jones @ 2026-11-09 13:38 UTC (permalink / raw)
  To: lee, Jiri Kosina, Benjamin Tissoires, Ping Cheng, linux-input,
	linux-kernel
  Cc: stable, Jiri Kosina

[ Upstream commit 3a523ecf9ac3fdd33f5eb31c43937ee44135395b ]

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>
Acked-by: Ping Cheng <ping.cheng@wacom.com>
Tested-by: Ping Cheng <ping.cheng@wacom.com>
Signed-off-by: Jiri Kosina <jkosina@suse.com>
[Lee: Use explicit lock/unlock calls and top-of-block declarations in
      wacom_remove_shared_data() and wacom_bamboo_pad_pen_event() for
      -std=gnu89 compatibility, and adapt to res_wacom]
Signed-off-by: Lee Jones <lee@kernel.org>
---
 drivers/hid/wacom_sys.c | 40 ++++++++++++++++++++++++++--------------
 drivers/hid/wacom_wac.c | 14 +++++++-------
 drivers/hid/wacom_wac.h |  4 ++--
 3 files changed, 35 insertions(+), 23 deletions(-)

diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c
index 149d3546367f..39c126bb3f2d 100644
--- a/drivers/hid/wacom_sys.c
+++ b/drivers/hid/wacom_sys.c
@@ -882,15 +882,22 @@ static void wacom_remove_shared_data(void *res)
 	struct wacom_wac *wacom_wac = &res_wacom->wacom_wac;
 
 	if (wacom_wac->shared) {
+		struct hid_device *touch, *pen;
+
 		data = container_of(wacom_wac->shared, struct wacom_hdev_data,
 				    shared);
 
 		mutex_lock(&wacom_udev_list_lock);
-		if (wacom_wac->shared->touch == res_wacom->hdev) {
-			wacom_wac->shared->touch = NULL;
+		touch = rcu_dereference_protected(wacom_wac->shared->touch,
+						  lockdep_is_held(&wacom_udev_list_lock));
+		pen = rcu_dereference_protected(wacom_wac->shared->pen,
+						lockdep_is_held(&wacom_udev_list_lock));
+
+		if (touch == res_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 == res_wacom->hdev) {
-			wacom_wac->shared->pen = NULL;
+		} else if (pen == res_wacom->hdev) {
+			rcu_assign_pointer(wacom_wac->shared->pen, NULL);
 		}
 		mutex_unlock(&wacom_udev_list_lock);
 
@@ -2356,14 +2363,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);
 	}
 }
 
@@ -2379,8 +2389,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;
 	}
 
@@ -2850,16 +2859,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 6549439f3203..965f9fdc140a 100644
--- a/drivers/hid/wacom_wac.c
+++ b/drivers/hid/wacom_wac.c
@@ -3235,6 +3235,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;
 
 	/*
@@ -3247,13 +3248,12 @@ 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);
+	rcu_read_lock();
+	pen = rcu_dereference(wacom->shared->pen);
+	if (pen)
+		hid_input_report(pen, HID_INPUT_REPORT, data,
+				 WACOM_PKGLEN_PENABLED, 1);
+	rcu_read_unlock();
 
 	data[0] = prefix;
 }
diff --git a/drivers/hid/wacom_wac.h b/drivers/hid/wacom_wac.h
index 4f9ef7b21de3..4d75accb48b4 100644
--- a/drivers/hid/wacom_wac.h
+++ b/drivers/hid/wacom_wac.h
@@ -287,8 +287,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.56.0.506.g506a7eabc5-goog


^ permalink raw reply related	[flat|nested] 4+ messages in thread

* Re: [STABLE v5.15 0/2] HID: wacom: Fix Use-After-Free vulnerabilities in Wacom pad handling
  2026-11-09 13:38 [STABLE v5.15 0/2] HID: wacom: Fix Use-After-Free vulnerabilities in Wacom pad handling Lee Jones
  2026-11-09 13:38 ` [STABLE v5.15 1/2] HID: wacom: Fix Use-After-Free in wacom_intuos_pad Lee Jones
  2026-11-09 13:38 ` [STABLE v5.15 2/2] HID: wacom: Fix Use-After-Free in wacom_bamboo_pad Lee Jones
@ 2026-11-10 20:59 ` Sasha Levin
  2 siblings, 0 replies; 4+ messages in thread
From: Sasha Levin @ 2026-11-10 20:59 UTC (permalink / raw)
  To: lee
  Cc: Sasha Levin, stable, Benjamin Tissoires, Dmitry Torokhov,
	Jiri Kosina, Peter Hutterer, Ping Cheng, linux-input

> HID: wacom: Fix Use-After-Free vulnerabilities in Wacom pad handling

Queued the series for 5.15, thanks.

-- 
Thanks,
Sasha

^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-11-10 20:59 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-09 13:38 [STABLE v5.15 0/2] HID: wacom: Fix Use-After-Free vulnerabilities in Wacom pad handling Lee Jones
2026-11-09 13:38 ` [STABLE v5.15 1/2] HID: wacom: Fix Use-After-Free in wacom_intuos_pad Lee Jones
2026-11-09 13:38 ` [STABLE v5.15 2/2] HID: wacom: Fix Use-After-Free in wacom_bamboo_pad Lee Jones
2026-11-10 20:59 ` [STABLE v5.15 0/2] HID: wacom: Fix Use-After-Free vulnerabilities in Wacom pad handling Sasha Levin

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.