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

如何让Syscall User Dispatch的SIGSYS处理程序对rt_sigprocmask的信号掩码变更持久化?是否需特殊监控rt_sigprocmask系统调用?

解决Syscall User Dispatch中rt_sigprocmask信号掩码变更失效的问题

这个问题的核心原因在于SIGSYS是同步信号:当触发SIGSYS时,内核会完整保存进程发起系统调用前的上下文(包括信号掩码),当你的SIGSYS处理程序返回时,内核会自动恢复这个原始上下文——这就导致你在处理程序中调用rt_sigprocmask修改的信号掩码被直接覆盖了。

你不需要更换监控方式,只需要针对rt_sigprocmask这类直接操作进程上下文状态的系统调用,直接修改SIGSYS处理程序收到的ucontext_t结构体中的信号掩码字段,而不是调用系统调用本身。

具体实现思路

ucontext_t中的uc_sigmask字段就是进程从信号处理程序返回后会使用的信号掩码。你可以直接修改这个字段,内核在恢复上下文时会自动采用新的掩码,而不是原始值。

步骤如下:

  1. 在SIGSYS处理程序中,判断触发的系统调用是否为__NR_rt_sigprocmask(x86_64架构下系统调用号是15,其他架构需对应调整)。
  2. 解析系统调用的参数(how、set、oldset、sigsetsize),这些参数可以从uc_mcontext.gregs寄存器数组中获取(x86_64对应RDI、RSI、RDX、R10)。
  3. 根据how的类型(SIG_BLOCK/SIG_UNBLOCK/SIG_SETMASK),直接修改uc_sigmask。
  4. 如果需要保存旧掩码到用户空间的oldset指针,手动将修改前的uc_sigmask复制到该地址。
  5. 设置系统调用的返回值为0(表示成功),并跳过原始系统调用的执行(因为你已经模拟了它的效果)。

代码示例(x86_64架构)

#include <signal.h>
#include <ucontext.h>
#include <sys/prctl.h>
#include <linux/syscalls.h>
#include <string.h>
#include <errno.h>

void sigsys_handler(int sig, siginfo_t *info, void *ucontext) {
    ucontext_t *uc = (ucontext_t *)ucontext;
    long syscall_num = uc->uc_mcontext.gregs[REG_RAX];

    // 专门处理rt_sigprocmask系统调用
    if (syscall_num == __NR_rt_sigprocmask) {
        unsigned long how = uc->uc_mcontext.gregs[REG_RDI];
        sigset_t *set = (sigset_t *)uc->uc_mcontext.gregs[REG_RSI];
        sigset_t *oldset = (sigset_t *)uc->uc_mcontext.gregs[REG_RDX];
        unsigned long sigsetsize = uc->uc_mcontext.gregs[REG_R10];

        // 校验参数合法性,避免非法内存访问
        if (sigsetsize != sizeof(sigset_t)) {
            uc->uc_mcontext.gregs[REG_RAX] = -EINVAL;
            return;
        }

        // 如果需要保存旧掩码,复制当前uc_sigmask到用户空间的oldset
        if (oldset) {
            memcpy(oldset, &uc->uc_sigmask, sigsetsize);
        }

        // 根据how类型修改信号掩码
        switch (how) {
            case SIG_BLOCK:
                sigorset(&uc->uc_sigmask, &uc->uc_sigmask, set);
                break;
            case SIG_UNBLOCK: {
                sigset_t temp_mask;
                sigemptyset(&temp_mask);
                sigandnotset(&temp_mask, &uc->uc_sigmask, set);
                uc->uc_sigmask = temp_mask;
                break;
            }
            case SIG_SETMASK:
                memcpy(&uc->uc_sigmask, set, sigsetsize);
                break;
            default:
                uc->uc_mcontext.gregs[REG_RAX] = -EINVAL;
                return;
        }

        // 设置系统调用成功返回值
        uc->uc_mcontext.gregs[REG_RAX] = 0;
        // 无需执行原始系统调用,直接返回即可
        return;
    }

    // 其他系统调用的原有处理逻辑
    // ...
}

关键注意点

  • 不同架构的寄存器映射不同:比如ARM64的系统调用参数保存在X0-X7,系统调用号在X8,需要根据目标架构调整gregs的索引。
  • 必须校验用户空间指针的有效性(可通过access_ok宏辅助检查),避免触发段错误。
  • 对于其他类似修改进程状态的系统调用(比如sigprocmask),也可以套用这个思路,直接操作ucontext_t中的对应字段,绕开内核的上下文恢复覆盖问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 12:37:33