mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: "Yu, Fei" <fei.yu@intel.com>
To: dri-devel@lists.freedesktop.org
Cc: Thomas Zimmermann <tzimmermann@suse.de>,
	Maarten Lankhorst <maarten.lankhorst@linux.intel.com>,
	Maxime Ripard <mripard@kernel.org>,
	David Airlie <airlied@gmail.com>, Simona Vetter <simona@ffwll.ch>,
	Dave Airlie <airlied@redhat.com>, Sean Paul <sean@poorly.run>,
	Patrik Jakobsson <patrik.r.jakobsson@gmail.com>,
	linux-kernel@vger.kernel.org,
	syzbot+f5ecf25277b844fd38f2@syzkaller.appspotmail.com,
	Fei Yu <fei.yu@intel.com>
Subject: [PATCH] drm/probe-helper: Don't run output polling on an unplugged device
Date: Wed,  4 Nov 2026 16:51:59 +0800	[thread overview]
Message-ID: <20261104085159.1074597-1-fei.yu@intel.com> (raw)

From: Fei Yu <fei.yu@intel.com>

syzbot reported a use-after-free in the output poll worker of a udl
device that was being disconnected:

  BUG: KASAN: slab-use-after-free in kobject_get_path+0x91/0x2f0 lib/kobject.c:158
  Read of size 8 at addr ffff88802a29ee18 by task kworker/1:2/1654
  Workqueue: events output_poll_execute
  Call Trace:
   kobject_get_path+0x91/0x2f0 lib/kobject.c:158
   kobject_uevent_env+0x29e/0x9e0 lib/kobject_uevent.c:548
   drm_sysfs_hotplug_event+0x10e/0x150 drivers/gpu/drm/drm_sysfs.c:451
   output_poll_execute+0x846/0xab0 drivers/gpu/drm/drm_probe_helper.c:854
  ...
  Freed by task 25:
   kobject_put+0x222/0x550 lib/kobject.c:737
   cleanup_glue_dir+0x1b2/0x220 drivers/base/core.c:3487
   device_del+0x77d/0x8f0 drivers/base/core.c:3986
   drm_minor_unregister drivers/gpu/drm/drm_drv.c:226 [inline]
   drm_dev_unregister+0x436/0x700 drivers/gpu/drm/drm_drv.c:1185
   drm_dev_unplug+0x4b/0xb0 drivers/gpu/drm/drm_drv.c:507
   udl_usb_disconnect+0x3e/0x50 drivers/gpu/drm/udl/udl_drv.c:115

udl calls drm_dev_unplug() on disconnect and stops the output polling
only later from the managed release of the drm_device. In between, the
poll worker keeps running. When the device goes away, the EDID read in
the connector's detect fails, the connector status changes, and the
worker sends hotplug uevents on dev->primary->kdev. This runs
concurrently with drm_minor_unregister(), whose device_del() drops the
"drm" class glue directory that is the parent of the minor's kobject.
kobject_get_path() then walks the freed parent.

Since commit ff9cb6d2035c ("drm/udl: Unregister device before cleaning
up on disconnect"), nothing orders the poll worker against the
unregistration. Any driver that uses drm_dev_unplug() together with
connector polling has the same problem.

Make the poll worker a drm_dev_enter()/drm_dev_exit() critical section.
drm_dev_unplug() sets dev->unplugged and calls synchronize_srcu() before
drm_dev_unregister(), so a running poll pass finishes before the minors
are unregistered, and later passes return without probing or sending
events. A poll pass on an unplugged device has nothing useful to report
anyway. Drivers that never call drm_dev_unplug() are not affected,
because drm_dev_enter() always succeeds for them.

Fixes: ff9cb6d2035c ("drm/udl: Unregister device before cleaning up on disconnect")
Reported-by: syzbot+f5ecf25277b844fd38f2@syzkaller.appspotmail.com
Closes: https://syzkaller.appspot.com/bug?extid=f5ecf25277b844fd38f2
Signed-off-by: Fei Yu <fei.yu@intel.com>
---
The fix is in the probe helper rather than in udl, because every driver
that calls drm_dev_unplug() while connector polling is still active has
the same race: udl and vc4 (poll cleanup is drmm-managed) and imx/dc
(drm_kms_helper_poll_fini() after drm_dev_unplug()). gud is not affected,
it stops polling before the unplug. A udl-only alternative is to call
drm_kms_helper_poll_disable() before drm_dev_unplug(). That reverts the
ordering that ff9cb6d2035c wanted, and userspace can still re-arm the
poll work through a connector probe in between.

Testing (v7.4-rc1, x86_64 KASAN, QEMU, same config for both kernels,
emulated DisplayLink adapter on raw-gadget/dummy_hcd):
- syzbot has no reproducer. A deterministic test opens the card, unplugs
  the device and watches kernel uevents for 25 s (poll period 10 s).
  base: 2 "change@/card1" HOTPLUG uevents for the already removed card
  (DEVPATH without its parent: the poll worker is still running on the
  unregistered device). patched: 0.
- Stress with connect/unplug cycles while the connector status flips on
  every probe: no KASAN report on base or patched in ~165 cycles each.
  The UAF window itself is very narrow, so the KASAN splat has not been
  reproduced locally.
- DRM KUnit: 667/667 on base and patched. W=1: no new warnings.

 drivers/gpu/drm/drm_probe_helper.c | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/drm_probe_helper.c b/drivers/gpu/drm/drm_probe_helper.c
index 856b40fdaac4..a10e72ff2d3f 100644
--- a/drivers/gpu/drm/drm_probe_helper.c
+++ b/drivers/gpu/drm/drm_probe_helper.c
@@ -35,6 +35,7 @@
 #include <drm/drm_bridge.h>
 #include <drm/drm_client_event.h>
 #include <drm/drm_crtc.h>
+#include <drm/drm_drv.h>
 #include <drm/drm_edid.h>
 #include <drm/drm_fourcc.h>
 #include <drm/drm_managed.h>
@@ -775,10 +776,19 @@ static void output_poll_execute(struct work_struct *work)
 	enum drm_connector_status old_status;
 	bool repoll = false, changed = false;
 	u64 old_epoch_counter;
+	int idx;
 
 	if (!dev->mode_config.poll_enabled)
 		return;
 
+	/*
+	 * Don't poll an unplugged device. drm_dev_unplug() waits for this
+	 * section to finish before it unregisters the device, so the hotplug
+	 * events below never race with the removal of the sysfs device.
+	 */
+	if (!drm_dev_enter(dev, &idx))
+		return;
+
 	if (!drm_kms_helper_poll) {
 		if (dev->mode_config.poll_running) {
 			drm_kms_helper_disable_hpd(dev);
@@ -793,12 +803,12 @@ static void output_poll_execute(struct work_struct *work)
 		if (changed)
 			drm_kms_helper_hotplug_event(dev);
 
-		return;
+		goto out;
 	}
 
 	if (!mutex_trylock(&dev->mode_config.mutex)) {
 		schedule_delayed_work(delayed_work, DRM_OUTPUT_POLL_PERIOD);
-		return;
+		goto out;
 	}
 
 	drm_connector_list_iter_begin(dev, &conn_iter);
@@ -874,6 +884,9 @@ static void output_poll_execute(struct work_struct *work)
 
 	if (repoll)
 		schedule_delayed_work(delayed_work, DRM_OUTPUT_POLL_PERIOD);
+
+out:
+	drm_dev_exit(idx);
 }
 
 /**

base-commit: 6628e7d4c6b25c8d18f1345e3a20d21829aae7e4
-- 
2.53.0


                 reply	other threads:[~2026-11-04  8:46 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20261104085159.1074597-1-fei.yu@intel.com \
    --to=fei.yu@intel.com \
    --cc=airlied@gmail.com \
    --cc=airlied@redhat.com \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=maarten.lankhorst@linux.intel.com \
    --cc=mripard@kernel.org \
    --cc=patrik.r.jakobsson@gmail.com \
    --cc=sean@poorly.run \
    --cc=simona@ffwll.ch \
    --cc=syzbot+f5ecf25277b844fd38f2@syzkaller.appspotmail.com \
    --cc=tzimmermann@suse.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®