From: Greg Kroah-Hartman Date: Mon, 27 Jul 2026 13:34:59 +0000 (+0200) Subject: 6.18-stable patches X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=41573f8f30236da9c37e6af69e8060daacc0b17b;p=thirdparty%2Fkernel%2Fstable-queue.git 6.18-stable patches 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 --- diff --git a/queue-6.18/series b/queue-6.18/series index 5bd94d2e63..bcdded23bd 100644 --- a/queue-6.18/series +++ b/queue-6.18/series @@ -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 index 0000000000..b002503539 --- /dev/null +++ b/queue-6.18/usb-chipidea-fix-usage_count-leak-when-autosuspend_delay-is-negative.patch @@ -0,0 +1,37 @@ +From fc3afb5728e297994863f8a2a01b88a920bbf53e Mon Sep 17 00:00:00 2001 +From: Xu Yang +Date: Thu, 16 Jul 2026 18:41:26 +0800 +Subject: usb: chipidea: fix usage_count leak when autosuspend_delay is negative + +From: Xu Yang + +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 +Assisted-by: Claude:claude-sonnet-4.6 +Signed-off-by: Xu Yang +Reviewed-by: Frank Li +Link: https://patch.msgid.link/20260716104126.2763454-1-xu.yang_2@oss.nxp.com +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..17018e8803 --- /dev/null +++ b/queue-6.18/usb-core-port-deattach-type-c-connector-on-component-unbind.patch @@ -0,0 +1,100 @@ +From e0b291fe117964037e0ba382eff4bb365d531c3a Mon Sep 17 00:00:00 2001 +From: "Chia-Lin Kao (AceLan)" +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) + +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 +Signed-off-by: Chia-Lin Kao (AceLan) +Reviewed-by: Heikki Krogerus +Link: https://patch.msgid.link/20260611071201.1235545-1-acelan.kao@canonical.com +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..4372bbe7d5 --- /dev/null +++ b/queue-6.18/usb-core-sysfs-add-lock-to-bos_descriptors_read.patch @@ -0,0 +1,52 @@ +From 4e0197fbb0eec588795d5431716a244d9ac8fa93 Mon Sep 17 00:00:00 2001 +From: Griffin Kroah-Hartman +Date: Wed, 15 Jul 2026 16:59:05 +0200 +Subject: usb: core: sysfs: add lock to bos_descriptors_read() + +From: Griffin Kroah-Hartman + +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 +Acked-by: Alan Stern +Cc: stable +Link: https://patch.msgid.link/20260715-usb_core_patches_3-v1-1-53021f5576fd@kroah.com +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..d35680e08c --- /dev/null +++ b/queue-6.18/usb-gadget-dummy_hcd-prevent-fifo_req-reuse-during-giveback.patch @@ -0,0 +1,141 @@ +From d5e5cd3654d2b5359a12ea6586120f05b28634ee Mon Sep 17 00:00:00 2001 +From: Jinchao Wang +Date: Thu, 16 Jul 2026 06:42:17 -0400 +Subject: usb: gadget: dummy_hcd: prevent fifo_req reuse during giveback + +From: Jinchao Wang + +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 +Signed-off-by: Jinchao Wang +Reviewed-by: Alan Stern +Link: https://patch.msgid.link/5db8bba5b3499a86cd2e776f9918126b68b2508b.1784198306.git.wangjinchao600@gmail.com +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..7ecfae928d --- /dev/null +++ b/queue-6.18/usb-gadget-f_midi-cancel-pending-in-work-before-freeing-the-midi-object.patch @@ -0,0 +1,68 @@ +From 5650c18d93a1db7e27cb5a40b394747eb4686d5b Mon Sep 17 00:00:00 2001 +From: Fan Wu +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 + +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 +Assisted-by: Codex:gpt-5.5 +Signed-off-by: Fan Wu +Link: https://patch.msgid.link/20260709150717.399083-1-fanwu01@zju.edu.cn +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..5e77af6053 --- /dev/null +++ b/queue-6.18/usb-gadget-f_ncm-validate-datagram-bounds-in-ncm_unwrap_ntb.patch @@ -0,0 +1,91 @@ +From 1febec7e47cdcd01f43fb0211094e3010474666e Mon Sep 17 00:00:00 2001 +From: Sonali Pradhan +Date: Fri, 3 Jul 2026 08:37:24 +0000 +Subject: usb: gadget: f_ncm: validate datagram bounds in ncm_unwrap_ntb() + +From: Sonali Pradhan + +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 +Assisted-by: Jetski:Gemini-2.5-Pro +Signed-off-by: Sonali Pradhan +Link: https://patch.msgid.link/20260703083725.1903850-1-sonalipradhan@google.com +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..779c240a3e --- /dev/null +++ b/queue-6.18/usb-gadget-fsl-udc-fix-dev_printk-device.patch @@ -0,0 +1,420 @@ +From c4dd150fceab281496acb3a643ae712aacb74864 Mon Sep 17 00:00:00 2001 +From: Johan Hovold +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 + +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 +Cc: Uwe Kleine-König +Signed-off-by: Johan Hovold +Acked-by: Uwe Kleine-König +Link: https://patch.msgid.link/20260702141536.90887-4-johan@kernel.org +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..f5ede5634a --- /dev/null +++ b/queue-6.18/usb-gadget-fsl-udc-fix-device-name-leak-on-probe-failure.patch @@ -0,0 +1,32 @@ +From 6b874d00c466e73c6448a89856407fe46b2f50e4 Mon Sep 17 00:00:00 2001 +From: Johan Hovold +Date: Thu, 2 Jul 2026 16:15:33 +0200 +Subject: USB: gadget: fsl-udc: fix device name leak on probe failure + +From: Johan Hovold + +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 +Signed-off-by: Johan Hovold +Link: https://patch.msgid.link/20260702141536.90887-2-johan@kernel.org +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..60dfcba8ba --- /dev/null +++ b/queue-6.18/usb-gadget-printer-fix-infinite-loop-in-printer_read.patch @@ -0,0 +1,77 @@ +From c2e819be6a5c7f34344926b4bd7e3dfca58cf48a Mon Sep 17 00:00:00 2001 +From: Melbin K Mathew +Date: Thu, 9 Jul 2026 21:56:22 +0100 +Subject: usb: gadget: printer: fix infinite loop in printer_read() + +From: Melbin K Mathew + +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 +Reviewed-by: Peter Chen +Signed-off-by: Melbin K Mathew +Link: https://patch.msgid.link/20260709205622.55700-1-mlbnkm1@gmail.com +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..ca246e8784 --- /dev/null +++ b/queue-6.18/usb-gadget-snps-udc-fix-device-name-leak-on-probe-failure.patch @@ -0,0 +1,32 @@ +From 29a142d3e8b35ebc9e0bcc78f4bc26c9b6a9ac0b Mon Sep 17 00:00:00 2001 +From: Johan Hovold +Date: Thu, 2 Jul 2026 16:15:34 +0200 +Subject: USB: gadget: snps-udc: fix device name leak on probe failure + +From: Johan Hovold + +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 +Signed-off-by: Johan Hovold +Link: https://patch.msgid.link/20260702141536.90887-3-johan@kernel.org +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..92ab9ff1f0 --- /dev/null +++ b/queue-6.18/usb-gadget-udc-bdc-free-irq-and-drain-func_wake_notify-before-teardown.patch @@ -0,0 +1,120 @@ +From 0583f2fbf8f86ae3a0ce054f96783dd83e65d9bb Mon Sep 17 00:00:00 2001 +From: Fan Wu +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 + +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 +Assisted-by: Codex:gpt-5.5 +Signed-off-by: Fan Wu +Link: https://patch.msgid.link/20260709020904.502611-1-fanwu01@zju.edu.cn +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..dcbc25a854 --- /dev/null +++ b/queue-6.18/usb-gadget-uvc-clamp-send_response-length-to-the-response-buffer.patch @@ -0,0 +1,46 @@ +From b70dc75e85ba968b7b76eebfe5d63000080b875b Mon Sep 17 00:00:00 2001 +From: Muhammad Bilal +Date: Tue, 30 Jun 2026 00:50:04 +0500 +Subject: usb: gadget: uvc: clamp SEND_RESPONSE length to the response buffer + +From: Muhammad Bilal + +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 +Signed-off-by: Muhammad Bilal +Link: https://patch.msgid.link/20260629195004.148405-1-meatuni001@gmail.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..218522a9cb --- /dev/null +++ b/queue-6.18/usb-musb-omap2430-do-not-put-borrowed-of_node-in-probe.patch @@ -0,0 +1,49 @@ +From c947360ae63eee1c9eacc030dd6f5a53f717addf Mon Sep 17 00:00:00 2001 +From: Guangshuo Li +Date: Mon, 13 Jul 2026 19:47:11 +0800 +Subject: usb: musb: omap2430: Do not put borrowed of_node in probe + +From: Guangshuo Li + +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 +Reviewed-by: Johan Hovold +Signed-off-by: Guangshuo Li +Link: https://patch.msgid.link/20260713114711.955253-1-lgs201920130244@gmail.com +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..ad9df5ad32 --- /dev/null +++ b/queue-6.18/usb-serial-ftdi_sio-add-support-for-e-h-fxa291.patch @@ -0,0 +1,57 @@ +From fad0fd120e29041b3e6cdf41bb12e3184fb524a2 Mon Sep 17 00:00:00 2001 +From: Tim Pambor +Date: Sat, 11 Jul 2026 17:36:30 +0000 +Subject: USB: serial: ftdi_sio: add support for E+H FXA291 + +From: Tim Pambor + +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 +Cc: stable@vger.kernel.org +Signed-off-by: Johan Hovold +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..a8f8cb071e --- /dev/null +++ b/queue-6.18/usb-serial-io_edgeport-cap-received-transmit-credits.patch @@ -0,0 +1,42 @@ +From faaddd811c5099f11a5f52e68a6b31a5898cda4f Mon Sep 17 00:00:00 2001 +From: Sunho Park +Date: Tue, 14 Jul 2026 19:42:30 +0900 +Subject: USB: serial: io_edgeport: cap received transmit credits + +From: Sunho Park + +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 +Signed-off-by: Johan Hovold +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..ae0db5eb1e --- /dev/null +++ b/queue-6.18/usb-serial-keyspan_pda-fix-data-loss-on-receive-throttling.patch @@ -0,0 +1,115 @@ +From 42a97c0480f96a2977e6d51ce512adc780f1ef5d Mon Sep 17 00:00:00 2001 +From: Johan Hovold +Date: Wed, 8 Jul 2026 16:31:35 +0200 +Subject: USB: serial: keyspan_pda: fix data loss on receive throttling + +From: Johan Hovold + +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 +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..d02559cfcc --- /dev/null +++ b/queue-6.18/usb-serial-option-add-tdtech-mt5710-cn.patch @@ -0,0 +1,59 @@ +From 55645e4f3c6022ffb160ad3617d2b624eaa38501 Mon Sep 17 00:00:00 2001 +From: Chukun Pan +Date: Wed, 8 Jul 2026 18:00:01 +0800 +Subject: USB: serial: option: add TDTECH MT5710-CN + +From: Chukun Pan + +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 +Cc: stable@vger.kernel.org +Signed-off-by: Johan Hovold +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..d951cfb349 --- /dev/null +++ b/queue-6.18/usb-storage-add-no_ata_1x-quirk-for-longmai-usb-key.patch @@ -0,0 +1,58 @@ +From 3b4ca2e01c1dd8c00b675b794732945f460a471b Mon Sep 17 00:00:00 2001 +From: Huang Wei +Date: Thu, 16 Jul 2026 11:33:41 +0800 +Subject: USB: storage: add NO_ATA_1X quirk for Longmai USB Key + +From: Huang Wei + +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 +Cc: stable +Signed-off-by: Huang Wei +Acked-by: Alan Stern +Link: https://patch.msgid.link/20260716033341.2830872-1-huangwei@kylinos.cn +Signed-off-by: Greg Kroah-Hartman +--- + 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 */ ++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 + * 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 index 0000000000..ec893c5cd5 --- /dev/null +++ b/queue-6.18/usb-typec-ucsi-add-duplicate-detection-to-nvidia-registration-path.patch @@ -0,0 +1,67 @@ +From f1aa17f72f9b9589bd724dc826c5b17d164193d1 Mon Sep 17 00:00:00 2001 +From: "Chia-Lin Kao (AceLan)" +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) + +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 +Signed-off-by: Chia-Lin Kao (AceLan) +Link: https://patch.msgid.link/20260713084323.287516-2-acelan.kao@canonical.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..8ecceb6a19 --- /dev/null +++ b/queue-6.18/usb-typec-ucsi-detect-and-skip-duplicate-altmodes-from-buggy-firmware.patch @@ -0,0 +1,214 @@ +From 67c92c6419ea6dbc5b1f3e9691aecea956e3e81c Mon Sep 17 00:00:00 2001 +From: "Chia-Lin Kao (AceLan)" +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) + +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 +Signed-off-by: Chia-Lin Kao (AceLan) +unchanged: still SVID+VDO exact-dup match. +Reviewed-by: Heikki Krogerus +Link: https://patch.msgid.link/20260713084323.287516-1-acelan.kao@canonical.com +Signed-off-by: Greg Kroah-Hartman +--- + 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 index 0000000000..1632853c15 --- /dev/null +++ b/queue-6.18/wifi-at76c50x-usb-avoid-length-underflow-in-at76_guess_freq.patch @@ -0,0 +1,59 @@ +From 61a799ffd1e5a4fd3702d547828b7ff3d161468e Mon Sep 17 00:00:00 2001 +From: Huihui Huang +Date: Wed, 15 Jul 2026 22:08:10 +0800 +Subject: wifi: at76c50x-usb: avoid length underflow in at76_guess_freq() + +From: Huihui Huang + +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 +Link: https://patch.msgid.link/20260715140815.1242033-1-hhhuang@smu.edu.sg +Signed-off-by: Johannes Berg +Signed-off-by: Greg Kroah-Hartman +--- + 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);