DetSim for Go runtime
Comment在 go 程序启动时,state_initialize 的过程中,试图从程序的真实入口点(对于 libc 来说,是 _start)推进到 main 之前的部分,调用 extract_one_syscall 的时候。在 waitpid 的时候,并没有停在 sigtrap | 0x80。
我使用 strace 来观察 loop 的输出,得到它实际上在 clone 了一个线程之后,收到了 SIGURG,并且 si_code=SI_TKILL。这可能与 go 的 runtime 有关。这似乎是某种抢占式调度设计。我尝试了在 extract syscall 的过程中禁止新线程创建,不过 go runtime 并没有允许我这样做,而是抛出了一个 exception。感觉已经万策尽了。
现在我们的解决办法,似乎只有这样:在所有 ptrace_syscall 调用后的 wait,额外处理 sigurg。
理论上,我们拥有实现“伪多线程”的可能性。也就是允许 runtime 执行这次 clone,但是我们做这样一件事:在 clone 调用时,为它的 flags 加上一个 CLONE_PTRACE。这样在 clone 之后,新线程理论上直接被我们自动 attach 并处于静止状态。然后我们再也不调度这个新线程执行,只调度主线程即可。
另外目前的 detsim 似乎还没能够识别到 go 程序从初始化到进入用户代码的那一步。已知在 go 的 runtime init 就会调用一次clone,那么 sigurg 实际上在init 时就已经在不断发送了。我们可能需要在 state_initialization 的时候,考虑到这一部分。状态初始化应该把程序推进到用户代码的前一个系统调用,但目前似乎没有这种明确的标志。我看到,在 loop程序中,go在实际执行 write hello 之前,先做了: mmap(null, 256KB, RW, private + anon), fcntl(1, f_getfl), fcntl(2, f_getfl)。
我们大概可以打包这三条作为进入用户代码前的关键系统调用识别。就像静态 C 的 mprotect 和动态 C 的 munmap 一样。(这个逻辑在 init_tracee_state 中被处理,它通过分析 elf 来设置一个初始化过程的 stop_nr. 这个判断机制大概应当被扩展)。