[PATCH v2 9/9] drm/debugfs: hold device reference for the lifetime of open files

Alvin Sun <[email protected]>
Newsgroups org.freedesktop.lists.dri-devel,dev.linux.lists.driver-core,org.kernel.feeds.b4-sent,org.kernel.vger.rust-for-linux
Message-ID <[email protected]>
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
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.