5

请原谅冗长的介绍,但我在 SO 上没有看到任何其他问题。

我正在使用 DRM(直接渲染管理器,Linux 内核模式设置的包装器),但我很难理解其设计的一部分。

基本上,我可以在我的虚拟终端中打开一个显卡设备,设置帧缓冲区,更改连接器及其 CRTC。这导致我能够以轻量级图形模式渲染到 VT,而不需要 X 服务器(这就是 kms 的意义所在,实际上 X 服务器在下面使用它)。

然后我想实现优雅的 VT 切换,所以当我点击 ctrl+alt+f3 等时,我可以看到我的其他控制台。ioctl()事实证明,使用来自的东西调用linux/vt.h和处理一些用户信号很容易。

但后来我尝试从我的图形程序切换到正在运行的 X 服务器。呸!根本没有用。X 服务器根本没有绘制任何东西。经过一番挖掘,我发现在Linux内核中,只有一个程序可以进行内核模式设置。所以会发生这样的事情:

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

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


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

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

为什么?

4

4 回答 4

4

是的,drmSetMaster并且drmDropMaster需要 root 权限,因为它们允许您进行模式设置。否则,任何随机应用程序都可以在您的屏幕上显示它想要的任何内容。韦斯顿通过一个 setuid 启动程序来处理这个问题。systemd 人员还向 systemd-logind(以 root 身份运行)添加了功能来drm{Set,Drop}Master为您执行调用。这就是使最近的 X 服务器能够在没有 root 权限的情况下运行的原因。如果你不介意依赖 systemd,你可以研究一下。

您的帖子似乎表明您可以在没有 root 权限的情况下成功调用 drmModeSetCRTC。这对我来说没有意义。你确定吗?

drmDropMaster在调用 ioctl 之前,需要显示 X、weston 以及您正在处理的任何服务器VT_RELDISP,以便下一个会话可以成功调用 drmSetMaster。

于 2015-04-17T23:31:35.790 回答
2

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

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

包括/xf86drm.c

int drmSetMaster(int fd)
{
    return ioctl(fd, DRM_IOCTL_SET_MASTER, 0);
}

这是由内核处理的。在我的程序中,控制显示的最重要功能是drmModeSetCRTCand 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 正确放弃其权利有关。也许仅此一项就可以让我在开始搞乱时自动成为新的 DRM 大师ioctl?)

无论如何,让我们看一下drmDropMasterand的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,要么我应该能够删除/设置 master。在任何情况下,撤销每个非 root 程序在屏幕上绘制的权利,因为

任何随机应用程序都可以在您的屏幕上显示它想要的任何内容

太激进了。作为用户,我应该可以自由地控制它,而无需授予对整个程序的 root 访问权限,也不依赖于 systemd,例如通过制作chmod 0777 /dev/dri/card0(或组管理)。就像现在一样,在我看来,这就像懒人对适当权限管理的回答。

于 2015-04-18T08:04:12.430 回答
2

谢谢你写这个。这确实是预期的结果;您无需在代码中寻找细微的错误。

这绝对是为了让您可以隐式成为大师。开发人员编写了示例代码作为 DRM 的初始文档,并且不使用 SetMaster。并且在源代码(现在的 drm_auth.c)中有一条注释“成功成为设备主设备(通过 SET_MASTER IOCTL,或者在当时没有其他人是当前主设备时通过打开主设备节点隐式地)”。

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 强制非行为 master 的方式不仅仅是为不同的 master 调用 SETMASTER - 这实际上会失败。首先,它必须在非行为主机上调用 DROPMASTER。所以 logind 依赖于这个权限检查,以确保无行为的 master 不能竞争 logind 并首先调用 SETMASTER。

同样 logind 假设非特权用户无权直接打开设备节点。我怀疑在 open() 上隐式成为 master 的能力是某种形式的向后兼容性。

请注意,如果您可以删除您的 master,您将无法使用 SETMASTER 将其取回。这意味着这样做的意义相当有限——您不能使用它来实现在多个图形服务器之间来回切换的传统方式。

一种方法可以删除 master 并将其取回:关闭 fd,并在需要时重新打开它。在我看来,这与旧式 X(DRM 之前?)的工作方式相匹配——难道不能在 X 服务器的多个实例之间切换,并且每个实例都必须完全接管硬件吗?因此,在 VT 切换后,您总是必须从头开始。但是,这不如能够切换主控器好;登录 说

            /* 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. */
于 2018-03-02T13:31:23.457 回答
2

从 Linux 5.8 开始,drmDropMaster()不再需要 root 权限。

相关的提交是45bc3d26c: drm: rework SET_MASTER and DROP_MASTER perm handling

源代码注释很好地总结了新旧情况:

在过去,当 CAP_SYS_ADMIN 未设置时,SET/DROP_MASTER ioctl 用于返回 EACCES。这用于防止流氓应用程序成为主应用程序和/或无法释放它。

同时,第一个客户端(对于给定的 VT)总是主控。因此,为了使 ioctl 成功,必须以root 身份显式运行应用程序或翻转 setuid 位。

如果 CAP_SYS_ADMIN 丢失,则没有其他客户端可以成为主...永远:-( 导致 a) 图形会话严重死亡或 b) 完全锁定的会话。

...

在这里,我们实现了下一个最好的事情:

  • 确保 fd 传递的登录样式不变,并且
  • 允许客户端删除/设置主机,如果它在给定时间点是/曾经是主机。

...

于 2020-08-05T08:08:53.723 回答