使用RLIMIT_NPROC阻止二进制程序创建新进程失败,如何修复?
如何禁止Python启动的二进制程序创建新进程
问题分析
你尝试通过RLIMIT_NPROC限制子进程创建新进程但未达预期,核心原因如下:
RLIMIT_NPROC是用户级资源限制,管控的是当前用户能拥有的总进程数,而非单个进程可创建的子进程数量。- 当你在
preexec_fn中设置RLIMIT_NPROC=(1,1)时,子进程a.out本身已计入用户进程总数,而系统中通常还存在Python主进程、Shell进程等其他进程,此时用户总进程数早已超过1,fork调用不会触发限制。 - 普通用户默认无法将
RLIMIT_NPROC的硬限制降低到系统默认值以下,即便强行设置,也无法实现单个进程的子进程创建限制。
解决方案
要实现单个进程级别的禁止创建新进程,推荐使用Linux内核的seccomp机制,它可以精确过滤进程的系统调用,直接阻断fork/clone/vfork这些用于创建新进程的调用。
方法1:使用Python seccomp库(推荐)
首先安装依赖:
sudo apt install python3-seccomp # Debian/Ubuntu # 或通过pip安装 pip install python-seccomp
修改foo.py代码:
import subprocess import resource import seccomp def restrict_process(): # 可选:设置RLIMIT_NPROC作为双重保障 resource.setrlimit(resource.RLIMIT_NPROC, (1, 1)) # 创建seccomp过滤规则,默认允许所有系统调用 ctx = seccomp.SyscallFilter(seccomp.ActivationFilter.ALLOW) # 禁止创建新进程的核心系统调用 ctx.add_rule(seccomp.KILL_PROCESS, "fork") ctx.add_rule(seccomp.KILL_PROCESS, "clone") ctx.add_rule(seccomp.KILL_PROCESS, "vfork") # 将规则加载到内核生效 ctx.load() if __name__ == '__main__': proc = subprocess.Popen(['./a.out'], preexec_fn=restrict_process) proc.wait() print(f"子进程退出码: {proc.returncode}")
方法2:直接调用C库(无需额外依赖)
如果无法安装seccomp库,可通过ctypes直接调用系统接口:
import subprocess import resource import ctypes from ctypes import cdll, c_int, c_ulong, POINTER libc = cdll.LoadLibrary('libc.so.6') # 定义prctl和seccomp相关常量 PR_SET_NO_NEW_PRIVS = 38 PR_SET_SECCOMP = 22 SECCOMP_MODE_FILTER = 2 SECCOMP_RET_KILL_PROCESS = 0x80000000 | 0x00000009 SECCOMP_RET_ALLOW = 0x7fff0000 | 0 # 定义seccomp_data结构体(对应内核系统调用数据结构) class seccomp_data(ctypes.Structure): _fields_ = [ ("nr", c_int), ("arch", c_ulong), ("instruction_pointer", c_ulong), ("args", c_ulong * 6) ] # 定义系统调用过滤回调函数 def seccomp_filter(ctx, data): data_ptr = ctypes.cast(data, POINTER(seccomp_data)) syscall_nr = data_ptr.contents.nr # 禁止x86_64架构下的fork(57), clone(56), vfork(58)系统调用 if syscall_nr in [56, 57, 58]: return SECCOMP_RET_KILL_PROCESS return SECCOMP_RET_ALLOW # 将Python回调转换为C可调用类型 SECCompFilterFunc = ctypes.CFUNCTYPE(c_int, c_ulong, POINTER(seccomp_data)) filter_func = SECCompFilterFunc(seccomp_filter) def restrict_process(): # 禁止子进程提升权限绕过限制 libc.prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) # 加载seccomp过滤规则 libc.prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, filter_func) # 可选:设置RLIMIT_NPROC作为双重保障 resource.setrlimit(resource.RLIMIT_NPROC, (1, 1)) if __name__ == '__main__': proc = subprocess.Popen(['./a.out'], preexec_fn=restrict_process) proc.wait() print(f"子进程退出码: {proc.returncode}")
效果验证
运行修改后的foo.py,a.out在调用fork时会被内核直接终止,退出码通常为-31(对应SIGSYS信号),符合你预期的“禁止创建新进程”的效果。
内容的提问来源于stack exchange,提问作者Joey
相关产品推荐
相关产品推荐

