drmDropMaster需要root权限吗?

时间:2015-04-17 20:17:00

标签: linux-kernel x11 wayland

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

我正在玩DRM(Direct Rendering Manager,Linux内核模式设置的包装器),我很难理解其部分设计。

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

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

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

  1. 我从X切换到虚拟终端
  2. 我运行我的程序
  3. 此程序使用drmOpendrmModeSetCRTC
  4. 进入图形模式
  5. 我切换回X
  6. X不再具有恢复其自身模式的权限。
  7. 然后我在Wayland源代码中找到了这个:drmDropMaster()drmSetMaster()。这些函数应该释放并重新获得设置模式的权限,以便X服务器可以继续工作,并在切换回我的程序后,可以从那里获取它。


    最后真正的问题。 这些功能需要root权限。这是我不明白的部分。我可以搞乱内核模式,但是我不能说"好的X11,我已经完成了播放,我现在正在为您提供访问权限"?为什么?或者这应该在理论上有效,而我在代码中做错了什么? (例如使用错误的文件描述符,或其他什么。)

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

    为什么?

4 个答案:

答案 0 :(得分:2)

是的,drmSetMasterdrmDropMaster需要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中调用drmModeSetCRTCdrmSetMaster只需调用ioctl

包含/ xf86drm.c

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

这由内核处理。在我的程序中,控制显示的最重要的功能是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),
        ...,
},

这由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主人?)

无论如何,我们来看看drmDropMasterdrmSetMaster定义:

<强>驱动/ 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传递的登录样式保持不变,并且
  • 允许客户端放弃/设置主服务器,前提是它在给定点是主服务器 及时。

...