From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-pj1-f74.google.com (mail-pj1-f74.google.com [209.85.216.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 766E2334695 for ; Tue, 24 Feb 2026 08:40:03 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.216.74 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771922404; cv=none; b=LFwoAjHdOz0afq4KrECskkXdBDgH0M2eHECkZIx1RTcLLD6Ook4wwTmFtg7PM/8t+sTrVDkfUskEBwhgR+Zv45K+pSYOkCKyuQOiYtmpkfE2y/yr85id+345Wj7ujP8ZyItAnVWgOsSE+ZMQ7HPwWdSGkKFrPAC/jMpmTGTR4nc= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1771922404; c=relaxed/simple; bh=U/TgxUQYVzZqCgle8jF8DXPoqtJ16jLgQc6UMKe3QvM=; h=Date:Mime-Version:Message-ID:Subject:From:To:Cc:Content-Type; b=bzlJaqq4VKx8FCOhM/YxVioivKpNJSkHLidOU9wsmpFECVZII1bxO7ROBjGgOQiQqZo5zdSGyve5EWSnhBNXK7QJllMysoykPgXKnsrGnMTmD+tY73WJQzknun16RADmgi7Soe0BAqpjZ3B5vM7xUqJI4KXb2nVhkLsbJbGE0JE= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--hhhuuu.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=eYoS2pAP; arc=none smtp.client-ip=209.85.216.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--hhhuuu.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="eYoS2pAP" Received: by mail-pj1-f74.google.com with SMTP id 98e67ed59e1d1-358f4ed4eceso29742a91.2 for ; Tue, 24 Feb 2026 00:40:03 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1771922403; x=1772527203; darn=vger.kernel.org; h=cc:to:from:subject:message-id:mime-version:date:from:to:cc:subject :date:message-id:reply-to; bh=AA20LKX/qg4rXIAP9XbWMbFshuFo56WlS+omTggeYsk=; b=eYoS2pAPwMqaCT9YmF3U9mleycoYxO5c/my893bGADyVzXLGCcIAY1yC/9A+MFV/Kn K7cjHeLrQrxlzJAkFUId77a+uD0zgKntBBHoDh1/az3PGzpXXCuZaOizxObLFlB9dU40 mqJiElYDTFyQsIz+B6dtJpQq+EnqUIwyveNx2dYkTrMuM7EOMj1PItFVpRbGlTAEizCy TQq88D+R2kStyt4qh5E9up1INZEv2RKY17/gcl18hUPWJ2T+SJslY6LtGbNi1AGZVxb0 VDQKZ2jiGPlCUTRn606kcTXxey0ZZOTLOtFhHoCPZrBkvfjgG0daL9/LxSrj+MqPFTDf o3Iw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1771922403; x=1772527203; h=cc:to:from:subject:message-id:mime-version:date:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=AA20LKX/qg4rXIAP9XbWMbFshuFo56WlS+omTggeYsk=; b=UCfYOopnVVhNuZxlz1FJ9S+cWnxAgtBjICK/qYnGXS1PQhbw7rvlmVXkznluqlA/OA fEB4uEsV66Iz308+WY5liHWlDui7DliynwjrvdH724NuZ1D/ssjRf3XFvAUqrb0EzXnw LZyPC7Jac/GiiFS5f/ZQSYdaxDiKxeTH5dYjPvHvd2m5NzUyLBG0sr1RXv3UiHNBxBha iDbcHD+4YaT52+JhLegJqKa4Clqtf0u0goL9taLBbc2+pzJPDngpLNg2qJlmxv7/BAlr dZbqvw6/7fcLNS73R286gIphDDi+CE/ZO/4bQm7jWrr7NS5EXhnjWKdLbOo2V48AhBO0 ACgA== X-Forwarded-Encrypted: i=1; AJvYcCWfkZQTBN6JgT8ckB2ThLMAJdpgErKue24QQpJf9B8uu0Uz+TrDh3COctipHpHldEM2yj90106bQAM=@vger.kernel.org X-Gm-Message-State: AOJu0Yw2+RZV3dl2W6RoyyZ+T4IMwHEgL0ssOxRjMpXrKuxoaWlzZdAK 7v/eorxjVYpFPDUXxxrhQwEkOJBygpCAWRn4cscdOC+wRJGL9NWGqpaUuqE9vGJgCCRuZwnfIVn MYcLS+A== X-Received: from pgc16.prod.google.com ([2002:a05:6a02:2f90:b0:c70:ab5b:1d92]) (user=hhhuuu job=prod-delivery.src-stubby-dispatcher) by 2002:a05:6a20:6a04:b0:394:8455:d1a9 with SMTP id adf61e73a8af0-39545e5081dmr8121765637.2.1771922402547; Tue, 24 Feb 2026 00:40:02 -0800 (PST) Date: Tue, 24 Feb 2026 16:39:55 +0800 Precedence: bulk X-Mailing-List: linux-usb@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 X-Mailer: git-send-email 2.53.0.371.g1d285c8824-goog Message-ID: <20260224083955.1375032-1-hhhuuu@google.com> Subject: [PATCH] usb: gadget: f_uvc: fix NULL pointer dereference during unbind race From: Jimmy Hu To: Greg Kroah-Hartman , Laurent Pinchart Cc: Dan Vacura , Jimmy Hu , Xu Yang , Frank Li , Hans Verkuil , linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, stable@vger.kernel.org, badhri@google.com Content-Type: text/plain; charset="UTF-8" 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 The fix introduces a 'func_unbinding' flag in struct uvc_device to protect critical sections: 1. In uvc_function_disconnect(), it prevents accessing the nullified cdev->gadget pointer. 2. In uvc_v4l2_release(), it ensures uvcg_free_buffers() is skipped if unbind is already in progress, avoiding races with concurrent bind operations or use-after-free on the video queue memory. Fixes: b81ac4395bbe ("usb: gadget: uvc: allow for application to cleanly shutdown") Cc: Signed-off-by: Jimmy Hu --- drivers/usb/gadget/function/f_uvc.c | 7 +++++++ drivers/usb/gadget/function/uvc.h | 1 + drivers/usb/gadget/function/uvc_v4l2.c | 6 ++++++ 3 files changed, 14 insertions(+) diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index a96476507d2f..f8c609ad1a43 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -413,6 +413,11 @@ uvc_function_disconnect(struct uvc_device *uvc) { int ret; + if (uvc->func_unbinding) { + pr_info("uvc: unbinding, skipping function deactivate\n"); + return; + } + if ((ret = usb_function_deactivate(&uvc->func)) < 0) uvcg_info(&uvc->func, "UVC disconnect failed with %d\n", ret); } @@ -659,6 +664,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) int ret = -EINVAL; uvcg_info(f, "%s()\n", __func__); + uvc->func_unbinding = false; opts = fi_to_f_uvc_opts(f->fi); /* Sanity check the streaming endpoint module parameters. */ @@ -994,6 +1000,7 @@ static void uvc_function_unbind(struct usb_configuration *c, long wait_ret = 1; uvcg_info(f, "%s()\n", __func__); + uvc->func_unbinding = true; kthread_cancel_work_sync(&video->hw_submit); diff --git a/drivers/usb/gadget/function/uvc.h b/drivers/usb/gadget/function/uvc.h index 676419a04976..7ca56ff737a4 100644 --- a/drivers/usb/gadget/function/uvc.h +++ b/drivers/usb/gadget/function/uvc.h @@ -155,6 +155,7 @@ struct uvc_device { enum uvc_state state; struct usb_function func; struct uvc_video video; + bool func_unbinding; bool func_connected; wait_queue_head_t func_connected_queue; diff --git a/drivers/usb/gadget/function/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c index fd4b998ccd16..a8a15b584648 100644 --- a/drivers/usb/gadget/function/uvc_v4l2.c +++ b/drivers/usb/gadget/function/uvc_v4l2.c @@ -594,7 +594,13 @@ static void uvc_v4l2_disable(struct uvc_device *uvc) { uvc_function_disconnect(uvc); uvcg_video_disable(&uvc->video); + if (uvc->func_unbinding) { + pr_info("uvc: unbinding, skipping buffer cleanup\n"); + goto skip_buffer_cleanup; + } uvcg_free_buffers(&uvc->video.queue); + +skip_buffer_cleanup: uvc->func_connected = false; wake_up_interruptible(&uvc->func_connected_queue); } base-commit: 24d479d26b25bce5faea3ddd9fa8f3a6c3129ea7 -- 2.53.0.371.g1d285c8824-goog