SIGSYS (31) 系统调用错误详解:C++ 开发者的崩溃调试指南 一、信号基础认知(开篇 5 分钟入门) 信号核心信息
信号编号 :31
信号名称 :SIGSYS (Bad System Call)
POSIX 标准 :是(POSIX.1-2001 定义)
可捕获 :是
默认行为 :终止进程并生成 coredump
核心定位 SIGSYS 的本质作用是系统调用错误告警 。当进程执行了无效的系统调用(如系统调用号不存在、参数无效、或系统调用被 seccomp 等安全机制阻止)时,操作系统会发送 SIGSYS 信号。
默认行为 Linux 内核的默认处理逻辑:
终止进程 :立即终止当前进程
生成 coredump :如果系统配置允许,会生成 core 文件
可捕获 :可以捕获,但通常应该让程序终止
与 C++ 的关联性 SIGSYS 在 C++ 开发中的高发场景:
无效系统调用 :使用了不存在的系统调用号
seccomp 限制 :程序被 seccomp 沙箱限制,禁止某些系统调用
架构不匹配 :在不同架构间移植代码时,系统调用号可能不同
安全策略 :容器或沙箱环境中的安全策略限制
调试工具 :某些调试或分析工具可能触发
二、信号触发场景(结合 C++ 代码实例) 核心触发原因 1. 编程失误类 场景 1.1:使用无效的系统调用号 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <iostream> #include <unistd.h> #include <sys/syscall.h> int main () { std::cout << "Attempting invalid system call..." << std::endl; long result = syscall (99999 , 0 , 0 , 0 , 0 , 0 , 0 ); std::cout << "Result: " << result << std::endl; return 0 ; }
Coredump 信息示例 :
1 2 3 4 5 6 7 8 Program received signal SIGSYS, Bad system call. 0x0000000000401123 in main () at main.cpp:8 8 long result = syscall(99999, 0, 0, 0, 0, 0, 0); (gdb) bt #0 0x0000000000401123 in main () at main.cpp:8 #1 0x00007ffff7e3d0b7 in __libc_start_main () from /lib/x86_64-linux-gnu/libc.so.6 (gdb) info registers rax 0xffffffffffffffda -38 # 系统调用返回错误码
场景 1.2:系统调用参数无效 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #include <iostream> #include <sys/mman.h> int main () { void * addr = mmap (nullptr , 4096 , PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANONYMOUS, -1 , 0 ); if (addr == MAP_FAILED) { perror ("mmap" ); return 1 ; } munmap (addr, 4096 ); return 0 ; }
2. 系统限制类 场景 2.1:seccomp 沙箱限制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <iostream> #include <sys/socket.h> int main () { int sock = socket (AF_INET, SOCK_STREAM, 0 ); if (sock < 0 ) { perror ("socket" ); return 1 ; } close (sock); return 0 ; }
seccomp 配置示例 (外部配置):
1 2 3 4 5 6 7 8 9 { "defaultAction" : "SCMP_ACT_ERRNO" , "syscalls" : [ { "names" : ["read" , "write" , "exit" ], "action" : "SCMP_ACT_ALLOW" } ] }
场景 2.2:容器安全策略 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #include <iostream> #include <sys/ptrace.h> int main () { long result = ptrace (PTRACE_TRACEME, 0 , nullptr , nullptr ); if (result < 0 ) { perror ("ptrace" ); } return 0 ; }
3. 运行时异常类 场景 3.1:架构不匹配的系统调用 1 2 3 4 5 6 7 8 9 #define SYS_read 0 #define SYS_write 1
易混淆场景辨析 SIGSYS vs SIGILL SIGSYS :系统调用错误(系统调用号无效、参数错误、被禁止)
SIGILL :非法指令(CPU 无法执行的指令)
1 __asm__ volatile (".byte 0x0f, 0x0b\n" ) ;
SIGSYS vs EINVAL
SIGSYS :系统调用本身无效或被禁止(由内核触发)
EINVAL :系统调用有效,但参数无效(返回错误码,不触发信号)
三、崩溃调试与定位(实操步骤) 基础定位:core 文件 + gdb 调试 步骤 1:开启 core 文件
步骤 2:gdb 加载 core 文件 1 2 3 g++ -g -O0 -o program main.cpp ./program gdb ./program core
步骤 3:关键调试命令 1 2 3 4 5 (gdb) bt (gdb) info registers (gdb) print $rax (gdb) x/10i $pc -20 (gdb) disas
实际调试示例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 $ gdb ./sigsys_example core GNU gdb (Ubuntu 9.2-0ubuntu1~20.04) 9.2 ... Core was generated by `./sigsys_example'. Program terminated with signal SIGSYS, Bad system call. #0 0x0000000000401123 in main () at main.cpp:8 8 long result = syscall(99999, 0, 0, 0, 0, 0, 0); (gdb) bt #0 0x0000000000401123 in main () at main.cpp:8 #1 0x00007ffff7e3d0b7 in __libc_start_main () from /lib/x86_64-linux-gnu/libc.so.6 (gdb) info registers rax 0xffffffffffffffda -38 # -ENOSYS (Function not implemented) rbx 0x0 0 rcx 0x1869f 99999 # 系统调用号 rdx 0x0 0 rsi 0x0 0 rdi 0x0 0 (gdb) x/5i $pc-10 0x401118 <main+8>: mov $0x1869f,%eax # 系统调用号 99999 0x40111d <main+13>: syscall # 系统调用指令 0x40111f <main+15>: mov %rax,-0x8(%rbp) # 保存返回值
进阶工具 工具 1:strace 跟踪系统调用 1 2 3 4 5 strace -e trace=all ./program 2>&1 | grep -i "syscall\|sigsys" strace -e trace=mmap,socket,ptrace ./program
strace 输出示例 :
1 2 3 syscall_99999(0, 0, 0, 0, 0, 0) = -1 ENOSYS (Function not implemented) --- SIGSYS {si_signo=SIGSYS, si_code=SYS_SECCOMP, si_call_addr=0x40111d, si_syscall=99999, si_arch=AUDIT_ARCH_X86_64} --- +++ killed by SIGSYS +++
工具 2:检查 seccomp 配置 1 2 3 4 5 cat /proc/<PID>/status | grep Seccomp docker inspect <container> | grep -i seccomp
工具 3:使用 libseccomp 检查
定位关键点 SIGSYS 崩溃的核心排查方向:
检查系统调用号 :确认使用的系统调用号是否有效
检查 seccomp 配置 :确认是否有 seccomp 限制
检查容器环境 :确认是否在受限容器中运行
检查架构匹配 :确认系统调用号是否与架构匹配
检查系统调用参数 :确认参数是否有效
四、崩溃修复方案(针对性解决) 分场景修复代码 场景 1:无效系统调用号 快速修复:使用正确的系统调用 1 2 3 4 5 6 7 8 long result = syscall (99999 , 0 , 0 , 0 , 0 , 0 , 0 ); #include <unistd.h> pid_t pid = getpid ();
优雅修复:使用标准库或检查系统调用可用性 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <unistd.h> #include <sys/syscall.h> pid_t pid = getpid ();#include <errno.h> long result = syscall (SYS_getpid);if (result < 0 && errno == ENOSYS) { pid_t pid = getpid (); }
场景 2:seccomp 限制 快速修复:避免被禁止的系统调用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <sys/ptrace.h> long result = ptrace (PTRACE_TRACEME, 0 , nullptr , nullptr );#include <errno.h> long result = ptrace (PTRACE_TRACEME, 0 , nullptr , nullptr );if (result < 0 ) { if (errno == EPERM || errno == EINVAL) { std::cerr << "ptrace not available" << std::endl; } }
优雅修复:使用功能检测和降级方案 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #include <sys/ptrace.h> #include <errno.h> class Tracer {private : bool ptraceAvailable; public : Tracer () { ptraceAvailable = checkPtraceAvailable (); } bool trace () { if (!ptraceAvailable) { return alternativeTrace (); } long result = ptrace (PTRACE_TRACEME, 0 , nullptr , nullptr ); return result == 0 ; } private : bool checkPtraceAvailable () { errno = 0 ; long result = ptrace (PTRACE_TRACEME, 0 , nullptr , nullptr ); if (result < 0 && errno == EPERM) { return false ; } return true ; } bool alternativeTrace () { return true ; } };
场景 3:架构不匹配 快速修复:使用条件编译 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 long result = syscall (0 ); #include <sys/syscall.h> #ifdef __x86_64__ #define SYS_READ 0 #elif defined(__aarch64__) #define SYS_READ 63 #elif defined(__arm__) #define SYS_READ 3 #endif long result = syscall (SYS_READ, fd, buf, count);
优雅修复:使用标准库或自动检测 1 2 3 4 #include <unistd.h> ssize_t result = read (fd, buf, count);
修复验证 单元测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <gtest/gtest.h> TEST (SyscallTest, ValidSyscall) { pid_t pid = getpid (); EXPECT_GT (pid, 0 ); } TEST (SyscallTest, InvalidSyscallHandling) { errno = 0 ; long result = syscall (99999 ); EXPECT_LT (result, 0 ); EXPECT_EQ (errno, ENOSYS); }
避坑提醒
避免直接使用 syscall :优先使用标准库函数,让库处理系统调用细节
检查 seccomp 环境 :在受限环境中运行时,避免使用被禁止的系统调用
处理架构差异 :不要硬编码系统调用号,使用标准库或条件编译
错误处理 :检查系统调用返回值,处理 ENOSYS 等错误
五、长期预防策略(从编码到部署全链路) 编码规范 C++ 开发中规避 SIGSYS 的编码习惯:
使用标准库函数 :避免直接调用 syscall
检查系统调用返回值
1 2 3 4 5 6 long result = syscall (...);if (result < 0 ) { if (errno == ENOSYS) { } }
处理受限环境 :检测并适应 seccomp 等限制
1 2 3 if (isSeccompRestricted ()) { }
编译阶段 开启防御性编译选项:
1 2 3 4 g++ -g -O2 -Wall -Wextra \ -Werror=implicit-function-declaration \ -o program main.cpp
测试策略 1 2 3 4 5 6 7 8 9 10 11 12 TEST (SyscallTest, ErrorHandling) { errno = 0 ; long result = syscall (99999 ); EXPECT_LT (result, 0 ); EXPECT_EQ (errno, ENOSYS); } TEST (RestrictedTest, SeccompEnvironment) { }
线上监控 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <signal.h> #include <execinfo.h> #include <iostream> #include <sys/syscall.h> void sysHandler (int sig, siginfo_t * info, void * context) { std::cerr << "SIGSYS caught!" << std::endl; std::cerr << "System call number: " << info->si_syscall << std::endl; std::cerr << "Architecture: " << info->si_arch << std::endl; void * array[10 ]; size_t size = backtrace (array, 10 ); backtrace_symbols_fd (array, size, STDERR_FILENO); exit (1 ); } int main () { struct sigaction sa ; sa.sa_sigaction = sysHandler; sigemptyset (&sa.sa_mask); sa.sa_flags = SA_SIGINFO; sigaction (SIGSYS, &sa, nullptr ); return 0 ; }
六、拓展延伸(加深理解) 相关信号对比 SIGSYS vs SIGILL vs SIGSEGV
特性
SIGSYS
SIGILL
SIGSEGV
触发原因
系统调用错误
非法指令
内存访问违规
常见场景
无效系统调用、seccomp 限制
非法指令
空指针、越界
可捕获
是
是
是
进阶技巧:检测 seccomp 限制 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <sys/syscall.h> #include <errno.h> #include <iostream> bool isSeccompRestricted () { errno = 0 ; long result = syscall (SYS_ptrace, PTRACE_TRACEME, 0 , nullptr , nullptr ); if (result < 0 ) { if (errno == EPERM) { return true ; } } return false ; } int main () { if (isSeccompRestricted ()) { std::cout << "Running in restricted environment" << std::endl; } return 0 ; }
实际案例分享 案例:容器中系统调用被禁止 问题描述 :程序在 Docker 容器中运行崩溃,coredump 显示 SIGSYS。
排查过程 :
GDB 分析显示崩溃在系统调用
检查 seccomp 配置,发现某些系统调用被禁止
使用 strace 确认被禁止的系统调用
根本原因 :
1 2 3 4 5 6 7 #include <sys/ptrace.h> int main () { ptrace (PTRACE_TRACEME, 0 , nullptr , nullptr ); return 0 ; }
解决方案 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <sys/ptrace.h> #include <errno.h> int main () { errno = 0 ; long result = ptrace (PTRACE_TRACEME, 0 , nullptr , nullptr ); if (result < 0 ) { if (errno == EPERM) { std::cerr << "ptrace not available in this environment" << std::endl; } } return 0 ; }
总结 SIGSYS 是系统调用错误信号,主要原因是无效的系统调用或被安全机制禁止。通过:
使用标准库 :避免直接调用 syscall,使用标准库函数
检查返回值 :处理 ENOSYS 等错误
适应受限环境 :检测并适应 seccomp 等限制
调试技巧 :使用 strace 跟踪系统调用,GDB 分析 coredump
预防策略 :编码规范、测试覆盖、环境检测
可以有效减少 SIGSYS 崩溃,提高程序在不同环境中的兼容性。