rr-*_*rr- 5 x11 linux-kernel wayland
请原谅冗长的介绍,但我在 SO 上没有看到任何其他问题。
我正在使用 DRM(直接渲染管理器,Linux 内核模式设置的包装器)并且我很难理解其设计的一部分。
基本上,我可以在我的虚拟终端中打开一个图形卡设备,设置帧缓冲区,更改连接器及其 CRTC 就好了。这导致我能够在不需要 X 服务器的情况下以轻量级图形模式渲染到 VT(这就是 kms 的含义,实际上 X 服务器在下面使用它)。
然后我想实现优雅的VT切换,所以当我按ctrl+alt+f3等时,我可以看到我的其他控制台。事实证明,调用ioctl()来自linux/vt.h某些用户信号的内容并处理一些用户信号很容易。
但后来我尝试从我的图形程序切换到正在运行的 X 服务器。呸!根本没有用。X 服务器根本没有绘制任何东西。经过一番挖掘,我发现在Linux内核中,只有一个程序可以进行内核模式设置。所以发生的事情是这样的:
drmOpen,drmModeSetCRTC等等。然后我在wayland源代码中找到了这个:drmDropMaster()和drmSetMaster()。这些函数应该释放和重新获得设置模式的权限,以便 X 服务器可以继续工作,并且在切换回我的程序后,它可以从那里获取它。
最后才是真正的问题。 这些功能需要 root 权限。这是我不明白的部分。我可以搞乱内核模式,但我不能说“好吧 X11,我玩完了,我现在给你访问权限”?为什么?或者这在理论上应该有效,而我只是在我的代码中做错了什么?(例如,使用错误的文件描述符或其他任何东西。)
如果我尝试以普通用户身份运行我的程序,我会收到“权限被拒绝”。如果我以 root 身份运行它,它工作正常 - 我可以从 X 切换到我的程序,反之亦然。
为什么?
在深入研究它为什么不起作用之前,我必须先了解它是如何工作的。
因此,在 libdrm 中调用drmModeSetCRTCanddrmSetMaster实际上只是调用ioctl:
包括/xf86drm.c
int drmSetMaster(int fd)
{
return ioctl(fd, DRM_IOCTL_SET_MASTER, 0);
}
Run Code Online (Sandbox Code Playgroud)
这是由内核处理的。在我的程序中,控制显示的最重要的功能是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),
...,
},
Run Code Online (Sandbox Code Playgroud)
这是使用的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;
}
Run Code Online (Sandbox Code Playgroud)
到目前为止一切都有意义。我确实可以打电话,drmModeSetCrtc因为我是当前的 DRM 大师。(我不确定为什么。这可能与我切换到另一个 VT 后 X11 正确放弃其权利有关。也许仅此一点就可以让我在开始搞乱时自动成为新的 DRM 大师ioctl?)
无论如何,让我们看一下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),
...
};
Run Code Online (Sandbox Code Playgroud)
所以我的困惑是正确的。我没有做错什么,事情确实就是这样。
我的印象是这是一个严重的内核错误。要么我根本无法设置 CRTC,要么我应该能够删除/设置 master。无论如何,撤销每个非 root 程序绘制到屏幕的权限,因为
任何随机应用程序都可以在您的屏幕上显示它想要的任何内容
太激进了。作为用户,我应该可以自由地控制它,而无需授予整个程序的 root 访问权限,也不依赖于 systemd,例如通过 make chmod 0777 /dev/dri/card0(或组管理)。就现在而言,在我看来,这就像懒人对适当权限管理的回答。