You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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返回状态规则和普通子进程退出场景不一致:

  1. 当子进程处于ptrace被跟踪状态时,任何递送给子进程的信号(哪怕是SIGSEGV这类致命信号)都会先触发子进程停止,waitpid返回的是子进程停止状态,而非终止状态。
  2. WIFSIGNALED(status)仅在子进程已经被信号彻底杀死、进程生命周期终止时才会返回真,这种场景下已经没有机会再通过ptrace向子进程发送控制命令。第三次循环拿到SIGSEGV时,子进程只是被ptrace拦下停住了,还没真的被信号杀死,自然进不了WIFSIGNALED分支。
  3. 改WEXITSTATUS(status) == SIGSEGV能命中完全是巧合:WEXITSTATUS和WSTOPSIG两个宏读取的是status变量的同一段bit位,前者仅在WIFEXITED为真时有效,后者仅在WIFSTOPPED为真时有效。在子进程停止状态下错误调用WEXITSTATUS,刚好读到了导致子进程停止的信号值,才会判断成立,这属于未定义行为,跨内核、跨架构不保证结果一致。
  4. 代码里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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 09:36:24