]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
6.18-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 27 Jul 2026 13:34:59 +0000 (15:34 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 27 Jul 2026 13:34:59 +0000 (15:34 +0200)
added patches:
usb-chipidea-fix-usage_count-leak-when-autosuspend_delay-is-negative.patch
usb-core-port-deattach-type-c-connector-on-component-unbind.patch
usb-core-sysfs-add-lock-to-bos_descriptors_read.patch
usb-gadget-dummy_hcd-prevent-fifo_req-reuse-during-giveback.patch
usb-gadget-f_midi-cancel-pending-in-work-before-freeing-the-midi-object.patch
usb-gadget-f_ncm-validate-datagram-bounds-in-ncm_unwrap_ntb.patch
usb-gadget-fsl-udc-fix-dev_printk-device.patch
usb-gadget-fsl-udc-fix-device-name-leak-on-probe-failure.patch
usb-gadget-printer-fix-infinite-loop-in-printer_read.patch
usb-gadget-snps-udc-fix-device-name-leak-on-probe-failure.patch
usb-gadget-udc-bdc-free-irq-and-drain-func_wake_notify-before-teardown.patch
usb-gadget-uvc-clamp-send_response-length-to-the-response-buffer.patch
usb-musb-omap2430-do-not-put-borrowed-of_node-in-probe.patch
usb-serial-ftdi_sio-add-support-for-e-h-fxa291.patch
usb-serial-io_edgeport-cap-received-transmit-credits.patch
usb-serial-keyspan_pda-fix-data-loss-on-receive-throttling.patch
usb-serial-option-add-tdtech-mt5710-cn.patch
usb-storage-add-no_ata_1x-quirk-for-longmai-usb-key.patch
usb-typec-ucsi-add-duplicate-detection-to-nvidia-registration-path.patch
usb-typec-ucsi-detect-and-skip-duplicate-altmodes-from-buggy-firmware.patch
wifi-at76c50x-usb-avoid-length-underflow-in-at76_guess_freq.patch

22 files changed:
queue-6.18/series
queue-6.18/usb-chipidea-fix-usage_count-leak-when-autosuspend_delay-is-negative.patch [new file with mode: 0644]
queue-6.18/usb-core-port-deattach-type-c-connector-on-component-unbind.patch [new file with mode: 0644]
queue-6.18/usb-core-sysfs-add-lock-to-bos_descriptors_read.patch [new file with mode: 0644]
queue-6.18/usb-gadget-dummy_hcd-prevent-fifo_req-reuse-during-giveback.patch [new file with mode: 0644]
queue-6.18/usb-gadget-f_midi-cancel-pending-in-work-before-freeing-the-midi-object.patch [new file with mode: 0644]
queue-6.18/usb-gadget-f_ncm-validate-datagram-bounds-in-ncm_unwrap_ntb.patch [new file with mode: 0644]
queue-6.18/usb-gadget-fsl-udc-fix-dev_printk-device.patch [new file with mode: 0644]
queue-6.18/usb-gadget-fsl-udc-fix-device-name-leak-on-probe-failure.patch [new file with mode: 0644]
queue-6.18/usb-gadget-printer-fix-infinite-loop-in-printer_read.patch [new file with mode: 0644]
queue-6.18/usb-gadget-snps-udc-fix-device-name-leak-on-probe-failure.patch [new file with mode: 0644]
queue-6.18/usb-gadget-udc-bdc-free-irq-and-drain-func_wake_notify-before-teardown.patch [new file with mode: 0644]
queue-6.18/usb-gadget-uvc-clamp-send_response-length-to-the-response-buffer.patch [new file with mode: 0644]
queue-6.18/usb-musb-omap2430-do-not-put-borrowed-of_node-in-probe.patch [new file with mode: 0644]
queue-6.18/usb-serial-ftdi_sio-add-support-for-e-h-fxa291.patch [new file with mode: 0644]
queue-6.18/usb-serial-io_edgeport-cap-received-transmit-credits.patch [new file with mode: 0644]
queue-6.18/usb-serial-keyspan_pda-fix-data-loss-on-receive-throttling.patch [new file with mode: 0644]
queue-6.18/usb-serial-option-add-tdtech-mt5710-cn.patch [new file with mode: 0644]
queue-6.18/usb-storage-add-no_ata_1x-quirk-for-longmai-usb-key.patch [new file with mode: 0644]
queue-6.18/usb-typec-ucsi-add-duplicate-detection-to-nvidia-registration-path.patch [new file with mode: 0644]
queue-6.18/usb-typec-ucsi-detect-and-skip-duplicate-altmodes-from-buggy-firmware.patch [new file with mode: 0644]
queue-6.18/wifi-at76c50x-usb-avoid-length-underflow-in-at76_guess_freq.patch [new file with mode: 0644]

index 5bd94d2e631ed07f4bcb18c772ec9aa1dabcb2ea..bcdded23bd05e7240fbb8771049577374be11975 100644 (file)
@@ -142,3 +142,24 @@ dpll-fix-null-pointer-dereference-in-dpll_msg_add_pi.patch
 net-sched-act_tunnel_key-defer-dst_release-to-rcu-ca.patch
 sctp-fix-auth_hmacs-array-size-in-struct-sctp_cookie.patch
 mpls-fix-null-deref-in-mpls_valid_fib_dump_req-on-co.patch
+usb-core-sysfs-add-lock-to-bos_descriptors_read.patch
+wifi-at76c50x-usb-avoid-length-underflow-in-at76_guess_freq.patch
+usb-core-port-deattach-type-c-connector-on-component-unbind.patch
+usb-musb-omap2430-do-not-put-borrowed-of_node-in-probe.patch
+usb-storage-add-no_ata_1x-quirk-for-longmai-usb-key.patch
+usb-chipidea-fix-usage_count-leak-when-autosuspend_delay-is-negative.patch
+usb-gadget-dummy_hcd-prevent-fifo_req-reuse-during-giveback.patch
+usb-gadget-f_midi-cancel-pending-in-work-before-freeing-the-midi-object.patch
+usb-gadget-printer-fix-infinite-loop-in-printer_read.patch
+usb-gadget-snps-udc-fix-device-name-leak-on-probe-failure.patch
+usb-gadget-fsl-udc-fix-device-name-leak-on-probe-failure.patch
+usb-gadget-fsl-udc-fix-dev_printk-device.patch
+usb-gadget-f_ncm-validate-datagram-bounds-in-ncm_unwrap_ntb.patch
+usb-gadget-udc-bdc-free-irq-and-drain-func_wake_notify-before-teardown.patch
+usb-gadget-uvc-clamp-send_response-length-to-the-response-buffer.patch
+usb-serial-ftdi_sio-add-support-for-e-h-fxa291.patch
+usb-serial-io_edgeport-cap-received-transmit-credits.patch
+usb-serial-keyspan_pda-fix-data-loss-on-receive-throttling.patch
+usb-serial-option-add-tdtech-mt5710-cn.patch
+usb-typec-ucsi-detect-and-skip-duplicate-altmodes-from-buggy-firmware.patch
+usb-typec-ucsi-add-duplicate-detection-to-nvidia-registration-path.patch
diff --git a/queue-6.18/usb-chipidea-fix-usage_count-leak-when-autosuspend_delay-is-negative.patch b/queue-6.18/usb-chipidea-fix-usage_count-leak-when-autosuspend_delay-is-negative.patch
new file mode 100644 (file)
index 0000000..b002503
--- /dev/null
@@ -0,0 +1,37 @@
+From fc3afb5728e297994863f8a2a01b88a920bbf53e Mon Sep 17 00:00:00 2001
+From: Xu Yang <xu.yang_2@nxp.com>
+Date: Thu, 16 Jul 2026 18:41:26 +0800
+Subject: usb: chipidea: fix usage_count leak when autosuspend_delay is negative
+
+From: Xu Yang <xu.yang_2@nxp.com>
+
+commit fc3afb5728e297994863f8a2a01b88a920bbf53e upstream.
+
+The probe() calls pm_runtime_use_autosuspend(), but remove() does not call
+pm_runtime_dont_use_autosuspend(). This can lead to a usage_count leak if
+autosuspend_delay is set to a negative value.
+
+The pm_runtime_use_autosuspend() also notes that it's important to undo
+this with pm_runtime_dont_use_autosuspend() at driver exit time.
+
+Fixes: 1f874edcb731 ("usb: chipidea: add runtime power management support")
+Cc: stable <stable@kernel.org>
+Assisted-by: Claude:claude-sonnet-4.6
+Signed-off-by: Xu Yang <xu.yang_2@nxp.com>
+Reviewed-by: Frank Li <Frank.Li@nxp.com>
+Link: https://patch.msgid.link/20260716104126.2763454-1-xu.yang_2@oss.nxp.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/chipidea/core.c |    1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/usb/chipidea/core.c
++++ b/drivers/usb/chipidea/core.c
+@@ -1265,6 +1265,7 @@ static void ci_hdrc_remove(struct platfo
+               usb_role_switch_unregister(ci->role_switch);
+       if (ci->supports_runtime_pm) {
++              pm_runtime_dont_use_autosuspend(&pdev->dev);
+               pm_runtime_get_sync(&pdev->dev);
+               pm_runtime_disable(&pdev->dev);
+               pm_runtime_put_noidle(&pdev->dev);
diff --git a/queue-6.18/usb-core-port-deattach-type-c-connector-on-component-unbind.patch b/queue-6.18/usb-core-port-deattach-type-c-connector-on-component-unbind.patch
new file mode 100644 (file)
index 0000000..17018e8
--- /dev/null
@@ -0,0 +1,100 @@
+From e0b291fe117964037e0ba382eff4bb365d531c3a Mon Sep 17 00:00:00 2001
+From: "Chia-Lin Kao (AceLan)" <acelan.kao@canonical.com>
+Date: Thu, 11 Jun 2026 15:12:01 +0800
+Subject: usb: core: port: Deattach Type-C connector on component unbind
+
+From: Chia-Lin Kao (AceLan) <acelan.kao@canonical.com>
+
+commit e0b291fe117964037e0ba382eff4bb365d531c3a upstream.
+
+connector_unbind() is the mirror of connector_bind(), but it is missing
+the symmetric call to typec_deattach() that connector_bind() makes via:
+
+    if (port_dev->child)
+        typec_attach(port_dev->connector, &port_dev->child->dev);
+
+When a Thunderbolt dock is unplugged, two teardown paths race:
+
+1. The component framework calls connector_unbind() first, which sets
+   port_dev->connector = NULL without calling typec_deattach().  This
+   leaves port->usb2_dev/port->usb3_dev in struct typec_port pointing at
+   the USB device that is about to be freed.
+
+2. usb_disconnect() then calls typec_deattach(port_dev->connector, ...),
+   but port_dev->connector is already NULL, so the call is a no-op and
+   port->usb2_dev is never cleared.
+
+3. Concurrently, UCSI detects a PD partner-disconnect event and calls
+   typec_unregister_partner(), which reads port->usb2_dev (now a dangling
+   pointer to freed memory) and passes it to typec_partner_unlink_device()
+   -> sysfs_remove_link() -> dev_name() on the freed device, corrupting
+   the typec/UCSI partner state.
+
+This corruption leaves the Thunderbolt tunnel in an inconsistent state on
+the next dock hot-plug.  On affected hardware the dock's I225/igc NIC fails
+to enumerate: AER fires a slot reset while the igc driver is still
+initialising ("PCIe link lost"), and the subsequent igc_reset attempt hits
+igc_rd32 on an already-detached device:
+
+    igc 0000:2e:00.0 eth0: PCIe link lost, device now detached
+    igc: Failed to read reg 0x0!
+    WARNING: CPU: 9 PID: 129 at drivers/net/ethernet/intel/igc/igc_main.c:7005
+             igc_rd32+0xa4/0xc0 [igc]
+    Call Trace:
+     igc_disable_pcie_master+0x16/0xa0 [igc]
+     igc_reset_hw_base+0x14/0x170 [igc]
+     igc_reset+0x63/0x110 [igc]
+     igc_io_slot_reset+0x9e/0xd0 [igc]
+     report_slot_reset+0x5d/0xc0
+     pcie_do_recovery+0x209/0x400
+     aer_isr_one_error_type+0x235/0x430
+     aer_isr+0x4e/0x80
+     irq_thread+0xf4/0x1f0
+
+4. UCSI later handles the PD partner-disconnect and calls
+   typec_unregister_partner(), which still sees the stale port->usb2_dev
+   and tries to remove its sysfs link a second time:
+
+   kernfs: can not remove 'typec', no directory
+   WARNING: CPU: 6 PID: 55 at fs/kernfs/dir.c:1706 kernfs_remove_by_name_ns+0xe9/0xf0
+   Workqueue: events ucsi_handle_connector_change [typec_ucsi]
+   Call Trace:
+    sysfs_remove_link+0x19/0x50
+    typec_unregister_partner+0x6e/0x120 [typec]
+    ucsi_unregister_partner+0x107/0x150 [typec_ucsi]
+    ucsi_handle_connector_change+0x3ec/0x490 [typec_ucsi]
+    process_one_work+0x18e/0x3e0
+    worker_thread+0x2e3/0x420
+    kthread+0x10a/0x230
+    ret_from_fork+0x121/0x140
+    ret_from_fork_asm+0x1a/0x30
+
+   With worse timing the same stale pointer is dereferenced after the
+   backing memory is freed, turning the warning into a use-after-free.
+
+Fix the asymmetry: call typec_deattach() before clearing
+port_dev->connector, matching what connector_bind() does on the bind side.
+typec_partner_deattach() is already protected by port->partner_link_lock,
+so it serialises safely with the concurrent typec_unregister_partner() path.
+
+Fixes: 11110783f5ea ("usb: Inform the USB Type-C class about enumerated devices")
+Cc: stable <stable@kernel.org>
+Signed-off-by: Chia-Lin Kao (AceLan) <acelan.kao@canonical.com>
+Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Link: https://patch.msgid.link/20260611071201.1235545-1-acelan.kao@canonical.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/core/port.c |    2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/usb/core/port.c
++++ b/drivers/usb/core/port.c
+@@ -726,6 +726,8 @@ static void connector_unbind(struct devi
+       sysfs_remove_link(&connector->kobj, dev_name(dev));
+       sysfs_remove_link(&dev->kobj, "connector");
++      if (port_dev->child)
++              typec_deattach(port_dev->connector, &port_dev->child->dev);
+       port_dev->connector = NULL;
+ }
diff --git a/queue-6.18/usb-core-sysfs-add-lock-to-bos_descriptors_read.patch b/queue-6.18/usb-core-sysfs-add-lock-to-bos_descriptors_read.patch
new file mode 100644 (file)
index 0000000..4372bbe
--- /dev/null
@@ -0,0 +1,52 @@
+From 4e0197fbb0eec588795d5431716a244d9ac8fa93 Mon Sep 17 00:00:00 2001
+From: Griffin Kroah-Hartman <griffin@kroah.com>
+Date: Wed, 15 Jul 2026 16:59:05 +0200
+Subject: usb: core: sysfs: add lock to bos_descriptors_read()
+
+From: Griffin Kroah-Hartman <griffin@kroah.com>
+
+commit 4e0197fbb0eec588795d5431716a244d9ac8fa93 upstream.
+
+Add a lock to the function bos_descriptors_read().
+
+This function accesses udev->bos, which could be simultaneously freed in
+usb_reset_and_verify_device(), a function that is commonly called in
+drivers all over the kernel.
+
+Assisted-by: gkh_clanker_t1000
+Signed-off-by: Griffin Kroah-Hartman <griffin@kroah.com>
+Acked-by: Alan Stern <stern@rowland.harvard.edu>
+Cc: stable <stable@kernel.org>
+Link: https://patch.msgid.link/20260715-usb_core_patches_3-v1-1-53021f5576fd@kroah.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/core/sysfs.c |    8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+--- a/drivers/usb/core/sysfs.c
++++ b/drivers/usb/core/sysfs.c
+@@ -899,10 +899,15 @@ bos_descriptors_read(struct file *filp,
+ {
+       struct device *dev = kobj_to_dev(kobj);
+       struct usb_device *udev = to_usb_device(dev);
+-      struct usb_host_bos *bos = udev->bos;
++      struct usb_host_bos *bos;
+       struct usb_bos_descriptor *desc;
+       size_t desclen, n = 0;
++      int rc;
++      rc = usb_lock_device_interruptible(udev);
++      if (rc < 0)
++              return -EINTR;
++      bos = udev->bos;
+       if (bos) {
+               desc = bos->desc;
+               desclen = le16_to_cpu(desc->wTotalLength);
+@@ -911,6 +916,7 @@ bos_descriptors_read(struct file *filp,
+                       memcpy(buf, (void *) desc + off, n);
+               }
+       }
++      usb_unlock_device(udev);
+       return n;
+ }
+ static const BIN_ATTR_RO(bos_descriptors, 65535); /* max-size BOS */
diff --git a/queue-6.18/usb-gadget-dummy_hcd-prevent-fifo_req-reuse-during-giveback.patch b/queue-6.18/usb-gadget-dummy_hcd-prevent-fifo_req-reuse-during-giveback.patch
new file mode 100644 (file)
index 0000000..d35680e
--- /dev/null
@@ -0,0 +1,141 @@
+From d5e5cd3654d2b5359a12ea6586120f05b28634ee Mon Sep 17 00:00:00 2001
+From: Jinchao Wang <wangjinchao600@gmail.com>
+Date: Thu, 16 Jul 2026 06:42:17 -0400
+Subject: usb: gadget: dummy_hcd: prevent fifo_req reuse during giveback
+
+From: Jinchao Wang <wangjinchao600@gmail.com>
+
+commit d5e5cd3654d2b5359a12ea6586120f05b28634ee upstream.
+
+dummy_hcd embeds a single shared usb_request (dum->fifo_req) that the
+"emulated single-request FIFO" fast-path in dummy_queue() reuses for
+small IN transfers: it copies the caller's request into it
+(req->req = *_req) and queues it, treating list_empty(&fifo_req.queue)
+as "the slot is free".
+
+The completion side (dummy_timer/transfer/nuke/dummy_dequeue) follows
+the standard pattern: list_del_init(&req->queue) unlinks the request,
+then the lock is dropped and usb_gadget_giveback_request() invokes
+req->complete().  But list_del_init() makes fifo_req.queue look empty
+*before* the completion callback returns, so a concurrent dummy_queue()
+on another CPU sees the slot as free, reuses fifo_req and runs
+req->req = *_req -- overwriting req->complete while dummy_timer is
+mid-calling it.  The indirect call then jumps to a clobbered pointer,
+causing a general protection fault / page fault in dummy_timer
+(syzkaller extid faf3a6cf579fc65591ca).  The clobbering write is an
+in-bounds memcpy on a live shared object, so KASAN cannot flag it.
+
+Add a fifo_req_busy bit covering the shared request's whole lifetime:
+set it in dummy_queue() when the FIFO fast-path takes fifo_req (making
+it the fast-path guard, replacing the list_empty(&fifo_req.queue)
+test), and clear it after the completion callback has returned, via a
+dummy_giveback() helper used at all four gadget-request giveback
+sites.  The shared slot can no longer be reused until its completion
+callback has finished.
+
+Reported-by: syzbot+faf3a6cf579fc65591ca@syzkaller.appspotmail.com
+Closes: https://syzkaller.appspot.com/bug?extid=faf3a6cf579fc65591ca
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Cc: stable <stable@kernel.org>
+Signed-off-by: Jinchao Wang <wangjinchao600@gmail.com>
+Reviewed-by: Alan Stern <stern@rowland.harvard.edu>
+Link: https://patch.msgid.link/5db8bba5b3499a86cd2e776f9918126b68b2508b.1784198306.git.wangjinchao600@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/udc/dummy_hcd.c |   40 ++++++++++++++++++++++++-------------
+ 1 file changed, 27 insertions(+), 13 deletions(-)
+
+--- a/drivers/usb/gadget/udc/dummy_hcd.c
++++ b/drivers/usb/gadget/udc/dummy_hcd.c
+@@ -278,6 +278,7 @@ struct dummy {
+       unsigned                        ints_enabled:1;
+       unsigned                        udc_suspended:1;
+       unsigned                        pullup:1;
++      unsigned                        fifo_req_busy:1;
+       /*
+        * HOST side support
+@@ -329,6 +330,26 @@ static inline struct dummy *gadget_dev_t
+ /* DEVICE/GADGET SIDE UTILITY ROUTINES */
++/*
++ * Give back a gadget request with dum->lock dropped around the callback.
++ * If @req is the shared fifo_req, clear fifo_req_busy afterward: the flag
++ * was set in dummy_queue() when the shared request was taken and must stay
++ * set until its completion callback has returned; list_del_init() alone
++ * makes the request look idle while the callback is still running.
++ * Caller holds dum->lock and has already done list_del_init() + status.
++ */
++static void dummy_giveback(struct dummy *dum, struct usb_ep *_ep,
++                         struct dummy_request *req)
++{
++      bool fifo = req == &dum->fifo_req;
++
++      spin_unlock(&dum->lock);
++      usb_gadget_giveback_request(_ep, &req->req);
++      spin_lock(&dum->lock);
++      if (fifo)
++              dum->fifo_req_busy = 0;
++}
++
+ /* called with spinlock held */
+ static void nuke(struct dummy *dum, struct dummy_ep *ep)
+ {
+@@ -339,9 +360,7 @@ static void nuke(struct dummy *dum, stru
+               list_del_init(&req->queue);
+               req->req.status = -ESHUTDOWN;
+-              spin_unlock(&dum->lock);
+-              usb_gadget_giveback_request(&ep->ep, &req->req);
+-              spin_lock(&dum->lock);
++              dummy_giveback(dum, &ep->ep, req);
+       }
+ }
+@@ -728,10 +747,11 @@ static int dummy_queue(struct usb_ep *_e
+       /* implement an emulated single-request FIFO */
+       if (ep->desc && (ep->desc->bEndpointAddress & USB_DIR_IN) &&
+-                      list_empty(&dum->fifo_req.queue) &&
++                      !dum->fifo_req_busy &&
+                       list_empty(&ep->queue) &&
+                       _req->length <= FIFO_SIZE) {
+               req = &dum->fifo_req;
++              dum->fifo_req_busy = 1;
+               req->req = *_req;
+               req->req.buf = dum->fifo_buf;
+               memcpy(dum->fifo_buf, _req->buf, _req->length);
+@@ -785,9 +805,7 @@ static int dummy_dequeue(struct usb_ep *
+               dev_dbg(udc_dev(dum),
+                               "dequeued req %p from %s, len %d buf %p\n",
+                               req, _ep->name, _req->length, _req->buf);
+-              spin_unlock(&dum->lock);
+-              usb_gadget_giveback_request(_ep, _req);
+-              spin_lock(&dum->lock);
++              dummy_giveback(dum, _ep, req);
+       }
+       spin_unlock_irqrestore(&dum->lock, flags);
+       return retval;
+@@ -1523,9 +1541,7 @@ top:
+               if (req->req.status != -EINPROGRESS) {
+                       list_del_init(&req->queue);
+-                      spin_unlock(&dum->lock);
+-                      usb_gadget_giveback_request(&ep->ep, &req->req);
+-                      spin_lock(&dum->lock);
++                      dummy_giveback(dum, &ep->ep, req);
+                       /* requests might have been unlinked... */
+                       rescan = 1;
+@@ -1910,9 +1926,7 @@ restart:
+                               dev_dbg(udc_dev(dum), "stale req = %p\n",
+                                               req);
+-                              spin_unlock(&dum->lock);
+-                              usb_gadget_giveback_request(&ep->ep, &req->req);
+-                              spin_lock(&dum->lock);
++                              dummy_giveback(dum, &ep->ep, req);
+                               ep->already_seen = 0;
+                               goto restart;
+                       }
diff --git a/queue-6.18/usb-gadget-f_midi-cancel-pending-in-work-before-freeing-the-midi-object.patch b/queue-6.18/usb-gadget-f_midi-cancel-pending-in-work-before-freeing-the-midi-object.patch
new file mode 100644 (file)
index 0000000..7ecfae9
--- /dev/null
@@ -0,0 +1,68 @@
+From 5650c18d93a1db7e27cb5a40b394747eb4686d5b Mon Sep 17 00:00:00 2001
+From: Fan Wu <fanwu01@zju.edu.cn>
+Date: Thu, 9 Jul 2026 15:07:17 +0000
+Subject: usb: gadget: f_midi: cancel pending IN work before freeing the midi object
+
+From: Fan Wu <fanwu01@zju.edu.cn>
+
+commit 5650c18d93a1db7e27cb5a40b394747eb4686d5b upstream.
+
+The f_midi driver embeds a work item (midi->work) whose handler,
+f_midi_in_work(), dereferences the enclosing struct f_midi through
+container_of().  This work is armed from two sites: f_midi_complete(),
+on a normal IN-endpoint completion, and f_midi_in_trigger(), on an ALSA
+rawmidi output-stream start.
+
+Neither f_midi_disable() nor f_midi_unbind() cancels midi->work.
+f_midi_disable() only disables the endpoints and drains the in_req_fifo;
+it does not synchronize the work item, and the sound card is released
+asynchronously to the final free of the midi object.
+
+The midi object is reference-counted (midi->free_ref) and is freed in
+f_midi_free() only once both the usb_function reference and the rawmidi
+private_data reference have been dropped.  In f_midi_unbind(),
+f_midi_disable() runs before the sound card is released, so while the
+USB endpoints are already disabled the rawmidi device is still usable by
+an open substream.  A concurrent userspace write on such a substream can
+reach f_midi_in_trigger() and queue midi->work again after
+f_midi_disable() has returned.  A work item armed this way may still be
+pending when the last reference drops and f_midi_free() proceeds to
+kfree(midi), letting f_midi_in_work() dereference the struct after it
+has been freed, a use-after-free.
+
+For this reason cancelling midi->work in f_midi_disable() would not be
+sufficient: the ALSA trigger path can rearm the work after disable()
+returns.  Cancelling at the refcount-zero free site is the boundary
+after which neither arming source can survive, because by then both
+references that keep the midi object alive have been dropped: the USB
+endpoints are already disabled and the rawmidi device has been released.
+
+Fix this by calling cancel_work_sync(&midi->work) in the refcount-zero
+block of f_midi_free(), before the embedded work_struct is freed along
+with the rest of the structure.  opts->lock is a sleeping mutex, so
+calling cancel_work_sync() under it is permitted, and the handler takes
+midi->transmit_lock rather than opts->lock, so no self-deadlock can
+occur while it waits for a running instance of the work to finish.
+
+This issue was found by an in-house static analysis tool.
+
+Fixes: 8653d71ce3763 ("usb/gadget: f_midi: Replace tasklet with work")
+Cc: stable <stable@kernel.org>
+Assisted-by: Codex:gpt-5.5
+Signed-off-by: Fan Wu <fanwu01@zju.edu.cn>
+Link: https://patch.msgid.link/20260709150717.399083-1-fanwu01@zju.edu.cn
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/function/f_midi.c |    1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/usb/gadget/function/f_midi.c
++++ b/drivers/usb/gadget/function/f_midi.c
+@@ -1302,6 +1302,7 @@ static void f_midi_free(struct usb_funct
+       opts = container_of(f->fi, struct f_midi_opts, func_inst);
+       mutex_lock(&opts->lock);
+       if (!--midi->free_ref) {
++              cancel_work_sync(&midi->work);
+               kfree(midi->id);
+               kfifo_free(&midi->in_req_fifo);
+               kfree(midi);
diff --git a/queue-6.18/usb-gadget-f_ncm-validate-datagram-bounds-in-ncm_unwrap_ntb.patch b/queue-6.18/usb-gadget-f_ncm-validate-datagram-bounds-in-ncm_unwrap_ntb.patch
new file mode 100644 (file)
index 0000000..5e77af6
--- /dev/null
@@ -0,0 +1,91 @@
+From 1febec7e47cdcd01f43fb0211094e3010474666e Mon Sep 17 00:00:00 2001
+From: Sonali Pradhan <sonalipradhan@google.com>
+Date: Fri, 3 Jul 2026 08:37:24 +0000
+Subject: usb: gadget: f_ncm: validate datagram bounds in ncm_unwrap_ntb()
+
+From: Sonali Pradhan <sonalipradhan@google.com>
+
+commit 1febec7e47cdcd01f43fb0211094e3010474666e upstream.
+
+When unpacking host-supplied NTBs, ncm_unwrap_ntb() checks datagram length
+against frame_max but does not verify that the datagram fits within the
+declared block length. Additionally, when decoding multiple NTBs from a
+single socket buffer, subsequent block lengths are not checked against the
+actual remaining buffer data.
+
+With these checks missing, a malicious USB host can specify datagram
+offsets and lengths that point beyond the block, or supply secondary NTB
+headers declaring lengths larger than the buffer. skb_put_data() then
+copies adjacent kernel memory from skb_shared_info into the network skb.
+
+Fix this by verifying that sufficient buffer space remains for the NTB
+header before parsing, handling zero-length block declarations, ensuring
+that block lengths never exceed the remaining buffer space, and verifying
+that each datagram payload stays strictly within the block boundary.
+
+Fixes: 427694cfaafa ("usb: gadget: ncm: Handle decoding of multiple NTB's in unwrap call")
+Fixes: 2b74b0a04d3e ("USB: gadget: f_ncm: add bounds checks to ncm_unwrap_ntb()")
+Cc: stable <stable@kernel.org>
+Assisted-by: Jetski:Gemini-2.5-Pro
+Signed-off-by: Sonali Pradhan <sonalipradhan@google.com>
+Link: https://patch.msgid.link/20260703083725.1903850-1-sonalipradhan@google.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/function/f_ncm.c |   17 +++++++++++++----
+ 1 file changed, 13 insertions(+), 4 deletions(-)
+
+--- a/drivers/usb/gadget/function/f_ncm.c
++++ b/drivers/usb/gadget/function/f_ncm.c
+@@ -1189,6 +1189,10 @@ static int ncm_unwrap_ntb(struct gether
+       frame_max = ncm_opts->max_segment_size;
+ parse_ntb:
++      if (to_process < (int)opts->nth_size) {
++              INFO(port->func.config->cdev, "Packet too small for headers\n");
++              goto err;
++      }
+       tmp = (__le16 *)ntb_ptr;
+       /* dwSignature */
+@@ -1209,8 +1213,12 @@ parse_ntb:
+       tmp++; /* skip wSequence */
+       block_len = get_ncm(&tmp, opts->block_length);
++      if (block_len == 0)
++              block_len = to_process;
++
+       /* (d)wBlockLength */
+-      if ((block_len < opts->nth_size + opts->ndp_size) || (block_len > ntb_max)) {
++      if ((block_len < opts->nth_size + opts->ndp_size) || (block_len > ntb_max) ||
++                      (block_len > to_process)) {
+               INFO(port->func.config->cdev, "Bad block length: %#X\n", block_len);
+               goto err;
+       }
+@@ -1273,7 +1281,7 @@ parse_ntb:
+                       index = index2;
+                       /* wDatagramIndex[0] */
+                       if ((index < opts->nth_size) ||
+-                                      (index > block_len - opts->dpe_size)) {
++                                      (index > block_len)) {
+                               INFO(port->func.config->cdev,
+                                    "Bad index: %#X\n", index);
+                               goto err;
+@@ -1285,7 +1293,8 @@ parse_ntb:
+                        * ethernet hdr + crc or larger than max frame size
+                        */
+                       if ((dg_len < 14 + crc_len) ||
+-                                      (dg_len > frame_max)) {
++                                      (dg_len > frame_max) ||
++                                      (dg_len > block_len - index)) {
+                               INFO(port->func.config->cdev,
+                                    "Bad dgram length: %#X\n", dg_len);
+                               goto err;
+@@ -1310,7 +1319,7 @@ parse_ntb:
+                       dg_len2 = get_ncm(&tmp, opts->dgram_item_len);
+                       /* wDatagramIndex[1] */
+-                      if (index2 > block_len - opts->dpe_size) {
++                      if (index2 > block_len) {
+                               INFO(port->func.config->cdev,
+                                    "Bad index: %#X\n", index2);
+                               goto err;
diff --git a/queue-6.18/usb-gadget-fsl-udc-fix-dev_printk-device.patch b/queue-6.18/usb-gadget-fsl-udc-fix-dev_printk-device.patch
new file mode 100644 (file)
index 0000000..779c240
--- /dev/null
@@ -0,0 +1,420 @@
+From c4dd150fceab281496acb3a643ae712aacb74864 Mon Sep 17 00:00:00 2001
+From: Johan Hovold <johan@kernel.org>
+Date: Thu, 2 Jul 2026 16:15:35 +0200
+Subject: USB: gadget: fsl-udc: fix dev_printk() device
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+From: Johan Hovold <johan@kernel.org>
+
+commit c4dd150fceab281496acb3a643ae712aacb74864 upstream.
+
+A change replacing custom printk() macros with dev_printk() incorrectly
+used the gadget struct device instead of the controller struct device
+(including for messages printed before the gadget device name has been
+initialised).
+
+Switch to using the controller platform device with dev_printk() so that
+the controller device and driver names are included in log messages as
+expected.
+
+Fixes: 6025f20f16c2 ("usb: gadget: fsl-udc: Replace custom log wrappers by dev_{err,warn,dbg,vdbg}")
+Cc: stable <stable@kernel.org>
+Cc: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
+Signed-off-by: Johan Hovold <johan@kernel.org>
+Acked-by: Uwe Kleine-König <u.kleine-koenig@baylibre.com>
+Link: https://patch.msgid.link/20260702141536.90887-4-johan@kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/udc/fsl_udc_core.c |   92 +++++++++++++++-------------------
+ drivers/usb/gadget/udc/fsl_usb2_udc.h |    1 
+ 2 files changed, 43 insertions(+), 50 deletions(-)
+
+--- a/drivers/usb/gadget/udc/fsl_udc_core.c
++++ b/drivers/usb/gadget/udc/fsl_udc_core.c
+@@ -184,7 +184,7 @@ __acquires(ep->udc->lock)
+       usb_gadget_unmap_request(&ep->udc->gadget, &req->req, ep_is_in(ep));
+       if (status && (status != -ESHUTDOWN))
+-              dev_vdbg(&udc->gadget.dev, "complete %s req %p stat %d len %u/%u\n",
++              dev_vdbg(udc->dev, "complete %s req %p stat %d len %u/%u\n",
+                        ep->ep.name, &req->req, status,
+                        req->req.actual, req->req.length);
+@@ -286,7 +286,7 @@ static int dr_controller_setup(struct fs
+       timeout = jiffies + FSL_UDC_RESET_TIMEOUT;
+       while (fsl_readl(&dr_regs->usbcmd) & USB_CMD_CTRL_RESET) {
+               if (time_after(jiffies, timeout)) {
+-                      dev_err(&udc->gadget.dev, "udc reset timeout!\n");
++                      dev_err(udc->dev, "udc reset timeout!\n");
+                       return -ETIMEDOUT;
+               }
+               cpu_relax();
+@@ -309,7 +309,7 @@ static int dr_controller_setup(struct fs
+       tmp &= USB_EP_LIST_ADDRESS_MASK;
+       fsl_writel(tmp, &dr_regs->endpointlistaddr);
+-      dev_vdbg(&udc->gadget.dev,
++      dev_vdbg(udc->dev,
+                "vir[qh_base] is %p phy[qh_base] is 0x%8x reg is 0x%8x\n",
+                udc->ep_qh, (int)tmp,
+                fsl_readl(&dr_regs->endpointlistaddr));
+@@ -500,7 +500,7 @@ static void struct_ep_qh_setup(struct fs
+               tmp = max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS;
+               break;
+       default:
+-              dev_vdbg(&udc->gadget.dev, "error ep type is %d\n", ep_type);
++              dev_vdbg(udc->dev, "error ep type is %d\n", ep_type);
+               return;
+       }
+       if (zlt)
+@@ -613,7 +613,7 @@ static int fsl_ep_enable(struct usb_ep *
+       spin_unlock_irqrestore(&udc->lock, flags);
+       retval = 0;
+-      dev_vdbg(&udc->gadget.dev, "enabled %s (ep%d%s) maxpacket %d\n",
++      dev_vdbg(udc->dev, "enabled %s (ep%d%s) maxpacket %d\n",
+                ep->ep.name, ep->ep.desc->bEndpointAddress & 0x0f,
+                (desc->bEndpointAddress & USB_DIR_IN) ? "in" : "out",
+                max);
+@@ -634,13 +634,8 @@ static int fsl_ep_disable(struct usb_ep
+       int ep_num;
+       ep = container_of(_ep, struct fsl_ep, ep);
+-      if (!_ep || !ep->ep.desc) {
+-              /*
+-               * dev_vdbg(&udc->gadget.dev, "%s not enabled\n",
+-               *       _ep ? ep->ep.name : NULL);
+-               */
++      if (!_ep || !ep->ep.desc)
+               return -EINVAL;
+-      }
+       /* disable ep on controller */
+       ep_num = ep_index(ep);
+@@ -664,7 +659,7 @@ static int fsl_ep_disable(struct usb_ep
+       ep->stopped = 1;
+       spin_unlock_irqrestore(&udc->lock, flags);
+-      dev_vdbg(&udc->gadget.dev, "disabled %s OK\n", _ep->name);
++      dev_vdbg(udc->dev, "disabled %s OK\n", _ep->name);
+       return 0;
+ }
+@@ -724,9 +719,6 @@ static void fsl_queue_td(struct fsl_ep *
+ {
+       u32 temp, bitmask, tmp_stat;
+-      /* dev_vdbg(&udc->gadget.dev, "QH addr Register 0x%8x\n", dr_regs->endpointlistaddr);
+-      dev_vdbg(&udc->gadget.dev, "ep_qh[%d] addr is 0x%8x\n", i, (u32)&(ep->udc->ep_qh[i])); */
+-
+       bitmask = ep_is_in(ep)
+               ? (1 << (ep_index(ep) + 16))
+               : (1 << (ep_index(ep)));
+@@ -813,7 +805,7 @@ static struct ep_td_struct *fsl_build_dt
+               *is_last = 0;
+       if ((*is_last) == 0)
+-              dev_vdbg(&udc_controller->gadget.dev, "multi-dtd request!\n");
++              dev_vdbg(udc_controller->dev, "multi-dtd request!\n");
+       /* Fill in the transfer size; set active bit */
+       swap_temp = ((*length << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE);
+@@ -825,7 +817,7 @@ static struct ep_td_struct *fsl_build_dt
+       mb();
+-      dev_vdbg(&udc_controller->gadget.dev, "length = %d address= 0x%x\n", *length, (int)*dma);
++      dev_vdbg(udc_controller->dev, "length = %d address= 0x%x\n", *length, (int)*dma);
+       return dtd;
+ }
+@@ -876,11 +868,11 @@ fsl_ep_queue(struct usb_ep *_ep, struct
+       /* catch various bogus parameters */
+       if (!_req || !req->req.complete || !req->req.buf
+                       || !list_empty(&req->queue)) {
+-              dev_vdbg(&udc->gadget.dev, "%s, bad params\n", __func__);
++              dev_vdbg(udc->dev, "%s, bad params\n", __func__);
+               return -EINVAL;
+       }
+       if (unlikely(!ep->ep.desc)) {
+-              dev_vdbg(&udc->gadget.dev, "%s, bad ep\n", __func__);
++              dev_vdbg(udc->dev, "%s, bad ep\n", __func__);
+               return -EINVAL;
+       }
+       if (usb_endpoint_xfer_isoc(ep->ep.desc)) {
+@@ -1040,7 +1032,7 @@ static int fsl_ep_set_halt(struct usb_ep
+               udc->ep0_dir = 0;
+       }
+ out:
+-      dev_vdbg(&udc->gadget.dev, "%s %s halt stat %d\n", ep->ep.name,
++      dev_vdbg(udc->dev, "%s %s halt stat %d\n", ep->ep.name,
+                value ?  "set" : "clear", status);
+       return status;
+@@ -1109,7 +1101,7 @@ static void fsl_ep_fifo_flush(struct usb
+               /* Wait until flush complete */
+               while (fsl_readl(&dr_regs->endptflush)) {
+                       if (time_after(jiffies, timeout)) {
+-                              dev_err(&udc_controller->gadget.dev,
++                              dev_err(udc_controller->dev,
+                                       "ep flush timeout\n");
+                               return;
+                       }
+@@ -1182,7 +1174,7 @@ static int fsl_vbus_session(struct usb_g
+       udc = container_of(gadget, struct fsl_udc, gadget);
+       spin_lock_irqsave(&udc->lock, flags);
+-      dev_vdbg(&gadget->dev, "VBUS %s\n", str_on_off(is_active));
++      dev_vdbg(udc->dev, "VBUS %s\n", str_on_off(is_active));
+       udc->vbus_active = (is_active != 0);
+       if (can_pullup(udc))
+               fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP),
+@@ -1548,7 +1540,7 @@ static void ep0_req_complete(struct fsl_
+               udc->ep0_state = WAIT_FOR_SETUP;
+               break;
+       case WAIT_FOR_SETUP:
+-              dev_err(&udc->gadget.dev, "Unexpected ep0 packets\n");
++              dev_err(udc->dev, "Unexpected ep0 packets\n");
+               break;
+       default:
+               ep0stall(udc);
+@@ -1617,7 +1609,7 @@ static int process_ep_req(struct fsl_udc
+               errors = hc32_to_cpu(curr_td->size_ioc_sts);
+               if (errors & DTD_ERROR_MASK) {
+                       if (errors & DTD_STATUS_HALTED) {
+-                              dev_err(&udc->gadget.dev, "dTD error %08x QH=%d\n", errors, pipe);
++                              dev_err(udc->dev, "dTD error %08x QH=%d\n", errors, pipe);
+                               /* Clear the errors and Halt condition */
+                               tmp = hc32_to_cpu(curr_qh->size_ioc_int_sts);
+                               tmp &= ~errors;
+@@ -1628,26 +1620,26 @@ static int process_ep_req(struct fsl_udc
+                               break;
+                       }
+                       if (errors & DTD_STATUS_DATA_BUFF_ERR) {
+-                              dev_vdbg(&udc->gadget.dev, "Transfer overflow\n");
++                              dev_vdbg(udc->dev, "Transfer overflow\n");
+                               status = -EPROTO;
+                               break;
+                       } else if (errors & DTD_STATUS_TRANSACTION_ERR) {
+-                              dev_vdbg(&udc->gadget.dev, "ISO error\n");
++                              dev_vdbg(udc->dev, "ISO error\n");
+                               status = -EILSEQ;
+                               break;
+                       } else
+-                              dev_err(&udc->gadget.dev,
++                              dev_err(udc->dev,
+                                       "Unknown error has occurred (0x%x)!\n",
+                                       errors);
+               } else if (hc32_to_cpu(curr_td->size_ioc_sts)
+                               & DTD_STATUS_ACTIVE) {
+-                      dev_vdbg(&udc->gadget.dev, "Request not complete\n");
++                      dev_vdbg(udc->dev, "Request not complete\n");
+                       status = REQ_UNCOMPLETE;
+                       return status;
+               } else if (remaining_length) {
+                       if (direction) {
+-                              dev_vdbg(&udc->gadget.dev,
++                              dev_vdbg(udc->dev,
+                                        "Transmit dTD remaining length not zero\n");
+                               status = -EPROTO;
+                               break;
+@@ -1655,8 +1647,7 @@ static int process_ep_req(struct fsl_udc
+                               break;
+                       }
+               } else {
+-                      dev_vdbg(&udc->gadget.dev,
+-                               "dTD transmitted successful\n");
++                      dev_vdbg(udc->dev, "dTD transmitted successful\n");
+               }
+               if (j != curr_req->dtd_count - 1)
+@@ -1699,7 +1690,7 @@ static void dtd_complete_irq(struct fsl_
+               /* If the ep is configured */
+               if (!curr_ep->ep.name) {
+-                      dev_warn(&udc->gadget.dev, "Invalid EP?\n");
++                      dev_warn(udc->dev, "Invalid EP?\n");
+                       continue;
+               }
+@@ -1708,7 +1699,7 @@ static void dtd_complete_irq(struct fsl_
+                               queue) {
+                       status = process_ep_req(udc, i, curr_req);
+-                      dev_vdbg(&udc->gadget.dev,
++                      dev_vdbg(udc->dev,
+                                "status of process_ep_req= %d, ep = %d\n",
+                                status, ep_num);
+                       if (status == REQ_UNCOMPLETE)
+@@ -1829,7 +1820,7 @@ static void reset_irq(struct fsl_udc *ud
+       while (fsl_readl(&dr_regs->endpointprime)) {
+               /* Wait until all endptprime bits cleared */
+               if (time_after(jiffies, timeout)) {
+-                      dev_err(&udc->gadget.dev, "Timeout for reset\n");
++                      dev_err(udc->dev, "Timeout for reset\n");
+                       break;
+               }
+               cpu_relax();
+@@ -1839,7 +1830,7 @@ static void reset_irq(struct fsl_udc *ud
+       fsl_writel(0xffffffff, &dr_regs->endptflush);
+       if (fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET) {
+-              dev_vdbg(&udc->gadget.dev, "Bus reset\n");
++              dev_vdbg(udc->dev, "Bus reset\n");
+               /* Bus is reseting */
+               udc->bus_reset = 1;
+               /* Reset all the queues, include XD, dTD, EP queue
+@@ -1847,7 +1838,7 @@ static void reset_irq(struct fsl_udc *ud
+               reset_queues(udc, true);
+               udc->usb_state = USB_STATE_DEFAULT;
+       } else {
+-              dev_vdbg(&udc->gadget.dev, "Controller reset\n");
++              dev_vdbg(udc->dev, "Controller reset\n");
+               /* initialize usb hw reg except for regs for EP, not
+                * touch usbintr reg */
+               dr_controller_setup(udc);
+@@ -1881,7 +1872,7 @@ static irqreturn_t fsl_udc_irq(int irq,
+       /* Clear notification bits */
+       fsl_writel(irq_src, &dr_regs->usbsts);
+-      /* dev_vdbg(&udc->gadget.dev, "irq_src [0x%8x]", irq_src); */
++      /* dev_vdbg(udc->dev, "irq_src [0x%8x]", irq_src); */
+       /* Need to resume? */
+       if (udc->usb_state == USB_STATE_SUSPENDED)
+@@ -1890,7 +1881,7 @@ static irqreturn_t fsl_udc_irq(int irq,
+       /* USB Interrupt */
+       if (irq_src & USB_STS_INT) {
+-              dev_vdbg(&udc->gadget.dev, "Packet int\n");
++              dev_vdbg(udc->dev, "Packet int\n");
+               /* Setup package, we only support ep0 as control ep */
+               if (fsl_readl(&dr_regs->endptsetupstat) & EP_SETUP_STATUS_EP0) {
+                       tripwire_handler(udc, 0,
+@@ -1919,7 +1910,7 @@ static irqreturn_t fsl_udc_irq(int irq,
+       /* Reset Received */
+       if (irq_src & USB_STS_RESET) {
+-              dev_vdbg(&udc->gadget.dev, "reset int\n");
++              dev_vdbg(udc->dev, "reset int\n");
+               reset_irq(udc);
+               status = IRQ_HANDLED;
+       }
+@@ -1931,7 +1922,7 @@ static irqreturn_t fsl_udc_irq(int irq,
+       }
+       if (irq_src & (USB_STS_ERR | USB_STS_SYS_ERR)) {
+-              dev_vdbg(&udc->gadget.dev, "Error IRQ %x\n", irq_src);
++              dev_vdbg(udc->dev, "Error IRQ %x\n", irq_src);
+       }
+       spin_unlock_irqrestore(&udc->lock, flags);
+@@ -1967,7 +1958,7 @@ static int fsl_udc_start(struct usb_gadg
+                                       udc_controller->transceiver->otg,
+                                                   &udc_controller->gadget);
+                       if (retval < 0) {
+-                              dev_err(&udc_controller->gadget.dev, "can't bind to transceiver\n");
++                              dev_err(udc_controller->dev, "can't bind to transceiver\n");
+                               udc_controller->driver = NULL;
+                               return retval;
+                       }
+@@ -2252,7 +2243,7 @@ static int struct_udc_setup(struct fsl_u
+       udc->eps = kcalloc(udc->max_ep, sizeof(struct fsl_ep), GFP_KERNEL);
+       if (!udc->eps) {
+-              dev_err(&udc->gadget.dev, "kmalloc udc endpoint status failed\n");
++              dev_err(udc->dev, "kmalloc udc endpoint status failed\n");
+               goto eps_alloc_failed;
+       }
+@@ -2267,7 +2258,7 @@ static int struct_udc_setup(struct fsl_u
+       udc->ep_qh = dma_alloc_coherent(&pdev->dev, size,
+                                       &udc->ep_qh_dma, GFP_KERNEL);
+       if (!udc->ep_qh) {
+-              dev_err(&udc->gadget.dev, "malloc QHs for udc failed\n");
++              dev_err(udc->dev, "malloc QHs for udc failed\n");
+               goto ep_queue_alloc_failed;
+       }
+@@ -2278,14 +2269,14 @@ static int struct_udc_setup(struct fsl_u
+       udc->status_req = container_of(fsl_alloc_request(NULL, GFP_KERNEL),
+                       struct fsl_req, req);
+       if (!udc->status_req) {
+-              dev_err(&udc->gadget.dev, "kzalloc for udc status request failed\n");
++              dev_err(udc->dev, "kzalloc for udc status request failed\n");
+               goto udc_status_alloc_failed;
+       }
+       /* allocate a small amount of memory to get valid address */
+       udc->status_req->req.buf = kmalloc(8, GFP_KERNEL);
+       if (!udc->status_req->req.buf) {
+-              dev_err(&udc->gadget.dev, "kzalloc for udc request buffer failed\n");
++              dev_err(udc->dev, "kzalloc for udc request buffer failed\n");
+               goto udc_req_buf_alloc_failed;
+       }
+@@ -2373,6 +2364,7 @@ static int fsl_udc_probe(struct platform
+       if (udc_controller == NULL)
+               return -ENOMEM;
++      udc_controller->dev = &pdev->dev;
+       pdata = dev_get_platdata(&pdev->dev);
+       udc_controller->pdata = pdata;
+       spin_lock_init(&udc_controller->lock);
+@@ -2382,7 +2374,7 @@ static int fsl_udc_probe(struct platform
+       if (pdata->operating_mode == FSL_USB2_DR_OTG) {
+               udc_controller->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
+               if (IS_ERR_OR_NULL(udc_controller->transceiver)) {
+-                      dev_err(&udc_controller->gadget.dev, "Can't find OTG driver!\n");
++                      dev_err(&pdev->dev, "Can't find OTG driver!\n");
+                       ret = -ENODEV;
+                       goto err_kfree;
+               }
+@@ -2398,7 +2390,7 @@ static int fsl_udc_probe(struct platform
+       if (pdata->operating_mode == FSL_USB2_DR_DEVICE) {
+               if (!request_mem_region(res->start, resource_size(res),
+                                       driver_name)) {
+-                      dev_err(&udc_controller->gadget.dev, "request mem region for %s failed\n", pdev->name);
++                      dev_err(&pdev->dev, "failed to request mem region\n");
+                       ret = -EBUSY;
+                       goto err_kfree;
+               }
+@@ -2429,7 +2421,7 @@ static int fsl_udc_probe(struct platform
+       /* Read Device Controller Capability Parameters register */
+       dccparams = fsl_readl(&dr_regs->dccparams);
+       if (!(dccparams & DCCPARAMS_DC)) {
+-              dev_err(&udc_controller->gadget.dev, "This SOC doesn't support device role\n");
++              dev_err(&pdev->dev, "This SOC doesn't support device role\n");
+               ret = -ENODEV;
+               goto err_exit;
+       }
+@@ -2447,14 +2439,14 @@ static int fsl_udc_probe(struct platform
+       ret = request_irq(udc_controller->irq, fsl_udc_irq, IRQF_SHARED,
+                       driver_name, udc_controller);
+       if (ret != 0) {
+-              dev_err(&udc_controller->gadget.dev, "cannot request irq %d err %d\n",
++              dev_err(&pdev->dev, "cannot request irq %d err %d\n",
+                               udc_controller->irq, ret);
+               goto err_exit;
+       }
+       /* Initialize the udc structure including QH member and other member */
+       if (struct_udc_setup(udc_controller, pdev)) {
+-              dev_err(&udc_controller->gadget.dev, "Can't initialize udc data structure\n");
++              dev_err(&pdev->dev, "Can't initialize udc data structure\n");
+               ret = -ENOMEM;
+               goto err_free_irq;
+       }
+--- a/drivers/usb/gadget/udc/fsl_usb2_udc.h
++++ b/drivers/usb/gadget/udc/fsl_usb2_udc.h
+@@ -470,6 +470,7 @@ struct fsl_ep {
+ #define EP_DIR_OUT    0
+ struct fsl_udc {
++      struct device *dev;
+       struct usb_gadget gadget;
+       struct usb_gadget_driver *driver;
+       struct fsl_usb2_platform_data *pdata;
diff --git a/queue-6.18/usb-gadget-fsl-udc-fix-device-name-leak-on-probe-failure.patch b/queue-6.18/usb-gadget-fsl-udc-fix-device-name-leak-on-probe-failure.patch
new file mode 100644 (file)
index 0000000..f5ede56
--- /dev/null
@@ -0,0 +1,32 @@
+From 6b874d00c466e73c6448a89856407fe46b2f50e4 Mon Sep 17 00:00:00 2001
+From: Johan Hovold <johan@kernel.org>
+Date: Thu, 2 Jul 2026 16:15:33 +0200
+Subject: USB: gadget: fsl-udc: fix device name leak on probe failure
+
+From: Johan Hovold <johan@kernel.org>
+
+commit 6b874d00c466e73c6448a89856407fe46b2f50e4 upstream.
+
+The gadget device name is set by UDC core when registering the gadget
+and must not be set before to avoid leaking the name in intermediate
+error paths (e.g. on dma pool creation failure).
+
+Fixes: eab35c4e6d95 ("usb: gadget: fsl_udc_core: let udc-core manage gadget->dev")
+Cc: stable <stable@kernel.org>
+Signed-off-by: Johan Hovold <johan@kernel.org>
+Link: https://patch.msgid.link/20260702141536.90887-2-johan@kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/udc/fsl_udc_core.c |    1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/drivers/usb/gadget/udc/fsl_udc_core.c
++++ b/drivers/usb/gadget/udc/fsl_udc_core.c
+@@ -2474,7 +2474,6 @@ static int fsl_udc_probe(struct platform
+       udc_controller->gadget.name = driver_name;
+       /* Setup gadget.dev and register with kernel */
+-      dev_set_name(&udc_controller->gadget.dev, "gadget");
+       udc_controller->gadget.dev.of_node = pdev->dev.of_node;
+       if (!IS_ERR_OR_NULL(udc_controller->transceiver))
diff --git a/queue-6.18/usb-gadget-printer-fix-infinite-loop-in-printer_read.patch b/queue-6.18/usb-gadget-printer-fix-infinite-loop-in-printer_read.patch
new file mode 100644 (file)
index 0000000..60dfcba
--- /dev/null
@@ -0,0 +1,77 @@
+From c2e819be6a5c7f34344926b4bd7e3dfca58cf48a Mon Sep 17 00:00:00 2001
+From: Melbin K Mathew <mlbnkm1@gmail.com>
+Date: Thu, 9 Jul 2026 21:56:22 +0100
+Subject: usb: gadget: printer: fix infinite loop in printer_read()
+
+From: Melbin K Mathew <mlbnkm1@gmail.com>
+
+commit c2e819be6a5c7f34344926b4bd7e3dfca58cf48a upstream.
+
+printer_read() uses the same variable for the requested copy size and
+the number of bytes actually copied to user space. copy_to_user()
+returns the number of bytes not copied, so when it fails to copy
+anything, the computed copied length becomes zero.
+
+In that case len, buf, current_rx_bytes and current_rx_buf are left
+unchanged. If RX data is available and the user buffer remains
+unwritable, the read loop can repeat indefinitely.
+
+Track the copied length separately and return -EFAULT, or the number of
+bytes already copied, if an iteration makes no progress.
+
+Fixes: b185f01a9ab7 ("usb: gadget: printer: factor out f_printer")
+Cc: stable <stable@kernel.org>
+Reviewed-by: Peter Chen <peter.chen@kernel.org>
+Signed-off-by: Melbin K Mathew <mlbnkm1@gmail.com>
+Link: https://patch.msgid.link/20260709205622.55700-1-mlbnkm1@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/function/f_printer.c |   23 ++++++++++++++++++-----
+ 1 file changed, 18 insertions(+), 5 deletions(-)
+
+--- a/drivers/usb/gadget/function/f_printer.c
++++ b/drivers/usb/gadget/function/f_printer.c
+@@ -431,7 +431,7 @@ printer_read(struct file *fd, char __use
+ {
+       struct printer_dev              *dev = fd->private_data;
+       unsigned long                   flags;
+-      size_t                          size;
++      size_t                          size, not_copied, copied;
+       size_t                          bytes_copied;
+       struct usb_request              *req;
+       /* This is a pointer to the current USB rx request. */
+@@ -524,10 +524,12 @@ printer_read(struct file *fd, char __use
+               else
+                       size = len;
+-              size -= copy_to_user(buf, current_rx_buf, size);
+-              bytes_copied += size;
+-              len -= size;
+-              buf += size;
++              not_copied = copy_to_user(buf, current_rx_buf, size);
++              copied = size - not_copied;
++
++              bytes_copied += copied;
++              len -= copied;
++              buf += copied;
+               spin_lock_irqsave(&dev->lock, flags);
+@@ -542,6 +544,17 @@ printer_read(struct file *fd, char __use
+               if (dev->interface < 0)
+                       goto out_disabled;
++              if (!copied) {
++                      dev->current_rx_req = current_rx_req;
++                      dev->current_rx_bytes = current_rx_bytes;
++                      dev->current_rx_buf = current_rx_buf;
++                      spin_unlock_irqrestore(&dev->lock, flags);
++                      mutex_unlock(&dev->lock_printer_io);
++                      return bytes_copied ? bytes_copied : -EFAULT;
++              }
++
++              size = copied;
++
+               /* If we not returning all the data left in this RX request
+                * buffer then adjust the amount of data left in the buffer.
+                * Othewise if we are done with this RX request buffer then
diff --git a/queue-6.18/usb-gadget-snps-udc-fix-device-name-leak-on-probe-failure.patch b/queue-6.18/usb-gadget-snps-udc-fix-device-name-leak-on-probe-failure.patch
new file mode 100644 (file)
index 0000000..ca246e8
--- /dev/null
@@ -0,0 +1,32 @@
+From 29a142d3e8b35ebc9e0bcc78f4bc26c9b6a9ac0b Mon Sep 17 00:00:00 2001
+From: Johan Hovold <johan@kernel.org>
+Date: Thu, 2 Jul 2026 16:15:34 +0200
+Subject: USB: gadget: snps-udc: fix device name leak on probe failure
+
+From: Johan Hovold <johan@kernel.org>
+
+commit 29a142d3e8b35ebc9e0bcc78f4bc26c9b6a9ac0b upstream.
+
+The gadget device name is set by UDC core when registering the gadget
+and must not be set before to avoid leaking the name in intermediate
+error paths (e.g. when detecting an older chip revision).
+
+Fixes: 12ad0fcaf2fb ("usb: gadget: amd5536udc: let udc-core manage gadget->dev")
+Cc: stable <stable@kernel.org>
+Signed-off-by: Johan Hovold <johan@kernel.org>
+Link: https://patch.msgid.link/20260702141536.90887-3-johan@kernel.org
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/udc/snps_udc_core.c |    1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/drivers/usb/gadget/udc/snps_udc_core.c
++++ b/drivers/usb/gadget/udc/snps_udc_core.c
+@@ -3133,7 +3133,6 @@ int udc_probe(struct udc *dev)
+       /* device struct setup */
+       dev->gadget.ops = &udc_ops;
+-      dev_set_name(&dev->gadget.dev, "gadget");
+       dev->gadget.name = name;
+       dev->gadget.max_speed = USB_SPEED_HIGH;
diff --git a/queue-6.18/usb-gadget-udc-bdc-free-irq-and-drain-func_wake_notify-before-teardown.patch b/queue-6.18/usb-gadget-udc-bdc-free-irq-and-drain-func_wake_notify-before-teardown.patch
new file mode 100644 (file)
index 0000000..92ab9ff
--- /dev/null
@@ -0,0 +1,120 @@
+From 0583f2fbf8f86ae3a0ce054f96783dd83e65d9bb Mon Sep 17 00:00:00 2001
+From: Fan Wu <fanwu01@zju.edu.cn>
+Date: Thu, 9 Jul 2026 02:09:04 +0000
+Subject: usb: gadget: udc: bdc: free IRQ and drain func_wake_notify before teardown
+
+From: Fan Wu <fanwu01@zju.edu.cn>
+
+commit 0583f2fbf8f86ae3a0ce054f96783dd83e65d9bb upstream.
+
+The Broadcom BDC UDC driver registers its IRQ handler with
+devm_request_irq() in bdc_udc_init(), so the IRQ is released by devm
+only after bdc_remove() returns.  devm releases resources in reverse
+LIFO order, but bdc_remove() runs bdc_udc_exit() and bdc_hw_exit() ->
+bdc_mem_free() manually before returning: bdc_udc_exit() tears down
+individual endpoint objects via bdc_free_ep(), while bdc_hw_exit() ->
+bdc_mem_free() frees and NULLs the DMA-coherent status-report ring
+(bdc->srr.sr_bds) and kfree()s bdc->bdc_ep_array.  Both happen while
+the IRQ handler (bdc_udc_interrupt, requested with IRQF_SHARED)
+remains deliverable in the window up to the post-remove devm
+free_irq().
+
+On receipt of a shared interrupt in that window, bdc_udc_interrupt()
+dereferences bdc->srr.sr_bds[bdc->srr.dqp_index] (NULL or freed DMA)
+and dispatches sr_handler callbacks that index into bdc_ep_array,
+causing a NULL-deref or use-after-free.
+
+The same window affects the delayed_work bdc->func_wake_notify, which is
+armed from the IRQ handler via bdc_sr_uspc() -> handle_link_state_change()
+-> schedule_delayed_work() and may self-rearm from its own callback
+bdc_func_wake_timer().  No cancel exists anywhere in the driver, so a
+queued work item that fires after bdc_remove() returns and the bdc
+structure is devm-freed dereferences freed memory.
+
+Replace devm_request_irq() with request_irq() and add an explicit
+free_irq(bdc->irq, bdc) in bdc_remove().  Clear BDC_GIE before
+free_irq() to stop the device from asserting interrupts, then
+free_irq() drains any in-flight handler, then cancel_delayed_work_sync()
+drains the func_wake_notify delayed work.  This ordering ensures the
+IRQ handler and delayed work cannot interfere with the subsequent
+endpoint and DMA teardown in bdc_udc_exit() and bdc_hw_exit().  Wire the
+matching free_irq() into the bdc_udc_init() error path so the IRQ is
+released on probe failure, and route the bdc_init_ep() failure through
+err0 instead of returning directly.
+
+This issue was found by an in-house static analysis tool.
+
+Fixes: efed421a94e6 ("usb: gadget: Add UDC driver for Broadcom USB3.0 device controller IP BDC")
+Cc: stable <stable@kernel.org>
+Assisted-by: Codex:gpt-5.5
+Signed-off-by: Fan Wu <fanwu01@zju.edu.cn>
+Link: https://patch.msgid.link/20260709020904.502611-1-fanwu01@zju.edu.cn
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/udc/bdc/bdc_core.c |   20 ++++++++++++++++++++
+ drivers/usb/gadget/udc/bdc/bdc_udc.c  |    7 ++++---
+ 2 files changed, 24 insertions(+), 3 deletions(-)
+
+--- a/drivers/usb/gadget/udc/bdc/bdc_core.c
++++ b/drivers/usb/gadget/udc/bdc/bdc_core.c
+@@ -586,9 +586,29 @@ disable_clk:
+ static void bdc_remove(struct platform_device *pdev)
+ {
+       struct bdc *bdc;
++      unsigned long flags;
++      u32 temp;
+       bdc  = platform_get_drvdata(pdev);
+       dev_dbg(bdc->dev, "%s ()\n", __func__);
++      /*
++       * Disable the device interrupt source before freeing the IRQ:
++       * clear BDC_GIE so the controller stops asserting interrupts,
++       * then free_irq drains any in-flight handler.
++       */
++      spin_lock_irqsave(&bdc->lock, flags);
++      temp = bdc_readl(bdc->regs, BDC_BDCSC);
++      temp &= ~BDC_GIE;
++      bdc_writel(bdc->regs, BDC_BDCSC, temp);
++      spin_unlock_irqrestore(&bdc->lock, flags);
++      free_irq(bdc->irq, bdc);
++      /*
++       * Drain func_wake_notify after free_irq: the IRQ handler arms this
++       * delayed_work via bdc_sr_uspc -> handle_link_state_change ->
++       * schedule_delayed_work (self-rearmed in bdc_func_wake_timer), so
++       * the IRQ must be released first to prevent re-arm after cancel.
++       */
++      cancel_delayed_work_sync(&bdc->func_wake_notify);
+       bdc_udc_exit(bdc);
+       bdc_hw_exit(bdc);
+       bdc_phy_exit(bdc);
+--- a/drivers/usb/gadget/udc/bdc/bdc_udc.c
++++ b/drivers/usb/gadget/udc/bdc/bdc_udc.c
+@@ -530,8 +530,8 @@ int bdc_udc_init(struct bdc *bdc)
+       bdc->gadget.name = BRCM_BDC_NAME;
+-      ret = devm_request_irq(bdc->dev, bdc->irq, bdc_udc_interrupt,
+-                              IRQF_SHARED, BRCM_BDC_NAME, bdc);
++      ret = request_irq(bdc->irq, bdc_udc_interrupt, IRQF_SHARED,
++                        BRCM_BDC_NAME, bdc);
+       if (ret) {
+               dev_err(bdc->dev,
+                       "failed to request irq #%d %d\n",
+@@ -542,7 +542,7 @@ int bdc_udc_init(struct bdc *bdc)
+       ret = bdc_init_ep(bdc);
+       if (ret) {
+               dev_err(bdc->dev, "bdc init ep fail: %d\n", ret);
+-              return ret;
++              goto err0;
+       }
+       ret = usb_add_gadget_udc(bdc->dev, &bdc->gadget);
+@@ -571,6 +571,7 @@ int bdc_udc_init(struct bdc *bdc)
+ err1:
+       usb_del_gadget_udc(&bdc->gadget);
+ err0:
++      free_irq(bdc->irq, bdc);
+       bdc_free_ep(bdc);
+       return ret;
diff --git a/queue-6.18/usb-gadget-uvc-clamp-send_response-length-to-the-response-buffer.patch b/queue-6.18/usb-gadget-uvc-clamp-send_response-length-to-the-response-buffer.patch
new file mode 100644 (file)
index 0000000..dcbc25a
--- /dev/null
@@ -0,0 +1,46 @@
+From b70dc75e85ba968b7b76eebfe5d63000080b875b Mon Sep 17 00:00:00 2001
+From: Muhammad Bilal <meatuni001@gmail.com>
+Date: Tue, 30 Jun 2026 00:50:04 +0500
+Subject: usb: gadget: uvc: clamp SEND_RESPONSE length to the response buffer
+
+From: Muhammad Bilal <meatuni001@gmail.com>
+
+commit b70dc75e85ba968b7b76eebfe5d63000080b875b upstream.
+
+uvc_send_response() builds the UVC control response from a user-supplied
+struct uvc_request_data:
+
+       req->length = min_t(unsigned int, uvc->event_length, data->length);
+       ...
+       memcpy(req->buf, data->data, req->length);
+
+req->length is clamped to uvc->event_length, which is taken from the
+host control request wLength (up to UVC_MAX_REQUEST_SIZE, 64), and to
+data->length, which comes from the UVCIOC_SEND_RESPONSE ioctl and is
+only checked for being negative.  The source buffer data->data is only
+60 bytes, so a response with uvc->event_length and data->length both
+greater than 60 makes memcpy() read past the end of data->data.
+
+Clamp req->length to sizeof(data->data) as well.
+
+Fixes: a5eaaa1f33e7 ("usb: gadget: uvc: use capped length value")
+Cc: stable <stable@kernel.org>
+Signed-off-by: Muhammad Bilal <meatuni001@gmail.com>
+Link: https://patch.msgid.link/20260629195004.148405-1-meatuni001@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/gadget/function/uvc_v4l2.c |    2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/usb/gadget/function/uvc_v4l2.c
++++ b/drivers/usb/gadget/function/uvc_v4l2.c
+@@ -200,6 +200,8 @@ uvc_send_response(struct uvc_device *uvc
+               return usb_ep_set_halt(cdev->gadget->ep0);
+       req->length = min_t(unsigned int, uvc->event_length, data->length);
++      if (req->length > sizeof(data->data))
++              req->length = sizeof(data->data);
+       req->zero = data->length < uvc->event_length;
+       memcpy(req->buf, data->data, req->length);
diff --git a/queue-6.18/usb-musb-omap2430-do-not-put-borrowed-of_node-in-probe.patch b/queue-6.18/usb-musb-omap2430-do-not-put-borrowed-of_node-in-probe.patch
new file mode 100644 (file)
index 0000000..218522a
--- /dev/null
@@ -0,0 +1,49 @@
+From c947360ae63eee1c9eacc030dd6f5a53f717addf Mon Sep 17 00:00:00 2001
+From: Guangshuo Li <lgs201920130244@gmail.com>
+Date: Mon, 13 Jul 2026 19:47:11 +0800
+Subject: usb: musb: omap2430: Do not put borrowed of_node in probe
+
+From: Guangshuo Li <lgs201920130244@gmail.com>
+
+commit c947360ae63eee1c9eacc030dd6f5a53f717addf upstream.
+
+omap2430_probe() stores pdev->dev.of_node in a local np variable. This is
+a borrowed pointer and the probe function does not take a reference to
+it.
+
+The success and error paths nevertheless call of_node_put(np). This drops
+a reference that is owned by the platform device, and can leave
+pdev->dev.of_node with an unbalanced reference count.
+
+Do not put the borrowed platform device node from omap2430_probe().
+References taken for the child MUSB device are handled by the device core,
+and the ctrl-module phandle reference is still released separately.
+
+Fixes: ffbe2feac59b ("usb: musb: omap2430: Fix probe regression for missing resources")
+Cc: stable <stable@kernel.org>
+Reviewed-by: Johan Hovold <johan@kernel.org>
+Signed-off-by: Guangshuo Li <lgs201920130244@gmail.com>
+Link: https://patch.msgid.link/20260713114711.955253-1-lgs201920130244@gmail.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/musb/omap2430.c |    2 --
+ 1 file changed, 2 deletions(-)
+
+--- a/drivers/usb/musb/omap2430.c
++++ b/drivers/usb/musb/omap2430.c
+@@ -455,7 +455,6 @@ static int omap2430_probe(struct platfor
+               dev_err(&pdev->dev, "failed to register musb device\n");
+               goto err_disable_rpm;
+       }
+-      of_node_put(np);
+       return 0;
+@@ -465,7 +464,6 @@ err_put_control_otghs:
+       if (!IS_ERR(glue->control_otghs))
+               put_device(glue->control_otghs);
+ err_put_musb:
+-      of_node_put(np);
+       platform_device_put(musb);
+       return ret;
diff --git a/queue-6.18/usb-serial-ftdi_sio-add-support-for-e-h-fxa291.patch b/queue-6.18/usb-serial-ftdi_sio-add-support-for-e-h-fxa291.patch
new file mode 100644 (file)
index 0000000..ad9df5a
--- /dev/null
@@ -0,0 +1,57 @@
+From fad0fd120e29041b3e6cdf41bb12e3184fb524a2 Mon Sep 17 00:00:00 2001
+From: Tim Pambor <timpambor@gmail.com>
+Date: Sat, 11 Jul 2026 17:36:30 +0000
+Subject: USB: serial: ftdi_sio: add support for E+H FXA291
+
+From: Tim Pambor <timpambor@gmail.com>
+
+commit fad0fd120e29041b3e6cdf41bb12e3184fb524a2 upstream.
+
+The Commubox FXA291 by Endress+Hauser AG is a USB serial converter
+based on FT232B which is used to communicate with field devices.
+
+It enumerates using the FTDI vendor ID and a custom PID.
+
+usb 1-9: New USB device found, idVendor=0403, idProduct=e510, bcdDevice= 4.00
+usb 1-9: New USB device strings: Mfr=1, Product=2, SerialNumber=0
+usb 1-9: Product: FXA291
+usb 1-9: Manufacturer: Endress+Hauser
+usb 1-9: SerialNumber: 00000000
+ftdi_sio 1-9:1.0: FTDI USB Serial Device converter detected
+usb 1-9: Detected FT232B
+usb 1-9: FTDI USB Serial Device converter now attached to ttyUSB0
+
+Signed-off-by: Tim Pambor <timpambor@gmail.com>
+Cc: stable@vger.kernel.org
+Signed-off-by: Johan Hovold <johan@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/serial/ftdi_sio.c     |    2 ++
+ drivers/usb/serial/ftdi_sio_ids.h |    5 +++++
+ 2 files changed, 7 insertions(+)
+
+--- a/drivers/usb/serial/ftdi_sio.c
++++ b/drivers/usb/serial/ftdi_sio.c
+@@ -1075,6 +1075,8 @@ static const struct usb_device_id id_tab
+       { USB_DEVICE_INTERFACE_NUMBER(ALTERA_VID, ALTERA_UB3_602E_PID, 3) },
+       /* Abacus Electrics */
+       { USB_DEVICE(FTDI_VID, ABACUS_OPTICAL_PROBE_PID) },
++      /* Endress+Hauser AG devices */
++      { USB_DEVICE(FTDI_VID, FTDI_EH_FXA291_PID) },
+       { }                                     /* Terminating entry */
+ };
+--- a/drivers/usb/serial/ftdi_sio_ids.h
++++ b/drivers/usb/serial/ftdi_sio_ids.h
+@@ -314,6 +314,11 @@
+ #define FTDI_ELV_UIO88_PID    0xFB5F  /* USB-I/O Interface (UIO 88) */
+ /*
++ * Endress+Hauser AG product ids (FTDI_VID)
++ */
++#define FTDI_EH_FXA291_PID    0xE510
++
++/*
+  * EVER Eco Pro UPS (http://www.ever.com.pl/)
+  */
diff --git a/queue-6.18/usb-serial-io_edgeport-cap-received-transmit-credits.patch b/queue-6.18/usb-serial-io_edgeport-cap-received-transmit-credits.patch
new file mode 100644 (file)
index 0000000..a8f8cb0
--- /dev/null
@@ -0,0 +1,42 @@
+From faaddd811c5099f11a5f52e68a6b31a5898cda4f Mon Sep 17 00:00:00 2001
+From: Sunho Park <shpark061104@gmail.com>
+Date: Tue, 14 Jul 2026 19:42:30 +0900
+Subject: USB: serial: io_edgeport: cap received transmit credits
+
+From: Sunho Park <shpark061104@gmail.com>
+
+commit faaddd811c5099f11a5f52e68a6b31a5898cda4f upstream.
+
+The interrupt-status packet reports transmit credits returned by the
+device. edge_interrupt_callback() adds the 16-bit value to txCredits
+without checking maxTxCredits.
+
+edge_write() uses txCredits minus the software FIFO count as the amount
+of data that fits. Since the FIFO is allocated with maxTxCredits bytes,
+txCredits exceeding maxTxCredits can cause OOB write in ring buffer.
+
+Cap accumulated credits at maxTxCredits. Conforming devices should never
+hit the cap.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Cc: stable@vger.kernel.org
+Assisted-by: Codex:GPT-5
+Signed-off-by: Sunho Park <shpark061104@gmail.com>
+Signed-off-by: Johan Hovold <johan@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/serial/io_edgeport.c |    3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/usb/serial/io_edgeport.c
++++ b/drivers/usb/serial/io_edgeport.c
+@@ -646,7 +646,8 @@ static void edge_interrupt_callback(stru
+                               if (edge_port && edge_port->open) {
+                                       spin_lock_irqsave(&edge_port->ep_lock,
+                                                         flags);
+-                                      edge_port->txCredits += txCredits;
++                                      edge_port->txCredits = min(edge_port->txCredits + txCredits,
++                                                                 edge_port->maxTxCredits);
+                                       spin_unlock_irqrestore(&edge_port->ep_lock,
+                                                              flags);
+                                       dev_dbg(dev, "%s - txcredits for port%d = %d\n",
diff --git a/queue-6.18/usb-serial-keyspan_pda-fix-data-loss-on-receive-throttling.patch b/queue-6.18/usb-serial-keyspan_pda-fix-data-loss-on-receive-throttling.patch
new file mode 100644 (file)
index 0000000..ae0db5e
--- /dev/null
@@ -0,0 +1,115 @@
+From 42a97c0480f96a2977e6d51ce512adc780f1ef5d Mon Sep 17 00:00:00 2001
+From: Johan Hovold <johan@kernel.org>
+Date: Wed, 8 Jul 2026 16:31:35 +0200
+Subject: USB: serial: keyspan_pda: fix data loss on receive throttling
+
+From: Johan Hovold <johan@kernel.org>
+
+commit 42a97c0480f96a2977e6d51ce512adc780f1ef5d upstream.
+
+Killing the interrupt-in urb when the line disciple requests throttling
+may lead to data loss if an ongoing transfer is cancelled.
+
+Instead set a flag to prevent the completion handler from resubmitting
+the urb until the port is unthrottled.
+
+Fixes: 1da177e4c3f4 ("Linux-2.6.12-rc2")
+Cc: stable@vger.kernel.org
+Signed-off-by: Johan Hovold <johan@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/serial/keyspan_pda.c |   44 +++++++++++++++++++++++++++++++--------
+ 1 file changed, 35 insertions(+), 9 deletions(-)
+
+--- a/drivers/usb/serial/keyspan_pda.c
++++ b/drivers/usb/serial/keyspan_pda.c
+@@ -35,6 +35,8 @@ struct keyspan_pda_private {
+       struct work_struct      unthrottle_work;
+       struct usb_serial       *serial;
+       struct usb_serial_port  *port;
++      bool                    throttled;
++      bool                    throttle_req;
+ };
+ static int keyspan_pda_write_start(struct usb_serial_port *port);
+@@ -150,6 +152,7 @@ static void keyspan_pda_rx_interrupt(str
+       int retval;
+       int status = urb->status;
+       struct keyspan_pda_private *priv;
++      bool throttled = false;
+       unsigned long flags;
+       priv = usb_get_serial_port_data(port);
+@@ -211,16 +214,24 @@ static void keyspan_pda_rx_interrupt(str
+       }
+ exit:
+-      retval = usb_submit_urb(urb, GFP_ATOMIC);
+-      if (retval)
+-              dev_err(&port->dev,
+-                      "%s - usb_submit_urb failed with result %d\n",
+-                      __func__, retval);
++      spin_lock_irqsave(&port->lock, flags);
++      if (priv->throttle_req) {
++              priv->throttled = true;
++              throttled = true;
++      }
++      spin_unlock_irqrestore(&port->lock, flags);
++
++      if (!throttled) {
++              retval = usb_submit_urb(urb, GFP_ATOMIC);
++              if (retval)
++                      dev_err(&port->dev, "failed to resubmit in urb: %d\n", retval);
++      }
+ }
+ static void keyspan_pda_rx_throttle(struct tty_struct *tty)
+ {
+       struct usb_serial_port *port = tty->driver_data;
++      struct keyspan_pda_private *priv = usb_get_serial_port_data(port);
+       /*
+        * Stop receiving characters. We just turn off the URB request, and
+@@ -230,16 +241,29 @@ static void keyspan_pda_rx_throttle(stru
+        * send an XOFF, although it might make sense to foist that off upon
+        * the device too.
+        */
+-      usb_kill_urb(port->interrupt_in_urb);
++      spin_lock_irq(&port->lock);
++      priv->throttle_req = true;
++      spin_unlock_irq(&port->lock);
+ }
+ static void keyspan_pda_rx_unthrottle(struct tty_struct *tty)
+ {
+       struct usb_serial_port *port = tty->driver_data;
++      struct keyspan_pda_private *priv = usb_get_serial_port_data(port);
++      bool throttled;
++      int ret;
++
++      spin_lock_irq(&port->lock);
++      throttled = priv->throttled;
++      priv->throttled = false;
++      priv->throttle_req = false;
++      spin_unlock_irq(&port->lock);
+-      /* just restart the receive interrupt URB */
+-      if (usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL))
+-              dev_dbg(&port->dev, "usb_submit_urb(read urb) failed\n");
++      if (throttled) {
++              ret = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
++              if (ret)
++                      dev_err(&port->dev, "failed to submit in urb: %d\n", ret);
++      }
+ }
+ static speed_t keyspan_pda_setbaud(struct usb_serial *serial, speed_t baud)
+@@ -579,6 +603,8 @@ static int keyspan_pda_open(struct tty_s
+       spin_lock_irq(&port->lock);
+       priv->tx_room = rc;
++      priv->throttled = false;
++      priv->throttle_req = false;
+       spin_unlock_irq(&port->lock);
+       rc = usb_submit_urb(port->interrupt_in_urb, GFP_KERNEL);
diff --git a/queue-6.18/usb-serial-option-add-tdtech-mt5710-cn.patch b/queue-6.18/usb-serial-option-add-tdtech-mt5710-cn.patch
new file mode 100644 (file)
index 0000000..d02559c
--- /dev/null
@@ -0,0 +1,59 @@
+From 55645e4f3c6022ffb160ad3617d2b624eaa38501 Mon Sep 17 00:00:00 2001
+From: Chukun Pan <amadeus@jmu.edu.cn>
+Date: Wed, 8 Jul 2026 18:00:01 +0800
+Subject: USB: serial: option: add TDTECH MT5710-CN
+
+From: Chukun Pan <amadeus@jmu.edu.cn>
+
+commit 55645e4f3c6022ffb160ad3617d2b624eaa38501 upstream.
+
+Add support for the TDTECH MT5710-CN (5G redcap) module based on the
+Huawei HiSilicon Balong chip.
+
+T:  Bus=01 Lev=02 Prnt=02 Port=00 Cnt=01 Dev#=  3 Spd=480  MxCh= 0
+D:  Ver= 2.10 Cls=00(>ifc ) Sub=00 Prot=00 MxPS=64 #Cfgs=  1
+P:  Vendor=3466 ProdID=3301 Rev=ff.ff
+S:  Manufacturer=TD Tech Ltd.
+S:  Product=TDTECH MT571X
+S:  SerialNumber=0123456789ABCDEF
+C:* #Ifs= 6 Cfg#= 1 Atr=c0 MxPwr=  0mA
+A:  FirstIf#= 0 IfCount= 2 Cls=02(comm.) Sub=0d Prot=00
+I:* If#= 0 Alt= 0 #EPs= 1 Cls=02(comm.) Sub=0d Prot=00 Driver=cdc_ncm
+E:  Ad=82(I) Atr=03(Int.) MxPS=  16 Ivl=32ms
+I:  If#= 1 Alt= 0 #EPs= 0 Cls=0a(data ) Sub=00 Prot=01 Driver=cdc_ncm
+I:* If#= 1 Alt= 1 #EPs= 2 Cls=0a(data ) Sub=00 Prot=01 Driver=cdc_ncm
+E:  Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+E:  Ad=01(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+I:* If#= 2 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=06 Prot=13 Driver=option
+E:  Ad=83(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+E:  Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+I:* If#= 3 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=06 Prot=12 Driver=option
+E:  Ad=84(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+E:  Ad=03(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+I:* If#= 4 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=06 Prot=1c Driver=option
+E:  Ad=85(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+E:  Ad=04(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+I:* If#= 5 Alt= 0 #EPs= 2 Cls=ff(vend.) Sub=06 Prot=14 Driver=option
+E:  Ad=86(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+E:  Ad=05(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+
+Interface: ECM / NCM + DIAG + AT + SERIAL + GPS
+
+Signed-off-by: Chukun Pan <amadeus@jmu.edu.cn>
+Cc: stable@vger.kernel.org
+Signed-off-by: Johan Hovold <johan@kernel.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/serial/option.c |    1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/usb/serial/option.c
++++ b/drivers/usb/serial/option.c
+@@ -2497,6 +2497,7 @@ static const struct usb_device_id option
+         .driver_info = RSVD(5) },
+       { USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x1003, 0xff),                     /* Rolling RW135R-GL (laptop MBIM) */
+         .driver_info = RSVD(5) },
++      { USB_DEVICE_INTERFACE_CLASS(0x3466, 0x3301, 0xff) },                   /* TDTECH MT5710-CN */
+       { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x30) },    /* NetPrisma LCUK54-WWD for Global */
+       { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0x00, 0x40) },
+       { USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x40) },
diff --git a/queue-6.18/usb-storage-add-no_ata_1x-quirk-for-longmai-usb-key.patch b/queue-6.18/usb-storage-add-no_ata_1x-quirk-for-longmai-usb-key.patch
new file mode 100644 (file)
index 0000000..d951cfb
--- /dev/null
@@ -0,0 +1,58 @@
+From 3b4ca2e01c1dd8c00b675b794732945f460a471b Mon Sep 17 00:00:00 2001
+From: Huang Wei <huangwei@kylinos.cn>
+Date: Thu, 16 Jul 2026 11:33:41 +0800
+Subject: USB: storage: add NO_ATA_1X quirk for Longmai USB Key
+
+From: Huang Wei <huangwei@kylinos.cn>
+
+commit 3b4ca2e01c1dd8c00b675b794732945f460a471b upstream.
+
+The Longmai Technologies USB Key (0x04b4:0xb708) advertises itself as a
+SCSI/Bulk-only mass storage device but does not correctly handle ATA
+pass-through commands. When such a command (ATA_12 or ATA_16) is sent to
+the device it fails to respond and the transfer eventually times out,
+leaving the device unusable.
+
+Add an unusual_devs entry for this device that sets the US_FL_NO_ATA_1X
+flag, so usb-storage short-circuits ATA pass-through commands and returns
+INVALID COMMAND OPERATION CODE (0x20 0x05 0x24 0x00) instead of forwarding
+them to the device.
+
+Information about the device in /sys/kernel/debug/usb/devices:
+
+T:  Bus=02 Lev=01 Prnt=01 Port=01 Cnt=01 Dev#= 12 Spd=480  MxCh= 0
+D:  Ver= 2.00 Cls=00(>ifc ) Sub=06 Prot=50 MxPS=64 #Cfgs=  1
+P:  Vendor=04b4 ProdID=b708 Rev= 1.00
+S:  Manufacturer=Longmai Technologies
+S:  Product=USB Key
+C:* #Ifs= 1 Cfg#= 1 Atr=80 MxPwr=100mA
+I:* If#= 0 Alt= 0 #EPs= 2 Cls=08(stor.) Sub=06 Prot=50 Driver=usb-storage
+E:  Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+E:  Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
+
+Reported-by: Ai Chao <aichao@kylinos.cn>
+Cc: stable <stable@kernel.org>
+Signed-off-by: Huang Wei <huangwei@kylinos.cn>
+Acked-by: Alan Stern <stern@rowland.harvard.edu>
+Link: https://patch.msgid.link/20260716033341.2830872-1-huangwei@kylinos.cn
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/storage/unusual_devs.h |    7 +++++++
+ 1 file changed, 7 insertions(+)
+
+--- a/drivers/usb/storage/unusual_devs.h
++++ b/drivers/usb/storage/unusual_devs.h
+@@ -395,6 +395,13 @@ UNUSUAL_DEV(  0x04b3, 0x4001, 0x0110, 0x
+               USB_SC_DEVICE, USB_PR_CB, NULL,
+               US_FL_MAX_SECTORS_MIN),
++/* Reported by Ai Chao <aichao@kylinos.cn> */
++UNUSUAL_DEV(  0x04b4, 0xb708, 0x0000, 0xffff,
++              "Longmai Technologies",
++              "USB Key",
++              USB_SC_SCSI, USB_PR_BULK, NULL,
++              US_FL_NO_ATA_1X),
++
+ /*
+  * Reported by Simon Levitt <simon@whattf.com>
+  * This entry needs Sub and Proto fields
diff --git a/queue-6.18/usb-typec-ucsi-add-duplicate-detection-to-nvidia-registration-path.patch b/queue-6.18/usb-typec-ucsi-add-duplicate-detection-to-nvidia-registration-path.patch
new file mode 100644 (file)
index 0000000..ec893c5
--- /dev/null
@@ -0,0 +1,67 @@
+From f1aa17f72f9b9589bd724dc826c5b17d164193d1 Mon Sep 17 00:00:00 2001
+From: "Chia-Lin Kao (AceLan)" <acelan.kao@canonical.com>
+Date: Mon, 13 Jul 2026 16:43:22 +0800
+Subject: usb: typec: ucsi: Add duplicate detection to nvidia registration path
+
+From: Chia-Lin Kao (AceLan) <acelan.kao@canonical.com>
+
+commit f1aa17f72f9b9589bd724dc826c5b17d164193d1 upstream.
+
+Extend the duplicate altmode detection to ucsi_register_altmodes_nvidia()
+which is used when a driver provides the update_altmodes() callback.
+
+This ensures all drivers benefit from duplicate detection, whether they
+use the standard registration path or the nvidia path with update_altmodes
+callback.
+
+Without this fix, drivers using the nvidia path (like yoga_c630) would
+still encounter duplicate altmode registration errors from buggy firmware.
+
+Fixes: a79f16efcd00 ("usb: typec: ucsi: Add support for the partner USB Modes")
+Cc: stable <stable@kernel.org>
+Signed-off-by: Chia-Lin Kao (AceLan) <acelan.kao@canonical.com>
+Link: https://patch.msgid.link/20260713084323.287516-2-acelan.kao@canonical.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/typec/ucsi/ucsi.c |   26 ++++++++++++++++----------
+ 1 file changed, 16 insertions(+), 10 deletions(-)
+
+--- a/drivers/usb/typec/ucsi/ucsi.c
++++ b/drivers/usb/typec/ucsi/ucsi.c
+@@ -664,19 +664,25 @@ ucsi_register_altmodes_nvidia(struct ucs
+       /* now register altmodes */
+       for (i = 0; i < max_altmodes; i++) {
+-              memset(&desc, 0, sizeof(desc));
+-              if (multi_dp) {
+-                      desc.svid = updated[i].svid;
+-                      desc.vdo = updated[i].mid;
+-              } else {
+-                      desc.svid = orig[i].svid;
+-                      desc.vdo = orig[i].mid;
+-              }
+-              desc.roles = TYPEC_PORT_DRD;
++              struct ucsi_altmode *altmode_array = multi_dp ? updated : orig;
+-              if (!desc.svid)
++              if (!altmode_array[i].svid)
+                       return 0;
++              /*
++               * Check for duplicates in current array and already
++               * registered altmodes. Skip if duplicate found.
++               */
++              if (ucsi_altmode_is_duplicate(con, recipient, altmode_array, i,
++                                            altmode_array[i].svid,
++                                            altmode_array[i].mid, i))
++                      continue;
++
++              memset(&desc, 0, sizeof(desc));
++              desc.svid = altmode_array[i].svid;
++              desc.vdo = altmode_array[i].mid;
++              desc.roles = TYPEC_PORT_DRD;
++
+               ret = ucsi_register_altmode(con, &desc, recipient);
+               if (ret)
+                       return ret;
diff --git a/queue-6.18/usb-typec-ucsi-detect-and-skip-duplicate-altmodes-from-buggy-firmware.patch b/queue-6.18/usb-typec-ucsi-detect-and-skip-duplicate-altmodes-from-buggy-firmware.patch
new file mode 100644 (file)
index 0000000..8ecceb6
--- /dev/null
@@ -0,0 +1,214 @@
+From 67c92c6419ea6dbc5b1f3e9691aecea956e3e81c Mon Sep 17 00:00:00 2001
+From: "Chia-Lin Kao (AceLan)" <acelan.kao@canonical.com>
+Date: Mon, 13 Jul 2026 16:43:21 +0800
+Subject: usb: typec: ucsi: Detect and skip duplicate altmodes from buggy firmware
+
+From: Chia-Lin Kao (AceLan) <acelan.kao@canonical.com>
+
+commit 67c92c6419ea6dbc5b1f3e9691aecea956e3e81c upstream.
+
+Some firmware implementations incorrectly return the same altmode
+multiple times at different offsets when queried via
+UCSI_GET_ALTERNATE_MODES.  This causes sysfs duplicate filename errors
+and kernel call traces when the driver attempts to register the same
+altmode twice:
+
+  sysfs: cannot create duplicate filename '/devices/.../typec/port0/port0.0/partner'
+  typec-thunderbolt port0-partner.1: failed to create symlinks
+  typec-thunderbolt port0-partner.1: probe with driver typec-thunderbolt failed with error -17
+
+The matching rules differ by recipient:
+
+  - UCSI_RECIPIENT_CON (port) and UCSI_RECIPIENT_SOP_P (plug):
+    Two altmodes with identical SVID and VDO are byte-for-byte
+    duplicates and the second has no observable function, so drop it.
+
+  - UCSI_RECIPIENT_SOP (partner):
+    The typec class binds each partner altmode to a port altmode of
+    the same SVID via altmode_match()/device_find_child(), which
+    returns the first port altmode with a matching SVID.  If the
+    partner advertises more altmodes for SVID X than the port
+    advertises, the surplus partner altmode(s) collapse onto an
+    already-paired port altmode and trigger the
+    "duplicate filename .../partner" sysfs error during
+    typec_altmode_create_links().  Use the port-side altmode count for
+    SVID X as the authoritative cap and reject any partner altmode
+    that would exceed it.  This preserves legitimate multi-Mode
+    partner altmodes (vendor SVIDs that the port really does
+    advertise more than once) while filtering the firmware-generated
+    duplicates that have no port counterpart, and is therefore
+    stricter than a plain SVID+VDO comparison (which still admits the
+    Thunderbolt case where firmware reports the same SVID twice with
+    different VDOs) without being over-broad like a plain SVID match
+    (which would falsely drop legitimate vendor multi-Mode entries).
+
+If a duplicate is detected, skip it and emit a clean warning instead
+of generating a kernel call trace:
+
+  ucsi_acpi USBC000:00: con2: Firmware bug: duplicate partner altmode SVID 0x8087 at offset 1, ignoring.
+  ucsi_acpi USBC000:00: con2: VDO mismatch: 0x8087a043 vs 0x00000001
+
+The duplicate detection logic lives in a reusable helper
+ucsi_altmode_is_duplicate() and is invoked from
+ucsi_register_altmodes().  It applies to all three recipient types:
+partner (SOP), port (CON), and plug (SOP_P) altmodes.
+
+Fixes: a79f16efcd00 ("usb: typec: ucsi: Add support for the partner USB Modes")
+Cc: stable <stable@kernel.org>
+Signed-off-by: Chia-Lin Kao (AceLan) <acelan.kao@canonical.com>
+unchanged: still SVID+VDO exact-dup match.
+Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>
+Link: https://patch.msgid.link/20260713084323.287516-1-acelan.kao@canonical.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/usb/typec/ucsi/ucsi.c |  132 ++++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 132 insertions(+)
+
+--- a/drivers/usb/typec/ucsi/ucsi.c
++++ b/drivers/usb/typec/ucsi/ucsi.c
+@@ -487,6 +487,129 @@ err:
+       return ret;
+ }
++static void ucsi_dump_duplicate_altmode(struct ucsi_connector *con,
++                                      u8 recipient, u16 svid,
++                                      u32 existing_vdo, u32 new_vdo,
++                                      int offset)
++{
++      static const char * const recipient_names[] = {
++              [UCSI_RECIPIENT_CON]    = "port",
++              [UCSI_RECIPIENT_SOP]    = "partner",
++              [UCSI_RECIPIENT_SOP_P]  = "plug",
++              [UCSI_RECIPIENT_SOP_PP] = "cable plug prime",
++      };
++
++      dev_warn(con->ucsi->dev,
++               "con%d: Firmware bug: duplicate %s altmode SVID 0x%04x at offset %d, ignoring but please contact the BIOS vendor to fix this issue.\n",
++               con->num, recipient_names[recipient], svid, offset);
++
++      if (existing_vdo != new_vdo)
++              dev_warn(con->ucsi->dev,
++                       "con%d: VDO mismatch: 0x%08x vs 0x%08x\n",
++                       con->num, existing_vdo, new_vdo);
++}
++
++/* Count altmodes in @altmodes that advertise @svid. */
++static int ucsi_altmode_count_svid(struct typec_altmode **altmodes, u16 svid)
++{
++      int count = 0;
++      int k;
++
++      for (k = 0; k < UCSI_MAX_ALTMODES; k++) {
++              if (!altmodes[k])
++                      break;
++              if (altmodes[k]->svid == svid)
++                      count++;
++      }
++
++      return count;
++}
++
++/*
++ * Check if an altmode is a duplicate. Some firmware implementations
++ * incorrectly return the same altmode multiple times, causing sysfs errors.
++ * Returns true if the altmode should be skipped.
++ *
++ * The matching rules differ by recipient:
++ *
++ *   - UCSI_RECIPIENT_CON (port) and UCSI_RECIPIENT_SOP_P (plug):
++ *     Two altmodes with identical SVID and VDO are byte-for-byte duplicates
++ *     and the second has no observable function. Drop them.
++ *
++ *   - UCSI_RECIPIENT_SOP (partner):
++ *     The typec class binds each partner altmode to a port altmode of the
++ *     same SVID via altmode_match()/device_find_child(), which returns the
++ *     first port altmode with a matching SVID. If the partner advertises
++ *     more altmodes for SVID X than the port advertises, the surplus
++ *     partner altmode(s) collapse onto an already-paired port altmode and
++ *     trigger a "duplicate filename .../partner" sysfs error during
++ *     typec_altmode_create_links(). Use the port-side altmode count for
++ *     SVID X as the authoritative cap and reject any partner altmode that
++ *     would exceed it. This preserves legitimate multi-Mode partner
++ *     altmodes (e.g. vendor SVIDs that the port really does advertise
++ *     twice) while filtering the firmware-generated duplicates that have
++ *     no port counterpart.
++ */
++static bool ucsi_altmode_is_duplicate(struct ucsi_connector *con, u8 recipient,
++                                    const struct ucsi_altmode *alt_batch, int batch_idx,
++                                    u16 svid, u32 vdo, int offset)
++{
++      struct typec_altmode **altmodes;
++      int port_count, partner_count;
++      int k;
++
++      /* Check for duplicates within the current batch first */
++      for (k = 0; k < batch_idx; k++) {
++              if (alt_batch[k].svid == svid && alt_batch[k].mid == vdo) {
++                      ucsi_dump_duplicate_altmode(con, recipient, svid,
++                                                  vdo, vdo, offset);
++                      return true;
++              }
++      }
++
++      switch (recipient) {
++      case UCSI_RECIPIENT_SOP:
++              /*
++               * Cap partner altmodes per SVID by the port-side count:
++               * any further partner altmode for that SVID would alias an
++               * already-paired port altmode and break typec sysfs.
++               */
++              port_count = ucsi_altmode_count_svid(con->port_altmode, svid);
++              partner_count = ucsi_altmode_count_svid(con->partner_altmode,
++                                                      svid);
++              if (port_count && partner_count >= port_count) {
++                      ucsi_dump_duplicate_altmode(con, recipient, svid,
++                                                  con->partner_altmode[partner_count - 1]->vdo,
++                                                  vdo, offset);
++                      return true;
++              }
++              return false;
++      case UCSI_RECIPIENT_CON:
++              altmodes = con->port_altmode;
++              break;
++      case UCSI_RECIPIENT_SOP_P:
++              altmodes = con->plug_altmode;
++              break;
++      default:
++              return false;
++      }
++
++      /* CON and SOP_P: drop only exact SVID+VDO duplicates. */
++      for (k = 0; k < UCSI_MAX_ALTMODES; k++) {
++              if (!altmodes[k])
++                      break;
++
++              if (altmodes[k]->svid != svid || altmodes[k]->vdo != vdo)
++                      continue;
++
++              ucsi_dump_duplicate_altmode(con, recipient, svid,
++                                          altmodes[k]->vdo, vdo, offset);
++              return true;
++      }
++
++      return false;
++}
++
+ static int
+ ucsi_register_altmodes_nvidia(struct ucsi_connector *con, u8 recipient)
+ {
+@@ -611,6 +734,15 @@ static int ucsi_register_altmodes(struct
+                       if (!alt[j].svid)
+                               return 0;
++                      /*
++                       * Check for duplicates in current batch and already
++                       * registered altmodes. Skip if duplicate found.
++                       */
++                      if (ucsi_altmode_is_duplicate(con, recipient, alt, j,
++                                                    alt[j].svid, alt[j].mid,
++                                                    i - num + j))
++                              continue;
++
+                       memset(&desc, 0, sizeof(desc));
+                       desc.vdo = alt[j].mid;
+                       desc.svid = alt[j].svid;
diff --git a/queue-6.18/wifi-at76c50x-usb-avoid-length-underflow-in-at76_guess_freq.patch b/queue-6.18/wifi-at76c50x-usb-avoid-length-underflow-in-at76_guess_freq.patch
new file mode 100644 (file)
index 0000000..1632853
--- /dev/null
@@ -0,0 +1,59 @@
+From 61a799ffd1e5a4fd3702d547828b7ff3d161468e Mon Sep 17 00:00:00 2001
+From: Huihui Huang <hhhuang@smu.edu.sg>
+Date: Wed, 15 Jul 2026 22:08:10 +0800
+Subject: wifi: at76c50x-usb: avoid length underflow in at76_guess_freq()
+
+From: Huihui Huang <hhhuang@smu.edu.sg>
+
+commit 61a799ffd1e5a4fd3702d547828b7ff3d161468e upstream.
+
+at76_guess_freq() checks only that the received frame is at least a bare
+802.11 header (24 bytes) before subtracting the fixed management-body
+offset:
+
+       len -= el_off;
+
+For both beacon and probe response frames, el_off is 36. If the frame is
+shorter than el_off, subtracting it causes the calculated IE length to
+wrap. The length is eventually passed to cfg80211_find_elem_match() as a
+very large unsigned value, so the element walk runs beyond the RX skb.
+
+This path is reached from at76_rx_tasklet() while scanning. If the device
+delivers a truncated beacon or probe response, the oversized IE length
+causes an out-of-bounds read during scanning.
+
+Skip the IE lookup if the frame does not reach the variable elements,
+before subtracting el_off.
+
+Fixes: 1264b951463a ("at76c50x-usb: add driver")
+Cc: stable@vger.kernel.org
+Assisted-by: Claude:claude-opus-4-8
+Signed-off-by: Huihui Huang <hhhuang@smu.edu.sg>
+Link: https://patch.msgid.link/20260715140815.1242033-1-hhhuang@smu.edu.sg
+Signed-off-by: Johannes Berg <johannes.berg@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ drivers/net/wireless/atmel/at76c50x-usb.c |    7 +++++--
+ 1 file changed, 5 insertions(+), 2 deletions(-)
+
+--- a/drivers/net/wireless/atmel/at76c50x-usb.c
++++ b/drivers/net/wireless/atmel/at76c50x-usb.c
+@@ -1521,13 +1521,16 @@ static inline int at76_guess_freq(struct
+       if (ieee80211_is_probe_resp(hdr->frame_control)) {
+               el_off = offsetof(struct ieee80211_mgmt, u.probe_resp.variable);
+-              el = ((struct ieee80211_mgmt *)hdr)->u.probe_resp.variable;
+       } else if (ieee80211_is_beacon(hdr->frame_control)) {
+               el_off = offsetof(struct ieee80211_mgmt, u.beacon.variable);
+-              el = ((struct ieee80211_mgmt *)hdr)->u.beacon.variable;
+       } else {
+               goto exit;
+       }
++
++      if (len < el_off)
++              goto exit;
++
++      el = priv->rx_skb->data + el_off;
+       len -= el_off;
+       el = cfg80211_find_ie(WLAN_EID_DS_PARAMS, el, len);