ptrace追踪子进程时WIFSIGNALED无法捕获SEGFAULT信号问题
问题背景
已明确掌握WIFEXITED、WIFSIGNALED宏的作用与工作机制,但代码运行存在异常:
- 测试程序
segfault_prog必然触发段错误 - 调试确认
while循环第三次迭代时waitpid获取到的信号为SIGSEGV - 但
WIFSIGNALED(status)判断分支始终无法命中 - 仅当将判断逻辑改为
WEXITSTATUS(status) == SIGSEGV时才可正常触发
需要对应排查思路。
复现代码
段错误测试程序 segfault_prog
#include <stdlib.h> #include <stdio.h> int main() { int *i = (int*)0x478734; printf("%d", *i); }
ptrace 监控父进程代码
int main(int argc, char **argv) { int status; int ret = fork(); if (ret == 0) { ptrace(PTRACE_TRACEME, ret, NULL, NULL); raise(SIGSTOP); execve(argv[1], &argv[1], NULL); return 1; } else { while (1) { pid_t val = waitpid(ret, &status, 0); ptrace(PTRACE_SETOPTIONS, ret, 0,PTRACE_O_EXITKILL | PTRACE_O_TRACEEXEC); if (WIFEXITED(status) && val == ret){ printf("exited normally"); return WEXITSTATUS (status); } else if (WIFSIGNALED(status)){ // 改为WEXITSTATUS(status) == SIGSEGV时可命中 // 三次循环waitpid返回对应信号:第一次SIGSTOP/19,第二次SIGTRAP/5,第三次SIGSEGV/11 printf("signal error"); return WEXITSTATUS (status); } ptrace(PTRACE_CONT, val, 0, 0); } } return 0; }
根因分析
核心原因是ptrace拦截信号时的waitpid返回状态规则和普通子进程退出场景不一致:
- 当子进程处于ptrace被跟踪状态时,任何递送给子进程的信号(哪怕是SIGSEGV这类致命信号)都会先触发子进程停止,waitpid返回的是子进程停止状态,而非终止状态。
WIFSIGNALED(status)仅在子进程已经被信号彻底杀死、进程生命周期终止时才会返回真,这种场景下已经没有机会再通过ptrace向子进程发送控制命令。第三次循环拿到SIGSEGV时,子进程只是被ptrace拦下停住了,还没真的被信号杀死,自然进不了WIFSIGNALED分支。- 改
WEXITSTATUS(status) == SIGSEGV能命中完全是巧合:WEXITSTATUS和WSTOPSIG两个宏读取的是status变量的同一段bit位,前者仅在WIFEXITED为真时有效,后者仅在WIFSTOPPED为真时有效。在子进程停止状态下错误调用WEXITSTATUS,刚好读到了导致子进程停止的信号值,才会判断成立,这属于未定义行为,跨内核、跨架构不保证结果一致。 - 代码里
ptrace(PTRACE_CONT, val, 0, 0)最后一个参数传0,代表ptrace恢复子进程运行时,不把拦下的SIGSEGV递送给子进程,相当于直接把触发段错误的信号丢了,子进程永远不会真的被SIGSEGV杀死,自然永远等不到WIFSIGNALED为真的状态。
排查修复点
- 新增
WIFSTOPPED(status)判断分支,ptrace场景下绝大多数waitpid返回的都是停止状态,必须优先处理,停止状态下的信号值用WSTOPSIG(status)获取,不要用其他宏跨场景读取。 - 如果要在拦截到SIGSEGV的第一时间处理错误,直接在
WIFSTOPPED分支判断WSTOPSIG(status) == SIGSEGV即可,不需要等进程终止。 - 如果确实要等子进程被信号杀死后再走
WIFSIGNALED分支,恢复运行时要把拦下的信号递回给子进程:ptrace(PTRACE_CONT, val, 0, WSTOPSIG(status)),等下一轮waitpid返回时就能拿到终止状态,此时用WTERMSIG(status)获取杀死进程的信号即可。 - 所有waitpid状态的取值宏,必须在对应的
WIF*判断为真时才调用,不要跨场景使用。
内容的提问来源于stack exchange,提问作者Olivia22
相关产品推荐
相关产品推荐

