这题不难,chroot
漏信息的方法太多了。但做法比较多,看看不同人的方法就比较有意思。
我一开始是考虑 chroot
后进程能获取/影响哪些系统全局属性,这些属性就能用来当侧信道。我选的是系统进程数量,因为这个比较容易精确控制。
我的实现在 看不见的彼方/side_channel/ 目录下,具体来讲:
-
Alice 一开始就 fork 出 256 个进程。每隔 10ms,Alice 通过杀死一个进程传递一个 bit 0,通过 fork 新进程传递一个 bit 1,这样传题目要求的 32 byte 也就不到 3s,时间是够的。
-
而 Bob 这边轮询
sysinfo(&info); info.procs
读系统进程数量,一旦发生变化就记录一个比特。
假设系统进程数量短时间内保持稳定,这个方法应当是有效的。实际上我本地测试有超过一半的时间都能正确传递信息。而评测环境用的 Docker,开了 PID namespace 后读取到的进程数量是局限在容器内的,理论上应当更加稳定。但实际上这个方法失败了,评测环境中 Bob 要么读不到进程变化,要么读到的全是 bit 1。我本地 Docker 调试也没法复现问题,就毫无头绪。
赛后跟 zzh 交流了一下,他说 “容器默认都限制 16 个 pid” …… 🤷🏽♀️
考虑常规 IPC 方法。executor.c
禁用了一些常见 IPC 机制,但漏网之鱼总是有的。以 Linux IPC 为关键词搜索其他的 IPC 机制,System V IPC 就是一例。System V IPC 是在 Linux 内核实现的,executor.c
里面并没有禁用相关 syscall,所以是可用的。
正常的 System V IPC 使用流程是:
- 两边进程选择同一个路径,调用
key_t ftok(const char *pathname, int proj_id)
函数,生成同一个 key; - 两边进程调用
int msgget(key_t key, int msg_flags)
,从 key 获得 message queue 的 ID; - 然后两边就可以用
msgsnd
和msgrcv
函数发消息了,体验跟管道差不多。
第一步 ftok
给人一种两边进程需要能访问同一个文件的错觉。但简单测试就会发现,跳过这一步直接自己选择一个整数作为 key(key_t
就是个整数类型)完全没有问题。至于标准中法中为什么 key 非要用个文件路径生成(甚至这个路径必须可访问),而不是任意字符串都 OK,我就不清楚了。man ftok
甚至还写了 Of course, no guarantee can be given that the resulting key_t is unique. 感觉就很离谱,可能是 everything is a file 哲学用力过猛吧。
总之,跳过 ftok
,Alice 和 Bob 直接用同一个 key
做 System V IPC 就好了。代码在 看不见的彼方/sysvipc/ 目录下,是基于这篇文章代码实现的 System V IPC,Alice 相当于 client,Bob 相当于 server。