aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Hellstrom <thellstrom@vmware.com>2014-02-25 13:57:44 -0500
committerThomas Hellstrom <thellstrom@vmware.com>2014-03-28 09:19:02 -0400
commitc996fd0b956450563454e7ccc97a82ca31f9d043 (patch)
tree68fa27376f60773bd2feaf5340351cb29e0a834b
parenta12cd0025cdc0b4d43b79249dbd8c266af284032 (diff)
drm: Protect the master management with a drm_device::master_mutex v3
The master management was previously protected by the drm_device::struct_mutex. In order to avoid locking order violations in a reworked dropped master security check in the vmwgfx driver, break it out into a separate master_mutex. Locking order is master_mutex -> struct_mutex. Also remove drm_master::blocked since it's not used. v2: Add an inline comment about what drm_device::master_mutex is protecting. v3: Remove unneeded struct_mutex locks. Fix error returns in drm_setmaster_ioctl(). Signed-off-by: Thomas Hellstrom <thellstrom@vmware.com> Reviewed-by: Brian Paul <brianp@vmware.com> Reviewed-by: David Herrmann <dh.herrmann@gmail.com> Acked-by: Daniel Vetter <daniel@ffwll.ch>
-rw-r--r--drivers/gpu/drm/drm_fops.c22
-rw-r--r--drivers/gpu/drm/drm_stub.c43
-rw-r--r--include/drm/drmP.h46
3 files changed, 64 insertions, 47 deletions
diff --git a/drivers/gpu/drm/drm_fops.c b/drivers/gpu/drm/drm_fops.c
index c7792b1d1773..a0ce39c96f8e 100644
--- a/drivers/gpu/drm/drm_fops.c
+++ b/drivers/gpu/drm/drm_fops.c
@@ -231,12 +231,11 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
231 231
232 /* if there is no current master make this fd it, but do not create 232 /* if there is no current master make this fd it, but do not create
233 * any master object for render clients */ 233 * any master object for render clients */
234 mutex_lock(&dev->struct_mutex); 234 mutex_lock(&dev->master_mutex);
235 if (drm_is_primary_client(priv) && !priv->minor->master) { 235 if (drm_is_primary_client(priv) && !priv->minor->master) {
236 /* create a new master */ 236 /* create a new master */
237 priv->minor->master = drm_master_create(priv->minor); 237 priv->minor->master = drm_master_create(priv->minor);
238 if (!priv->minor->master) { 238 if (!priv->minor->master) {
239 mutex_unlock(&dev->struct_mutex);
240 ret = -ENOMEM; 239 ret = -ENOMEM;
241 goto out_close; 240 goto out_close;
242 } 241 }
@@ -244,29 +243,23 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
244 priv->is_master = 1; 243 priv->is_master = 1;
245 /* take another reference for the copy in the local file priv */ 244 /* take another reference for the copy in the local file priv */
246 priv->master = drm_master_get(priv->minor->master); 245 priv->master = drm_master_get(priv->minor->master);
247
248 priv->authenticated = 1; 246 priv->authenticated = 1;
249 247
250 mutex_unlock(&dev->struct_mutex);
251 if (dev->driver->master_create) { 248 if (dev->driver->master_create) {
252 ret = dev->driver->master_create(dev, priv->master); 249 ret = dev->driver->master_create(dev, priv->master);
253 if (ret) { 250 if (ret) {
254 mutex_lock(&dev->struct_mutex);
255 /* drop both references if this fails */ 251 /* drop both references if this fails */
256 drm_master_put(&priv->minor->master); 252 drm_master_put(&priv->minor->master);
257 drm_master_put(&priv->master); 253 drm_master_put(&priv->master);
258 mutex_unlock(&dev->struct_mutex);
259 goto out_close; 254 goto out_close;
260 } 255 }
261 } 256 }
262 mutex_lock(&dev->struct_mutex);
263 if (dev->driver->master_set) { 257 if (dev->driver->master_set) {
264 ret = dev->driver->master_set(dev, priv, true); 258 ret = dev->driver->master_set(dev, priv, true);
265 if (ret) { 259 if (ret) {
266 /* drop both references if this fails */ 260 /* drop both references if this fails */
267 drm_master_put(&priv->minor->master); 261 drm_master_put(&priv->minor->master);
268 drm_master_put(&priv->master); 262 drm_master_put(&priv->master);
269 mutex_unlock(&dev->struct_mutex);
270 goto out_close; 263 goto out_close;
271 } 264 }
272 } 265 }
@@ -274,7 +267,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
274 /* get a reference to the master */ 267 /* get a reference to the master */
275 priv->master = drm_master_get(priv->minor->master); 268 priv->master = drm_master_get(priv->minor->master);
276 } 269 }
277 mutex_unlock(&dev->struct_mutex); 270 mutex_unlock(&dev->master_mutex);
278 271
279 mutex_lock(&dev->struct_mutex); 272 mutex_lock(&dev->struct_mutex);
280 list_add(&priv->lhead, &dev->filelist); 273 list_add(&priv->lhead, &dev->filelist);
@@ -302,6 +295,7 @@ static int drm_open_helper(struct inode *inode, struct file *filp,
302 return 0; 295 return 0;
303 296
304out_close: 297out_close:
298 mutex_unlock(&dev->master_mutex);
305 if (dev->driver->postclose) 299 if (dev->driver->postclose)
306 dev->driver->postclose(dev, priv); 300 dev->driver->postclose(dev, priv);
307out_prime_destroy: 301out_prime_destroy:
@@ -489,11 +483,13 @@ int drm_release(struct inode *inode, struct file *filp)
489 } 483 }
490 mutex_unlock(&dev->ctxlist_mutex); 484 mutex_unlock(&dev->ctxlist_mutex);
491 485
492 mutex_lock(&dev->struct_mutex); 486 mutex_lock(&dev->master_mutex);
493 487
494 if (file_priv->is_master) { 488 if (file_priv->is_master) {
495 struct drm_master *master = file_priv->master; 489 struct drm_master *master = file_priv->master;
496 struct drm_file *temp; 490 struct drm_file *temp;
491
492 mutex_lock(&dev->struct_mutex);
497 list_for_each_entry(temp, &dev->filelist, lhead) { 493 list_for_each_entry(temp, &dev->filelist, lhead) {
498 if ((temp->master == file_priv->master) && 494 if ((temp->master == file_priv->master) &&
499 (temp != file_priv)) 495 (temp != file_priv))
@@ -512,6 +508,7 @@ int drm_release(struct inode *inode, struct file *filp)
512 master->lock.file_priv = NULL; 508 master->lock.file_priv = NULL;
513 wake_up_interruptible_all(&master->lock.lock_queue); 509 wake_up_interruptible_all(&master->lock.lock_queue);
514 } 510 }
511 mutex_unlock(&dev->struct_mutex);
515 512
516 if (file_priv->minor->master == file_priv->master) { 513 if (file_priv->minor->master == file_priv->master) {
517 /* drop the reference held my the minor */ 514 /* drop the reference held my the minor */
@@ -521,10 +518,13 @@ int drm_release(struct inode *inode, struct file *filp)
521 } 518 }
522 } 519 }
523 520
524 /* drop the reference held my the file priv */ 521 /* drop the master reference held by the file priv */
525 if (file_priv->master) 522 if (file_priv->master)
526 drm_master_put(&file_priv->master); 523 drm_master_put(&file_priv->master);
527 file_priv->is_master = 0; 524 file_priv->is_master = 0;
525 mutex_unlock(&dev->master_mutex);
526
527 mutex_lock(&dev->struct_mutex);
528 list_del(&file_priv->lhead); 528 list_del(&file_priv->lhead);
529 mutex_unlock(&dev->struct_mutex); 529 mutex_unlock(&dev->struct_mutex);
530 530
diff --git a/drivers/gpu/drm/drm_stub.c b/drivers/gpu/drm/drm_stub.c
index a378af49ed2e..fac6f9834257 100644
--- a/drivers/gpu/drm/drm_stub.c
+++ b/drivers/gpu/drm/drm_stub.c
@@ -144,6 +144,7 @@ static void drm_master_destroy(struct kref *kref)
144 struct drm_device *dev = master->minor->dev; 144 struct drm_device *dev = master->minor->dev;
145 struct drm_map_list *r_list, *list_temp; 145 struct drm_map_list *r_list, *list_temp;
146 146
147 mutex_lock(&dev->struct_mutex);
147 if (dev->driver->master_destroy) 148 if (dev->driver->master_destroy)
148 dev->driver->master_destroy(dev, master); 149 dev->driver->master_destroy(dev, master);
149 150
@@ -171,6 +172,7 @@ static void drm_master_destroy(struct kref *kref)
171 172
172 drm_ht_remove(&master->magiclist); 173 drm_ht_remove(&master->magiclist);
173 174
175 mutex_unlock(&dev->struct_mutex);
174 kfree(master); 176 kfree(master);
175} 177}
176 178
@@ -186,19 +188,20 @@ int drm_setmaster_ioctl(struct drm_device *dev, void *data,
186{ 188{
187 int ret = 0; 189 int ret = 0;
188 190
191 mutex_lock(&dev->master_mutex);
189 if (file_priv->is_master) 192 if (file_priv->is_master)
190 return 0; 193 goto out_unlock;
191 194
192 if (file_priv->minor->master && file_priv->minor->master != file_priv->master) 195 if (file_priv->minor->master) {
193 return -EINVAL; 196 ret = -EINVAL;
194 197 goto out_unlock;
195 if (!file_priv->master) 198 }
196 return -EINVAL;
197 199
198 if (file_priv->minor->master) 200 if (!file_priv->master) {
199 return -EINVAL; 201 ret = -EINVAL;
202 goto out_unlock;
203 }
200 204
201 mutex_lock(&dev->struct_mutex);
202 file_priv->minor->master = drm_master_get(file_priv->master); 205 file_priv->minor->master = drm_master_get(file_priv->master);
203 file_priv->is_master = 1; 206 file_priv->is_master = 1;
204 if (dev->driver->master_set) { 207 if (dev->driver->master_set) {
@@ -208,27 +211,33 @@ int drm_setmaster_ioctl(struct drm_device *dev, void *data,
208 drm_master_put(&file_priv->minor->master); 211 drm_master_put(&file_priv->minor->master);
209 } 212 }
210 } 213 }
211 mutex_unlock(&dev->struct_mutex);
212 214
215out_unlock:
216 mutex_unlock(&dev->master_mutex);
213 return ret; 217 return ret;
214} 218}
215 219
216int drm_dropmaster_ioctl(struct drm_device *dev, void *data, 220int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
217 struct drm_file *file_priv) 221 struct drm_file *file_priv)
218{ 222{
223 int ret = -EINVAL;
224
225 mutex_lock(&dev->master_mutex);
219 if (!file_priv->is_master) 226 if (!file_priv->is_master)
220 return -EINVAL; 227 goto out_unlock;
221 228
222 if (!file_priv->minor->master) 229 if (!file_priv->minor->master)
223 return -EINVAL; 230 goto out_unlock;
224 231
225 mutex_lock(&dev->struct_mutex); 232 ret = 0;
226 if (dev->driver->master_drop) 233 if (dev->driver->master_drop)
227 dev->driver->master_drop(dev, file_priv, false); 234 dev->driver->master_drop(dev, file_priv, false);
228 drm_master_put(&file_priv->minor->master); 235 drm_master_put(&file_priv->minor->master);
229 file_priv->is_master = 0; 236 file_priv->is_master = 0;
230 mutex_unlock(&dev->struct_mutex); 237
231 return 0; 238out_unlock:
239 mutex_unlock(&dev->master_mutex);
240 return ret;
232} 241}
233 242
234/* 243/*
@@ -559,6 +568,7 @@ struct drm_device *drm_dev_alloc(struct drm_driver *driver,
559 spin_lock_init(&dev->event_lock); 568 spin_lock_init(&dev->event_lock);
560 mutex_init(&dev->struct_mutex); 569 mutex_init(&dev->struct_mutex);
561 mutex_init(&dev->ctxlist_mutex); 570 mutex_init(&dev->ctxlist_mutex);
571 mutex_init(&dev->master_mutex);
562 572
563 dev->anon_inode = drm_fs_inode_new(); 573 dev->anon_inode = drm_fs_inode_new();
564 if (IS_ERR(dev->anon_inode)) { 574 if (IS_ERR(dev->anon_inode)) {
@@ -612,6 +622,7 @@ err_minors:
612 drm_minor_free(dev, DRM_MINOR_CONTROL); 622 drm_minor_free(dev, DRM_MINOR_CONTROL);
613 drm_fs_inode_free(dev->anon_inode); 623 drm_fs_inode_free(dev->anon_inode);
614err_free: 624err_free:
625 mutex_destroy(&dev->master_mutex);
615 kfree(dev); 626 kfree(dev);
616 return NULL; 627 return NULL;
617} 628}
@@ -633,6 +644,8 @@ static void drm_dev_release(struct kref *ref)
633 drm_minor_free(dev, DRM_MINOR_CONTROL); 644 drm_minor_free(dev, DRM_MINOR_CONTROL);
634 645
635 kfree(dev->devname); 646 kfree(dev->devname);
647
648 mutex_destroy(&dev->master_mutex);
636 kfree(dev); 649 kfree(dev);
637} 650}
638 651
diff --git a/include/drm/drmP.h b/include/drm/drmP.h
index 3d594ca7fa62..4e24a1a0daeb 100644
--- a/include/drm/drmP.h
+++ b/include/drm/drmP.h
@@ -405,7 +405,8 @@ struct drm_prime_file_private {
405struct drm_file { 405struct drm_file {
406 unsigned always_authenticated :1; 406 unsigned always_authenticated :1;
407 unsigned authenticated :1; 407 unsigned authenticated :1;
408 unsigned is_master :1; /* this file private is a master for a minor */ 408 /* Whether we're master for a minor. Protected by master_mutex */
409 unsigned is_master :1;
409 /* true when the client has asked us to expose stereo 3D mode flags */ 410 /* true when the client has asked us to expose stereo 3D mode flags */
410 unsigned stereo_allowed :1; 411 unsigned stereo_allowed :1;
411 412
@@ -684,28 +685,29 @@ struct drm_gem_object {
684 685
685#include <drm/drm_crtc.h> 686#include <drm/drm_crtc.h>
686 687
687/* per-master structure */ 688/**
689 * struct drm_master - drm master structure
690 *
691 * @refcount: Refcount for this master object.
692 * @minor: Link back to minor char device we are master for. Immutable.
693 * @unique: Unique identifier: e.g. busid. Protected by drm_global_mutex.
694 * @unique_len: Length of unique field. Protected by drm_global_mutex.
695 * @unique_size: Amount allocated. Protected by drm_global_mutex.
696 * @magiclist: Hash of used authentication tokens. Protected by struct_mutex.
697 * @magicfree: List of used authentication tokens. Protected by struct_mutex.
698 * @lock: DRI lock information.
699 * @driver_priv: Pointer to driver-private information.
700 */
688struct drm_master { 701struct drm_master {
689 702 struct kref refcount;
690 struct kref refcount; /* refcount for this master */ 703 struct drm_minor *minor;
691 704 char *unique;
692 struct drm_minor *minor; /**< link back to minor we are a master for */ 705 int unique_len;
693 706 int unique_size;
694 char *unique; /**< Unique identifier: e.g., busid */
695 int unique_len; /**< Length of unique field */
696 int unique_size; /**< amount allocated */
697
698 int blocked; /**< Blocked due to VC switch? */
699
700 /** \name Authentication */
701 /*@{ */
702 struct drm_open_hash magiclist; 707 struct drm_open_hash magiclist;
703 struct list_head magicfree; 708 struct list_head magicfree;
704 /*@} */ 709 struct drm_lock_data lock;
705 710 void *driver_priv;
706 struct drm_lock_data lock; /**< Information on hardware lock */
707
708 void *driver_priv; /**< Private structure for driver to use */
709}; 711};
710 712
711/* Size of ringbuffer for vblank timestamps. Just double-buffer 713/* Size of ringbuffer for vblank timestamps. Just double-buffer
@@ -1020,7 +1022,8 @@ struct drm_minor {
1020 struct list_head debugfs_list; 1022 struct list_head debugfs_list;
1021 struct mutex debugfs_lock; /* Protects debugfs_list. */ 1023 struct mutex debugfs_lock; /* Protects debugfs_list. */
1022 1024
1023 struct drm_master *master; /* currently active master for this node */ 1025 /* currently active master for this node. Protected by master_mutex */
1026 struct drm_master *master;
1024 struct drm_mode_group mode_group; 1027 struct drm_mode_group mode_group;
1025}; 1028};
1026 1029
@@ -1070,6 +1073,7 @@ struct drm_device {
1070 /*@{ */ 1073 /*@{ */
1071 spinlock_t count_lock; /**< For inuse, drm_device::open_count, drm_device::buf_use */ 1074 spinlock_t count_lock; /**< For inuse, drm_device::open_count, drm_device::buf_use */
1072 struct mutex struct_mutex; /**< For others */ 1075 struct mutex struct_mutex; /**< For others */
1076 struct mutex master_mutex; /**< For drm_minor::master and drm_file::is_master */
1073 /*@} */ 1077 /*@} */
1074 1078
1075 /** \name Usage Counters */ 1079 /** \name Usage Counters */