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

如何正确搭建sigreturn系统调用的栈帧以实现原子跳转?

问题分析与解决方案

你的核心问题是直接将libc的ucontext_t结构放到栈上调用sigreturn,但sigreturn系统调用期望栈顶是内核定义的信号帧(rt_sigframe)结构,两者格式不兼容,导致内核无法正确解析上下文并完成跳转。

关键原理

sigreturn/rt_sigreturn是内核为信号处理函数返回提供的入口,它读取栈上的信号上下文帧来恢复进程状态:

  • 旧版sigreturn(syscall 15)仅支持32位信号掩码,现代x86_64系统推荐使用rt_sigreturn(syscall 139),支持64位掩码。
  • 内核的rt_sigframe结构包含struct sigcontext(寄存器上下文)和sigset_t(信号掩码)等字段,和libc的ucontext_t结构存在映射关系,但不能直接替换使用。

修正后的代码

#define _GNU_SOURCE
#include <pthread.h>
#include <ucontext.h>
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
#include <malloc.h>
#include <string.h>

// x86_64下rt_sigreturn的系统调用号
#define __NR_rt_sigreturn 139

// 模拟内核的rt_sigframe结构(简化版,仅保留关键字段)
typedef struct {
    struct sigcontext sc;
    sigset_t mask;
} rt_sigframe_t;

void sigreturn_to(ucontext_t *ctx) {
    // 分配足够空间构造信号帧(预留冗余空间避免越界)
    const size_t frame_buf_size = sizeof(rt_sigframe_t) + 256;
    void *frame_buf = malloc(frame_buf_size);
    if (!frame_buf) return;

    // 将信号帧放在缓冲区的尾部(栈向下生长,符合内核预期的栈布局)
    rt_sigframe_t *sf = (rt_sigframe_t*)((char*)frame_buf + frame_buf_size - sizeof(rt_sigframe_t));

    // 拷贝寄存器上下文:ucontext_t的uc_mcontext就是内核的sigcontext结构
    memcpy(&sf->sc, &ctx->uc_mcontext, sizeof(struct sigcontext));
    // 拷贝信号掩码
    memcpy(&sf->mask, &ctx->uc_sigmask, sizeof(sigset_t));

    // 切换RSP到信号帧起始位置,调用rt_sigreturn
    __asm__ __volatile__(
        "mov %0, %%rsp\n\t"
        "mov %1, %%rax\n\t"
        "syscall"
        :
        : "r"(sf), "i"(__NR_rt_sigreturn)
        : "memory", "rax", "rsp"
    );

    // 代码不会执行到这里,提前释放只是为了消除编译警告
    free(frame_buf);
}

void come_here() {
    while (1) {
        printf("123\n");
        sleep(1);
    }
}

int main() {
    ucontext_t ctx;
    getcontext(&ctx);

    // 分配新栈空间:栈向下生长,ss_sp是栈底,makecontext会自动设置RSP为ss_sp + ss_size
    void *main_stack = malloc(40960);
    if (!main_stack) return 1;

    // 初始化上下文参数
    sigemptyset(&ctx.uc_sigmask);
    ctx.uc_link = NULL;
    ctx.uc_stack.ss_sp = main_stack;
    ctx.uc_stack.ss_size = 40960;
    ctx.uc_stack.ss_flags = 0;

    // 修改上下文的RIP为come_here,RSP为新栈顶端
    makecontext(&ctx, come_here, 0);

    // 触发rt_sigreturn完成原子性上下文切换
    sigreturn_to(&ctx);

    // 代码不会执行到这里
    free(main_stack);
    return 0;
}

关键修正点

  1. 构造正确的信号帧:手动模拟内核rt_sigframe结构,将ucontext_t中的寄存器和掩码拷贝到对应字段。
  2. 使用rt_sigreturn:替换旧版sigreturn,支持64位信号掩码,符合现代x86_64系统规范。
  3. 栈布局调整:将信号帧放在缓冲区尾部,符合x86_64栈向下生长的特性,确保内核能正确读取上下文。
  4. 寄存器约束:内联汇编显式声明被修改的寄存器,避免编译器优化导致的错误。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 03:10:01