Linux kernel drmDropMaster需要根权限吗?
请原谅我做了这么长的介绍,但是我还没有看到其他关于这个的问题 我在玩DRM(Direct Rendering Manager,Linux内核模式设置的包装器),我很难理解其设计的一部分 基本上,我可以在虚拟终端中打开一个图形卡设备,设置帧缓冲区,更换连接器及其CRTC。这使得我能够以轻量级的图形模式渲染到VT,而不需要X服务器(这就是kms的作用,事实上X服务器在下面使用它) 然后我想实现优雅的VT切换,这样当我按下ctrl+alt+f3等键时,我可以看到我的其他控制台。事实证明,从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/
linux/vt.h
调用ioctl()
并处理一些用户信号很容易
但后来我尝试从图形程序切换到运行的X服务器。Bzzt!根本不起作用。X服务器根本没有画任何东西。经过一番挖掘,我发现在Linux内核中,只有一个程序可以进行内核模式设置。那么发生的情况是:
drmOpen
,drmModeSetCRTC
等进入图形模式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.cint 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