LCOV - code coverage report
Current view: top level - drivers/gpu/drm - drm_auth.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 0 157 0.0 %
Date: 2023-08-24 13:40:31 Functions: 0 19 0.0 %

          Line data    Source code
       1             : /*
       2             :  * Created: Tue Feb  2 08:37:54 1999 by faith@valinux.com
       3             :  *
       4             :  * Copyright 1999 Precision Insight, Inc., Cedar Park, Texas.
       5             :  * Copyright 2000 VA Linux Systems, Inc., Sunnyvale, California.
       6             :  * All Rights Reserved.
       7             :  *
       8             :  * Author Rickard E. (Rik) Faith <faith@valinux.com>
       9             :  * Author Gareth Hughes <gareth@valinux.com>
      10             :  *
      11             :  * Permission is hereby granted, free of charge, to any person obtaining a
      12             :  * copy of this software and associated documentation files (the "Software"),
      13             :  * to deal in the Software without restriction, including without limitation
      14             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      15             :  * and/or sell copies of the Software, and to permit persons to whom the
      16             :  * Software is furnished to do so, subject to the following conditions:
      17             :  *
      18             :  * The above copyright notice and this permission notice (including the next
      19             :  * paragraph) shall be included in all copies or substantial portions of the
      20             :  * Software.
      21             :  *
      22             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      23             :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      24             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
      25             :  * VA LINUX SYSTEMS AND/OR ITS SUPPLIERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
      26             :  * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
      27             :  * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
      28             :  * OTHER DEALINGS IN THE SOFTWARE.
      29             :  */
      30             : 
      31             : #include <linux/slab.h>
      32             : 
      33             : #include <drm/drm_auth.h>
      34             : #include <drm/drm_drv.h>
      35             : #include <drm/drm_file.h>
      36             : #include <drm/drm_lease.h>
      37             : #include <drm/drm_print.h>
      38             : 
      39             : #include "drm_internal.h"
      40             : #include "drm_legacy.h"
      41             : 
      42             : /**
      43             :  * DOC: master and authentication
      44             :  *
      45             :  * &struct drm_master is used to track groups of clients with open
      46             :  * primary/legacy device nodes. For every &struct drm_file which has had at
      47             :  * least once successfully became the device master (either through the
      48             :  * SET_MASTER IOCTL, or implicitly through opening the primary device node when
      49             :  * no one else is the current master that time) there exists one &drm_master.
      50             :  * This is noted in &drm_file.is_master. All other clients have just a pointer
      51             :  * to the &drm_master they are associated with.
      52             :  *
      53             :  * In addition only one &drm_master can be the current master for a &drm_device.
      54             :  * It can be switched through the DROP_MASTER and SET_MASTER IOCTL, or
      55             :  * implicitly through closing/opening the primary device node. See also
      56             :  * drm_is_current_master().
      57             :  *
      58             :  * Clients can authenticate against the current master (if it matches their own)
      59             :  * using the GETMAGIC and AUTHMAGIC IOCTLs. Together with exchanging masters,
      60             :  * this allows controlled access to the device for an entire group of mutually
      61             :  * trusted clients.
      62             :  */
      63             : 
      64           0 : static bool drm_is_current_master_locked(struct drm_file *fpriv)
      65             : {
      66             :         lockdep_assert_once(lockdep_is_held(&fpriv->master_lookup_lock) ||
      67             :                             lockdep_is_held(&fpriv->minor->dev->master_mutex));
      68             : 
      69           0 :         return fpriv->is_master && drm_lease_owner(fpriv->master) == fpriv->minor->dev->master;
      70             : }
      71             : 
      72             : /**
      73             :  * drm_is_current_master - checks whether @priv is the current master
      74             :  * @fpriv: DRM file private
      75             :  *
      76             :  * Checks whether @fpriv is current master on its device. This decides whether a
      77             :  * client is allowed to run DRM_MASTER IOCTLs.
      78             :  *
      79             :  * Most of the modern IOCTL which require DRM_MASTER are for kernel modesetting
      80             :  * - the current master is assumed to own the non-shareable display hardware.
      81             :  */
      82           0 : bool drm_is_current_master(struct drm_file *fpriv)
      83             : {
      84             :         bool ret;
      85             : 
      86           0 :         spin_lock(&fpriv->master_lookup_lock);
      87           0 :         ret = drm_is_current_master_locked(fpriv);
      88           0 :         spin_unlock(&fpriv->master_lookup_lock);
      89             : 
      90           0 :         return ret;
      91             : }
      92             : EXPORT_SYMBOL(drm_is_current_master);
      93             : 
      94           0 : int drm_getmagic(struct drm_device *dev, void *data, struct drm_file *file_priv)
      95             : {
      96           0 :         struct drm_auth *auth = data;
      97           0 :         int ret = 0;
      98             : 
      99           0 :         mutex_lock(&dev->master_mutex);
     100           0 :         if (!file_priv->magic) {
     101           0 :                 ret = idr_alloc(&file_priv->master->magic_map, file_priv,
     102             :                                 1, 0, GFP_KERNEL);
     103           0 :                 if (ret >= 0)
     104           0 :                         file_priv->magic = ret;
     105             :         }
     106           0 :         auth->magic = file_priv->magic;
     107           0 :         mutex_unlock(&dev->master_mutex);
     108             : 
     109           0 :         drm_dbg_core(dev, "%u\n", auth->magic);
     110             : 
     111           0 :         return ret < 0 ? ret : 0;
     112             : }
     113             : 
     114           0 : int drm_authmagic(struct drm_device *dev, void *data,
     115             :                   struct drm_file *file_priv)
     116             : {
     117           0 :         struct drm_auth *auth = data;
     118             :         struct drm_file *file;
     119             : 
     120           0 :         drm_dbg_core(dev, "%u\n", auth->magic);
     121             : 
     122           0 :         mutex_lock(&dev->master_mutex);
     123           0 :         file = idr_find(&file_priv->master->magic_map, auth->magic);
     124           0 :         if (file) {
     125           0 :                 file->authenticated = 1;
     126           0 :                 idr_replace(&file_priv->master->magic_map, NULL, auth->magic);
     127             :         }
     128           0 :         mutex_unlock(&dev->master_mutex);
     129             : 
     130           0 :         return file ? 0 : -EINVAL;
     131             : }
     132             : 
     133           0 : struct drm_master *drm_master_create(struct drm_device *dev)
     134             : {
     135             :         struct drm_master *master;
     136             : 
     137           0 :         master = kzalloc(sizeof(*master), GFP_KERNEL);
     138           0 :         if (!master)
     139             :                 return NULL;
     140             : 
     141           0 :         kref_init(&master->refcount);
     142             :         drm_master_legacy_init(master);
     143           0 :         idr_init_base(&master->magic_map, 1);
     144           0 :         master->dev = dev;
     145             : 
     146             :         /* initialize the tree of output resource lessees */
     147           0 :         INIT_LIST_HEAD(&master->lessees);
     148           0 :         INIT_LIST_HEAD(&master->lessee_list);
     149           0 :         idr_init(&master->leases);
     150           0 :         idr_init_base(&master->lessee_idr, 1);
     151             : 
     152           0 :         return master;
     153             : }
     154             : 
     155           0 : static void drm_set_master(struct drm_device *dev, struct drm_file *fpriv,
     156             :                            bool new_master)
     157             : {
     158           0 :         dev->master = drm_master_get(fpriv->master);
     159           0 :         if (dev->driver->master_set)
     160           0 :                 dev->driver->master_set(dev, fpriv, new_master);
     161             : 
     162           0 :         fpriv->was_master = true;
     163           0 : }
     164             : 
     165           0 : static int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv)
     166             : {
     167             :         struct drm_master *old_master;
     168             :         struct drm_master *new_master;
     169             : 
     170             :         lockdep_assert_held_once(&dev->master_mutex);
     171             : 
     172           0 :         WARN_ON(fpriv->is_master);
     173           0 :         old_master = fpriv->master;
     174           0 :         new_master = drm_master_create(dev);
     175           0 :         if (!new_master)
     176             :                 return -ENOMEM;
     177           0 :         spin_lock(&fpriv->master_lookup_lock);
     178           0 :         fpriv->master = new_master;
     179           0 :         spin_unlock(&fpriv->master_lookup_lock);
     180             : 
     181           0 :         fpriv->is_master = 1;
     182           0 :         fpriv->authenticated = 1;
     183             : 
     184           0 :         drm_set_master(dev, fpriv, true);
     185             : 
     186           0 :         if (old_master)
     187             :                 drm_master_put(&old_master);
     188             : 
     189             :         return 0;
     190             : }
     191             : 
     192             : /*
     193             :  * In the olden days the SET/DROP_MASTER ioctls used to return EACCES when
     194             :  * CAP_SYS_ADMIN was not set. This was used to prevent rogue applications
     195             :  * from becoming master and/or failing to release it.
     196             :  *
     197             :  * At the same time, the first client (for a given VT) is _always_ master.
     198             :  * Thus in order for the ioctls to succeed, one had to _explicitly_ run the
     199             :  * application as root or flip the setuid bit.
     200             :  *
     201             :  * If the CAP_SYS_ADMIN was missing, no other client could become master...
     202             :  * EVER :-( Leading to a) the graphics session dying badly or b) a completely
     203             :  * locked session.
     204             :  *
     205             :  *
     206             :  * As some point systemd-logind was introduced to orchestrate and delegate
     207             :  * master as applicable. It does so by opening the fd and passing it to users
     208             :  * while in itself logind a) does the set/drop master per users' request and
     209             :  * b)  * implicitly drops master on VT switch.
     210             :  *
     211             :  * Even though logind looks like the future, there are a few issues:
     212             :  *  - some platforms don't have equivalent (Android, CrOS, some BSDs) so
     213             :  * root is required _solely_ for SET/DROP MASTER.
     214             :  *  - applications may not be updated to use it,
     215             :  *  - any client which fails to drop master* can DoS the application using
     216             :  * logind, to a varying degree.
     217             :  *
     218             :  * * Either due missing CAP_SYS_ADMIN or simply not calling DROP_MASTER.
     219             :  *
     220             :  *
     221             :  * Here we implement the next best thing:
     222             :  *  - ensure the logind style of fd passing works unchanged, and
     223             :  *  - allow a client to drop/set master, iff it is/was master at a given point
     224             :  * in time.
     225             :  *
     226             :  * Note: DROP_MASTER cannot be free for all, as an arbitrator user could:
     227             :  *  - DoS/crash the arbitrator - details would be implementation specific
     228             :  *  - open the node, become master implicitly and cause issues
     229             :  *
     230             :  * As a result this fixes the following when using root-less build w/o logind
     231             :  * - startx
     232             :  * - weston
     233             :  * - various compositors based on wlroots
     234             :  */
     235             : static int
     236           0 : drm_master_check_perm(struct drm_device *dev, struct drm_file *file_priv)
     237             : {
     238           0 :         if (file_priv->pid == task_pid(current) && file_priv->was_master)
     239             :                 return 0;
     240             : 
     241           0 :         if (!capable(CAP_SYS_ADMIN))
     242             :                 return -EACCES;
     243             : 
     244             :         return 0;
     245             : }
     246             : 
     247           0 : int drm_setmaster_ioctl(struct drm_device *dev, void *data,
     248             :                         struct drm_file *file_priv)
     249             : {
     250             :         int ret;
     251             : 
     252           0 :         mutex_lock(&dev->master_mutex);
     253             : 
     254           0 :         ret = drm_master_check_perm(dev, file_priv);
     255           0 :         if (ret)
     256             :                 goto out_unlock;
     257             : 
     258           0 :         if (drm_is_current_master_locked(file_priv))
     259             :                 goto out_unlock;
     260             : 
     261           0 :         if (dev->master) {
     262             :                 ret = -EBUSY;
     263             :                 goto out_unlock;
     264             :         }
     265             : 
     266           0 :         if (!file_priv->master) {
     267             :                 ret = -EINVAL;
     268             :                 goto out_unlock;
     269             :         }
     270             : 
     271           0 :         if (!file_priv->is_master) {
     272           0 :                 ret = drm_new_set_master(dev, file_priv);
     273           0 :                 goto out_unlock;
     274             :         }
     275             : 
     276           0 :         if (file_priv->master->lessor != NULL) {
     277           0 :                 drm_dbg_lease(dev,
     278             :                               "Attempt to set lessee %d as master\n",
     279             :                               file_priv->master->lessee_id);
     280           0 :                 ret = -EINVAL;
     281           0 :                 goto out_unlock;
     282             :         }
     283             : 
     284           0 :         drm_set_master(dev, file_priv, false);
     285             : out_unlock:
     286           0 :         mutex_unlock(&dev->master_mutex);
     287           0 :         return ret;
     288             : }
     289             : 
     290           0 : static void drm_drop_master(struct drm_device *dev,
     291             :                             struct drm_file *fpriv)
     292             : {
     293           0 :         if (dev->driver->master_drop)
     294           0 :                 dev->driver->master_drop(dev, fpriv);
     295           0 :         drm_master_put(&dev->master);
     296           0 : }
     297             : 
     298           0 : int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
     299             :                          struct drm_file *file_priv)
     300             : {
     301             :         int ret;
     302             : 
     303           0 :         mutex_lock(&dev->master_mutex);
     304             : 
     305           0 :         ret = drm_master_check_perm(dev, file_priv);
     306           0 :         if (ret)
     307             :                 goto out_unlock;
     308             : 
     309           0 :         if (!drm_is_current_master_locked(file_priv)) {
     310             :                 ret = -EINVAL;
     311             :                 goto out_unlock;
     312             :         }
     313             : 
     314           0 :         if (!dev->master) {
     315             :                 ret = -EINVAL;
     316             :                 goto out_unlock;
     317             :         }
     318             : 
     319           0 :         if (file_priv->master->lessor != NULL) {
     320           0 :                 drm_dbg_lease(dev,
     321             :                               "Attempt to drop lessee %d as master\n",
     322             :                               file_priv->master->lessee_id);
     323           0 :                 ret = -EINVAL;
     324           0 :                 goto out_unlock;
     325             :         }
     326             : 
     327           0 :         drm_drop_master(dev, file_priv);
     328             : out_unlock:
     329           0 :         mutex_unlock(&dev->master_mutex);
     330           0 :         return ret;
     331             : }
     332             : 
     333           0 : int drm_master_open(struct drm_file *file_priv)
     334             : {
     335           0 :         struct drm_device *dev = file_priv->minor->dev;
     336           0 :         int ret = 0;
     337             : 
     338             :         /* if there is no current master make this fd it, but do not create
     339             :          * any master object for render clients
     340             :          */
     341           0 :         mutex_lock(&dev->master_mutex);
     342           0 :         if (!dev->master) {
     343           0 :                 ret = drm_new_set_master(dev, file_priv);
     344             :         } else {
     345           0 :                 spin_lock(&file_priv->master_lookup_lock);
     346           0 :                 file_priv->master = drm_master_get(dev->master);
     347           0 :                 spin_unlock(&file_priv->master_lookup_lock);
     348             :         }
     349           0 :         mutex_unlock(&dev->master_mutex);
     350             : 
     351           0 :         return ret;
     352             : }
     353             : 
     354           0 : void drm_master_release(struct drm_file *file_priv)
     355             : {
     356           0 :         struct drm_device *dev = file_priv->minor->dev;
     357             :         struct drm_master *master;
     358             : 
     359           0 :         mutex_lock(&dev->master_mutex);
     360           0 :         master = file_priv->master;
     361           0 :         if (file_priv->magic)
     362           0 :                 idr_remove(&file_priv->master->magic_map, file_priv->magic);
     363             : 
     364           0 :         if (!drm_is_current_master_locked(file_priv))
     365             :                 goto out;
     366             : 
     367           0 :         drm_legacy_lock_master_cleanup(dev, master);
     368             : 
     369           0 :         if (dev->master == file_priv->master)
     370           0 :                 drm_drop_master(dev, file_priv);
     371             : out:
     372           0 :         if (drm_core_check_feature(dev, DRIVER_MODESET) && file_priv->is_master) {
     373             :                 /* Revoke any leases held by this or lessees, but only if
     374             :                  * this is the "real" master
     375             :                  */
     376           0 :                 drm_lease_revoke(master);
     377             :         }
     378             : 
     379             :         /* drop the master reference held by the file priv */
     380           0 :         if (file_priv->master)
     381           0 :                 drm_master_put(&file_priv->master);
     382           0 :         mutex_unlock(&dev->master_mutex);
     383           0 : }
     384             : 
     385             : /**
     386             :  * drm_master_get - reference a master pointer
     387             :  * @master: &struct drm_master
     388             :  *
     389             :  * Increments the reference count of @master and returns a pointer to @master.
     390             :  */
     391           0 : struct drm_master *drm_master_get(struct drm_master *master)
     392             : {
     393           0 :         kref_get(&master->refcount);
     394           0 :         return master;
     395             : }
     396             : EXPORT_SYMBOL(drm_master_get);
     397             : 
     398             : /**
     399             :  * drm_file_get_master - reference &drm_file.master of @file_priv
     400             :  * @file_priv: DRM file private
     401             :  *
     402             :  * Increments the reference count of @file_priv's &drm_file.master and returns
     403             :  * the &drm_file.master. If @file_priv has no &drm_file.master, returns NULL.
     404             :  *
     405             :  * Master pointers returned from this function should be unreferenced using
     406             :  * drm_master_put().
     407             :  */
     408           0 : struct drm_master *drm_file_get_master(struct drm_file *file_priv)
     409             : {
     410           0 :         struct drm_master *master = NULL;
     411             : 
     412           0 :         spin_lock(&file_priv->master_lookup_lock);
     413           0 :         if (!file_priv->master)
     414             :                 goto unlock;
     415           0 :         master = drm_master_get(file_priv->master);
     416             : 
     417             : unlock:
     418           0 :         spin_unlock(&file_priv->master_lookup_lock);
     419           0 :         return master;
     420             : }
     421             : EXPORT_SYMBOL(drm_file_get_master);
     422             : 
     423           0 : static void drm_master_destroy(struct kref *kref)
     424             : {
     425           0 :         struct drm_master *master = container_of(kref, struct drm_master, refcount);
     426           0 :         struct drm_device *dev = master->dev;
     427             : 
     428           0 :         if (drm_core_check_feature(dev, DRIVER_MODESET))
     429           0 :                 drm_lease_destroy(master);
     430             : 
     431           0 :         drm_legacy_master_rmmaps(dev, master);
     432             : 
     433           0 :         idr_destroy(&master->magic_map);
     434           0 :         idr_destroy(&master->leases);
     435           0 :         idr_destroy(&master->lessee_idr);
     436             : 
     437           0 :         kfree(master->unique);
     438           0 :         kfree(master);
     439           0 : }
     440             : 
     441             : /**
     442             :  * drm_master_put - unreference and clear a master pointer
     443             :  * @master: pointer to a pointer of &struct drm_master
     444             :  *
     445             :  * This decrements the &drm_master behind @master and sets it to NULL.
     446             :  */
     447           0 : void drm_master_put(struct drm_master **master)
     448             : {
     449           0 :         kref_put(&(*master)->refcount, drm_master_destroy);
     450           0 :         *master = NULL;
     451           0 : }
     452             : EXPORT_SYMBOL(drm_master_put);
     453             : 
     454             : /* Used by drm_client and drm_fb_helper */
     455           0 : bool drm_master_internal_acquire(struct drm_device *dev)
     456             : {
     457           0 :         mutex_lock(&dev->master_mutex);
     458           0 :         if (dev->master) {
     459           0 :                 mutex_unlock(&dev->master_mutex);
     460           0 :                 return false;
     461             :         }
     462             : 
     463             :         return true;
     464             : }
     465             : EXPORT_SYMBOL(drm_master_internal_acquire);
     466             : 
     467             : /* Used by drm_client and drm_fb_helper */
     468           0 : void drm_master_internal_release(struct drm_device *dev)
     469             : {
     470           0 :         mutex_unlock(&dev->master_mutex);
     471           0 : }
     472             : EXPORT_SYMBOL(drm_master_internal_release);

Generated by: LCOV version 1.14