Thread (25 messages) 25 messages, 2 authors, 27d ago

Re: [PATCH v7 06/12] Input: xbox_gip - Allow sending fragmented and ACME messages

flat view

From: sashiko-bot@kernel.org
Date: 2026-09-12 03:23:03

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:

New issues:
- [Critical] Loop index typo in `gip_disconnect` causes OOB array access and failure to cancel delayed work.
- [High] Retrying reliable (ACME) messages corrupts their headers due to incorrect flag and length field encoding.
- [High] Teardown sequence race condition causing Use-After-Free of device structures due to URBs re-scheduling cancelled works.

Pre-existing issues:
- [High] AB-BA deadlock during device reset due to unregistering input device under lock.
- [High] Teardown ordering race condition causing UAF of rumble_work and attachment memory.
--

commit d64a449cbd12693ac78473a4f8297a24eb7916f4
Author: Vicki Pfau [off-list ref]
Subject: Input: xbox_gip - Allow sending fragmented and ACME messages

This patch adds support for sending fragmented and ACME (reliable) messages
in the xbox_gip driver. It sits on top of a reliable message transmission
flow for messages that exceed the MTU and handles the required sequence and
acknowledgment tracking.
quoted hunk ↗ jump to hunk
diff --git a/drivers/input/joystick/gip/gip-core.c b/drivers/input/joystick/gip/gip-core.c
--- a/drivers/input/joystick/gip/gip-core.c
+++ b/drivers/input/joystick/gip/gip-core.c
[ ... ]
quoted hunk ↗ jump to hunk
+static int gip_send_next_fragment(struct gip_attachment *attachment,
+	struct gip_out_fragment *fragment)
+{
+	struct gip_interface *intf;
+	int next_bytes;
+	int flags = GIP_FLAG_FRAGMENT | fragment->flags | attachment->attachment_index;
[Severity: High]
Does this unconditionally force GIP_FLAG_FRAGMENT onto all retries? If an 
unfragmented reliable message times out, retrying it with this flag will
cause gip_send_raw_message() to process it as a fragment. When processed as a
fragment, it encodes the fragment_offset (which is 0) instead of the
total_length, resulting in a corrupted header that the hardware may reject.
+	int mtu;
+	int rc;
[ ... ]
quoted hunk ↗ jump to hunk
@@ -3144,17 +3179,17 @@ static void gip_shutdown(struct gip_device *gip)
 {
[ ... ]
 	for (i = 0; i < MAX_ATTACHMENTS; i++) {
 		struct gip_attachment *attachment;
 
 		mutex_lock(&gip->attachment_lock);
 		attachment = gip->attachments[i];
 		mutex_unlock(&gip->attachment_lock);
 
 		if (!attachment)
 			continue;
 
 		cancel_delayed_work_sync(&attachment->metadata_next);
 		cancel_delayed_work_sync(&attachment->in_fragment_timeout);
 		cancel_work_sync(&attachment->rumble_work);
 
 		guard(mutex)(&attachment->lock);
 		gip_reset_attachment(attachment);
[Severity: High]
This is a pre-existing issue, but does this teardown sequence leave a race 
window open for rumble_work to be rescheduled? The rumble_work is cancelled
before gip_reset_attachment() unregisters the input device. A concurrent 
ioctl or ff-memless timer can call gip_play_effect() and reschedule the work 
while the device is still registered, leading to a Use-After-Free when the 
attachment memory is freed.

[Severity: High]
This isn't a bug introduced by this patch, but is there a risk of an AB-BA
deadlock here during device reset?

When a hardware reconnect triggers gip_receive_message() and eventually
gip_reset_attachment(), it acquires attachment->lock and calls
input_unregister_device() via gip_free_devices(). The unregistration process
synchronously removes sysfs groups, which blocks waiting for all active
kernfs references to drain.

If a local user concurrently writes to the LED sysfs brightness attribute,
gip_guide_led_set() acquires an active kernfs reference and then blocks
attempting to acquire attachment->lock, completing the deadlock.
quoted hunk ↗ jump to hunk
 		gip_reset_metadata(attachment);
 	}
 }
 
 static void gip_disconnect(struct usb_interface *intf)
 {
 	struct gip_device *gip = usb_get_intfdata(intf);
-	int i;
+	int i, j;
 
 	if (!gip)
 		return;
 
 	gip_shutdown(gip);
 
 	usb_kill_anchored_urbs(&gip->data.out_anchor);
[Severity: High]
Is this the correct teardown sequence? If gip_shutdown() cancels the
send_fragment work before usb_kill_anchored_urbs() is called, active URBs
might complete in the window between them. If an URB completes and triggers
gip_urb_out(), it will reschedule the send_fragment work. After
gip_disconnect() frees the device structures, the rescheduled work could
execute and access freed memory. Should usb_kill_anchored_urbs() be called 
before gip_shutdown()?

[ ... ]
quoted hunk ↗ jump to hunk
@@ -3206,8 +3217,8 @@ static void gip_disconnect(struct usb_interface *intf)
 		if (!attachment)
 			continue;
 
+		for (j = 0; j < MAX_OUT_FRAGMENTS; j++)
+			cancel_delayed_work_sync(&attachment->out_fragments[i].timeout);
[Severity: Critical]
Does this inner loop mistakenly use the outer index i instead of j?
Iterating up to MAX_OUT_FRAGMENTS using the outer attachment index i could
lead to an out-of-bounds access when i is larger than the fragment array 
bounds. Additionally, it fails to cancel the actual delayed work items at 
indexes j, which could lead to a Use-After-Free if they execute after the 
attachment is freed.
 		devm_kfree(to_gip_device(attachment), attachment->uniq);
 		devm_kfree(to_gip_device(attachment), attachment);
-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260912030426.2997003-1-vi@endrift.com?part=6
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help