Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c32fc9c8 authored by Dave Airlie's avatar Dave Airlie
Browse files

Merge tag 'vmwgfx-next-2014-03-28' of git://people.freedesktop.org/~thomash/linux into drm-next

vmwgfx render-node support and drm + ttm changes it depends upon.

Pull request of 2014-03-28

* tag 'vmwgfx-next-2014-03-28' of git://people.freedesktop.org/~thomash/linux:
  drm/vmwgfx: Bump driver minor and date
  drm/vmwgfx: Enable render nodes
  drm/vmwgfx: Tighten the security around buffer maps
  drm/ttm: Add a ttm_ref_object_exists function
  drm/vmwgfx: Tighten security around surface sharing v2
  drm/vmwgfx: Allow prime fds in the surface reference ioctls
  drm/vmwgfx: Drop authentication requirement on UNREF ioctls
  drm/vmwgfx: Reinstate and tighten security around legacy master model
  drm/vmwgfx: Use a per-device semaphore for reservation protection
  drm: Add a function to get the ioctl flags
  drm: Protect the master management with a drm_device::master_mutex v3
  drm: Remove the minor master list
  drm: Improve on minor type helpers v3
  drm: Make control nodes master-less v3
  drm: Break out ioctl permission check to a separate function v2
  drm: Have the crtc code only reference master from legacy nodes v2
parents 60f2b4af 03c5b8f0
Loading
Loading
Loading
Loading
+8 −6
Original line number Diff line number Diff line
@@ -1492,9 +1492,9 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
	mutex_unlock(&file_priv->fbs_lock);

	drm_modeset_lock_all(dev);
	mode_group = &file_priv->master->minor->mode_group;
	if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
	if (!drm_is_primary_client(file_priv)) {

		mode_group = NULL;
		list_for_each(lh, &dev->mode_config.crtc_list)
			crtc_count++;

@@ -1505,6 +1505,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
			encoder_count++;
	} else {

		mode_group = &file_priv->master->minor->mode_group;
		crtc_count = mode_group->num_crtcs;
		connector_count = mode_group->num_connectors;
		encoder_count = mode_group->num_encoders;
@@ -1519,7 +1520,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
	if (card_res->count_crtcs >= crtc_count) {
		copied = 0;
		crtc_id = (uint32_t __user *)(unsigned long)card_res->crtc_id_ptr;
		if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
		if (!mode_group) {
			list_for_each_entry(crtc, &dev->mode_config.crtc_list,
					    head) {
				DRM_DEBUG_KMS("[CRTC:%d]\n", crtc->base.id);
@@ -1546,7 +1547,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
	if (card_res->count_encoders >= encoder_count) {
		copied = 0;
		encoder_id = (uint32_t __user *)(unsigned long)card_res->encoder_id_ptr;
		if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
		if (!mode_group) {
			list_for_each_entry(encoder,
					    &dev->mode_config.encoder_list,
					    head) {
@@ -1577,7 +1578,7 @@ int drm_mode_getresources(struct drm_device *dev, void *data,
	if (card_res->count_connectors >= connector_count) {
		copied = 0;
		connector_id = (uint32_t __user *)(unsigned long)card_res->connector_id_ptr;
		if (file_priv->master->minor->type == DRM_MINOR_CONTROL) {
		if (!mode_group) {
			list_for_each_entry(connector,
					    &dev->mode_config.connector_list,
					    head) {
@@ -2846,7 +2847,8 @@ int drm_mode_getfb(struct drm_device *dev,
	r->bpp = fb->bits_per_pixel;
	r->pitch = fb->pitches[0];
	if (fb->funcs->create_handle) {
		if (file_priv->is_master || capable(CAP_SYS_ADMIN)) {
		if (file_priv->is_master || capable(CAP_SYS_ADMIN) ||
		    drm_is_control_client(file_priv)) {
			ret = fb->funcs->create_handle(fb, file_priv,
						       &r->handle);
		} else {
+94 −38
Original line number Diff line number Diff line
@@ -285,6 +285,45 @@ static int drm_version(struct drm_device *dev, void *data,
	return err;
}

/**
 * drm_ioctl_permit - Check ioctl permissions against caller
 *
 * @flags: ioctl permission flags.
 * @file_priv: Pointer to struct drm_file identifying the caller.
 *
 * Checks whether the caller is allowed to run an ioctl with the
 * indicated permissions. If so, returns zero. Otherwise returns an
 * error code suitable for ioctl return.
 */
static int drm_ioctl_permit(u32 flags, struct drm_file *file_priv)
{
	/* ROOT_ONLY is only for CAP_SYS_ADMIN */
	if (unlikely((flags & DRM_ROOT_ONLY) && !capable(CAP_SYS_ADMIN)))
		return -EACCES;

	/* AUTH is only for authenticated or render client */
	if (unlikely((flags & DRM_AUTH) && !drm_is_render_client(file_priv) &&
		     !file_priv->authenticated))
		return -EACCES;

	/* MASTER is only for master or control clients */
	if (unlikely((flags & DRM_MASTER) && !file_priv->is_master &&
		     !drm_is_control_client(file_priv)))
		return -EACCES;

	/* Control clients must be explicitly allowed */
	if (unlikely(!(flags & DRM_CONTROL_ALLOW) &&
		     drm_is_control_client(file_priv)))
		return -EACCES;

	/* Render clients must be explicitly allowed */
	if (unlikely(!(flags & DRM_RENDER_ALLOW) &&
		     drm_is_render_client(file_priv)))
		return -EACCES;

	return 0;
}

/**
 * Called whenever a process performs an ioctl on /dev/drm.
 *
@@ -350,16 +389,16 @@ long drm_ioctl(struct file *filp,
	/* Do not trust userspace, use our own definition */
	func = ioctl->func;

	if (!func) {
	if (unlikely(!func)) {
		DRM_DEBUG("no function\n");
		retcode = -EINVAL;
	} else if (((ioctl->flags & DRM_ROOT_ONLY) && !capable(CAP_SYS_ADMIN)) ||
		   ((ioctl->flags & DRM_AUTH) && !drm_is_render_client(file_priv) && !file_priv->authenticated) ||
		   ((ioctl->flags & DRM_MASTER) && !file_priv->is_master) ||
		   (!(ioctl->flags & DRM_CONTROL_ALLOW) && (file_priv->minor->type == DRM_MINOR_CONTROL)) ||
		   (!(ioctl->flags & DRM_RENDER_ALLOW) && drm_is_render_client(file_priv))) {
		retcode = -EACCES;
	} else {
		goto err_i1;
	}

	retcode = drm_ioctl_permit(ioctl->flags, file_priv);
	if (unlikely(retcode))
		goto err_i1;

	if (cmd & (IOC_IN | IOC_OUT)) {
		if (asize <= sizeof(stack_kdata)) {
			kdata = stack_kdata;
@@ -396,7 +435,6 @@ long drm_ioctl(struct file *filp,
				 usize) != 0)
			retcode = -EFAULT;
	}
	}

      err_i1:
	if (!ioctl)
@@ -412,3 +450,21 @@ long drm_ioctl(struct file *filp,
	return retcode;
}
EXPORT_SYMBOL(drm_ioctl);

/**
 * drm_ioctl_flags - Check for core ioctl and return ioctl permission flags
 *
 * @nr: Ioctl number.
 * @flags: Where to return the ioctl permission flags
 */
bool drm_ioctl_flags(unsigned int nr, unsigned int *flags)
{
	if ((nr >= DRM_COMMAND_END && nr < DRM_CORE_IOCTL_COUNT) ||
	    (nr < DRM_COMMAND_BASE)) {
		*flags = drm_ioctls[nr].flags;
		return true;
	}

	return false;
}
EXPORT_SYMBOL(drm_ioctl_flags);
+13 −13
Original line number Diff line number Diff line
@@ -231,12 +231,11 @@ static int drm_open_helper(struct inode *inode, struct file *filp,

	/* if there is no current master make this fd it, but do not create
	 * any master object for render clients */
	mutex_lock(&dev->struct_mutex);
	if (!priv->minor->master && !drm_is_render_client(priv)) {
	mutex_lock(&dev->master_mutex);
	if (drm_is_primary_client(priv) && !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_close;
		}
@@ -244,37 +243,31 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
		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_close;
			}
		}
		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_close;
			}
		}
	} else if (!drm_is_render_client(priv)) {
	} else if (drm_is_primary_client(priv)) {
		/* get a reference to the master */
		priv->master = drm_master_get(priv->minor->master);
	}
	mutex_unlock(&dev->struct_mutex);
	mutex_unlock(&dev->master_mutex);

	mutex_lock(&dev->struct_mutex);
	list_add(&priv->lhead, &dev->filelist);
@@ -302,6 +295,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
	return 0;

out_close:
	mutex_unlock(&dev->master_mutex);
	if (dev->driver->postclose)
		dev->driver->postclose(dev, priv);
out_prime_destroy:
@@ -489,11 +483,13 @@ int drm_release(struct inode *inode, struct file *filp)
	}
	mutex_unlock(&dev->ctxlist_mutex);

	mutex_lock(&dev->struct_mutex);
	mutex_lock(&dev->master_mutex);

	if (file_priv->is_master) {
		struct drm_master *master = file_priv->master;
		struct drm_file *temp;

		mutex_lock(&dev->struct_mutex);
		list_for_each_entry(temp, &dev->filelist, lhead) {
			if ((temp->master == file_priv->master) &&
			    (temp != file_priv))
@@ -512,6 +508,7 @@ int drm_release(struct inode *inode, struct file *filp)
			master->lock.file_priv = NULL;
			wake_up_interruptible_all(&master->lock.lock_queue);
		}
		mutex_unlock(&dev->struct_mutex);

		if (file_priv->minor->master == file_priv->master) {
			/* drop the reference held my the minor */
@@ -521,10 +518,13 @@ int drm_release(struct inode *inode, struct file *filp)
		}
	}

	/* drop the reference held my the file priv */
	/* drop the master reference held by the file priv */
	if (file_priv->master)
		drm_master_put(&file_priv->master);
	file_priv->is_master = 0;
	mutex_unlock(&dev->master_mutex);

	mutex_lock(&dev->struct_mutex);
	list_del(&file_priv->lhead);
	mutex_unlock(&dev->struct_mutex);

+28 −20
Original line number Diff line number Diff line
@@ -127,8 +127,6 @@ struct drm_master *drm_master_create(struct drm_minor *minor)
	INIT_LIST_HEAD(&master->magicfree);
	master->minor = minor;

	list_add_tail(&master->head, &minor->master_list);

	return master;
}

@@ -146,8 +144,7 @@ static void drm_master_destroy(struct kref *kref)
	struct drm_device *dev = master->minor->dev;
	struct drm_map_list *r_list, *list_temp;

	list_del(&master->head);

	mutex_lock(&dev->struct_mutex);
	if (dev->driver->master_destroy)
		dev->driver->master_destroy(dev, master);

@@ -175,6 +172,7 @@ static void drm_master_destroy(struct kref *kref)

	drm_ht_remove(&master->magiclist);

	mutex_unlock(&dev->struct_mutex);
	kfree(master);
}

@@ -190,19 +188,20 @@ int drm_setmaster_ioctl(struct drm_device *dev, void *data,
{
	int ret = 0;

	mutex_lock(&dev->master_mutex);
	if (file_priv->is_master)
		return 0;

	if (file_priv->minor->master && file_priv->minor->master != file_priv->master)
		return -EINVAL;
		goto out_unlock;

	if (!file_priv->master)
		return -EINVAL;
	if (file_priv->minor->master) {
		ret = -EINVAL;
		goto out_unlock;
	}

	if (file_priv->minor->master)
		return -EINVAL;
	if (!file_priv->master) {
		ret = -EINVAL;
		goto out_unlock;
	}

	mutex_lock(&dev->struct_mutex);
	file_priv->minor->master = drm_master_get(file_priv->master);
	file_priv->is_master = 1;
	if (dev->driver->master_set) {
@@ -212,27 +211,33 @@ int drm_setmaster_ioctl(struct drm_device *dev, void *data,
			drm_master_put(&file_priv->minor->master);
		}
	}
	mutex_unlock(&dev->struct_mutex);

out_unlock:
	mutex_unlock(&dev->master_mutex);
	return ret;
}

int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
			 struct drm_file *file_priv)
{
	int ret = -EINVAL;

	mutex_lock(&dev->master_mutex);
	if (!file_priv->is_master)
		return -EINVAL;
		goto out_unlock;

	if (!file_priv->minor->master)
		return -EINVAL;
		goto out_unlock;

	mutex_lock(&dev->struct_mutex);
	ret = 0;
	if (dev->driver->master_drop)
		dev->driver->master_drop(dev, file_priv, false);
	drm_master_put(&file_priv->minor->master);
	file_priv->is_master = 0;
	mutex_unlock(&dev->struct_mutex);
	return 0;

out_unlock:
	mutex_unlock(&dev->master_mutex);
	return ret;
}

/*
@@ -273,7 +278,6 @@ static int drm_minor_alloc(struct drm_device *dev, unsigned int type)

	minor->type = type;
	minor->dev = dev;
	INIT_LIST_HEAD(&minor->master_list);

	*drm_minor_get_slot(dev, type) = minor;
	return 0;
@@ -564,6 +568,7 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
	spin_lock_init(&dev->event_lock);
	mutex_init(&dev->struct_mutex);
	mutex_init(&dev->ctxlist_mutex);
	mutex_init(&dev->master_mutex);

	dev->anon_inode = drm_fs_inode_new();
	if (IS_ERR(dev->anon_inode)) {
@@ -617,6 +622,7 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
	drm_minor_free(dev, DRM_MINOR_CONTROL);
	drm_fs_inode_free(dev->anon_inode);
err_free:
	mutex_destroy(&dev->master_mutex);
	kfree(dev);
	return NULL;
}
@@ -638,6 +644,8 @@ static void drm_dev_release(struct kref *ref)
	drm_minor_free(dev, DRM_MINOR_CONTROL);

	kfree(dev->devname);

	mutex_destroy(&dev->master_mutex);
	kfree(dev);
}

+46 −0
Original line number Diff line number Diff line
@@ -270,6 +270,52 @@ ttm_base_object_lookup_for_ref(struct ttm_object_device *tdev, uint32_t key)
}
EXPORT_SYMBOL(ttm_base_object_lookup_for_ref);

/**
 * ttm_ref_object_exists - Check whether a caller has a valid ref object
 * (has opened) a base object.
 *
 * @tfile: Pointer to a struct ttm_object_file identifying the caller.
 * @base: Pointer to a struct base object.
 *
 * Checks wether the caller identified by @tfile has put a valid USAGE
 * reference object on the base object identified by @base.
 */
bool ttm_ref_object_exists(struct ttm_object_file *tfile,
			   struct ttm_base_object *base)
{
	struct drm_open_hash *ht = &tfile->ref_hash[TTM_REF_USAGE];
	struct drm_hash_item *hash;
	struct ttm_ref_object *ref;

	rcu_read_lock();
	if (unlikely(drm_ht_find_item_rcu(ht, base->hash.key, &hash) != 0))
		goto out_false;

	/*
	 * Verify that the ref object is really pointing to our base object.
	 * Our base object could actually be dead, and the ref object pointing
	 * to another base object with the same handle.
	 */
	ref = drm_hash_entry(hash, struct ttm_ref_object, hash);
	if (unlikely(base != ref->obj))
		goto out_false;

	/*
	 * Verify that the ref->obj pointer was actually valid!
	 */
	rmb();
	if (unlikely(atomic_read(&ref->kref.refcount) == 0))
		goto out_false;

	rcu_read_unlock();
	return true;

 out_false:
	rcu_read_unlock();
	return false;
}
EXPORT_SYMBOL(ttm_ref_object_exists);

int ttm_ref_object_add(struct ttm_object_file *tfile,
		       struct ttm_base_object *base,
		       enum ttm_ref_type ref_type, bool *existed)
Loading