简体   繁体   English

在Python中运行ioctl会返回ENOTTY-设备不适合的ioctl

[英]Running ioctl in Python returns ENOTTY - inappropriate ioctl for device

Hey i am having an issue trying to call ioctl linux system call from python. 嘿,我在尝试从python调用ioctl linux系统调用时遇到问题。

Running the following line in C application i manage to get the parent file descriptor of a given linux namespace file descriptor. 在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);

But running the same following script in python gives me "inappropriate ioctl for device" 但是在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)

By running strace on both scripts i see that both issue the same system calls 通过在两个脚本上运行strace,我看到它们都发出相同的系统调用

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

The difference is that the C code actually returns a file descriptor to the parent name space while the python code returns 区别在于,C代码实际上将文件描述符返回到父名称空间,而python代码返回

-1 ENOTTY (Inappropriate ioctl for device)

The issue replicates on 2 different machines(Linux Kernel 4.13.0-37) and both scripts are running with the same user. 该问题在两台不同的计算机(Linux Kernel 4.13.0-37)上复制,并且两个脚本都使用同一用户运行。 Anyone knows what might lead to that issue? 有谁知道可能导致该问题的原因?

Note: There's now a Python 3 library available which covers the Linux namespace ioctl's: PyPi: linuxns-rel , GitHub: thediveo/linuxns_rel . 注意:现在提供了一个Python 3库,该库涵盖了Linux命名空间ioctl的名称:PyPi: linuxns-rel ,GitHub: thediveo / linuxns_rel

This was a tough question. 这是一个棘手的问题。 There are two (three) aspects to why your example didn't work, and it took me some time to figure them out and learn alot about Linux namespaces. 为什么您的示例不起作用有两个(三个)方面,我花了一些时间弄清楚它们并了解了很多有关Linux名称空间的知识。

First, it's NSIO=0xb7 (and not NSIO=0x7b ) as in your Python example, the correct value for NS_GET_PARENT thus is (I missed that bug totally at first, and noticed only after I had a working PoC done from scratch, mimiking the Linux header file macro defines very closely): 首先,它是NSIO=0xb7 (而不是 NSIO=0x7b ),如您的Python示例中所示,因此NS_GET_PARENT的正确值是(我一开始完全忽略了该错误,并且只有在我从头开始完成PoC时才注意到, Linux头文件宏定义非常紧密):

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

Second, your information about the exception thrown by Python's ioctl() seems to have a second mistake, based on what I see in my own experiments with Python 3: an inappropriate ioctl is ENOTTY (25). 其次,根据我在Python 3的实验中看到的内容,关于Python的ioctl()引发的异常的信息似乎还有第二个错误,不合适的ioctl是ENOTTY (25)。 The ENOTTY would fit in with the mistaken NSIO of 0x7b instead of 0xb7 . ENOTTY将适合错误的NSIO 0x7b而不是0xb7 In fact, when I tried your example verbatim, I got an ENOTTY . 实际上,当我逐字尝试您的示例时,我得到了ENOTTY At that point I didn't realize that this is the indication of an invalid request code. 那时我还没有意识到这表明请求代码无效。

With the correct NS_GET_PARENT request code, the error EPERM (1) would fit the situation where the Linux kernel doesn't allow you to see the parent namespace, due to rights restrictions (even if you are superuser, you might be only locally inside another user userspace, et cetera). 使用正确的NS_GET_PARENT请求代码,错误EPERM (1)将适合以下情况:由于权限限制,Linux内核不允许您查看父名称空间(即使您是超级用户,您也可能只在另一个本地本地用户用户空间等)。

So, this corrected code works, subject to access restrictions throwing OSError EPERM s: 因此,此更正后的代码有效,但受访问限制(抛出OSError EPERM的限制:

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)

Since you don't give details about your system's user namespaces setup, I suspect that you felt victim to the same trap I felt for too: asking a process in the "root" user namespace for its parent user workspace: that simply throws a misleading EPERM , but that's correct as to what ioctl_ns(2) has to say. 由于您没有提供有关系统用户名称空间设置的详细信息,因此我怀疑您也陷入了同样的陷阱:在“ root”用户名称空间中为其父用户工作空间请求一个进程:这只会引起误解EPERM ,但这对于ioctl_ns(2)所说的是正确的。

A good trick for verification that the above code works, is to first start Firefox, and then sudo lsns -t user . 验证以上代码是否有效的一个好技巧是首先启动Firefox,然后启动sudo lsns -t user This will show a set of Firefox child processes that run in their own user, ipc, and network namespaces. 这将显示一组在其自己的用户,ipc和网络名称空间中运行的Firefox子进程。 Take the PID of one of these Firefox child processes, and run the above example with it: it should now succeed and if you fstat() the file descriptor returned, it should reference the root user namespace. 以这些Firefox子进程之一的PID为例,并运行上面的示例:它现在应该成功,并且如果fstat()返回了文件描述符,则它应该引用root用户名称空间。 Trying to get the parent for the root user namespace should again fail with EPERM . 尝试获取根用户名称空间的父级应该再次失败,并带有EPERM

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM