Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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>

+64 -47
+11 -11
drivers/gpu/drm/drm_fops.c
··· 231 231 232 232 /* if there is no current master make this fd it, but do not create 233 233 * any master object for render clients */ 234 - mutex_lock(&dev->struct_mutex); 234 + mutex_lock(&dev->master_mutex); 235 235 if (drm_is_primary_client(priv) && !priv->minor->master) { 236 236 /* create a new master */ 237 237 priv->minor->master = drm_master_create(priv->minor); 238 238 if (!priv->minor->master) { 239 - mutex_unlock(&dev->struct_mutex); 240 239 ret = -ENOMEM; 241 240 goto out_close; 242 241 } ··· 243 244 priv->is_master = 1; 244 245 /* take another reference for the copy in the local file priv */ 245 246 priv->master = drm_master_get(priv->minor->master); 246 - 247 247 priv->authenticated = 1; 248 248 249 - mutex_unlock(&dev->struct_mutex); 250 249 if (dev->driver->master_create) { 251 250 ret = dev->driver->master_create(dev, priv->master); 252 251 if (ret) { 253 - mutex_lock(&dev->struct_mutex); 254 252 /* drop both references if this fails */ 255 253 drm_master_put(&priv->minor->master); 256 254 drm_master_put(&priv->master); 257 - mutex_unlock(&dev->struct_mutex); 258 255 goto out_close; 259 256 } 260 257 } 261 - mutex_lock(&dev->struct_mutex); 262 258 if (dev->driver->master_set) { 263 259 ret = dev->driver->master_set(dev, priv, true); 264 260 if (ret) { 265 261 /* drop both references if this fails */ 266 262 drm_master_put(&priv->minor->master); 267 263 drm_master_put(&priv->master); 268 - mutex_unlock(&dev->struct_mutex); 269 264 goto out_close; 270 265 } 271 266 } ··· 267 274 /* get a reference to the master */ 268 275 priv->master = drm_master_get(priv->minor->master); 269 276 } 270 - mutex_unlock(&dev->struct_mutex); 277 + mutex_unlock(&dev->master_mutex); 271 278 272 279 mutex_lock(&dev->struct_mutex); 273 280 list_add(&priv->lhead, &dev->filelist); ··· 295 302 return 0; 296 303 297 304 out_close: 305 + mutex_unlock(&dev->master_mutex); 298 306 if (dev->driver->postclose) 299 307 dev->driver->postclose(dev, priv); 300 308 out_prime_destroy: ··· 483 489 } 484 490 mutex_unlock(&dev->ctxlist_mutex); 485 491 486 - mutex_lock(&dev->struct_mutex); 492 + mutex_lock(&dev->master_mutex); 487 493 488 494 if (file_priv->is_master) { 489 495 struct drm_master *master = file_priv->master; 490 496 struct drm_file *temp; 497 + 498 + mutex_lock(&dev->struct_mutex); 491 499 list_for_each_entry(temp, &dev->filelist, lhead) { 492 500 if ((temp->master == file_priv->master) && 493 501 (temp != file_priv)) ··· 508 512 master->lock.file_priv = NULL; 509 513 wake_up_interruptible_all(&master->lock.lock_queue); 510 514 } 515 + mutex_unlock(&dev->struct_mutex); 511 516 512 517 if (file_priv->minor->master == file_priv->master) { 513 518 /* drop the reference held my the minor */ ··· 518 521 } 519 522 } 520 523 521 - /* drop the reference held my the file priv */ 524 + /* drop the master reference held by the file priv */ 522 525 if (file_priv->master) 523 526 drm_master_put(&file_priv->master); 524 527 file_priv->is_master = 0; 528 + mutex_unlock(&dev->master_mutex); 529 + 530 + mutex_lock(&dev->struct_mutex); 525 531 list_del(&file_priv->lhead); 526 532 mutex_unlock(&dev->struct_mutex); 527 533
+28 -15
drivers/gpu/drm/drm_stub.c
··· 144 144 struct drm_device *dev = master->minor->dev; 145 145 struct drm_map_list *r_list, *list_temp; 146 146 147 + mutex_lock(&dev->struct_mutex); 147 148 if (dev->driver->master_destroy) 148 149 dev->driver->master_destroy(dev, master); 149 150 ··· 172 171 173 172 drm_ht_remove(&master->magiclist); 174 173 174 + mutex_unlock(&dev->struct_mutex); 175 175 kfree(master); 176 176 } 177 177 ··· 188 186 { 189 187 int ret = 0; 190 188 189 + mutex_lock(&dev->master_mutex); 191 190 if (file_priv->is_master) 192 - return 0; 191 + goto out_unlock; 193 192 194 - if (file_priv->minor->master && file_priv->minor->master != file_priv->master) 195 - return -EINVAL; 193 + if (file_priv->minor->master) { 194 + ret = -EINVAL; 195 + goto out_unlock; 196 + } 196 197 197 - if (!file_priv->master) 198 - return -EINVAL; 198 + if (!file_priv->master) { 199 + ret = -EINVAL; 200 + goto out_unlock; 201 + } 199 202 200 - if (file_priv->minor->master) 201 - return -EINVAL; 202 - 203 - mutex_lock(&dev->struct_mutex); 204 203 file_priv->minor->master = drm_master_get(file_priv->master); 205 204 file_priv->is_master = 1; 206 205 if (dev->driver->master_set) { ··· 211 208 drm_master_put(&file_priv->minor->master); 212 209 } 213 210 } 214 - mutex_unlock(&dev->struct_mutex); 215 211 212 + out_unlock: 213 + mutex_unlock(&dev->master_mutex); 216 214 return ret; 217 215 } 218 216 219 217 int drm_dropmaster_ioctl(struct drm_device *dev, void *data, 220 218 struct drm_file *file_priv) 221 219 { 220 + int ret = -EINVAL; 221 + 222 + mutex_lock(&dev->master_mutex); 222 223 if (!file_priv->is_master) 223 - return -EINVAL; 224 + goto out_unlock; 224 225 225 226 if (!file_priv->minor->master) 226 - return -EINVAL; 227 + goto out_unlock; 227 228 228 - mutex_lock(&dev->struct_mutex); 229 + ret = 0; 229 230 if (dev->driver->master_drop) 230 231 dev->driver->master_drop(dev, file_priv, false); 231 232 drm_master_put(&file_priv->minor->master); 232 233 file_priv->is_master = 0; 233 - mutex_unlock(&dev->struct_mutex); 234 - return 0; 234 + 235 + out_unlock: 236 + mutex_unlock(&dev->master_mutex); 237 + return ret; 235 238 } 236 239 237 240 /* ··· 568 559 spin_lock_init(&dev->event_lock); 569 560 mutex_init(&dev->struct_mutex); 570 561 mutex_init(&dev->ctxlist_mutex); 562 + mutex_init(&dev->master_mutex); 571 563 572 564 dev->anon_inode = drm_fs_inode_new(); 573 565 if (IS_ERR(dev->anon_inode)) { ··· 622 612 drm_minor_free(dev, DRM_MINOR_CONTROL); 623 613 drm_fs_inode_free(dev->anon_inode); 624 614 err_free: 615 + mutex_destroy(&dev->master_mutex); 625 616 kfree(dev); 626 617 return NULL; 627 618 } ··· 644 633 drm_minor_free(dev, DRM_MINOR_CONTROL); 645 634 646 635 kfree(dev->devname); 636 + 637 + mutex_destroy(&dev->master_mutex); 647 638 kfree(dev); 648 639 } 649 640
+25 -21
include/drm/drmP.h
··· 405 405 struct drm_file { 406 406 unsigned always_authenticated :1; 407 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 410 /* true when the client has asked us to expose stereo 3D mode flags */ 410 411 unsigned stereo_allowed :1; 411 412 ··· 685 684 686 685 #include <drm/drm_crtc.h> 687 686 688 - /* per-master structure */ 687 + /** 688 + * struct drm_master - drm master structure 689 + * 690 + * @refcount: Refcount for this master object. 691 + * @minor: Link back to minor char device we are master for. Immutable. 692 + * @unique: Unique identifier: e.g. busid. Protected by drm_global_mutex. 693 + * @unique_len: Length of unique field. Protected by drm_global_mutex. 694 + * @unique_size: Amount allocated. Protected by drm_global_mutex. 695 + * @magiclist: Hash of used authentication tokens. Protected by struct_mutex. 696 + * @magicfree: List of used authentication tokens. Protected by struct_mutex. 697 + * @lock: DRI lock information. 698 + * @driver_priv: Pointer to driver-private information. 699 + */ 689 700 struct drm_master { 690 - 691 - struct kref refcount; /* refcount for this master */ 692 - 693 - struct drm_minor *minor; /**< link back to minor we are a master for */ 694 - 695 - char *unique; /**< Unique identifier: e.g., busid */ 696 - int unique_len; /**< Length of unique field */ 697 - int unique_size; /**< amount allocated */ 698 - 699 - int blocked; /**< Blocked due to VC switch? */ 700 - 701 - /** \name Authentication */ 702 - /*@{ */ 701 + struct kref refcount; 702 + struct drm_minor *minor; 703 + char *unique; 704 + int unique_len; 705 + int unique_size; 703 706 struct drm_open_hash magiclist; 704 707 struct list_head magicfree; 705 - /*@} */ 706 - 707 - struct drm_lock_data lock; /**< Information on hardware lock */ 708 - 709 - void *driver_priv; /**< Private structure for driver to use */ 708 + struct drm_lock_data lock; 709 + void *driver_priv; 710 710 }; 711 711 712 712 /* Size of ringbuffer for vblank timestamps. Just double-buffer ··· 1022 1020 struct list_head debugfs_list; 1023 1021 struct mutex debugfs_lock; /* Protects debugfs_list. */ 1024 1022 1025 - struct drm_master *master; /* currently active master for this node */ 1023 + /* currently active master for this node. Protected by master_mutex */ 1024 + struct drm_master *master; 1026 1025 struct drm_mode_group mode_group; 1027 1026 }; 1028 1027 ··· 1073 1070 /*@{ */ 1074 1071 spinlock_t count_lock; /**< For inuse, drm_device::open_count, drm_device::buf_use */ 1075 1072 struct mutex struct_mutex; /**< For others */ 1073 + struct mutex master_mutex; /**< For drm_minor::master and drm_file::is_master */ 1076 1074 /*@} */ 1077 1075 1078 1076 /** \name Usage Counters */