Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Created: Tue Feb  2 08:37:54 1999 by faith@valinux.com
0003  *
0004  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
0005  * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
0006  * All Rights Reserved.
0007  *
0008  * Author Rickard E. (Rik) Faith <faith@valinux.com>
0009  * Author Gareth Hughes <gareth@valinux.com>
0010  *
0011  * Permission is hereby granted, free of charge, to any person obtaining a
0012  * copy of this software and associated documentation files (the "Software"),
0013  * to deal in the Software without restriction, including without limitation
0014  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
0015  * and/or sell copies of the Software, and to permit persons to whom the
0016  * Software is furnished to do so, subject to the following conditions:
0017  *
0018  * The above copyright notice and this permission notice (including the next
0019  * paragraph) shall be included in all copies or substantial portions of the
0020  * Software.
0021  *
0022  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0023  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0024  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
0025  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
0026  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
0027  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
0028  * OTHER DEALINGS IN THE SOFTWARE.
0029  */
0030 
0031 #include <linux/slab.h>
0032 
0033 #include <drm/drm_auth.h>
0034 #include <drm/drm_drv.h>
0035 #include <drm/drm_file.h>
0036 #include <drm/drm_lease.h>
0037 #include <drm/drm_print.h>
0038 
0039 #include "drm_internal.h"
0040 #include "drm_legacy.h"
0041 
0042 /**
0043  * DOC: master and authentication
0044  *
0045  * &struct drm_master is used to track groups of clients with open
0046  * primary/legacy device nodes. For every &struct drm_file which has had at
0047  * least once successfully became the device master (either through the
0048  * SET_MASTER IOCTL, or implicitly through opening the primary device node when
0049  * no one else is the current master that time) there exists one &drm_master.
0050  * This is noted in &drm_file.is_master. All other clients have just a pointer
0051  * to the &drm_master they are associated with.
0052  *
0053  * In addition only one &drm_master can be the current master for a &drm_device.
0054  * It can be switched through the DROP_MASTER and SET_MASTER IOCTL, or
0055  * implicitly through closing/opening the primary device node. See also
0056  * drm_is_current_master().
0057  *
0058  * Clients can authenticate against the current master (if it matches their own)
0059  * using the GETMAGIC and AUTHMAGIC IOCTLs. Together with exchanging masters,
0060  * this allows controlled access to the device for an entire group of mutually
0061  * trusted clients.
0062  */
0063 
0064 static bool drm_is_current_master_locked(struct drm_file *fpriv)
0065 {
0066     lockdep_assert_once(lockdep_is_held(&fpriv->master_lookup_lock) ||
0067                 lockdep_is_held(&fpriv->minor->dev->master_mutex));
0068 
0069     return fpriv->is_master && drm_lease_owner(fpriv->master) == fpriv->minor->dev->master;
0070 }
0071 
0072 /**
0073  * drm_is_current_master - checks whether @priv is the current master
0074  * @fpriv: DRM file private
0075  *
0076  * Checks whether @fpriv is current master on its device. This decides whether a
0077  * client is allowed to run DRM_MASTER IOCTLs.
0078  *
0079  * Most of the modern IOCTL which require DRM_MASTER are for kernel modesetting
0080  * - the current master is assumed to own the non-shareable display hardware.
0081  */
0082 bool drm_is_current_master(struct drm_file *fpriv)
0083 {
0084     bool ret;
0085 
0086     spin_lock(&fpriv->master_lookup_lock);
0087     ret = drm_is_current_master_locked(fpriv);
0088     spin_unlock(&fpriv->master_lookup_lock);
0089 
0090     return ret;
0091 }
0092 EXPORT_SYMBOL(drm_is_current_master);
0093 
0094 int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv)
0095 {
0096     struct drm_auth *auth = data;
0097     int ret = 0;
0098 
0099     mutex_lock(&dev->master_mutex);
0100     if (!file_priv->magic) {
0101         ret = idr_alloc(&file_priv->master->magic_map, file_priv,
0102                 1, 0, GFP_KERNEL);
0103         if (ret >= 0)
0104             file_priv->magic = ret;
0105     }
0106     auth->magic = file_priv->magic;
0107     mutex_unlock(&dev->master_mutex);
0108 
0109     drm_dbg_core(dev, "%u\n", auth->magic);
0110 
0111     return ret < 0 ? ret : 0;
0112 }
0113 
0114 int drm_authmagic(struct drm_device *dev, void *data,
0115           struct drm_file *file_priv)
0116 {
0117     struct drm_auth *auth = data;
0118     struct drm_file *file;
0119 
0120     drm_dbg_core(dev, "%u\n", auth->magic);
0121 
0122     mutex_lock(&dev->master_mutex);
0123     file = idr_find(&file_priv->master->magic_map, auth->magic);
0124     if (file) {
0125         file->authenticated = 1;
0126         idr_replace(&file_priv->master->magic_map, NULL, auth->magic);
0127     }
0128     mutex_unlock(&dev->master_mutex);
0129 
0130     return file ? 0 : -EINVAL;
0131 }
0132 
0133 struct drm_master *drm_master_create(struct drm_device *dev)
0134 {
0135     struct drm_master *master;
0136 
0137     master = kzalloc(sizeof(*master), GFP_KERNEL);
0138     if (!master)
0139         return NULL;
0140 
0141     kref_init(&master->refcount);
0142     drm_master_legacy_init(master);
0143     idr_init(&master->magic_map);
0144     master->dev = dev;
0145 
0146     /* initialize the tree of output resource lessees */
0147     INIT_LIST_HEAD(&master->lessees);
0148     INIT_LIST_HEAD(&master->lessee_list);
0149     idr_init(&master->leases);
0150     idr_init(&master->lessee_idr);
0151 
0152     return master;
0153 }
0154 
0155 static void drm_set_master(struct drm_device *dev, struct drm_file *fpriv,
0156                bool new_master)
0157 {
0158     dev->master = drm_master_get(fpriv->master);
0159     if (dev->driver->master_set)
0160         dev->driver->master_set(dev, fpriv, new_master);
0161 
0162     fpriv->was_master = true;
0163 }
0164 
0165 static int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv)
0166 {
0167     struct drm_master *old_master;
0168     struct drm_master *new_master;
0169 
0170     lockdep_assert_held_once(&dev->master_mutex);
0171 
0172     WARN_ON(fpriv->is_master);
0173     old_master = fpriv->master;
0174     new_master = drm_master_create(dev);
0175     if (!new_master)
0176         return -ENOMEM;
0177     spin_lock(&fpriv->master_lookup_lock);
0178     fpriv->master = new_master;
0179     spin_unlock(&fpriv->master_lookup_lock);
0180 
0181     fpriv->is_master = 1;
0182     fpriv->authenticated = 1;
0183 
0184     drm_set_master(dev, fpriv, true);
0185 
0186     if (old_master)
0187         drm_master_put(&old_master);
0188 
0189     return 0;
0190 }
0191 
0192 /*
0193  * In the olden days the SET/DROP_MASTER ioctls used to return EACCES when
0194  * CAP_SYS_ADMIN was not set. This was used to prevent rogue applications
0195  * from becoming master and/or failing to release it.
0196  *
0197  * At the same time, the first client (for a given VT) is _always_ master.
0198  * Thus in order for the ioctls to succeed, one had to _explicitly_ run the
0199  * application as root or flip the setuid bit.
0200  *
0201  * If the CAP_SYS_ADMIN was missing, no other client could become master...
0202  * EVER :-( Leading to a) the graphics session dying badly or b) a completely
0203  * locked session.
0204  *
0205  *
0206  * As some point systemd-logind was introduced to orchestrate and delegate
0207  * master as applicable. It does so by opening the fd and passing it to users
0208  * while in itself logind a) does the set/drop master per users' request and
0209  * b)  * implicitly drops master on VT switch.
0210  *
0211  * Even though logind looks like the future, there are a few issues:
0212  *  - some platforms don't have equivalent (Android, CrOS, some BSDs) so
0213  * root is required _solely_ for SET/DROP MASTER.
0214  *  - applications may not be updated to use it,
0215  *  - any client which fails to drop master* can DoS the application using
0216  * logind, to a varying degree.
0217  *
0218  * * Either due missing CAP_SYS_ADMIN or simply not calling DROP_MASTER.
0219  *
0220  *
0221  * Here we implement the next best thing:
0222  *  - ensure the logind style of fd passing works unchanged, and
0223  *  - allow a client to drop/set master, iff it is/was master at a given point
0224  * in time.
0225  *
0226  * Note: DROP_MASTER cannot be free for all, as an arbitrator user could:
0227  *  - DoS/crash the arbitrator - details would be implementation specific
0228  *  - open the node, become master implicitly and cause issues
0229  *
0230  * As a result this fixes the following when using root-less build w/o logind
0231  * - startx
0232  * - weston
0233  * - various compositors based on wlroots
0234  */
0235 static int
0236 drm_master_check_perm(struct drm_device *dev, struct drm_file *file_priv)
0237 {
0238     if (file_priv->pid == task_pid(current) && file_priv->was_master)
0239         return 0;
0240 
0241     if (!capable(CAP_SYS_ADMIN))
0242         return -EACCES;
0243 
0244     return 0;
0245 }
0246 
0247 int drm_setmaster_ioctl(struct drm_device *dev, void *data,
0248             struct drm_file *file_priv)
0249 {
0250     int ret;
0251 
0252     mutex_lock(&dev->master_mutex);
0253 
0254     ret = drm_master_check_perm(dev, file_priv);
0255     if (ret)
0256         goto out_unlock;
0257 
0258     if (drm_is_current_master_locked(file_priv))
0259         goto out_unlock;
0260 
0261     if (dev->master) {
0262         ret = -EBUSY;
0263         goto out_unlock;
0264     }
0265 
0266     if (!file_priv->master) {
0267         ret = -EINVAL;
0268         goto out_unlock;
0269     }
0270 
0271     if (!file_priv->is_master) {
0272         ret = drm_new_set_master(dev, file_priv);
0273         goto out_unlock;
0274     }
0275 
0276     if (file_priv->master->lessor != NULL) {
0277         drm_dbg_lease(dev,
0278                   "Attempt to set lessee %d as master\n",
0279                   file_priv->master->lessee_id);
0280         ret = -EINVAL;
0281         goto out_unlock;
0282     }
0283 
0284     drm_set_master(dev, file_priv, false);
0285 out_unlock:
0286     mutex_unlock(&dev->master_mutex);
0287     return ret;
0288 }
0289 
0290 static void drm_drop_master(struct drm_device *dev,
0291                 struct drm_file *fpriv)
0292 {
0293     if (dev->driver->master_drop)
0294         dev->driver->master_drop(dev, fpriv);
0295     drm_master_put(&dev->master);
0296 }
0297 
0298 int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
0299              struct drm_file *file_priv)
0300 {
0301     int ret;
0302 
0303     mutex_lock(&dev->master_mutex);
0304 
0305     ret = drm_master_check_perm(dev, file_priv);
0306     if (ret)
0307         goto out_unlock;
0308 
0309     if (!drm_is_current_master_locked(file_priv)) {
0310         ret = -EINVAL;
0311         goto out_unlock;
0312     }
0313 
0314     if (!dev->master) {
0315         ret = -EINVAL;
0316         goto out_unlock;
0317     }
0318 
0319     if (file_priv->master->lessor != NULL) {
0320         drm_dbg_lease(dev,
0321                   "Attempt to drop lessee %d as master\n",
0322                   file_priv->master->lessee_id);
0323         ret = -EINVAL;
0324         goto out_unlock;
0325     }
0326 
0327     drm_drop_master(dev, file_priv);
0328 out_unlock:
0329     mutex_unlock(&dev->master_mutex);
0330     return ret;
0331 }
0332 
0333 int drm_master_open(struct drm_file *file_priv)
0334 {
0335     struct drm_device *dev = file_priv->minor->dev;
0336     int ret = 0;
0337 
0338     /* if there is no current master make this fd it, but do not create
0339      * any master object for render clients
0340      */
0341     mutex_lock(&dev->master_mutex);
0342     if (!dev->master) {
0343         ret = drm_new_set_master(dev, file_priv);
0344     } else {
0345         spin_lock(&file_priv->master_lookup_lock);
0346         file_priv->master = drm_master_get(dev->master);
0347         spin_unlock(&file_priv->master_lookup_lock);
0348     }
0349     mutex_unlock(&dev->master_mutex);
0350 
0351     return ret;
0352 }
0353 
0354 void drm_master_release(struct drm_file *file_priv)
0355 {
0356     struct drm_device *dev = file_priv->minor->dev;
0357     struct drm_master *master;
0358 
0359     mutex_lock(&dev->master_mutex);
0360     master = file_priv->master;
0361     if (file_priv->magic)
0362         idr_remove(&file_priv->master->magic_map, file_priv->magic);
0363 
0364     if (!drm_is_current_master_locked(file_priv))
0365         goto out;
0366 
0367     drm_legacy_lock_master_cleanup(dev, master);
0368 
0369     if (dev->master == file_priv->master)
0370         drm_drop_master(dev, file_priv);
0371 out:
0372     if (drm_core_check_feature(dev, DRIVER_MODESET) && file_priv->is_master) {
0373         /* Revoke any leases held by this or lessees, but only if
0374          * this is the "real" master
0375          */
0376         drm_lease_revoke(master);
0377     }
0378 
0379     /* drop the master reference held by the file priv */
0380     if (file_priv->master)
0381         drm_master_put(&file_priv->master);
0382     mutex_unlock(&dev->master_mutex);
0383 }
0384 
0385 /**
0386  * drm_master_get - reference a master pointer
0387  * @master: &struct drm_master
0388  *
0389  * Increments the reference count of @master and returns a pointer to @master.
0390  */
0391 struct drm_master *drm_master_get(struct drm_master *master)
0392 {
0393     kref_get(&master->refcount);
0394     return master;
0395 }
0396 EXPORT_SYMBOL(drm_master_get);
0397 
0398 /**
0399  * drm_file_get_master - reference &drm_file.master of @file_priv
0400  * @file_priv: DRM file private
0401  *
0402  * Increments the reference count of @file_priv's &drm_file.master and returns
0403  * the &drm_file.master. If @file_priv has no &drm_file.master, returns NULL.
0404  *
0405  * Master pointers returned from this function should be unreferenced using
0406  * drm_master_put().
0407  */
0408 struct drm_master *drm_file_get_master(struct drm_file *file_priv)
0409 {
0410     struct drm_master *master = NULL;
0411 
0412     spin_lock(&file_priv->master_lookup_lock);
0413     if (!file_priv->master)
0414         goto unlock;
0415     master = drm_master_get(file_priv->master);
0416 
0417 unlock:
0418     spin_unlock(&file_priv->master_lookup_lock);
0419     return master;
0420 }
0421 EXPORT_SYMBOL(drm_file_get_master);
0422 
0423 static void drm_master_destroy(struct kref *kref)
0424 {
0425     struct drm_master *master = container_of(kref, struct drm_master, refcount);
0426     struct drm_device *dev = master->dev;
0427 
0428     if (drm_core_check_feature(dev, DRIVER_MODESET))
0429         drm_lease_destroy(master);
0430 
0431     drm_legacy_master_rmmaps(dev, master);
0432 
0433     idr_destroy(&master->magic_map);
0434     idr_destroy(&master->leases);
0435     idr_destroy(&master->lessee_idr);
0436 
0437     kfree(master->unique);
0438     kfree(master);
0439 }
0440 
0441 /**
0442  * drm_master_put - unreference and clear a master pointer
0443  * @master: pointer to a pointer of &struct drm_master
0444  *
0445  * This decrements the &drm_master behind @master and sets it to NULL.
0446  */
0447 void drm_master_put(struct drm_master **master)
0448 {
0449     kref_put(&(*master)->refcount, drm_master_destroy);
0450     *master = NULL;
0451 }
0452 EXPORT_SYMBOL(drm_master_put);
0453 
0454 /* Used by drm_client and drm_fb_helper */
0455 bool drm_master_internal_acquire(struct drm_device *dev)
0456 {
0457     mutex_lock(&dev->master_mutex);
0458     if (dev->master) {
0459         mutex_unlock(&dev->master_mutex);
0460         return false;
0461     }
0462 
0463     return true;
0464 }
0465 EXPORT_SYMBOL(drm_master_internal_acquire);
0466 
0467 /* Used by drm_client and drm_fb_helper */
0468 void drm_master_internal_release(struct drm_device *dev)
0469 {
0470     mutex_unlock(&dev->master_mutex);
0471 }
0472 EXPORT_SYMBOL(drm_master_internal_release);