hc
2023-12-11 d2ccde1c8e90d38cee87a1b0309ad2827f3fd30d
kernel/drivers/gpu/drm/drm_auth.c
....@@ -28,10 +28,16 @@
2828 * OTHER DEALINGS IN THE SOFTWARE.
2929 */
3030
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
+
3239 #include "drm_internal.h"
3340 #include "drm_legacy.h"
34
-#include <drm/drm_lease.h>
3541
3642 /**
3743 * DOC: master and authentication
....@@ -103,14 +109,11 @@
103109 return NULL;
104110
105111 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);
108113 idr_init(&master->magic_map);
109114 master->dev = dev;
110115
111116 /* initialize the tree of output resource lessees */
112
- master->lessor = NULL;
113
- master->lessee_id = 0;
114117 INIT_LIST_HEAD(&master->lessees);
115118 INIT_LIST_HEAD(&master->lessee_list);
116119 idr_init(&master->leases);
....@@ -119,26 +122,19 @@
119122 return master;
120123 }
121124
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)
124127 {
125
- int ret = 0;
126
-
127128 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);
134131
135
- return ret;
132
+ fpriv->was_master = true;
136133 }
137134
138135 static int drm_new_set_master(struct drm_device *dev, struct drm_file *fpriv)
139136 {
140137 struct drm_master *old_master;
141
- int ret;
142138
143139 lockdep_assert_held_once(&dev->master_mutex);
144140
....@@ -150,43 +146,88 @@
150146 return -ENOMEM;
151147 }
152148
153
- if (dev->driver->master_create) {
154
- ret = dev->driver->master_create(dev, fpriv->master);
155
- if (ret)
156
- goto out_err;
157
- }
158149 fpriv->is_master = 1;
159150 fpriv->authenticated = 1;
160151
161
- ret = drm_set_master(dev, fpriv, true);
162
- if (ret)
163
- goto out_err;
152
+ drm_set_master(dev, fpriv, true);
164153
165154 if (old_master)
166155 drm_master_put(&old_master);
167156
168157 return 0;
158
+}
169159
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;
175208
176
- return ret;
209
+ if (!capable(CAP_SYS_ADMIN))
210
+ return -EACCES;
211
+
212
+ return 0;
177213 }
178214
179215 int drm_setmaster_ioctl(struct drm_device *dev, void *data,
180216 struct drm_file *file_priv)
181217 {
182
- int ret = 0;
218
+ int ret;
183219
184220 mutex_lock(&dev->master_mutex);
221
+
222
+ ret = drm_master_check_perm(dev, file_priv);
223
+ if (ret)
224
+ goto out_unlock;
225
+
185226 if (drm_is_current_master(file_priv))
186227 goto out_unlock;
187228
188229 if (dev->master) {
189
- ret = -EINVAL;
230
+ ret = -EBUSY;
190231 goto out_unlock;
191232 }
192233
....@@ -206,7 +247,7 @@
206247 goto out_unlock;
207248 }
208249
209
- ret = drm_set_master(dev, file_priv, false);
250
+ drm_set_master(dev, file_priv, false);
210251 out_unlock:
211252 mutex_unlock(&dev->master_mutex);
212253 return ret;
....@@ -223,14 +264,23 @@
223264 int drm_dropmaster_ioctl(struct drm_device *dev, void *data,
224265 struct drm_file *file_priv)
225266 {
226
- int ret = -EINVAL;
267
+ int ret;
227268
228269 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)
230273 goto out_unlock;
231274
232
- if (!dev->master)
275
+ if (!drm_is_current_master(file_priv)) {
276
+ ret = -EINVAL;
233277 goto out_unlock;
278
+ }
279
+
280
+ if (!dev->master) {
281
+ ret = -EINVAL;
282
+ goto out_unlock;
283
+ }
234284
235285 if (file_priv->master->lessor != NULL) {
236286 DRM_DEBUG_LEASE("Attempt to drop lessee %d as master\n", file_priv->master->lessee_id);
....@@ -238,7 +288,6 @@
238288 goto out_unlock;
239289 }
240290
241
- ret = 0;
242291 drm_drop_master(dev, file_priv);
243292 out_unlock:
244293 mutex_unlock(&dev->master_mutex);
....@@ -275,21 +324,7 @@
275324 if (!drm_is_current_master(file_priv))
276325 goto out;
277326
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);
293328
294329 if (dev->master == file_priv->master)
295330 drm_drop_master(dev, file_priv);
....@@ -344,9 +379,6 @@
344379 if (drm_core_check_feature(dev, DRIVER_MODESET))
345380 drm_lease_destroy(master);
346381
347
- if (dev->driver->master_destroy)
348
- dev->driver->master_destroy(dev, master);
349
-
350382 drm_legacy_master_rmmaps(dev, master);
351383
352384 idr_destroy(&master->magic_map);
....@@ -369,3 +401,23 @@
369401 *master = NULL;
370402 }
371403 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);