Linux kernel drmDropMaster需要根权限吗?

Linux kernel drmDropMaster需要根权限吗?,linux-kernel,x11,wayland,Linux Kernel,X11,Wayland,请原谅我做了这么长的介绍,但是我还没有看到其他关于这个的问题 我在玩DRM(Direct Rendering Manager,Linux内核模式设置的包装器),我很难理解其设计的一部分 基本上,我可以在虚拟终端中打开一个图形卡设备,设置帧缓冲区,更换连接器及其CRTC。这使得我能够以轻量级的图形模式渲染到VT,而不需要X服务器(这就是kms的作用,事实上X服务器在下面使用它) 然后我想实现优雅的VT切换,这样当我按下ctrl+alt+f3等键时,我可以看到我的其他控制台。事实证明,从linux/

请原谅我做了这么长的介绍,但是我还没有看到其他关于这个的问题

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

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

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

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

  • 我从X切换到虚拟终端
  • 我运行我的程序
  • 此程序通过
    drmOpen
    drmModeSetCRTC
    等进入图形模式
  • 我切换回X
  • X不再具有恢复其自身模式的权限
  • 然后我在wayland源代码中发现了这一点:
    drmDropMaster()
    drmSetMaster()
    。这些函数应该释放并重新获得设置模式的权限,以便X服务器可以继续工作,并且在切换回我的程序后,它可以从此处开始


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

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


    为什么?

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


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


    在调用
    VT\u RELDISP
    ioctl之前,可以显示像X、weston这样的服务器,以及您正在处理的任何东西来调用
    drmDropMaster
    ,以便下一个会话可以成功地调用drmSetMaster。

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

    因此,在libdrm中调用
    drmModeSetCRTC
    drmSetMaster
    ,实际上只调用
    ioctl

    包括/xf86drm.c

    int drmSetMaster(int fd)
    {
        return ioctl(fd, DRM_IOCTL_SET_MASTER, 0);
    }
    
    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),
            ...,
    },
    
    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;
    }
    
    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),
            ...
     };
    
    这是由内核处理的。在我的程序中,控制显示的最重要的功能是
    drmModeSetCRTC
    drmmodeadfb
    ,其余的只是诊断。让我们看看内核是如何处理它们的。原来有一个大表将
    ioctl
    事件映射到它们的处理程序:

    驱动程序/gpu/drm/drm\u ioctl.c

    int drmSetMaster(int fd)
    {
        return ioctl(fd, DRM_IOCTL_SET_MASTER, 0);
    }
    
    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),
            ...,
    },
    
    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;
    }
    
    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),
            ...
     };
    
    这是由
    drm\u ioctl
    使用的,其中最有趣的部分是
    drm\u ioctl\u许可证

    驱动程序/gpu/drm/drm\u ioctl.c

    int drmSetMaster(int fd)
    {
        return ioctl(fd, DRM_IOCTL_SET_MASTER, 0);
    }
    
    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),
            ...,
    },
    
    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;
    }
    
    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),
            ...
     };
    
    到目前为止,一切都有道理。我确实可以调用
    drmModeSetCrtc
    ,因为我是当前的DRM主机。(我不知道为什么。这可能与X11在我切换到另一台VT后正确放弃其权利有关。也许这本身就允许我在开始处理
    ioctl
    ?)时自动成为新的DRM主机。)

    无论如何,让我们来看看<代码> DRMDROPGOMDS和<代码> DRMSETMASTER < /COD>定义:

    驱动程序/gpu/drm/drm\u ioctl.c

    int drmSetMaster(int fd)
    {
        return ioctl(fd, DRM_IOCTL_SET_MASTER, 0);
    }
    
    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),
            ...,
    },
    
    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;
    }
    
    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用户授予对整个程序的访问权限,也不需要依赖systemd,例如通过制作
    chmod 0777/dev/dri/card0
    (或组管理)。现在,在我看来,这就像是懒人对适当权限管理的回答。

    谢谢你写这篇文章。这确实是预期的结果;您不需要在代码中寻找细微的bug

    它的明确目的是你可以成为主人隐式。开发人员编写了DRM的初始文档,并且没有使用SetMaster。源代码中有一条注释(现在是drm_auth.c)“成功地成为设备主节点(通过SET_master IOCTL,或者在没有其他人是当前主节点时通过隐式打开主设备节点)”

    DRM_ROOT_仅注释为

    /**
     * @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.
     */
    
    上面需要一些澄清。logind强制非行为主机的方式并不是简单地为不同的主机调用SETMASTER,这实际上会失败。首先,它必须调用DRO