From: Greg Kroah-Hartman Date: Wed, 8 Apr 2026 12:15:23 +0000 (+0200) Subject: 6.1-stable patches X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=22bcfcf1cce2d19eba59bd2ac8b854796ff22576;p=thirdparty%2Fkernel%2Fstable-queue.git 6.1-stable patches added patches: usb-gadget-f_rndis-protect-rndis-options-with-mutex.patch usb-gadget-f_subset-fix-unbalanced-refcnt-in-geth_free.patch usb-gadget-f_uac1_legacy-validate-control-request-size.patch usb-gadget-uvc-fix-null-pointer-dereference-during-unbind-race.patch --- diff --git a/queue-6.1/series b/queue-6.1/series index cf078d3d30..5a70daf5ae 100644 --- a/queue-6.1/series +++ b/queue-6.1/series @@ -265,3 +265,7 @@ nvme-fix-admin-request_queue-lifetime.patch-5284 nvme-fix-admin-queue-leak-on-controller-reset.patch mm-damon-sysfs-check-contexts-nr-before-accessing-co.patch net-enetc-fix-pf-of_device_is_available-teardown-pat.patch +usb-gadget-uvc-fix-null-pointer-dereference-during-unbind-race.patch +usb-gadget-f_subset-fix-unbalanced-refcnt-in-geth_free.patch +usb-gadget-f_rndis-protect-rndis-options-with-mutex.patch +usb-gadget-f_uac1_legacy-validate-control-request-size.patch diff --git a/queue-6.1/usb-gadget-f_rndis-protect-rndis-options-with-mutex.patch b/queue-6.1/usb-gadget-f_rndis-protect-rndis-options-with-mutex.patch new file mode 100644 index 0000000000..c95eebbe34 --- /dev/null +++ b/queue-6.1/usb-gadget-f_rndis-protect-rndis-options-with-mutex.patch @@ -0,0 +1,49 @@ +From 8d8c68b1fc06ece60cf43e1306ff0f4ac121547e Mon Sep 17 00:00:00 2001 +From: Kuen-Han Tsai +Date: Fri, 20 Mar 2026 16:54:45 +0800 +Subject: usb: gadget: f_rndis: Protect RNDIS options with mutex + +From: Kuen-Han Tsai + +commit 8d8c68b1fc06ece60cf43e1306ff0f4ac121547e upstream. + +The class/subclass/protocol options are suspectible to race conditions +as they can be accessed concurrently through configfs. + +Use existing mutex to protect these options. This issue was identified +during code inspection. + +Fixes: 73517cf49bd4 ("usb: gadget: add RNDIS configfs options for class/subclass/protocol") +Cc: stable@vger.kernel.org +Signed-off-by: Kuen-Han Tsai +Link: https://patch.msgid.link/20260320-usb-net-lifecycle-v1-2-4886b578161b@google.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/usb/gadget/function/f_rndis.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +--- a/drivers/usb/gadget/function/f_rndis.c ++++ b/drivers/usb/gadget/function/f_rndis.c +@@ -11,6 +11,7 @@ + + /* #define VERBOSE_DEBUG */ + ++#include + #include + #include + #include +@@ -691,9 +692,11 @@ rndis_bind(struct usb_configuration *c, + return -ENOMEM; + } + +- rndis_iad_descriptor.bFunctionClass = rndis_opts->class; +- rndis_iad_descriptor.bFunctionSubClass = rndis_opts->subclass; +- rndis_iad_descriptor.bFunctionProtocol = rndis_opts->protocol; ++ scoped_guard(mutex, &rndis_opts->lock) { ++ rndis_iad_descriptor.bFunctionClass = rndis_opts->class; ++ rndis_iad_descriptor.bFunctionSubClass = rndis_opts->subclass; ++ rndis_iad_descriptor.bFunctionProtocol = rndis_opts->protocol; ++ } + + /* + * in drivers/usb/gadget/configfs.c:configfs_composite_bind() diff --git a/queue-6.1/usb-gadget-f_subset-fix-unbalanced-refcnt-in-geth_free.patch b/queue-6.1/usb-gadget-f_subset-fix-unbalanced-refcnt-in-geth_free.patch new file mode 100644 index 0000000000..1c1e7003b5 --- /dev/null +++ b/queue-6.1/usb-gadget-f_subset-fix-unbalanced-refcnt-in-geth_free.patch @@ -0,0 +1,48 @@ +From caa27923aacd8a5869207842f2ab1657c6c0c7bc Mon Sep 17 00:00:00 2001 +From: Kuen-Han Tsai +Date: Fri, 20 Mar 2026 16:54:44 +0800 +Subject: usb: gadget: f_subset: Fix unbalanced refcnt in geth_free + +From: Kuen-Han Tsai + +commit caa27923aacd8a5869207842f2ab1657c6c0c7bc upstream. + +geth_alloc() increments the reference count, but geth_free() fails to +decrement it. This prevents the configuration of attributes via configfs +after unlinking the function. + +Decrement the reference count in geth_free() to ensure proper cleanup. + +Fixes: 02832e56f88a ("usb: gadget: f_subset: add configfs support") +Cc: stable@vger.kernel.org +Signed-off-by: Kuen-Han Tsai +Link: https://patch.msgid.link/20260320-usb-net-lifecycle-v1-1-4886b578161b@google.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/usb/gadget/function/f_subset.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/drivers/usb/gadget/function/f_subset.c ++++ b/drivers/usb/gadget/function/f_subset.c +@@ -6,6 +6,7 @@ + * Copyright (C) 2008 Nokia Corporation + */ + ++#include + #include + #include + #include +@@ -451,8 +452,13 @@ static struct usb_function_instance *get + static void geth_free(struct usb_function *f) + { + struct f_gether *eth; ++ struct f_gether_opts *opts; ++ ++ opts = container_of(f->fi, struct f_gether_opts, func_inst); + + eth = func_to_geth(f); ++ scoped_guard(mutex, &opts->lock) ++ opts->refcnt--; + kfree(eth); + } + diff --git a/queue-6.1/usb-gadget-f_uac1_legacy-validate-control-request-size.patch b/queue-6.1/usb-gadget-f_uac1_legacy-validate-control-request-size.patch new file mode 100644 index 0000000000..a87e0d14b0 --- /dev/null +++ b/queue-6.1/usb-gadget-f_uac1_legacy-validate-control-request-size.patch @@ -0,0 +1,92 @@ +From 6e0e34d85cd46ceb37d16054e97a373a32770f6c Mon Sep 17 00:00:00 2001 +From: Taegu Ha +Date: Thu, 2 Apr 2026 04:13:11 +0900 +Subject: usb: gadget: f_uac1_legacy: validate control request size + +From: Taegu Ha + +commit 6e0e34d85cd46ceb37d16054e97a373a32770f6c upstream. + +f_audio_complete() copies req->length bytes into a 4-byte stack +variable: + + u32 data = 0; + memcpy(&data, req->buf, req->length); + +req->length is derived from the host-controlled USB request path, +which can lead to a stack out-of-bounds write. + +Validate req->actual against the expected payload size for the +supported control selectors and decode only the expected amount +of data. + +This avoids copying a host-influenced length into a fixed-size +stack object. + +Signed-off-by: Taegu Ha +Cc: stable +Link: https://patch.msgid.link/20260401191311.3604898-1-hataegu0826@gmail.com +Signed-off-by: Greg Kroah-Hartman +--- + drivers/usb/gadget/function/f_uac1_legacy.c | 47 ++++++++++++++++++++++------ + 1 file changed, 37 insertions(+), 10 deletions(-) + +--- a/drivers/usb/gadget/function/f_uac1_legacy.c ++++ b/drivers/usb/gadget/function/f_uac1_legacy.c +@@ -360,19 +360,46 @@ static int f_audio_out_ep_complete(struc + static void f_audio_complete(struct usb_ep *ep, struct usb_request *req) + { + struct f_audio *audio = req->context; +- int status = req->status; +- u32 data = 0; + struct usb_ep *out_ep = audio->out_ep; + +- switch (status) { +- +- case 0: /* normal completion? */ +- if (ep == out_ep) ++ switch (req->status) { ++ case 0: ++ if (ep == out_ep) { + f_audio_out_ep_complete(ep, req); +- else if (audio->set_con) { +- memcpy(&data, req->buf, req->length); +- audio->set_con->set(audio->set_con, audio->set_cmd, +- le16_to_cpu(data)); ++ } else if (audio->set_con) { ++ struct usb_audio_control *con = audio->set_con; ++ u8 type = con->type; ++ u32 data; ++ bool valid_request = false; ++ ++ switch (type) { ++ case UAC_FU_MUTE: { ++ u8 value; ++ ++ if (req->actual == sizeof(value)) { ++ memcpy(&value, req->buf, sizeof(value)); ++ data = value; ++ valid_request = true; ++ } ++ break; ++ } ++ case UAC_FU_VOLUME: { ++ __le16 value; ++ ++ if (req->actual == sizeof(value)) { ++ memcpy(&value, req->buf, sizeof(value)); ++ data = le16_to_cpu(value); ++ valid_request = true; ++ } ++ break; ++ } ++ } ++ ++ if (valid_request) ++ con->set(con, audio->set_cmd, data); ++ else ++ usb_ep_set_halt(ep); ++ + audio->set_con = NULL; + } + break; diff --git a/queue-6.1/usb-gadget-uvc-fix-null-pointer-dereference-during-unbind-race.patch b/queue-6.1/usb-gadget-uvc-fix-null-pointer-dereference-during-unbind-race.patch new file mode 100644 index 0000000000..89a6dcc738 --- /dev/null +++ b/queue-6.1/usb-gadget-uvc-fix-null-pointer-dereference-during-unbind-race.patch @@ -0,0 +1,213 @@ +From eba2936bbe6b752a31725a9eb5c674ecbf21ee7d Mon Sep 17 00:00:00 2001 +From: Jimmy Hu +Date: Fri, 20 Mar 2026 14:54:27 +0800 +Subject: usb: gadget: uvc: fix NULL pointer dereference during unbind race + +From: Jimmy Hu + +commit eba2936bbe6b752a31725a9eb5c674ecbf21ee7d upstream. + +Commit b81ac4395bbe ("usb: gadget: uvc: allow for application to cleanly +shutdown") introduced two stages of synchronization waits totaling 1500ms +in uvc_function_unbind() to prevent several types of kernel panics. +However, this timing-based approach is insufficient during power +management (PM) transitions. + +When the PM subsystem starts freezing user space processes, the +wait_event_interruptible_timeout() is aborted early, which allows the +unbind thread to proceed and nullify the gadget pointer +(cdev->gadget = NULL): + +[ 814.123447][ T947] configfs-gadget.g1 gadget.0: uvc: uvc_function_unbind() +[ 814.178583][ T3173] PM: suspend entry (deep) +[ 814.192487][ T3173] Freezing user space processes +[ 814.197668][ T947] configfs-gadget.g1 gadget.0: uvc: uvc_function_unbind no clean disconnect, wait for release + +When the PM subsystem resumes or aborts the suspend and tasks are +restarted, the V4L2 release path is executed and attempts to access the +already nullified gadget pointer, triggering a kernel panic: + +[ 814.292597][ C0] PM: pm_system_irq_wakeup: 479 triggered dhdpcie_host_wake +[ 814.386727][ T3173] Restarting tasks ... +[ 814.403522][ T4558] Unable to handle kernel NULL pointer dereference at virtual address 0000000000000030 +[ 814.404021][ T4558] pc : usb_gadget_deactivate+0x14/0xf4 +[ 814.404031][ T4558] lr : usb_function_deactivate+0x54/0x94 +[ 814.404078][ T4558] Call trace: +[ 814.404080][ T4558] usb_gadget_deactivate+0x14/0xf4 +[ 814.404083][ T4558] usb_function_deactivate+0x54/0x94 +[ 814.404087][ T4558] uvc_function_disconnect+0x1c/0x5c +[ 814.404092][ T4558] uvc_v4l2_release+0x44/0xac +[ 814.404095][ T4558] v4l2_release+0xcc/0x130 + +Address the race condition and NULL pointer dereference by: + +1. State Synchronization (flag + mutex) +Introduce a 'func_unbound' flag in struct uvc_device. This allows +uvc_function_disconnect() to safely skip accessing the nullified +cdev->gadget pointer. As suggested by Alan Stern, this flag is protected +by a new mutex (uvc->lock) to ensure proper memory ordering and prevent +instruction reordering or speculative loads. This mutex is also used to +protect 'func_connected' for consistent state management. + +2. Explicit Synchronization (completion) +Use a completion to synchronize uvc_function_unbind() with the +uvc_vdev_release() callback. This prevents Use-After-Free (UAF) by +ensuring struct uvc_device is freed after all video device resources +are released. + +Fixes: b81ac4395bbe ("usb: gadget: uvc: allow for application to cleanly shutdown") +Cc: stable +Suggested-by: Alan Stern +Signed-off-by: Jimmy Hu +Link: https://patch.msgid.link/20260320065427.1374555-1-hhhuuu@google.com +Signed-off-by: Greg Kroah-Hartman +Signed-off-by: Greg Kroah-Hartman +--- + drivers/usb/gadget/function/f_uvc.c | 39 ++++++++++++++++++++++++++++++--- + drivers/usb/gadget/function/uvc.h | 3 ++ + drivers/usb/gadget/function/uvc_v4l2.c | 5 +++- + 3 files changed, 43 insertions(+), 4 deletions(-) + +--- a/drivers/usb/gadget/function/f_uvc.c ++++ b/drivers/usb/gadget/function/f_uvc.c +@@ -397,6 +397,12 @@ uvc_function_disconnect(struct uvc_devic + { + int ret; + ++ guard(mutex)(&uvc->lock); ++ if (uvc->func_unbound) { ++ dev_dbg(&uvc->vdev.dev, "skipping function deactivate (unbound)\n"); ++ return; ++ } ++ + if ((ret = usb_function_deactivate(&uvc->func)) < 0) + uvcg_info(&uvc->func, "UVC disconnect failed with %d\n", ret); + } +@@ -415,6 +421,15 @@ static ssize_t function_name_show(struct + + static DEVICE_ATTR_RO(function_name); + ++static void uvc_vdev_release(struct video_device *vdev) ++{ ++ struct uvc_device *uvc = video_get_drvdata(vdev); ++ ++ /* Signal uvc_function_unbind() that the video device has been released */ ++ if (uvc->vdev_release_done) ++ complete(uvc->vdev_release_done); ++} ++ + static int + uvc_register_video(struct uvc_device *uvc) + { +@@ -427,7 +442,7 @@ uvc_register_video(struct uvc_device *uv + uvc->vdev.v4l2_dev->dev = &cdev->gadget->dev; + uvc->vdev.fops = &uvc_v4l2_fops; + uvc->vdev.ioctl_ops = &uvc_v4l2_ioctl_ops; +- uvc->vdev.release = video_device_release_empty; ++ uvc->vdev.release = uvc_vdev_release; + uvc->vdev.vfl_dir = VFL_DIR_TX; + uvc->vdev.lock = &uvc->video.mutex; + uvc->vdev.device_caps = V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_STREAMING; +@@ -602,6 +617,8 @@ uvc_function_bind(struct usb_configurati + int ret = -EINVAL; + + uvcg_info(f, "%s()\n", __func__); ++ scoped_guard(mutex, &uvc->lock) ++ uvc->func_unbound = false; + + opts = fi_to_f_uvc_opts(f->fi); + /* Sanity check the streaming endpoint module parameters. */ +@@ -897,12 +914,19 @@ static void uvc_free(struct usb_function + static void uvc_function_unbind(struct usb_configuration *c, + struct usb_function *f) + { ++ DECLARE_COMPLETION_ONSTACK(vdev_release_done); + struct usb_composite_dev *cdev = c->cdev; + struct uvc_device *uvc = to_uvc(f); + struct uvc_video *video = &uvc->video; + long wait_ret = 1; ++ bool connected; + + uvcg_info(f, "%s()\n", __func__); ++ scoped_guard(mutex, &uvc->lock) { ++ uvc->func_unbound = true; ++ uvc->vdev_release_done = &vdev_release_done; ++ connected = uvc->func_connected; ++ } + + if (video->async_wq) + destroy_workqueue(video->async_wq); +@@ -913,7 +937,7 @@ static void uvc_function_unbind(struct u + * though the video device removal uevent. Allow some time for the + * application to close out before things get deleted. + */ +- if (uvc->func_connected) { ++ if (connected) { + uvcg_dbg(f, "waiting for clean disconnect\n"); + wait_ret = wait_event_interruptible_timeout(uvc->func_connected_queue, + uvc->func_connected == false, msecs_to_jiffies(500)); +@@ -924,7 +948,10 @@ static void uvc_function_unbind(struct u + video_unregister_device(&uvc->vdev); + v4l2_device_unregister(&uvc->v4l2_dev); + +- if (uvc->func_connected) { ++ scoped_guard(mutex, &uvc->lock) ++ connected = uvc->func_connected; ++ ++ if (connected) { + /* + * Wait for the release to occur to ensure there are no longer any + * pending operations that may cause panics when resources are cleaned +@@ -936,6 +963,10 @@ static void uvc_function_unbind(struct u + uvcg_dbg(f, "done waiting for release with ret: %ld\n", wait_ret); + } + ++ /* Wait for the video device to be released */ ++ wait_for_completion(&vdev_release_done); ++ uvc->vdev_release_done = NULL; ++ + usb_ep_free_request(cdev->gadget->ep0, uvc->control_req); + kfree(uvc->control_buf); + +@@ -954,6 +985,8 @@ static struct usb_function *uvc_alloc(st + return ERR_PTR(-ENOMEM); + + mutex_init(&uvc->video.mutex); ++ mutex_init(&uvc->lock); ++ uvc->func_unbound = true; + uvc->state = UVC_STATE_DISCONNECTED; + init_waitqueue_head(&uvc->func_connected_queue); + opts = fi_to_f_uvc_opts(fi); +--- a/drivers/usb/gadget/function/uvc.h ++++ b/drivers/usb/gadget/function/uvc.h +@@ -131,6 +131,9 @@ struct uvc_device { + enum uvc_state state; + struct usb_function func; + struct uvc_video video; ++ struct completion *vdev_release_done; ++ struct mutex lock; /* protects func_unbound and func_connected */ ++ bool func_unbound; + bool func_connected; + wait_queue_head_t func_connected_queue; + +--- a/drivers/usb/gadget/function/uvc_v4l2.c ++++ b/drivers/usb/gadget/function/uvc_v4l2.c +@@ -497,6 +497,8 @@ uvc_v4l2_subscribe_event(struct v4l2_fh + if (sub->type < UVC_EVENT_FIRST || sub->type > UVC_EVENT_LAST) + return -EINVAL; + ++ guard(mutex)(&uvc->lock); ++ + if (sub->type == UVC_EVENT_SETUP && uvc->func_connected) + return -EBUSY; + +@@ -518,7 +520,8 @@ static void uvc_v4l2_disable(struct uvc_ + uvc_function_disconnect(uvc); + uvcg_video_enable(&uvc->video, 0); + uvcg_free_buffers(&uvc->video.queue); +- uvc->func_connected = false; ++ scoped_guard(mutex, &uvc->lock) ++ uvc->func_connected = false; + wake_up_interruptible(&uvc->func_connected_queue); + } +