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

栈无执行权限程序触发SIGSEGV后的终止机制及相关疑问

关于栈上执行代码、SIGSEGV及相关问题的解答

1. 为什么trap '' SIGSEGV无法阻止程序终止?

trap '' SIGSEGV是shell层面的信号忽略配置,但进程自身的信号处理规则优先级更高。内核会优先检查进程自身的信号处理表:如果进程没有显式设置SIG_IGN或自定义处理函数,才会继承shell的设置。另外,涉及特权指令触发的严重错误,内核可能强制终止进程,这类场景下信号忽略配置无法生效。

2. 无执行权限的栈上代码为何能运行(eax值改变)?

这是因为CPU的页权限检查存在延迟,或者你的栈页被临时标记为可执行(部分编译器、调试器的默认行为)。另外,x86的NX(不可执行)位是页级硬件检查,若你的代码很短,修改eax的指令可能在页错误触发前就已执行完成,之后才触发后续的权限或指令错误。

3. 为什么outsl触发的是SIGSEGV而非其他信号?

outsl是I/O指令,要求当前特权级(CPL)为0(内核级),用户态执行时会触发通用保护错误(#GP)。但Linux内核会将这类用户态特权指令错误统一映射为SIGSEGV信号发送给进程,只有非法指令编码这类场景才会触发SIGILL。

4. 如何定位导致终止的指令?

  • 用GDB调试:运行gdb ./your_program,启动程序后触发崩溃,执行info registers查看寄存器状态,x/i $rip直接查看崩溃时的指令地址及内容。
  • 生成core dump:先执行ulimit -c unlimited,程序崩溃后生成core文件,再用gdb ./your_program core加载分析,同样通过x/i $rip定位指令。
  • strace辅助:查看strace输出的最后一条系统调用,结合寄存器上下文推断,但GDB的定位更直接准确。

5. 如何处理SIGSEGV不立即终止以用于ptrace?

需要在进程内部注册自定义SIGSEGV处理函数,而非依赖shell的trap。示例代码:

#include <signal.h>
#include <stdio.h>
#include <ucontext.h>

void sigsegv_handler(int sig, siginfo_t *info, void *ctx) {
    ucontext_t *uc = (ucontext_t*)ctx;
    printf("SIGSEGV触发地址: %p\n", (void*)uc->uc_mcontext.gregs[REG_RIP]);
    // 修改RIP跳转到下一条指令,避免无限触发信号(需根据指令长度调整偏移)
    uc->uc_mcontext.gregs[REG_RIP] += 4;
}

int main() {
    struct sigaction sa;
    sa.sa_sigaction = sigsegv_handler;
    sa.sa_flags = SA_SIGINFO;
    sigemptyset(&sa.sa_mask);
    sigaction(SIGSEGV, &sa, NULL);

    // 你的栈上代码执行逻辑
    return 0;
}

若用ptrace跟踪,内核会将信号转发给跟踪进程,你可以在跟踪端处理信号,控制被跟踪进程继续执行。

6. 为什么strace只捕获一次SIGSEGV而非无限次?

默认情况下,进程收到SIGSEGV且无自定义处理函数时,内核会直接终止进程,因此只会触发一次信号。如果注册了自定义处理函数并让进程继续执行(比如修改RIP跳过错误指令),strace会捕获到多次SIGSEGV;若信号直接导致进程终止,自然不会有后续信号产生。

内容的提问来源于stack exchange,提问作者nadav levin

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 15:51:53