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

时间:2018-03-28 16:21:51

标签: python linux linux-kernel system-calls ioctl

嘿,我在尝试从python调用ioctl linux系统调用时遇到了问题。

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

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

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

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

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

-1 ENOTTY (Inappropriate ioctl for device)

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

document.getElementById('play').addEventListener('click', function (e) {
  e.preventDefault();
  document.getElementById('audio').play();
});

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

1 个答案:

答案 0 :(得分:1)

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

这是一个棘手的问题。为什么您的示例不起作用有两个(三个)方面,我花了一些时间弄清楚它们并了解了很多有关Linux名称空间的信息。

首先,它是NSIO=0xb7(而不是不是 NSIO=0x7b),如您的Python示例中那样,NS_GET_PARENT的正确值是(我完全错过了该错误起初,只有在我从头开始完成工作PoC后,才注意到Linux头文件宏定义非常紧密):

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

第二,基于我在Python 3的实验中看到的内容,关于Python ioctl()引发的异常的信息似乎有第二个错误,不正确的ioctl是ENOTTY(25) 。 ENOTTY将与错误的NSIO的{​​{1}}而不是0x7b相适应。实际上,当我逐字尝试您的示例时,我得到了0xb7。那时我还没有意识到这表明请求代码无效。

使用正确的ENOTTY请求代码,错误NS_GET_PARENT(1)会适合Linux内核由于权限限制而不允许您查看父名称空间的情况(即使您是超级用户,则您可能只在本地另一个用户用户空间中,等等。

因此,此经过纠正的代码有效,但受访问限制(抛出EPERM OSError s的限制):

EPERM

由于您没有提供有关系统用户名称空间设置的详细信息,因此我怀疑您也陷入了同样的陷阱:向“ root”用户名称空间中的进程查询其父用户工作空间:会产生误导性的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) ,但是对于ioctl_ns(2)所说的话,这是正确的。

验证以上代码是否有效的一个好技巧是首先启动Firefox,然后启动EPERM。这将显示一组在其自己的用户,ipc和网络名称空间中运行的Firefox子进程。采取以下Firefox子进程之一的PID,然后运行上面的示例:它现在应该成功,并且如果您sudo lsns -t user返回的文件描述符,它应该引用root用户名称空间。尝试获取根用户名称空间的父级应该再次失败,并显示fstat()