From 2291f9f43f528d8f18893ab4669bae482239f719 Mon Sep 17 00:00:00 2001
From: Nicolas Boichat <nicolas@boichat.ch>
Date: Fri, 17 Jan 2014 11:29:25 +0800
Subject: [PATCH 2/8] CHROMIUM: drm: Add drm_master_relax debugfs file
 (non-root set/drop master ioctls)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Writing 'Y' to /sys/kernel/debug/dri/drm_master_relax allows
non-CAP_SYS_ADMIN user to call the 2 ioctls DRM_IOCTL_SET_MASTER and
DRM_IOCTL_DROP_MASTER.

This makes it possible to run 2 X11 servers at the same time, where at
least one of them runs under a non-root user (e.g. Chromium OS X server
as xorg user, and a crouton X server as root).

The behavior is not modified if /sys/kernel/debug/dri/drm_master_relax
is kept to its default value ('N').

Modifying drm_master_relax is only possible by root user, so it cannot
possibly lead to exploits. Also, even when the flag is set to 'Y',
DROP_MASTER permissions are changed to DRM_MASTER which means that only
a process that has DRM master can drop it (which is actually quite
logical), and the SET_MASTER IOCTL is changed to DRM_AUTH which allows
all authenticated DRM users to get master.

R=seanpaul@chromium.org, dbehr@chromium.org
BUG=chromium:328115
TEST=With or without the debugfs flag, WebGL examples work, and we can
     switch between VT1 and VT2.
TEST=On a peppy in developer mode: With the debugfs flag to 'Y', Ubuntu
     saucy chroot generated by crouton (xfce target) is able to start a
     second X11 display. Switching back and forth between the two
     displays works, and 3D acceleration is working in the chroot.

Signed-off-by: Nicolas Boichat <nicolas@boichat.ch>
Reviewed-on: https://chromium-review.googlesource.com/183190
Reviewed-by: Kees Cook <keescook@chromium.org>
Reviewed-by: Dominik Behr <dbehr@chromium.org>
Commit-Queue: David Schneider <dnschneid@chromium.org>
Tested-by: David Schneider <dnschneid@chromium.org>
(cherry picked from commit d53b6142c2b5c742d2c547568c9814597c41ede6)
Signed-off-by: Dominik Behr <dbehr@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/189390
Reviewed-by: Haixia Shi <hshi@chromium.org>

[benzh: 3.14 rebase. Resolved trivial conflicts]
Signed-off-by: Ben Zhang <benzh@chromium.org>
[bfreed: 3.18 rebase. Resolved conflicts]
Signed-off-by: Bryan Freed <bfreed@chromium.org>
[briannorris: 4.4 rebase. Resolved conflicts; change drm_master_relax to
'bool' to match usage and debugfs_create_bool() API change]
Signed-off-by: Brian Norris <briannorris@chromium.org>

Change-Id: I7b8a00ca8e21321019875012a24cb88fea3c1317
Reviewed-on: https://chromium-review.googlesource.com/267810
Trybot-Ready: Bryan Freed <bfreed@chromium.org>
Tested-by: Bryan Freed <bfreed@chromium.org>
Reviewed-by: Stéphane Marchesin <marcheu@chromium.org>
Commit-Queue: Bryan Freed <bfreed@chromium.org>
Reviewed-on: https://chromium-review.googlesource.com/339283
Commit-Queue: Brian Norris <briannorris@chromium.org>
Tested-by: Brian Norris <briannorris@chromium.org>

Conflicts:
	drivers/gpu/drm/drm_ioctl.c
	include/drm/drmP.h (variable moved to include/drm/drm_drv.h)

[rebase412(groeck): Context conflicts]
Signed-off-by: Guenter Roeck <groeck@chromium.org>

Conflicts:
	drivers/gpu/drm/drm_ioctl.c
[rebase414(groeck): drm_ioctl() reorganized]
Signed-off-by: Guenter Roeck <groeck@chromium.org>
[tfiga: Rebase onto Linux 4.20]
Signed-off-by: Tomasz Figa <tfiga@chromium.org>
---
 drivers/gpu/drm/drm_drv.c   | 11 ++++++++++-
 drivers/gpu/drm/drm_ioctl.c | 13 ++++++++++++-
 include/drm/drm_drv.h       |  1 +
 3 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/drm_drv.c b/drivers/gpu/drm/drm_drv.c
index 7e7d7e0efff083..9cf4ae4d678a9a 100644
--- a/drivers/gpu/drm/drm_drv.c
+++ b/drivers/gpu/drm/drm_drv.c
@@ -948,6 +948,9 @@ static int drm_stub_open(struct inode *inode, struct file *filp)
 	return err;
 }
 
+/* When set to true, allow set/drop master ioctls as normal user */
+bool drm_master_relax;
+
 static const struct file_operations drm_stub_fops = {
 	.owner = THIS_MODULE,
 	.open = drm_stub_open,
@@ -957,7 +960,7 @@ static const struct file_operations drm_stub_fops = {
 static void drm_core_exit(void)
 {
 	unregister_chrdev(DRM_MAJOR, "drm");
-	debugfs_remove(drm_debugfs_root);
+	debugfs_remove_recursive(drm_debugfs_root);
 	drm_sysfs_destroy();
 	idr_destroy(&drm_minors_idr);
 	drm_connector_ida_destroy();
@@ -983,6 +986,12 @@ static int __init drm_core_init(void)
 		goto error;
 	}
 
+	if (!debugfs_create_bool("drm_master_relax", S_IRUSR | S_IWUSR,
+				drm_debugfs_root, &drm_master_relax)) {
+		DRM_ERROR(
+			  "Cannot create /sys/kernel/debug/dri/drm_master_relax\n");
+	}
+
 	ret = register_chrdev(DRM_MAJOR, "drm", &drm_stub_fops);
 	if (ret < 0)
 		goto error;
diff --git a/drivers/gpu/drm/drm_ioctl.c b/drivers/gpu/drm/drm_ioctl.c
index 94bd872d56c48b..22a6c32e4039a8 100644
--- a/drivers/gpu/drm/drm_ioctl.c
+++ b/drivers/gpu/drm/drm_ioctl.c
@@ -790,6 +790,7 @@ long drm_ioctl(struct file *filp,
 	char *kdata = NULL;
 	unsigned int in_size, out_size, drv_size, ksize;
 	bool is_driver_ioctl;
+	int flags;
 
 	dev = file_priv->minor->dev;
 
@@ -823,6 +824,15 @@ long drm_ioctl(struct file *filp,
 		  (long)old_encode_dev(file_priv->minor->kdev->devt),
 		  file_priv->authenticated, ioctl->name);
 
+	flags = ioctl->flags;
+	if (drm_master_relax) {
+		if (nr == DRM_IOCTL_NR(DRM_IOCTL_SET_MASTER))
+			flags = DRM_AUTH;
+		else if (nr == DRM_IOCTL_NR(DRM_IOCTL_DROP_MASTER))
+			flags = DRM_MASTER;
+	}
+
+
 	/* Do not trust userspace, use our own definition */
 	func = ioctl->func;
 
@@ -850,7 +860,8 @@ long drm_ioctl(struct file *filp,
 	if (ksize > in_size)
 		memset(kdata + in_size, 0, ksize - in_size);
 
-	retcode = drm_ioctl_kernel(filp, func, kdata, ioctl->flags);
+	retcode = drm_ioctl_kernel(filp, func, kdata, flags);
+
 	if (copy_to_user((void __user *)arg, kdata, out_size) != 0)
 		retcode = -EFAULT;
 
diff --git a/include/drm/drm_drv.h b/include/drm/drm_drv.h
index 252dd216cc607e..ebde29c1fc2c83 100644
--- a/include/drm/drm_drv.h
+++ b/include/drm/drm_drv.h
@@ -612,6 +612,7 @@ struct drm_driver {
 };
 
 extern unsigned int drm_debug;
+extern bool drm_master_relax;
 
 int drm_dev_init(struct drm_device *dev,
 		 struct drm_driver *driver,
-- 
2.20.0.rc2.403.gdbc3b29805-goog

