Takashi Iwai 81bf8e
From 0b0860a3cf5eccf183760b1177a1dcdb821b0b66 Mon Sep 17 00:00:00 2001
Takashi Iwai 81bf8e
From: Desmond Cheong Zhi Xi <desmondcheongzx@gmail.com>
Takashi Iwai 81bf8e
Date: Mon, 12 Jul 2021 12:35:07 +0800
Takashi Iwai 81bf8e
Subject: [PATCH] drm: serialize drm_file.master with a new spinlock
Takashi Iwai 81bf8e
Git-commit: 0b0860a3cf5eccf183760b1177a1dcdb821b0b66
Takashi Iwai 81bf8e
Patch-mainline: v5.15-rc1
Takashi Iwai 81bf8e
References: bsc#1197914
Takashi Iwai 81bf8e
Takashi Iwai 81bf8e
Currently, drm_file.master pointers should be protected by
Takashi Iwai 81bf8e
drm_device.master_mutex when being dereferenced. This is because
Takashi Iwai 81bf8e
drm_file.master is not invariant for the lifetime of drm_file. If
Takashi Iwai 81bf8e
drm_file is not the creator of master, then drm_file.is_master is
Takashi Iwai 81bf8e
false, and a call to drm_setmaster_ioctl will invoke
Takashi Iwai 81bf8e
drm_new_set_master, which then allocates a new master for drm_file and
Takashi Iwai 81bf8e
puts the old master.
Takashi Iwai 81bf8e
Takashi Iwai 81bf8e
Thus, without holding drm_device.master_mutex, the old value of
Takashi Iwai 81bf8e
drm_file.master could be freed while it is being used by another
Takashi Iwai 81bf8e
concurrent process.
Takashi Iwai 81bf8e
Takashi Iwai 81bf8e
However, it is not always possible to lock drm_device.master_mutex to
Takashi Iwai 81bf8e
dereference drm_file.master. Through the fbdev emulation code, this
Takashi Iwai 81bf8e
might occur in a deep nest of other locks. But drm_device.master_mutex
Takashi Iwai 81bf8e
is also the outermost lock in the nesting hierarchy, so this leads to
Takashi Iwai 81bf8e
potential deadlocks.
Takashi Iwai 81bf8e
Takashi Iwai 81bf8e
To address this, we introduce a new spin lock at the bottom of the
Takashi Iwai 81bf8e
lock hierarchy that only serializes drm_file.master. With this change,
Takashi Iwai 81bf8e
the value of drm_file.master changes only when both
Takashi Iwai 81bf8e
drm_device.master_mutex and drm_file.master_lookup_lock are
Takashi Iwai 81bf8e
held. Hence, any process holding either of those locks can ensure that
Takashi Iwai 81bf8e
the value of drm_file.master will not change concurrently.
Takashi Iwai 81bf8e
Takashi Iwai 81bf8e
Since no lock depends on the new drm_file.master_lookup_lock, when
Takashi Iwai 81bf8e
drm_file.master is dereferenced, but drm_device.master_mutex cannot be
Takashi Iwai 81bf8e
held, we can safely protect the master pointer with
Takashi Iwai 81bf8e
drm_file.master_lookup_lock.
Takashi Iwai 81bf8e
Takashi Iwai 81bf8e
Reported-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Takashi Iwai 81bf8e
Signed-off-by: Desmond Cheong Zhi Xi <desmondcheongzx@gmail.com>
Takashi Iwai 81bf8e
Signed-off-by: Daniel Vetter <daniel.vetter@ffwll.ch>
Takashi Iwai 81bf8e
Link: https://patchwork.freedesktop.org/patch/msgid/20210712043508.11584-5-desmondcheongzx@gmail.com
Takashi Iwai 81bf8e
Acked-by: Takashi Iwai <tiwai@suse.de>
Takashi Iwai 81bf8e
Takashi Iwai 81bf8e
---
Takashi Iwai 81bf8e
 drivers/gpu/drm/drm_auth.c |   17 +++++++++++------
Takashi Iwai 81bf8e
 drivers/gpu/drm/drm_file.c |    1 +
Takashi Iwai 81bf8e
 include/drm/drm_file.h     |   12 +++++++++---
Takashi Iwai 81bf8e
 3 files changed, 21 insertions(+), 9 deletions(-)
Takashi Iwai 81bf8e
Takashi Iwai 81bf8e
--- a/drivers/gpu/drm/drm_auth.c
Takashi Iwai 81bf8e
+++ b/drivers/gpu/drm/drm_auth.c
Takashi Iwai 81bf8e
@@ -164,16 +164,18 @@ static void drm_set_master(struct drm_de
Takashi Iwai 81bf8e
 static int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv)
Takashi Iwai 81bf8e
 {
Takashi Iwai 81bf8e
 	struct drm_master *old_master;
Takashi Iwai 81bf8e
+	struct drm_master *new_master;
Takashi Iwai 81bf8e
 
Takashi Iwai 81bf8e
 	lockdep_assert_held_once(&dev->master_mutex);
Takashi Iwai 81bf8e
 
Takashi Iwai 81bf8e
 	WARN_ON(fpriv->is_master);
Takashi Iwai 81bf8e
 	old_master = fpriv->master;
Takashi Iwai 81bf8e
-	fpriv->master = drm_master_create(dev);
Takashi Iwai 81bf8e
-	if (!fpriv->master) {
Takashi Iwai 81bf8e
-		fpriv->master = old_master;
Takashi Iwai 81bf8e
+	new_master = drm_master_create(dev);
Takashi Iwai 81bf8e
+	if (!new_master)
Takashi Iwai 81bf8e
 		return -ENOMEM;
Takashi Iwai 81bf8e
-	}
Takashi Iwai 81bf8e
+	spin_lock(&fpriv->master_lookup_lock);
Takashi Iwai 81bf8e
+	fpriv->master = new_master;
Takashi Iwai 81bf8e
+	spin_unlock(&fpriv->master_lookup_lock);
Takashi Iwai 81bf8e
 
Takashi Iwai 81bf8e
 	fpriv->is_master = 1;
Takashi Iwai 81bf8e
 	fpriv->authenticated = 1;
Takashi Iwai 81bf8e
@@ -331,10 +333,13 @@ int drm_master_open(struct drm_file *fil
Takashi Iwai 81bf8e
 	/* if there is no current master make this fd it, but do not create
Takashi Iwai 81bf8e
 	 * any master object for render clients */
Takashi Iwai 81bf8e
 	mutex_lock(&dev->master_mutex);
Takashi Iwai 81bf8e
-	if (!dev->master)
Takashi Iwai 81bf8e
+	if (!dev->master) {
Takashi Iwai 81bf8e
 		ret = drm_new_set_master(dev, file_priv);
Takashi Iwai 81bf8e
-	else
Takashi Iwai 81bf8e
+	} else {
Takashi Iwai 81bf8e
+		spin_lock(&file_priv->master_lookup_lock);
Takashi Iwai 81bf8e
 		file_priv->master = drm_master_get(dev->master);
Takashi Iwai 81bf8e
+		spin_unlock(&file_priv->master_lookup_lock);
Takashi Iwai 81bf8e
+	}
Takashi Iwai 81bf8e
 	mutex_unlock(&dev->master_mutex);
Takashi Iwai 81bf8e
 
Takashi Iwai 81bf8e
 	return ret;
Takashi Iwai 81bf8e
--- a/drivers/gpu/drm/drm_file.c
Takashi Iwai 81bf8e
+++ b/drivers/gpu/drm/drm_file.c
Takashi Iwai 81bf8e
@@ -177,6 +177,7 @@ struct drm_file *drm_file_alloc(struct d
Takashi Iwai 81bf8e
 	init_waitqueue_head(&file->event_wait);
Takashi Iwai 81bf8e
 	file->event_space = 4096; /* set aside 4k for event buffer */
Takashi Iwai 81bf8e
 
Takashi Iwai 81bf8e
+	spin_lock_init(&file->master_lookup_lock);
Takashi Iwai 81bf8e
 	mutex_init(&file->event_read_lock);
Takashi Iwai 81bf8e
 
Takashi Iwai 81bf8e
 	if (drm_core_check_feature(dev, DRIVER_GEM))
Takashi Iwai 81bf8e
--- a/include/drm/drm_file.h
Takashi Iwai 81bf8e
+++ b/include/drm/drm_file.h
Takashi Iwai 81bf8e
@@ -226,15 +226,21 @@ struct drm_file {
Takashi Iwai 81bf8e
 	/**
Takashi Iwai 81bf8e
 	 * @master:
Takashi Iwai 81bf8e
 	 *
Takashi Iwai 81bf8e
-	 * Master this node is currently associated with. Only relevant if
Takashi Iwai 81bf8e
-	 * drm_is_primary_client() returns true. Note that this only
Takashi Iwai 81bf8e
-	 * matches &drm_device.master if the master is the currently active one.
Takashi Iwai 81bf8e
+	 * Master this node is currently associated with. Protected by struct
Takashi Iwai 81bf8e
+	 * &drm_device.master_mutex, and serialized by @master_lookup_lock.
Takashi Iwai 81bf8e
+	 *
Takashi Iwai 81bf8e
+	 * Only relevant if drm_is_primary_client() returns true. Note that
Takashi Iwai 81bf8e
+	 * this only matches &drm_device.master if the master is the currently
Takashi Iwai 81bf8e
+	 * active one.
Takashi Iwai 81bf8e
 	 *
Takashi Iwai 81bf8e
 	 * See also @authentication and @is_master and the :ref:`section on
Takashi Iwai 81bf8e
 	 * primary nodes and authentication <drm_primary_node>`.
Takashi Iwai 81bf8e
 	 */
Takashi Iwai 81bf8e
 	struct drm_master *master;
Takashi Iwai 81bf8e
 
Takashi Iwai 81bf8e
+	/** @master_lock: Serializes @master. */
Takashi Iwai 81bf8e
+	spinlock_t master_lookup_lock;
Takashi Iwai 81bf8e
+
Takashi Iwai 81bf8e
 	/** @pid: Process that opened this file. */
Takashi Iwai 81bf8e
 	struct pid *pid;
Takashi Iwai 81bf8e