请原谅长篇介绍,但我还没有在SO上看到任何其他问题。
我正在玩DRM(Direct Rendering Manager,Linux内核模式设置的包装器),我很难理解其部分设计。
基本上,我可以在我的虚拟终端中打开一个图形卡设备,设置帧缓冲区,更换连接器及其CRTC就好了。这使我能够以轻量级图形模式呈现到VT,而不需要X服务器(这就是kms的意思,事实上X服务器在下面使用它)。
然后我想实现优雅的VT切换,所以当我按下ctrl + alt + f3等时,我可以看到我的其他控制台。事实证明,使用ioctl()
中的内容调用linux/vt.h
并处理一些用户信号很容易。
但后来我尝试从我的图形程序切换到正在运行的X服务器。 Bzzt!没有工作。 X服务器根本没有绘制任何东西。经过一番挖掘后,我发现在Linux内核中,只有一个程序可以进行内核模式设置。那么会发生什么:
drmOpen
,drmModeSetCRTC
等然后我在Wayland源代码中找到了这个:drmDropMaster()
和drmSetMaster()
。这些函数应该释放并重新获得设置模式的权限,以便X服务器可以继续工作,并在切换回我的程序后,可以从那里获取它。
最后真正的问题。 这些功能需要root权限。这是我不明白的部分。我可以搞乱内核模式,但是我不能说"好的X11,我已经完成了播放,我现在正在为您提供访问权限"?为什么?或者这应该在理论上有效,而我在代码中做错了什么? (例如使用错误的文件描述符,或其他什么。)
如果我尝试以普通用户身份运行我的程序,我会被#34;许可拒绝"。如果我以root身份运行它,它可以正常工作 - 我可以从X切换到我的程序,反之亦然。
为什么?
答案 0 :(得分:2)
是的,drmSetMaster
和drmDropMaster
需要root权限,因为它们允许您进行模式设置。否则,任何随机应用程序都可以在屏幕上显示任何想要的内容。 weston通过setuid启动程序处理这个问题。 systemd人员还为systemd-logind(以root身份运行)添加了功能,以便为您执行drm{Set,Drop}Master
调用。这使得最近的X服务器能够在没有root权限的情况下运行。如果你不介意取决于systemd,你可以调查一下。
您的帖子似乎暗示您可以在没有root权限的情况下成功调用drmModeSetCRTC。这对我来说没有意义。你确定吗?
要显示像X,weston这样的服务器,以及在调用drmDropMaster
ioctl之前调用VT_RELDISP
的任何内容,以便下一个会话可以成功调用drmSetMaster
答案 1 :(得分:1)
在深入研究为什么它不起作用之前,我必须了解它是如何工作的。
所以,实际上在libdrm中调用drmModeSetCRTC
和drmSetMaster
只需调用ioctl
:
包含/ xf86drm.c 强>
int drmSetMaster(int fd)
{
return ioctl(fd, DRM_IOCTL_SET_MASTER, 0);
}
这由内核处理。在我的程序中,控制显示的最重要的功能是drmModeSetCRTC
和drmModeAddFB
,其余的只是诊断。那么让我们看看它们是如何由内核处理的。事实证明,有一个大表将ioctl
事件映射到他们的处理程序:
<强>驱动/ GPU / DRM / drm_ioctl.c 强>
static const struct drm_ioctl_desc drm_ioctls[] = {
...
DRM_IOCTL_DEF(DRM_IOCTL_MODE_GETCRTC, drm_mode_getcrtc, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_SETCRTC, drm_mode_setcrtc, DRM_MASTER|DRM_CONTROL_ALLOW|DRM_UNLOCKED),
...,
DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB, drm_mode_addfb, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
DRM_IOCTL_DEF(DRM_IOCTL_MODE_ADDFB2, drm_mode_addfb2, DRM_CONTROL_ALLOW|DRM_UNLOCKED),
...,
},
这由drm_ioctl
使用,其中最有趣的部分是drm_ioctl_permit
。
<强>驱动/ GPU / DRM / drm_ioctl.c 强>
long drm_ioctl(struct file *filp,
unsigned int cmd, unsigned long arg)
{
...
retcode = drm_ioctl_permit(ioctl->flags, file_priv);
if (unlikely(retcode))
goto err_i1;
...
}
static int drm_ioctl_permit(u32 flags, struct drm_file *file_priv)
{
/* ROOT_ONLY is only for CAP_SYS_ADMIN */
if (unlikely((flags & DRM_ROOT_ONLY) && !capable(CAP_SYS_ADMIN)))
return -EACCES;
/* AUTH is only for authenticated or render client */
if (unlikely((flags & DRM_AUTH) && !drm_is_render_client(file_priv) &&
!file_priv->authenticated))
return -EACCES;
/* MASTER is only for master or control clients */
if (unlikely((flags & DRM_MASTER) && !file_priv->is_master &&
!drm_is_control_client(file_priv)))
return -EACCES;
/* Control clients must be explicitly allowed */
if (unlikely(!(flags & DRM_CONTROL_ALLOW) &&
drm_is_control_client(file_priv)))
return -EACCES;
/* Render clients must be explicitly allowed */
if (unlikely(!(flags & DRM_RENDER_ALLOW) &&
drm_is_render_client(file_priv)))
return -EACCES;
return 0;
}
到目前为止,一切都是有道理的。我确实可以打电话给drmModeSetCrtc
,因为我是当前的DRM主人。 (我不知道为什么。一旦我切换到另一个VT,这可能与X11正确放弃其权利有关。也许只有这一点,一旦我开始弄乱ioctl
,我就会自动成为新的DRM主人?)
无论如何,我们来看看drmDropMaster
和drmSetMaster
定义:
<强>驱动/ GPU / DRM / drm_ioctl.c 强>
static const struct drm_ioctl_desc drm_ioctls[] = {
...
DRM_IOCTL_DEF(DRM_IOCTL_SET_MASTER, drm_setmaster_ioctl, DRM_ROOT_ONLY),
DRM_IOCTL_DEF(DRM_IOCTL_DROP_MASTER, drm_dropmaster_ioctl, DRM_ROOT_ONLY),
...
};
所以我的困惑是正确的。我没有做错任何事情,事情就是这样。
我的印象是这是一个严重的内核错误。要么我根本不能设置CRTC,要么我应该能够删除/设置主设备。在任何情况下,撤销每个非根程序权限以绘制到屏幕,因为
任何随机应用程序都可以在屏幕上显示任何想要的内容
过于激进。作为用户,我应该可以自由地控制,无需提供对整个程序的root访问权限,也不依赖于systemd,例如通过制作chmod 0777 /dev/dri/card0
(或组管理)。就像现在一样,它让我觉得懒惰的人对正确的权限管理的回答。
答案 2 :(得分:1)
感谢你写这篇文章。这确实是预期的结果;你不需要在你的代码中寻找一个微妙的错误。
这绝对意味着你可以隐含地成为主人。一个开发人员写了example code作为DRM的初始文档,它没有使用SetMaster。并且源代码中有一条注释(现在是drm_auth.c)&#34;成功地成为了设备主机(通过SET_MASTER IOCTL,或通过打开主设备节点,当没有其他人是当前主设备时) &#34;
DRM_ROOT_ONLY被评为
/**
* @DRM_ROOT_ONLY:
*
* Anything that could potentially wreak a master file descriptor needs
* to have this flag set. Current that's only for the SETMASTER and
* DROPMASTER ioctl, which e.g. logind can call to force a non-behaving
* master (display compositor) into compliance.
*
* This is equivalent to callers with the SYSADMIN capability.
*/
以上需要对IMO进行一些澄清。 logind强制一个不行为的主人的方式不仅仅是通过调用SETMASTER来获得另一个主人 - 这实际上会失败。首先,它必须在非行为主控上调用DROPMASTER。因此logind依赖于此权限检查,以确保非行为主服务器无法竞争logind并首先调用SETMASTER。
同样,logind假设没有特权的用户没有权限直接打开设备节点。我怀疑在open()上隐式成为master的能力是某种形式的向后兼容性。
请注意,如果您可以删除主人,则无法使用SETMASTER将其取回。这意味着这样做的意义相当有限 - 您无法使用它来实现传统的在多个图形服务器之间来回切换。
是一种可以放弃主人并将其取回的方法:关闭fd,并在需要时重新打开它。在我看来,这样可以匹配旧式X(DRM前)的工作方式 - 无法在X服务器的多个实例之间切换,并且每个实例都必须完全接管硬件?所以你总是必须在VT开关后从头开始。这不如能够切换大师那么好; logind说
/* On DRM devices we simply drop DRM-Master but keep it open.
* This allows the user to keep resources allocated. The
* CAP_SYS_ADMIN restriction to DRM-Master prevents users from
* circumventing this. */
答案 3 :(得分:1)
从Linux 5.8开始,drmDropMaster()
不再需要root特权。
相关的提交是45bc3d26c: drm: rework SET_MASTER and DROP_MASTER perm handling 。
source code comments为新旧情况提供了很好的摘要:
在过去,SET / DROP_MASTER ioctl用于在以下情况下返回EACCES: 未设置CAP_SYS_ADMIN。这是用来防止流氓应用程序 成为主控者和/或未能释放它。
同时,第一个客户端(对于给定的VT)总是 主服务器。 因此,为了使ioctl成功,必须明确地运行 以root身份应用程序或翻转setuid位。
如果缺少CAP_SYS_ADMIN,则没有其他客户端可以成为主机... 曾经:-(导致a)图形会话严重崩溃或b)完全 锁定的会话。
...
在这里,我们实现下一个最好的方法:
- 确保fd传递的登录样式保持不变,并且
- 允许客户端放弃/设置主服务器,前提是它在给定点是主服务器 及时。
...