如何让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字段就是进程从信号处理程序返回后会使用的信号掩码。你可以直接修改这个字段,内核在恢复上下文时会自动采用新的掩码,而不是原始值。
步骤如下:
- 在SIGSYS处理程序中,判断触发的系统调用是否为
__NR_rt_sigprocmask(x86_64架构下系统调用号是15,其他架构需对应调整)。 - 解析系统调用的参数(
how、set、oldset、sigsetsize),这些参数可以从uc_mcontext.gregs寄存器数组中获取(x86_64对应RDI、RSI、RDX、R10)。 - 根据
how的类型(SIG_BLOCK/SIG_UNBLOCK/SIG_SETMASK),直接修改uc_sigmask。 - 如果需要保存旧掩码到用户空间的
oldset指针,手动将修改前的uc_sigmask复制到该地址。 - 设置系统调用的返回值为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
相关产品推荐
相关产品推荐

