]> git.ipfire.org Git - thirdparty/kernel/stable.git/commitdiff
drm: Fix an unwanted master inheritance v2
authorThomas Hellstrom <thellstrom@vmware.com>
Wed, 2 Dec 2015 17:24:46 +0000 (09:24 -0800)
committerBen Hutchings <ben@decadent.org.uk>
Wed, 30 Dec 2015 02:26:00 +0000 (02:26 +0000)
commit a0af2e538c80f3e47f1d6ddf120a153ad909e8ad upstream.

A client calling drmSetMaster() using a file descriptor that was opened
when another client was master would inherit the latter client's master
object and all its authenticated clients.

This is unwanted behaviour, and when this happens, instead allocate a
brand new master object for the client calling drmSetMaster().

Fixes a BUG() throw in vmw_master_set().

Signed-off-by: Thomas Hellstrom <thellstrom@vmware.com>
Signed-off-by: Dave Airlie <airlied@redhat.com>
[bwh: Backported to 3.2:
 - s/master_mutex/struct_mutex/
 - drm_new_set_master() must drop struct_mutex while calling
   drm_driver::master_create
 - Adjust filename, context, indentation]
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
drivers/gpu/drm/drm_fops.c
drivers/gpu/drm/drm_stub.c
include/drm/drmP.h

index 020b1033df101d83d04bf3bfb94fbccde3071dec..5f1a65378ee4d7fe5448c31aba3d749eca41df3b 100644 (file)
@@ -218,6 +218,62 @@ static int drm_cpu_valid(void)
        return 1;
 }
 
+/**
+ * drm_new_set_master - Allocate a new master object and become master for the
+ * associated master realm.
+ *
+ * @dev: The associated device.
+ * @fpriv: File private identifying the client.
+ *
+ * This function must be called with dev::struct_mutex held.
+ * Returns negative error code on failure. Zero on success.
+ */
+int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv)
+{
+       struct drm_master *old_master;
+       int ret;
+
+       lockdep_assert_held_once(&dev->struct_mutex);
+
+       /* create a new master */
+       fpriv->minor->master = drm_master_create(fpriv->minor);
+       if (!fpriv->minor->master)
+               return -ENOMEM;
+
+       /* take another reference for the copy in the local file priv */
+       old_master = fpriv->master;
+       fpriv->master = drm_master_get(fpriv->minor->master);
+
+       if (dev->driver->master_create) {
+               mutex_unlock(&dev->struct_mutex);
+               ret = dev->driver->master_create(dev, fpriv->master);
+               mutex_lock(&dev->struct_mutex);
+               if (ret)
+                       goto out_err;
+       }
+       if (dev->driver->master_set) {
+               ret = dev->driver->master_set(dev, fpriv, true);
+               if (ret)
+                       goto out_err;
+       }
+
+       fpriv->is_master = 1;
+       fpriv->allowed_master = 1;
+       fpriv->authenticated = 1;
+       if (old_master)
+               drm_master_put(&old_master);
+
+       return 0;
+
+out_err:
+       /* drop both references and restore old master on failure */
+       drm_master_put(&fpriv->minor->master);
+       drm_master_put(&fpriv->master);
+       fpriv->master = old_master;
+
+       return ret;
+}
+
 /**
  * Called whenever a process opens /dev/drm.
  *
@@ -279,43 +335,10 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
        mutex_lock(&dev->struct_mutex);
        if (!priv->minor->master) {
                /* create a new master */
-               priv->minor->master = drm_master_create(priv->minor);
-               if (!priv->minor->master) {
-                       mutex_unlock(&dev->struct_mutex);
-                       ret = -ENOMEM;
-                       goto out_free;
-               }
-
-               priv->is_master = 1;
-               /* take another reference for the copy in the local file priv */
-               priv->master = drm_master_get(priv->minor->master);
-
-               priv->authenticated = 1;
-
-               mutex_unlock(&dev->struct_mutex);
-               if (dev->driver->master_create) {
-                       ret = dev->driver->master_create(dev, priv->master);
-                       if (ret) {
-                               mutex_lock(&dev->struct_mutex);
-                               /* drop both references if this fails */
-                               drm_master_put(&priv->minor->master);
-                               drm_master_put(&priv->master);
-                               mutex_unlock(&dev->struct_mutex);
-                               goto out_free;
-                       }
-               }
-               mutex_lock(&dev->struct_mutex);
-               if (dev->driver->master_set) {
-                       ret = dev->driver->master_set(dev, priv, true);
-                       if (ret) {
-                               /* drop both references if this fails */
-                               drm_master_put(&priv->minor->master);
-                               drm_master_put(&priv->master);
-                               mutex_unlock(&dev->struct_mutex);
-                               goto out_free;
-                       }
-               }
+               ret = drm_new_set_master(dev, priv);
                mutex_unlock(&dev->struct_mutex);
+               if (ret)
+                       goto out_free;
        } else {
                /* get a reference to the master */
                priv->master = drm_master_get(priv->minor->master);
index 6d7b083c5b776d482d3b0a21f60a43adebd8f66d..6c1f6cea40a507928b4328109ae0602561c254eb 100644 (file)
@@ -225,6 +225,10 @@ int drm_setmaster_ioctl(struct drm_device *dev, void *data,
        if (!file_priv->minor->master &&
            file_priv->minor->master != file_priv->master) {
                mutex_lock(&dev->struct_mutex);
+               if (!file_priv->allowed_master) {
+                       ret = drm_new_set_master(dev, file_priv);
+                       goto out_unlock;
+               }
                file_priv->minor->master = drm_master_get(file_priv->master);
                file_priv->is_master = 1;
                if (dev->driver->master_set) {
@@ -234,10 +238,11 @@ int drm_setmaster_ioctl(struct drm_device *dev, void *data,
                                drm_master_put(&file_priv->minor->master);
                        }
                }
+       out_unlock:
                mutex_unlock(&dev->struct_mutex);
        }
 
-       return 0;
+       return ret;
 }
 
 int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
index bf4b2dc260e3f17fd64e28dc076b93a0e9f90b62..e7cd03c6b14e8cc7dac218ddfdaf976904dc0fce 100644 (file)
@@ -430,6 +430,11 @@ struct drm_file {
        void *driver_priv;
 
        int is_master; /* this file private is a master for a minor */
+       /*
+        * This client is allowed to gain master privileges for @master.
+        * Protected by struct drm_device::struct_mutex.
+        */
+       unsigned allowed_master:1;
        struct drm_master *master; /* master this node is currently associated with
                                      N.B. not always minor->master */
        struct list_head fbs;
@@ -1254,6 +1259,7 @@ extern int drm_fasync(int fd, struct file *filp, int on);
 extern ssize_t drm_read(struct file *filp, char __user *buffer,
                        size_t count, loff_t *offset);
 extern int drm_release(struct inode *inode, struct file *filp);
+extern int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv);
 
                                /* Mapping support (drm_vm.h) */
 extern int drm_mmap(struct file *filp, struct vm_area_struct *vma);