seccomp是linux内核提供的轻量级沙箱机制,用于限制c++程序可执行的系统调用;它在main开头安装白名单策略,能有效防御漏洞利用,但需注意标准库隐式调用和异常处理相关syscall。

seccomp 是什么,为什么 C++ 程序需要它
seccomp(secure computing mode)是 Linux 内核提供的轻量级沙箱机制,它能让进程在启动后主动限制自己能执行的系统调用。对 C++ 程序来说,这不是“锦上添花”,而是关键防御手段:一旦程序因漏洞被劫持控制流(比如栈溢出、UAF),攻击者往往依赖 execve、openat、connect 等系统调用进一步提权或外连。seccomp 能直接让这些调用返回 EACCES 或直接终止进程,大幅压缩利用面。
它不依赖外部工具或容器运行时,纯内核态生效,开销极低——但必须在进程生命周期早期安装(通常在 main() 开头、任何线程创建前),且策略不可更改。
用 libseccomp 在 C++ 中设置白名单策略
libseccomp 是官方推荐的封装库,比直接用 prctl(PR_SET_SECCOMP, ...) 更安全、可读性更强。你需要:
- 安装开发包:
sudo apt install libseccomp-dev(Debian/Ubuntu)或sudo dnf install libseccomp-devel(RHEL/Fedora) - 链接时加
-lseccomp - 策略必须在
fork()或pthread_create()之前完成加载
#include <seccomp.h>
#include <unistd.h>
#include <iostream><p>int install_seccomp_policy() {
scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL); // 默认拒绝所有
if (!ctx) return -1;</p>
<pre class="brush:php;toolbar:false;">// 允许基础调用:read/write/exit/brk/mmap/munmap/rt_sigreturn 等
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(brk), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(munmap), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigreturn), 0);
// 允许特定 write:只允许写到 stdout/stderr(fd=1,2)
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 1,
SCMP_CMP(0, SCMP_CMP_EQ, 1));
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 1,
SCMP_CMP(0, SCMP_CMP_EQ, 2));
int rc = seccomp_load(ctx);
seccomp_release(ctx);
return rc;
}
int main() { if (install_seccomp_policy() != 0) { std::cerr
注意:C++ 标准库(如 std::cout、std::string 构造)隐式依赖很多系统调用(mprotect、getrandom、clock_gettime),盲目禁用会导致 SIGSYS。建议先用 strace -e trace=all ./your_program 2>&1 | head -50 观察真实调用,再逐步收紧。
常见崩溃原因和绕过陷阱
C++ 程序启用 seccomp 后突然 crash,八成是以下情况之一:
-
std::thread或std::async在策略安装前触发,导致内核对clone或epoll_wait返回SIGSYS - 使用了
std::filesystem(会调用openat、fstatat),但没放行对应 syscall 和参数约束 - 启用了 ASLR 或堆栈保护(如
-fPIE -pie),触发mprotect或arch_prctl,而策略未允许 - 静态链接 libc(
-static)时,glibc 可能改用socketcall等旧接口,需额外放行 - 某些发行版默认启用
seccomp的 systemd service,与你的策略冲突,可用systemctl show --property=SystemCallFilter查看
不要试图用 SCMP_ACT_TRAP + 自定义信号处理器来“拦截并模拟”系统调用——这在多线程下极易竞态,且无法可靠还原寄存器上下文。
生产环境必须验证的三件事
上线前务必确认:
- 策略是否在
main()最开头安装(不是构造函数、不是全局对象初始化阶段) - 是否测试过所有运行路径:正常退出、异常抛出、信号处理(如
SIGINT)、子进程 spawn(如果允许fork或clone) - 是否开启
SECCOMP_MODE_STRICT兼容检查:在安装前调用prctl(PR_GET_SECCOMP, 0, 0, 0, 0),若返回 1 表示已有策略,应提前 abort
最易被忽略的是:C++ 异常栈展开过程会调用 sigaltstack、rt_sigprocmask 等,若程序可能抛异常,这些也得放进白名单——否则 throw 就会触发 SIGSYS。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!






