diff options
author | Thomas Hellstrom <thellstrom@vmware.com> | 2014-02-25 13:57:44 -0500 |
---|---|---|
committer | Thomas Hellstrom <thellstrom@vmware.com> | 2014-03-28 09:19:02 -0400 |
commit | c996fd0b956450563454e7ccc97a82ca31f9d043 (patch) | |
tree | 68fa27376f60773bd2feaf5340351cb29e0a834b | |
parent | a12cd0025cdc0b4d43b79249dbd8c266af284032 (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.c | 22 | ||||
-rw-r--r-- | drivers/gpu/drm/drm_stub.c | 43 | ||||
-rw-r--r-- | include/drm/drmP.h | 46 |
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 | ||
304 | out_close: | 297 | out_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); |
307 | out_prime_destroy: | 301 | out_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 | ||
215 | out_unlock: | ||
216 | mutex_unlock(&dev->master_mutex); | ||
213 | return ret; | 217 | return ret; |
214 | } | 218 | } |
215 | 219 | ||
216 | int drm_dropmaster_ioctl(struct drm_device *dev, void *data, | 220 | int 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; | 238 | out_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); |
614 | err_free: | 624 | err_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 { | |||
405 | struct drm_file { | 405 | struct 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 | */ | ||
688 | struct drm_master { | 701 | struct 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 */ |