Thread (22 messages) flat view 22 messages, 2 authors, 11d ago

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

From: sashiko-bot@kernel.org
Date: 2026-09-02 01:19:47

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [Critical] Unconditional dereference of NULL fragment pointer on error path for non-fragmented messages.
- [High] Missing synchronization when allocating and initializing `out_fragments` slots.
- [High] Unsynchronized read-modify-write of the `intf->has_pending_out` bitmask.
- [High] Dropped ACKs permanently leak `out_fragments` slots.
--

commit 317888ee9cccdc0f09f7f1c8d616d2664382777a
Author: Vicki Pfau [off-list ref]

Input: xbox_gip - Allow sending fragmented and ACME messages

This commit introduces support for sending fragmented messages that exceed the
MTU length and messages requiring ACKs using the ACME flag in the GIP protocol.
It adds queuing, processing, and acknowledgment handling logic to the xbox_gip
driver.
quoted hunk ↗ jump to hunk
diff --git a/drivers/input/joystick/gip/gip-core.c b/drivers/input/joystick/gip/gip-core.c
index 57d638408daf4..544becf8badd6 100644
--- a/drivers/input/joystick/gip/gip-core.c
+++ b/drivers/input/joystick/gip/gip-core.c
[ ... ]
+static void gip_free_fragment(struct gip_out_fragment *fragment)
+{
+	kfree(fragment->data);
+	memset(fragment, 0, sizeof(*fragment));
+}
[ ... ]
quoted hunk ↗ jump to hunk
 static int gip_send_raw_message(struct gip_attachment *attachment,
 	uint8_t message_type, uint8_t flags, uint8_t seq, const uint8_t *bytes,
 	int num_bytes)
@@ -486,20 +529,25 @@ static int gip_send_raw_message(struct gip_attachment *attachment,
 	struct gip_interface *intf;
 	int offset = 3;
 	struct gip_urb *urb = NULL;
+	struct gip_out_fragment *fragment = NULL;
 	int i;
 	int rc = 0;
[ ... ]
+	if (num_bytes > reduced_mtu ||
+		(flags & (GIP_FLAG_ACME | GIP_FLAG_FRAGMENT)) == GIP_FLAG_ACME) {
+		for (i = 0; i < MAX_OUT_FRAGMENTS; i++) {
+			if (attachment->out_fragments[i].active)
+				continue;
+
+			fragment = &attachment->out_fragments[i];
+			break;
+		}
[Severity: High]
Does this code have a missing synchronization issue?

The fragment slot is claimed and initialized locklessly here. If concurrent
callers (like force feedback workers, sysfs, or incoming message handlers)
pick the same inactive slot, could this lead to double-allocation (a memory
leak) and state corruption?
+
+		if (!fragment) {
+			gip_err(attachment, "Fragmented message queue is full; dropping message\n");
+			return -EALREADY;
+		}
+
+		fragment->data = kmalloc(num_bytes, GFP_KERNEL);
+		if (!fragment->data)
+			return -ENOMEM;
+		fragment->active = true;
[Severity: High]
Can these out_fragments slots permanently leak if ACKs are dropped?

When an outgoing message requires an ACK, the slot is marked active here and
relies entirely on a device-initiated ACK to be freed. Because there is no
timeout mechanism, if the device drops the message or fails to respond, could
the slot remain locked forever? Since MAX_OUT_FRAGMENTS is only 2, a couple
of dropped packets might permanently exhaust the queue.

[ ... ]
 	guard(spinlock_irqsave)(&attachment->device->message_lock);
 	for (i = 0; i < MAX_OUT_MESSAGES && !urb; i++) {
 		if (!intf->out_queue[i].urb)
[ ... ]
+	if (fragment) {
+		/*
+		 * Sending further fragments is handled in gip_urb_out if this fragment doesn't need
+		 * ACKing or in gip_handle_command_protocol_control if it does. The flag should also
+		 * be cleared if we're done sending this packet.
+		 */
+		if (fragment->total_length > reduced_mtu && !(flags & GIP_FLAG_ACME))
+			intf->has_pending_out |= gip_fragment_id(attachment, fragment);
+		else
+			intf->has_pending_out &= ~gip_fragment_id(attachment, fragment);
 	}
 
 	return rc;
+
+err_free_fragment:
+	gip_free_fragment(fragment);
+	return rc;
+}
[Severity: Critical]
Does this error path unconditionally dereference a NULL pointer?

If gip_send_raw_message() is called for a standard, non-fragmented message,
the fragment pointer remains NULL. If the output queue is full or
usb_submit_urb() fails, the function jumps to err_free_fragment and calls
gip_free_fragment().

Since gip_free_fragment() unconditionally accesses fragment->data and calls
memset(fragment, ...), won't this immediately crash the kernel when fragment
is NULL?

[ ... ]
+static int gip_send_next_fragment(struct gip_attachment *attachment,
+	struct gip_out_fragment *fragment)
+{
+	struct gip_interface *intf;
[ ... ]
+	if (fragment->fragment_offset == fragment->total_length) {
+		if ((fragment->message & GIP_DATA_CLASS_MASK) == GIP_DATA_CLASS_AUDIO)
+			intf = &attachment->device->audio;
+		else
+			intf = &attachment->device->data;
+
+		next_bytes = 0;
+		intf->has_pending_out &= ~gip_fragment_id(attachment, fragment);
[Severity: High]
Is there an unsynchronized read-modify-write data race here?

Here in gip_send_next_fragment(), intf->has_pending_out is updated without
holding message_lock. However, as seen in gip_send_raw_message(), this shared
bitmask is also modified under message_lock.

Could concurrent updates to this shared variable cause lost updates,
permanently stalling fragment transmission for any attachments whose bits get
lost?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260902010237.865772-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