Intel-XE Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Raag Jadav" <raag.jadav@intel.com>, intel-xe@lists.freedesktop.org
Subject: Re: [PATCH v1] drm/xe: Register drm device at the end of probe
Date: Fri, 23 Oct 2026 03:27:22 +0000	[thread overview]
Message-ID: <sashiko-outbox-182267@kernel.org> (raw)
In-Reply-To: <20261023030446.337222-1-raag.jadav@intel.com>

Thank you for your contribution! Sashiko AI review found 5 potential issue(s) to consider:
- [Critical] drm/xe: drm_dev_unplug() before xe_display_unregister() causes panel UAF
- [High] drm/xe: xe_oa_register() fails before drm_dev_register() adds minor kdev
- [High] drm/xe: xe_debugfs_register() uses NULL debugfs_root before drm_dev_register()
- [High] drm/xe: xe_display_register() runs before drm_dev_register() in probe
- [High] drm/xe: drm_dev_unplug() runs on unregistered device in xe_pci_remove()
--

commit 59e43ed89ab7b80b15dcc4432f5bc6716fd8b83a
Author: Raag Jadav <raag.jadav@intel.com>

drm/xe: Register drm device at the end of probe

This commit moves drm_dev_register() from xe_device_probe() to the end of
__xe_pci_probe() and moves drm_dev_unplug() from xe_device_remove() to the
beginning of xe_pci_remove().

> diff --git a/drivers/gpu/drm/xe/xe_device.c b/drivers/gpu/drm/xe/xe_device.c
> index e1979bbcf7bc6..ad25e9833283b 100644
> --- a/drivers/gpu/drm/xe/xe_device.c
> +++ b/drivers/gpu/drm/xe/xe_device.c
> @@ -1049,10 +1049,6 @@ int xe_device_probe(struct xe_device *xe)
>  	if (err)
>  		return err;
>  
> -	err = drm_dev_register(&xe->drm, 0);
> -	if (err)
> -		return err;
> -
>  	xe_display_register(xe);

[Severity: High]
With drm_dev_register() moved to the end of __xe_pci_probe(),
xe_display_register() in drivers/gpu/drm/xe/xe_device.c now runs before
drm_dev_register(). Can xe_display_register() safely execute before the DRM
device and its connectors are registered?

In drivers/gpu/drm/i915/display/intel_display_driver.c,
xe_display_register() -> intel_display_driver_register() calls both
intel_acpi_video_register() and intel_fbdev_setup():

    intel_opregion_register(display);
    intel_acpi_video_register(display);
    ...
    intel_fbdev_setup(display);

In drivers/gpu/drm/clients/drm_fbdev_client.c, intel_fbdev_setup() ->
drm_client_setup_with_color_mode() -> drm_fbdev_client_setup() checks
whether the DRM device has been registered:

    drm_WARN(dev, !dev->registered, "Device has not been registered.\n");

Because dev->registered is only set in drm_dev_register(), this triggers a
warning during probe and runs fbdev setup before drm_modeset_register_all()
has invoked the connector late_register callbacks (which initialize panels,
DP AUX, and LSPCON).

In addition, intel_acpi_video_register() in
drivers/gpu/drm/i915/display/intel_acpi.c checks whether a native backlight
device was registered:

    drm_for_each_connector_iter(connector, &conn_iter) {
        struct intel_panel *panel = &to_intel_connector(connector)->panel;

        if (panel->backlight.funcs && !panel->backlight.device) {
            acpi_video_register_backlight();
            break;
        }
    }

Because panel->backlight.device is only populated during
drm_dev_register() -> drm_modeset_register_all() ->
intel_connector_register() -> intel_panel_register(), running
intel_acpi_video_register() first finds panel->backlight.device == NULL and
registers acpi_video_register_backlight() even when a native panel backlight
is present.

Furthermore, later in xe_device_probe(), xe_ras_process_errors() can call
xe_device_declare_wedged() -> drm_dev_wedged_event() when boot-time RAS
errors are present, which fails with -EINVAL in kobject_uevent_env() on
&dev->primary->kdev->kobj before drm_dev_register() has added primary->kdev.

>  
>  	err = xe_oa_register(xe);

[Severity: High]
Does xe_oa_register() fail with -ENOMEM during probe when called before
drm_dev_register()?

In drivers/gpu/drm/xe/xe_oa.c, xe_oa_register() creates a sysfs subdirectory
under xe->drm.primary->kdev->kobj:

    oa->metrics_kobj = kobject_create_and_add("metrics",
                                              &xe->drm.primary->kdev->kobj);
    if (!oa->metrics_kobj)
        return -ENOMEM;

During xe_device_create() -> drm_dev_init() -> drm_minor_alloc() ->
drm_sysfs_minor_alloc(), xe->drm.primary->kdev is only initialized with
device_initialize(), so primary->kdev->kobj.sd is NULL until
drm_dev_register() -> drm_minor_register() calls device_add(minor->kdev) in
drivers/gpu/drm/drm_drv.c.

When xe_oa_register() runs in xe_device_probe() prior to drm_dev_register(),
sysfs_create_dir_ns() sees a NULL parent kernfs_node and fails with -ENOENT,
causing kobject_create_and_add() to return NULL and aborting probe on any
OA-capable device.

[ ... ]

> @@ -1116,8 +1110,6 @@ void xe_device_remove(struct xe_device *xe)
>  {
>  	xe_display_unregister(xe);
>  
> -	drm_dev_unplug(&xe->drm);
> -
>  	xe_bo_pci_dev_remove_all(xe);
>  }
>  

[Severity: Critical]
By moving drm_dev_unplug(&xe->drm) out of xe_device_remove() to the start of
xe_pci_remove(), drm_dev_unplug() now runs before xe_display_unregister().
Does this cause a use-after-free on panel->base during display shutdown on
devices with an active eDP or DSI panel?

During drm_dev_register() -> drm_modeset_register_all(), eDP and DSI
connectors allocate panel->base using __devm_drm_panel_alloc() bound to
connector->base.kdev.

When xe_pci_remove() calls drm_dev_unplug() -> drm_dev_unregister() ->
drm_modeset_unregister_all() -> drm_connector_unregister(),
drm_sysfs_connector_remove() calls device_unregister(connector->kdev). That
releases devres on connector->kdev and frees the struct drm_panel while
panel->base remains non-NULL.

Subsequently, xe_pci_remove() calls xe_device_remove() ->
xe_display_unregister() -> intel_display_driver_unregister() in
drivers/gpu/drm/i915/display/intel_display_driver.c:

    drm_kms_helper_poll_fini(display->drm);

    intel_display_driver_disable_user_access(display);

    intel_audio_deinit(display);

    drm_atomic_helper_shutdown(display->drm);

When drm_atomic_helper_shutdown() disables an active eDP or DSI output,
intel_panel_unprepare() in drivers/gpu/drm/i915/display/intel_panel.c calls
drm_panel_unprepare(panel->base), which in drivers/gpu/drm/drm_panel.c
dereferences the freed panel and locks panel->follower_lock:

    if (!panel->prepared) {
        dev_warn(panel->dev, "Skipping unprepare of already unprepared panel\n");
        return;
    }

    mutex_lock(&panel->follower_lock);

> diff --git a/drivers/gpu/drm/xe/xe_pci.c b/drivers/gpu/drm/xe/xe_pci.c
> index 50e4920e8e08c..a549f05e150c9 100644
> --- a/drivers/gpu/drm/xe/xe_pci.c
> +++ b/drivers/gpu/drm/xe/xe_pci.c
> @@ -1113,6 +1113,8 @@ static void xe_pci_remove(struct pci_dev *pdev)
>  {
>  	struct xe_device *xe = pdev_to_xe_device(pdev);
>  
> +	drm_dev_unplug(&xe->drm);
> +
>  	if (IS_SRIOV_PF(xe))
>  		xe_pci_sriov_configure(pdev, 0);

[Severity: High]
What happens in xe_pci_remove() when the device was probed in Boot
Survivability mode, or when __xe_pci_probe() jumps to err_driver_cleanup
before drm_dev_register() succeeds?

In __xe_pci_probe() in drivers/gpu/drm/xe/xe_pci.c, if Boot Survivability
mode is enabled during xe_device_probe_early(), probe returns 0 early:

    err = xe_device_probe_early(xe);
    if (xe_survivability_mode_is_boot_enabled(xe))
        return 0;

At that point xe->drm has DRIVER_MODESET set by xe_device_create(), but
xe_display_probe(), drmm_mode_config_init(), and drm_dev_register() have not
been called.

Because drm_dev_unplug(&xe->drm) is placed before the
xe_survivability_mode_is_boot_enabled(xe) check in xe_pci_remove(), removing
the device calls drm_dev_unplug() -> drm_dev_unregister() ->
drm_modeset_unregister_all() -> drm_connector_unregister_all() ->
drm_connector_list_iter_next() in drivers/gpu/drm/drm_connector.c:

    spin_lock_irqsave(&config->connector_list_lock, flags);
    lhead = old_conn ? &old_conn->head : &config->connector_list;

    do {
        if (lhead->next == &config->connector_list) {

This locks the uninitialized connector_list_lock spinlock and dereferences a
NULL connector_list.next pointer.

Similarly, if xe_pm_init() or drm_dev_register() fails at the end of
__xe_pci_probe(), jumping to err_driver_cleanup calls xe_pci_remove() ->
drm_dev_unplug(&xe->drm) on a drm_device that was never registered.

> @@ -1291,6 +1293,11 @@ static int __xe_pci_probe(struct pci_dev *pdev, const struct xe_device_desc *des
>  	if (err)
>  		goto err_driver_cleanup;
>  
> +	/* Must be last */
> +	err = drm_dev_register(&xe->drm, 0);
> +	if (err)
> +		goto err_driver_cleanup;

[Severity: High]
When drm_dev_register() is deferred until after xe_device_probe(), what
happens to xe_debugfs_register(xe) inside xe_device_probe()?

In drivers/gpu/drm/xe/xe_debugfs.c, xe_debugfs_register() retrieves the
debugfs root from the primary minor:

    struct drm_minor *minor = xe->drm.primary;
    struct dentry *root = minor->debugfs_root;

However, minor->debugfs_root is only populated when drm_dev_register() ->
drm_minor_register() calls drm_debugfs_register() in
drivers/gpu/drm/drm_debugfs.c:

    minor->debugfs_root = dev->debugfs_root;

When xe_debugfs_register() executes in xe_device_probe() before
drm_dev_register(), minor->debugfs_root is still NULL:

1. In drivers/gpu/drm/xe/xe_gt_debugfs.c, xe_gt_debugfs_register() hits
   xe_gt_assert(gt, minor->debugfs_root) on every GT.
2. In drivers/gpu/drm/xe/xe_pxp_debugfs.c and drivers/gpu/drm/xe/xe_psmi.c,
   xe_pxp_debugfs_register() and xe_psmi_debugfs_register() check
   if (!minor->debugfs_root) and return early without registering their
   debugfs entries.
3. xe_debugfs_register() and xe_tile_debugfs_register() pass root (NULL) as
   the parent dentry to debugfs_create_file(), debugfs_create_dir(),
   drm_debugfs_create_files(), and ttm_resource_manager_create_debugfs(),
   creating the driver's debugfs entries in the top-level /sys/kernel/debug/
   directory where they are not cleaned up on device unbind and retain
   dangling pointers to freed driver structures.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261023030446.337222-1-raag.jadav@intel.com?part=1

  parent reply	other threads:[~2026-10-23  3:27 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-23  3:04 [PATCH v1] drm/xe: Register drm device at the end of probe Raag Jadav
2026-10-23  3:14 ` ✓ CI.KUnit: success for " Patchwork
2026-10-23  3:27 ` sashiko-bot [this message]
2026-10-23  3:44 ` ✗ Xe.CI.BAT: failure " Patchwork
2026-10-23  5:31 ` ✗ Xe.CI.FULL: " Patchwork
2026-10-23  7:26 ` [PATCH v1] " Jani Nikula
2026-10-23  7:55 ` ✗ Xe.CI.FULL: failure for " Patchwork

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=sashiko-outbox-182267@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=intel-xe@lists.freedesktop.org \
    --cc=raag.jadav@intel.com \
    --cc=sashiko-reviews@lists.linux.dev \
    /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