Hold a device reference (via drm_dev_get/put) across the lifetime of open
debugfs files. Prevents use-after-free when a device is unregistered while
a debugfs file remains open. Both drm_debugfs_open (legacy info_list)
and drm_debugfs_entry_open (drm_debugfs_add_file) paths are covered.

Fixes: 1c9cacbea8805 ("drm/debugfs: create device-centered debugfs functions")
Fixes: 28a62277e06f9 ("drm: Convert proc files to seq_file and introduce 
debugfs")

Signed-off-by: Alvin Sun <[email protected]>
---
 drivers/gpu/drm/drm_debugfs.c | 42 ++++++++++++++++++++++++++++++++++++++----
 1 file changed, 38 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/drm/drm_debugfs.c b/drivers/gpu/drm/drm_debugfs.c
index 38cf6ce387cc8..5262a708f1602 100644
--- a/drivers/gpu/drm/drm_debugfs.c
+++ b/drivers/gpu/drm/drm_debugfs.c
@@ -159,11 +159,28 @@ static const struct drm_debugfs_info drm_debugfs_list[] = 
{
 static int drm_debugfs_open(struct inode *inode, struct file *file)
 {
        struct drm_info_node *node = inode->i_private;
+       struct drm_device *dev = node->minor->dev;
+       int ret;
 
        if (!device_is_registered(node->minor->kdev))
                return -ENODEV;
 
-       return single_open(file, node->info_ent->show, node);
+       drm_dev_get(dev);
+
+       ret = single_open(file, node->info_ent->show, node);
+       if (ret)
+               drm_dev_put(dev);
+
+       return ret;
+}
+
+static int drm_debugfs_release(struct inode *inode, struct file *file)
+{
+       struct drm_info_node *node =
+               ((struct seq_file *)file->private_data)->private;
+
+       drm_dev_put(node->minor->dev);
+       return single_release(inode, file);
 }
 
 static int drm_debugfs_entry_open(struct inode *inode, struct file *file)
@@ -171,11 +188,28 @@ static int drm_debugfs_entry_open(struct inode *inode, 
struct file *file)
        struct drm_debugfs_entry *entry = inode->i_private;
        struct drm_debugfs_info *node = &entry->file;
        struct drm_minor *minor = entry->dev->primary ?: entry->dev->accel;
+       struct drm_device *dev = entry->dev;
+       int ret;
 
        if (!device_is_registered(minor->kdev))
                return -ENODEV;
 
-       return single_open(file, node->show, entry);
+       drm_dev_get(dev);
+
+       ret = single_open(file, node->show, entry);
+       if (ret)
+               drm_dev_put(dev);
+
+       return ret;
+}
+
+static int drm_debugfs_entry_release(struct inode *inode, struct file *file)
+{
+       struct drm_debugfs_entry *entry =
+               ((struct seq_file *)file->private_data)->private;
+
+       drm_dev_put(entry->dev);
+       return single_release(inode, file);
 }
 
 static const struct file_operations drm_debugfs_entry_fops = {
@@ -183,7 +217,7 @@ static const struct file_operations drm_debugfs_entry_fops 
= {
        .open = drm_debugfs_entry_open,
        .read = seq_read,
        .llseek = seq_lseek,
-       .release = single_release,
+       .release = drm_debugfs_entry_release,
 };
 
 static const struct file_operations drm_debugfs_fops = {
@@ -191,7 +225,7 @@ static const struct file_operations drm_debugfs_fops = {
        .open = drm_debugfs_open,
        .read = seq_read,
        .llseek = seq_lseek,
-       .release = single_release,
+       .release = drm_debugfs_release,
 };
 
 /**

-- 
2.43.0


Reply via email to