drmDropMaster 需要 root 权限?

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内核中,只有一个程序可以进行内核模式设置。所以发生的事情是这样的:

  1. 我从 X 切换到虚拟终端
  2. 我运行我的程序
  3. 该程序进入与图形模式drmOpendrmModeSetCRTC等等。
  4. 我切换回 X
  5. X 不再具有恢复其自身模式的权限。

然后我在wayland源代码中找到了这个:drmDropMaster()drmSetMaster()。这些函数应该释放和重新获得设置模式的权限,以便 X 服务器可以继续工作,并且在切换回我的程序后,它可以从那里获取它。


最后才是真正的问题。 这些功能需要 root 权限。这是我不明白的部分。我可以搞乱内核模式,但我不能说“好吧 X11,我玩完了,我现在给你访问权限”?为什么?或者这在理论上应该有效,而我只是在我的代码中做错了什么?(例如,使用错误的文件描述符或其他任何东西。)

如果我尝试以普通用户身份运行我的程序,我会收到“权限被拒绝”。如果我以 root 身份运行它,它工作正常 - 我可以从 X 切换到我的程序,反之亦然。

为什么?

rr-*_*rr- 2

在深入研究它为什么不起作用之前,我必须先了解它是如何工作的。

因此,在 libdrm 中调用drmModeSetCRTCanddrmSetMaster实际上只是调用ioctl

包括/xf86drm.c

int drmSetMaster(int fd)
{
    return ioctl(fd, DRM_IOCTL_SET_MASTER, 0);
}
Run Code Online (Sandbox Code Playgroud)

这是由内核处理的。在我的程序中,控制显示的最重要的功能是drmModeSetCRTCdrmModeAddFB,其余的实际上只是诊断。那么让我们看看内核是如何处理它们的。事实证明,有一个大表将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(或组管理)。就现在而言,在我看来,这就像懒人对适当权限管理的回答。