[PATCH] drm: Fix NULL pointer dereference in drm_minor_alloc() on error path

shechenglong <[email protected]>
Newsgroups gmane.linux.kernel,gmane.comp.video.dri.devel
Message-ID <[email protected]>
When drm_sysfs_minor_alloc() fails (e.g., due to -ENOMEM), the
following Oops occurs because an ERR_PTR is passed to put_device():

  BUG: kernel NULL pointer dereference, address: 0000000000000030
  #PF: supervisor read access in kernel mode
  #PF: error_code(0x0000) - not-present page
  RIP: 0010:kobject_put+0xd/0x60
  Call Trace:
   drm_minor_alloc_release+0x1c/0x50 [drm]
   drm_managed_release+0x96/0x160 [drm]
   drm_dev_init+0x269/0x330 [drm]
   drm_dev_alloc+0x3f/0x80 [drm]
   virtio_gpu_probe+0x40/0x180 [virtio_gpu]
   virtio_dev_probe+0x1fd/0x360
   ...
   do_syscall_64+0xaf/0x500
   entry_SYSCALL_64_after_hwframe+0x76/0x7e

The call path that leads to this crash is:

  virtio_gpu_probe()
    drm_dev_alloc()
      drm_dev_init()
        drm_minor_alloc()              // allocates minor
          drm_sysfs_minor_alloc()      // returns ERR_PTR(-ENOMEM)
          minor->kdev = ERR_PTR(-ENOMEM)  // stored directly
          return -ENOMEM
        // drm_dev_init() fails, triggers cleanup:
        drm_managed_release()
          drm_minor_alloc_release(dev, minor)
            put_device(minor->kdev)     // ERR_PTR(-ENOMEM) dereferenced
              kobject_put()             // crashes at address 0x30

drm_minor_alloc() assigns the return value of drm_sysfs_minor_alloc()
directly to minor->kdev, even when it is an error pointer.  The
subsequent cleanup unconditionally calls put_device(minor->kdev),
which dereferences the error pointer and causes a NULL-pointer
dereference (offset 0x30 into a non-page-mapped area).

Fix this by using a temporary variable to hold the result of
drm_sysfs_minor_alloc().  If the allocation fails, we return the error
immediately, leaving minor->kdev as NULL (the whole minor structure is
zero-allocated).  put_device(NULL) is explicitly allowed and safe.

Fixes: f96306f9892b ("drm: manage drm_minor cleanup with drmm_")

Signed-off-by: shechenglong <[email protected]>
---
 drivers/gpu/drm/drm_drv.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 675675480..2348fbb21 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -143,6 +143,7 @@ static void drm_minor_alloc_release(struct drm_device *dev, void *data)
 static int drm_minor_alloc(struct drm_device *dev, enum drm_minor_type type)
 {
 	struct drm_minor *minor;
+	struct device *kdev;
 	int r;
 
 	minor = drmm_kzalloc(dev, sizeof(*minor), GFP_KERNEL);
@@ -164,9 +165,11 @@ static int drm_minor_alloc(struct drm_device *dev, enum drm_minor_type type)
 	if (r)
 		return r;
 
-	minor->kdev = drm_sysfs_minor_alloc(minor);
-	if (IS_ERR(minor->kdev))
-		return PTR_ERR(minor->kdev);
+	kdev = drm_sysfs_minor_alloc(minor);
+	if (IS_ERR(kdev))
+		return PTR_ERR(kdev);
+
+	minor->kdev = kdev;
 
 	*drm_minor_get_slot(dev, type) = minor;
 	return 0;
-- 
2.43.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.