You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何让Python subprocess模块接受文件描述符而非路径(等效execveat(2) AT_EMPTY_PATH)

解决subprocess.Popen使用文件描述符避免TOCTTOU的问题

你遇到的TypeError是因为subprocess.Popenexecutable参数只接受字符串类型的路径,而不是整数类型的文件描述符——subprocess的底层实现(尤其是在POSIX系统下)基于execve系统调用,它需要路径字符串而非文件描述符,直接传os.open返回的fd自然会触发类型错误。

你的核心需求是避开验证二进制文件与调用execve之间的TOCTTOU(时间检查到时间使用)漏洞,同时不想放弃subprocess的现有功能,这里有个Linux下的简便解决方案:利用/proc/self/fd/[fd]这个特殊路径。

具体实现代码

import os
import subprocess

# 先以只读+执行时关闭的方式打开目标可执行文件,拿到fd
target_exe_fd = os.open("/path/to/your/target/exe", os.O_RDONLY | os.O_CLOEXEC)
try:
    # 使用/proc/self/fd/{fd}作为可执行文件的路径
    # 这个路径是内核维护的符号链接,直接指向已打开的inode
    proc = subprocess.Popen(
        [f"/proc/self/fd/{target_exe_fd}", "argv1"],
        shell=False
        # 如果需要显式指定executable(比如argv0和实际执行文件不同),可以加上:
        # executable=f"/proc/self/fd/{target_exe_fd}"
    )
    # 等待子进程完成(按需选择)
    proc.wait()
finally:
    # 无论成功失败,关闭fd
    os.close(target_exe_fd)

为什么这个方法能解决TOCTTOU问题?

当你通过os.open打开目标文件拿到fd后,这个fd已经绑定到了文件的inode上。即使原路径对应的文件被删除、替换或者修改权限,/proc/self/fd/{fd}依然会指向你最初打开的那个inode——内核会维护这个映射,完全没有时间窗口让攻击者篡改文件,效果和fexecve/execveat(AT_EMPTY_PATH)一致。

为什么不能直接给executable传fd?

subprocess的POSIX后端(_posixsubprocess.c)在处理executable参数时,会直接把它当作C字符串传递给execve系统调用,而execve不接受文件描述符。Python的类型检查会先发现你传了整数而非字符串,直接抛出TypeError,连系统调用都到不了。

兼容性说明

这个方法仅适用于Linux系统,因为/proc文件系统是Linux特有的。如果是其他UNIX-like系统(比如FreeBSD),可以用/dev/fd/[fd]替代,原理相同。

内容的提问来源于stack exchange,提问作者phg

火山引擎 最新活动