PRoot 解剖
以 PRoot v5.5.0 官方源码为样本 · 拆解一个 2 万行的用户态 chroot 如何用 ptrace 改写系统调用
共 1 个子专栏 · 7 篇文章
子专栏
全部文章
-
PRoot 是什么:把 chroot 做进 ptrace 里
·源码分析·ptrace·chrootPRoot 的官方定义只有一句话:chroot、 mount bind 和 binfmt misc 的用户态实现。它不需要 root,不需要内核模块,不需要 user namespace,甚至不需要容器运行时——只要一个 ptrace(2) 。代价同样干脆:被它接管的进程,每走一步都要停下来问它一次。
-
事件循环:一个 waitpid 撑起的进程树
·源码分析·ptrace·seccompPRoot 没有线程池,没有 epoll,没有协程。整个 tracer 就是一个 while (1) 加一次 waitpid( 1, ..., WALL) ——所有被追踪的进程、所有系统调用、所有信号,全都从这一个点流进来。
-
路径翻译:canonicalize、binding 与 glue
·源码分析·文件系统·符号链接内核只认宿主路径。所以 PRoot 接到 openat(AT FDCWD, /etc/passwd , ...) 时,必须先把它变成 /home/u/rootfs/etc/passwd ,再把指针换掉——而 guest 里那个 /etc/passwd 可能根本不存在,也可能是三层符号链接套出来的。
-
系统调用改写:在别人的地址空间里施工
·源码分析·系统调用·ptrace路径翻译只解决了「算」,还没解决「改」。要改一个正在运行的程序的内存,手段只有两个: process vm writev (或 PTRACE POKEDATA )和改寄存器。PRoot 两样都用,而且必须在系统调用的两个 stop 点之间完成。
-
execve:为什么 PRoot 要自带一个 ELF loader
·源码分析·ELF·动态链接前四篇里,PRoot 做的都是「改参数、改返回值」。但 execve 是个硬边界:内核要打开 ELF、读 PT INTERP 、映射动态链接器——这些全在内核里完成,PRoot 插不进手。更要命的是,ELF 文件里写的 PT INTERP 是 guest 路径( /lib/ld linux x86 64.so.2 ),
-
扩展机制:把「撒谎」做成插件
·源码分析·插件·系统调用PRoot 的三个招牌功能—— 0 (假装 root)、 l (硬链接转符号链接)、 k (假装旧内核)——都不是核心代码,而是插件。核心只提供一串事件回调,插件决定在这些点上改什么。
-
边界:PRoot 拿不走什么,又偷走了什么
·沙箱·对比·源码分析前六篇讲的都是「怎么做到」。最后一篇讲代价:PRoot 给你一个看起来很完整的 guest 系统,但它既不是沙箱,也不是免费午餐。