.. | .. |
---|
28 | 28 | * OTHER DEALINGS IN THE SOFTWARE. |
---|
29 | 29 | */ |
---|
30 | 30 | |
---|
31 | | -#include <drm/drmP.h> |
---|
| 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 | + |
---|
32 | 39 | #include "drm_internal.h" |
---|
33 | 40 | #include "drm_legacy.h" |
---|
34 | | -#include <drm/drm_lease.h> |
---|
35 | 41 | |
---|
36 | 42 | /** |
---|
37 | 43 | * DOC: master and authentication |
---|
.. | .. |
---|
103 | 109 | return NULL; |
---|
104 | 110 | |
---|
105 | 111 | kref_init(&master->refcount); |
---|
106 | | - spin_lock_init(&master->lock.spinlock); |
---|
107 | | - init_waitqueue_head(&master->lock.lock_queue); |
---|
| 112 | + drm_master_legacy_init(master); |
---|
108 | 113 | idr_init(&master->magic_map); |
---|
109 | 114 | master->dev = dev; |
---|
110 | 115 | |
---|
111 | 116 | /* initialize the tree of output resource lessees */ |
---|
112 | | - master->lessor = NULL; |
---|
113 | | - master->lessee_id = 0; |
---|
114 | 117 | INIT_LIST_HEAD(&master->lessees); |
---|
115 | 118 | INIT_LIST_HEAD(&master->lessee_list); |
---|
116 | 119 | idr_init(&master->leases); |
---|
.. | .. |
---|
119 | 122 | return master; |
---|
120 | 123 | } |
---|
121 | 124 | |
---|
122 | | -static int drm_set_master(struct drm_device *dev, struct drm_file *fpriv, |
---|
123 | | - bool new_master) |
---|
| 125 | +static void drm_set_master(struct drm_device *dev, struct drm_file *fpriv, |
---|
| 126 | + bool new_master) |
---|
124 | 127 | { |
---|
125 | | - int ret = 0; |
---|
126 | | - |
---|
127 | 128 | dev->master = drm_master_get(fpriv->master); |
---|
128 | | - if (dev->driver->master_set) { |
---|
129 | | - ret = dev->driver->master_set(dev, fpriv, new_master); |
---|
130 | | - if (unlikely(ret != 0)) { |
---|
131 | | - drm_master_put(&dev->master); |
---|
132 | | - } |
---|
133 | | - } |
---|
| 129 | + if (dev->driver->master_set) |
---|
| 130 | + dev->driver->master_set(dev, fpriv, new_master); |
---|
134 | 131 | |
---|
135 | | - return ret; |
---|
| 132 | + fpriv->was_master = true; |
---|
136 | 133 | } |
---|
137 | 134 | |
---|
138 | 135 | static int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv) |
---|
139 | 136 | { |
---|
140 | 137 | struct drm_master *old_master; |
---|
141 | | - int ret; |
---|
142 | 138 | |
---|
143 | 139 | lockdep_assert_held_once(&dev->master_mutex); |
---|
144 | 140 | |
---|
.. | .. |
---|
150 | 146 | return -ENOMEM; |
---|
151 | 147 | } |
---|
152 | 148 | |
---|
153 | | - if (dev->driver->master_create) { |
---|
154 | | - ret = dev->driver->master_create(dev, fpriv->master); |
---|
155 | | - if (ret) |
---|
156 | | - goto out_err; |
---|
157 | | - } |
---|
158 | 149 | fpriv->is_master = 1; |
---|
159 | 150 | fpriv->authenticated = 1; |
---|
160 | 151 | |
---|
161 | | - ret = drm_set_master(dev, fpriv, true); |
---|
162 | | - if (ret) |
---|
163 | | - goto out_err; |
---|
| 152 | + drm_set_master(dev, fpriv, true); |
---|
164 | 153 | |
---|
165 | 154 | if (old_master) |
---|
166 | 155 | drm_master_put(&old_master); |
---|
167 | 156 | |
---|
168 | 157 | return 0; |
---|
| 158 | +} |
---|
169 | 159 | |
---|
170 | | -out_err: |
---|
171 | | - /* drop references and restore old master on failure */ |
---|
172 | | - drm_master_put(&fpriv->master); |
---|
173 | | - fpriv->master = old_master; |
---|
174 | | - fpriv->is_master = 0; |
---|
| 160 | +/* |
---|
| 161 | + * In the olden days the SET/DROP_MASTER ioctls used to return EACCES when |
---|
| 162 | + * CAP_SYS_ADMIN was not set. This was used to prevent rogue applications |
---|
| 163 | + * from becoming master and/or failing to release it. |
---|
| 164 | + * |
---|
| 165 | + * At the same time, the first client (for a given VT) is _always_ master. |
---|
| 166 | + * Thus in order for the ioctls to succeed, one had to _explicitly_ run the |
---|
| 167 | + * application as root or flip the setuid bit. |
---|
| 168 | + * |
---|
| 169 | + * If the CAP_SYS_ADMIN was missing, no other client could become master... |
---|
| 170 | + * EVER :-( Leading to a) the graphics session dying badly or b) a completely |
---|
| 171 | + * locked session. |
---|
| 172 | + * |
---|
| 173 | + * |
---|
| 174 | + * As some point systemd-logind was introduced to orchestrate and delegate |
---|
| 175 | + * master as applicable. It does so by opening the fd and passing it to users |
---|
| 176 | + * while in itself logind a) does the set/drop master per users' request and |
---|
| 177 | + * b) * implicitly drops master on VT switch. |
---|
| 178 | + * |
---|
| 179 | + * Even though logind looks like the future, there are a few issues: |
---|
| 180 | + * - some platforms don't have equivalent (Android, CrOS, some BSDs) so |
---|
| 181 | + * root is required _solely_ for SET/DROP MASTER. |
---|
| 182 | + * - applications may not be updated to use it, |
---|
| 183 | + * - any client which fails to drop master* can DoS the application using |
---|
| 184 | + * logind, to a varying degree. |
---|
| 185 | + * |
---|
| 186 | + * * Either due missing CAP_SYS_ADMIN or simply not calling DROP_MASTER. |
---|
| 187 | + * |
---|
| 188 | + * |
---|
| 189 | + * Here we implement the next best thing: |
---|
| 190 | + * - ensure the logind style of fd passing works unchanged, and |
---|
| 191 | + * - allow a client to drop/set master, iff it is/was master at a given point |
---|
| 192 | + * in time. |
---|
| 193 | + * |
---|
| 194 | + * Note: DROP_MASTER cannot be free for all, as an arbitrator user could: |
---|
| 195 | + * - DoS/crash the arbitrator - details would be implementation specific |
---|
| 196 | + * - open the node, become master implicitly and cause issues |
---|
| 197 | + * |
---|
| 198 | + * As a result this fixes the following when using root-less build w/o logind |
---|
| 199 | + * - startx |
---|
| 200 | + * - weston |
---|
| 201 | + * - various compositors based on wlroots |
---|
| 202 | + */ |
---|
| 203 | +static int |
---|
| 204 | +drm_master_check_perm(struct drm_device *dev, struct drm_file *file_priv) |
---|
| 205 | +{ |
---|
| 206 | + if (file_priv->pid == task_pid(current) && file_priv->was_master) |
---|
| 207 | + return 0; |
---|
175 | 208 | |
---|
176 | | - return ret; |
---|
| 209 | + if (!capable(CAP_SYS_ADMIN)) |
---|
| 210 | + return -EACCES; |
---|
| 211 | + |
---|
| 212 | + return 0; |
---|
177 | 213 | } |
---|
178 | 214 | |
---|
179 | 215 | int drm_setmaster_ioctl(struct drm_device *dev, void *data, |
---|
180 | 216 | struct drm_file *file_priv) |
---|
181 | 217 | { |
---|
182 | | - int ret = 0; |
---|
| 218 | + int ret; |
---|
183 | 219 | |
---|
184 | 220 | mutex_lock(&dev->master_mutex); |
---|
| 221 | + |
---|
| 222 | + ret = drm_master_check_perm(dev, file_priv); |
---|
| 223 | + if (ret) |
---|
| 224 | + goto out_unlock; |
---|
| 225 | + |
---|
185 | 226 | if (drm_is_current_master(file_priv)) |
---|
186 | 227 | goto out_unlock; |
---|
187 | 228 | |
---|
188 | 229 | if (dev->master) { |
---|
189 | | - ret = -EINVAL; |
---|
| 230 | + ret = -EBUSY; |
---|
190 | 231 | goto out_unlock; |
---|
191 | 232 | } |
---|
192 | 233 | |
---|
.. | .. |
---|
206 | 247 | goto out_unlock; |
---|
207 | 248 | } |
---|
208 | 249 | |
---|
209 | | - ret = drm_set_master(dev, file_priv, false); |
---|
| 250 | + drm_set_master(dev, file_priv, false); |
---|
210 | 251 | out_unlock: |
---|
211 | 252 | mutex_unlock(&dev->master_mutex); |
---|
212 | 253 | return ret; |
---|
.. | .. |
---|
223 | 264 | int drm_dropmaster_ioctl(struct drm_device *dev, void *data, |
---|
224 | 265 | struct drm_file *file_priv) |
---|
225 | 266 | { |
---|
226 | | - int ret = -EINVAL; |
---|
| 267 | + int ret; |
---|
227 | 268 | |
---|
228 | 269 | mutex_lock(&dev->master_mutex); |
---|
229 | | - if (!drm_is_current_master(file_priv)) |
---|
| 270 | + |
---|
| 271 | + ret = drm_master_check_perm(dev, file_priv); |
---|
| 272 | + if (ret) |
---|
230 | 273 | goto out_unlock; |
---|
231 | 274 | |
---|
232 | | - if (!dev->master) |
---|
| 275 | + if (!drm_is_current_master(file_priv)) { |
---|
| 276 | + ret = -EINVAL; |
---|
233 | 277 | goto out_unlock; |
---|
| 278 | + } |
---|
| 279 | + |
---|
| 280 | + if (!dev->master) { |
---|
| 281 | + ret = -EINVAL; |
---|
| 282 | + goto out_unlock; |
---|
| 283 | + } |
---|
234 | 284 | |
---|
235 | 285 | if (file_priv->master->lessor != NULL) { |
---|
236 | 286 | DRM_DEBUG_LEASE("Attempt to drop lessee %d as master\n", file_priv->master->lessee_id); |
---|
.. | .. |
---|
238 | 288 | goto out_unlock; |
---|
239 | 289 | } |
---|
240 | 290 | |
---|
241 | | - ret = 0; |
---|
242 | 291 | drm_drop_master(dev, file_priv); |
---|
243 | 292 | out_unlock: |
---|
244 | 293 | mutex_unlock(&dev->master_mutex); |
---|
.. | .. |
---|
275 | 324 | if (!drm_is_current_master(file_priv)) |
---|
276 | 325 | goto out; |
---|
277 | 326 | |
---|
278 | | - if (drm_core_check_feature(dev, DRIVER_LEGACY)) { |
---|
279 | | - /* |
---|
280 | | - * Since the master is disappearing, so is the |
---|
281 | | - * possibility to lock. |
---|
282 | | - */ |
---|
283 | | - mutex_lock(&dev->struct_mutex); |
---|
284 | | - if (master->lock.hw_lock) { |
---|
285 | | - if (dev->sigdata.lock == master->lock.hw_lock) |
---|
286 | | - dev->sigdata.lock = NULL; |
---|
287 | | - master->lock.hw_lock = NULL; |
---|
288 | | - master->lock.file_priv = NULL; |
---|
289 | | - wake_up_interruptible_all(&master->lock.lock_queue); |
---|
290 | | - } |
---|
291 | | - mutex_unlock(&dev->struct_mutex); |
---|
292 | | - } |
---|
| 327 | + drm_legacy_lock_master_cleanup(dev, master); |
---|
293 | 328 | |
---|
294 | 329 | if (dev->master == file_priv->master) |
---|
295 | 330 | drm_drop_master(dev, file_priv); |
---|
.. | .. |
---|
344 | 379 | if (drm_core_check_feature(dev, DRIVER_MODESET)) |
---|
345 | 380 | drm_lease_destroy(master); |
---|
346 | 381 | |
---|
347 | | - if (dev->driver->master_destroy) |
---|
348 | | - dev->driver->master_destroy(dev, master); |
---|
349 | | - |
---|
350 | 382 | drm_legacy_master_rmmaps(dev, master); |
---|
351 | 383 | |
---|
352 | 384 | idr_destroy(&master->magic_map); |
---|
.. | .. |
---|
369 | 401 | *master = NULL; |
---|
370 | 402 | } |
---|
371 | 403 | EXPORT_SYMBOL(drm_master_put); |
---|
| 404 | + |
---|
| 405 | +/* Used by drm_client and drm_fb_helper */ |
---|
| 406 | +bool drm_master_internal_acquire(struct drm_device *dev) |
---|
| 407 | +{ |
---|
| 408 | + mutex_lock(&dev->master_mutex); |
---|
| 409 | + if (dev->master) { |
---|
| 410 | + mutex_unlock(&dev->master_mutex); |
---|
| 411 | + return false; |
---|
| 412 | + } |
---|
| 413 | + |
---|
| 414 | + return true; |
---|
| 415 | +} |
---|
| 416 | +EXPORT_SYMBOL(drm_master_internal_acquire); |
---|
| 417 | + |
---|
| 418 | +/* Used by drm_client and drm_fb_helper */ |
---|
| 419 | +void drm_master_internal_release(struct drm_device *dev) |
---|
| 420 | +{ |
---|
| 421 | + mutex_unlock(&dev->master_mutex); |
---|
| 422 | +} |
---|
| 423 | +EXPORT_SYMBOL(drm_master_internal_release); |
---|