在Python中运行ioctl会返回ENOTTY – 设备的不适当的ioctl

嘿我有一个问题试图从python调用ioctl linux系统调用.

在C应用程序中运行以下行我设法获取给定linux命名空间文件描述符的父文件描述符.

#define NS_GET_PARENT   _IO(NSIO, 0x2)
struct stat sb;
fd = open("/proc/1337/ns/user", O_RDONLY);
parent_fd = ioctl(fd, NS_GET_PARENT);

但是在python中运行相同的以下脚本会给我“设备的不适当的ioctl”

 from fcntl import ioctl
 NS_GET_PARENT = (0x7b << (4*2)) | 2
 f = open('/proc/1337/ns/user')
 fd = ioctl(f.fileno(),NS_GET_PARENT)

通过在两个脚本上运行strace,我发现它们都发出相同的系统调用

 open("/proc/1337/ns/user", O_RDONLY) = 3
 ioctl(3, _IOC(0, 0xb7, 0x02, 0x00), 0)

区别在于,当python代码返回时,C代码实际上将文件描述符返回到父名称空间

-1 ENOTTY (Inappropriate ioctl for device)

该问题在2台不同的计算机上复制(Linux Kernel 4.13.0-37),并且这两个脚本都在同一个用户上运行.
有谁知道可能导致这个问题的原因?

解决方法:

注意:现在有一个可用的Python 3库,它涵盖Linux名称空间ioctl:PyPi:linuxns-rel,GitHub:thediveo/linuxns_rel.

这是一个棘手的问题.有两个(三个)方面为什么你的例子不起作用,我花了一些时间来弄清楚它们并学习很多关于Linux命名空间的知识.

首先,它是NSIO = 0xb7(而不是NSIO = 0x7b),因为在你的Python示例中,NS_GET_PARENT的正确值是这样的(我最初完全错过了这个bug,并且只有在我从头开始工作PoC之后才会注意到,模仿Linux头文件宏定义非常接近):

NS_GET_PARENT = (0xb7 << (4*2)) | 2

其次,关于Python的ioctl()引发的异常的信息似乎有第二个错误,基于我在自己的Python 3实验中看到的:一个不合适的ioctl是ENOTTY(25). ENOTTY适合错误的NSIO 0x7b而不是0xb7.事实上,当我逐字尝试你的例子时,我得到了一个ENOTTY.那时我没有意识到这是无效请求代码的指示.

使用正确的NS_GET_PARENT请求代码,错误EPERM(1)将适合Linux内核不允许您查看父命名空间的情况,因为权限限制(即使您是超级用户,您可能只在另一个内部)用户用户空间等.

因此,这个更正的代码可以工作,受限于抛出OSError EPERMs的访问限制:

from fcntl import ioctl
NS_GET_PARENT = (0xb7 << (4*2)) | 2
with open('/proc/self/ns/user') as f:
    fd = ioctl(f.fileno(), NS_GET_PARENT)

由于您没有提供有关系统用户命名空间设置的详细信息,因此我怀疑您感受到了我所感受到的相同陷阱的受害者:在“root”用户命名空间中询问其父用户工作空间的进程:这只会引发误导EPERM,但这对于ioctl_ns(2)的说法是正确的.

验证上述代码工作的一个好方法是首先启动Firefox,然后是sudo lsns -t user.这将显示一组在其自己的用户,ipc和网络命名空间中运行的Firefox子进程.获取其中一个Firefox子进程的PID,并使用它运行上面的示例:它现在应该成功,如果你返回fstat()文件描述符,它应该引用root用户命名空间.尝试获取root用户命名空间的父级应该再次失败并使用EPERM.

上一篇:linux – 获取WIFI信号强度 – 寻求最佳方式(IOCTL,iwlist(iw)等)


下一篇:Linux输入设备读取ioctl(EVIOCGKEY())与读取(input_event)