Commit 7150da7c authored by Thomas Hellstrom's avatar Thomas Hellstrom Committed by Luis Henriques

drm: Fix an unwanted master inheritance v2

commit a0af2e53 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: default avatarThomas Hellstrom <thellstrom@vmware.com>
Signed-off-by: default avatarDave Airlie <airlied@redhat.com>
[ luis: backported to 3.16:
  - drm_setmaster_ioctl() in file drm_stub.c instead of drm_drv.c
  - adjusted context ]
Signed-off-by: default avatarLuis Henriques <luis.henriques@canonical.com>
parent 02d4d951
...@@ -167,6 +167,60 @@ static int drm_cpu_valid(void) ...@@ -167,6 +167,60 @@ static int drm_cpu_valid(void)
return 1; 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->master_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) {
ret = dev->driver->master_create(dev, fpriv->master);
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. * Called whenever a process opens /dev/drm.
* *
...@@ -231,35 +285,9 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor) ...@@ -231,35 +285,9 @@ static int drm_open_helper(struct file *filp, struct drm_minor *minor)
mutex_lock(&dev->master_mutex); mutex_lock(&dev->master_mutex);
if (drm_is_primary_client(priv) && !priv->minor->master) { if (drm_is_primary_client(priv) && !priv->minor->master) {
/* create a new master */ /* create a new master */
priv->minor->master = drm_master_create(priv->minor); ret = drm_new_set_master(dev, priv);
if (!priv->minor->master) { if (ret)
ret = -ENOMEM;
goto out_close; goto out_close;
}
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;
if (dev->driver->master_create) {
ret = dev->driver->master_create(dev, priv->master);
if (ret) {
/* drop both references if this fails */
drm_master_put(&priv->minor->master);
drm_master_put(&priv->master);
goto out_close;
}
}
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);
goto out_close;
}
}
} else if (drm_is_primary_client(priv)) { } else if (drm_is_primary_client(priv)) {
/* get a reference to the master */ /* get a reference to the master */
priv->master = drm_master_get(priv->minor->master); priv->master = drm_master_get(priv->minor->master);
......
...@@ -202,6 +202,11 @@ int drm_setmaster_ioctl(struct drm_device *dev, void *data, ...@@ -202,6 +202,11 @@ int drm_setmaster_ioctl(struct drm_device *dev, void *data,
goto out_unlock; goto out_unlock;
} }
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->minor->master = drm_master_get(file_priv->master);
file_priv->is_master = 1; file_priv->is_master = 1;
if (dev->driver->master_set) { if (dev->driver->master_set) {
......
...@@ -407,6 +407,11 @@ struct drm_file { ...@@ -407,6 +407,11 @@ struct drm_file {
* in the plane list * in the plane list
*/ */
unsigned universal_planes:1; unsigned universal_planes:1;
/*
* This client is allowed to gain master privileges for @master.
* Protected by struct drm_device::master_mutex.
*/
unsigned allowed_master:1;
struct pid *pid; struct pid *pid;
kuid_t uid; kuid_t uid;
...@@ -1219,6 +1224,7 @@ extern int drm_stub_open(struct inode *inode, struct file *filp); ...@@ -1219,6 +1224,7 @@ extern int drm_stub_open(struct inode *inode, struct file *filp);
extern ssize_t drm_read(struct file *filp, char __user *buffer, extern ssize_t drm_read(struct file *filp, char __user *buffer,
size_t count, loff_t *offset); size_t count, loff_t *offset);
extern int drm_release(struct inode *inode, struct file *filp); 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) */ /* Mapping support (drm_vm.h) */
extern int drm_mmap(struct file *filp, struct vm_area_struct *vma); extern int drm_mmap(struct file *filp, struct vm_area_struct *vma);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment