type
status
date
slug
summary
tags
category
icon
password
Property
Oct 23, 2025 12:16 PM
概述
Seccomp (Secure Computing) 是 Linux 内核提供的一种安全机制,允许进程限制自己可以执行的系统调用。Seccomp Mode 2 (SECCOMP_MODE_FILTER) 使用 BPF (Berkeley Packet Filter) 程序实现灵活的系统调用过滤。但是不同于 eBPF,Seccomp BPF 使用 cBPF,对比 eBPF 仅保留了部分指令可以使用。
Seccomp BPF 的安装和验证的主要流程如下:
安装
- 用户空间通过
prctl
提交 BPF 程序
- 内核验证权限(
no_new_privs
或CAP_SYS_ADMIN
)
- 从用户空间安全复制 BPF 指令
- 执行严格的 BPF 验证(基本检查 → 经典检查 → seccomp 特定检查)
- 转换为 eBPF 格式并尝试 JIT 编译
- 构建系统调用缓存以优化性能
- 附加到进程的过滤器链
- 设置
TIF_SECCOMP
标志启用检查
验证
- 系统调用入口检测
TIF_SECCOMP
标志
- 填充
seccomp_data
结构(系统调用号、参数、架构等)
- 首先检查缓存(快速路径)
- 遍历过滤器链,执行每个 BPF 程序
- 选择最严格的返回动作
- 根据动作类型处理(允许、拒绝、跟踪、杀死等)
- 记录审计日志(如果配置)
具体源码分析见下文
安装流程
1. 用户空间接口
用户通过
prctl
系统调用安装 seccomp 过滤器:2. 内核调用链
2.1 入口函数
该函数是
prctl(PR_SET_SECCOMP, ...)
的内核入口点,负责:
- 根据 seccomp_mode
确定操作类型(STRICT 或 FILTER)
- 将参数转换为内部格式
- 调用 do_seccomp()
执行实际操作关键代码流程:
2.2 统一入口处理
该函数是
prctl
和 seccomp
系统调用的统一处理入口:
- SECCOMP_SET_MODE_STRICT
: 调用 seccomp_set_mode_strict()
- SECCOMP_SET_MODE_FILTER
: 调用 seccomp_set_mode_filter()
- SECCOMP_GET_ACTION_AVAIL
: 查询动作是否可用
- SECCOMP_GET_NOTIF_SIZES
: 获取通知结构大小2.3 Filter 模式设置
这是安装 BPF 过滤器的核心函数,执行以下步骤:
步骤 1:验证 flags
支持的 flags 包括:
-
SECCOMP_FILTER_FLAG_TSYNC
: 同步所有线程
- SECCOMP_FILTER_FLAG_LOG
: 记录所有非 ALLOW 动作
- SECCOMP_FILTER_FLAG_SPEC_ALLOW
: 允许规避推测执行缓解
- SECCOMP_FILTER_FLAG_NEW_LISTENER
: 创建用户态通知监听器步骤 2:准备过滤器
步骤 3:获取锁
步骤 4:检查并附加过滤器
步骤 5:设置 seccomp 模式
3. 过滤器准备
3.1 从用户空间复制过滤器
该函数处理用户空间到内核空间的过滤器转换:
处理兼容模式:
调用核心准备函数:
3.2 核心过滤器准备
权限检查:
必须满足以下条件之一:
- 进程设置了
no_new_privs
标志
- 进程具有 CAP_SYS_ADMIN
能力分配 seccomp_filter 结构:
创建 BPF 程序:
这里调用了
net/core/filter.c
中的函数,seccomp_check_filter
作为转换函数传递。初始化引用计数:
4. BPF 程序创建与验证
4.1 从用户空间创建 BPF 程序
验证基本参数:
分配 BPF 程序结构:
从用户空间复制指令:
保存原始程序(用于检查点恢复):
准备并验证过滤器:
4.2 准备 BPF 过滤器
检查经典 BPF 指令:
这一步验证:
- 指令长度不超过
BPF_MAXINSNS
- 没有越界跳转
- 内存访问合法
- 不使用除零操作执行 seccomp 特定转换:
4.3 Seccomp 特定检查
该函数对 seccomp BPF 程序进行特殊处理:
重定向数据加载指令:
将
BPF_LD
改为 BPF_LDX
以确保从 seccomp_data
结构加载数据,而不是网络包数据。替换长度加载:
允许的指令白名单:
只允许安全的 ALU、跳转、内存操作和返回指令,拒绝其他所有指令。
JIT 编译:
如果架构支持,将 BPF 程序编译为原生机器码以提高性能。
解释器迁移:
如果无法 JIT 编译,将经典 BPF (cBPF) 转换为扩展 BPF (eBPF) 供解释器使用。
5. 附加过滤器
验证过滤器总长度:
限制:
MAX_INSNS_PER_PATH = (1 << 18) / sizeof(struct sock_filter) = 256KB
链接过滤器:
过滤器以链表形式组织,新过滤器总是添加到链表头部。
线程同步(如果需要):
6. 设置 Seccomp 模式
设置模式:
内存屏障:
启用推测执行缓解:
设置 TIF_SECCOMP 标志:
此标志导致内核在每次系统调用时检查 seccomp 过滤器。
验证执行流程
1. 系统调用拦截
当进程执行系统调用时,如果设置了
TIF_SECCOMP
标志,内核会在系统调用处理前调用 seccomp 检查。架构相关入口:
2. Seccomp 检查入口
检查暂停标志:
获取系统调用号:
根据模式分发:
3. 过滤器执行
3.1 填充 seccomp_data
3.2 运行过滤器链
获取过滤器链:
检查缓存:
系统调用缓存优化:如果某个系统调用对所有过滤器都返回 ALLOW,将其缓存以避免重复执行 BPF 程序。
遍历过滤器链:
优先级规则:
- 数值越小,优先级越高(更严格)
-
SECCOMP_RET_KILL_PROCESS
< SECCOMP_RET_KILL_THREAD
< SECCOMP_RET_TRAP
< … < SECCOMP_RET_ALLOW
3.3 BPF 程序执行
BPF 程序通过以下方式之一执行:
- JIT 编译的原生代码(如果
fp->jited == true
)
- eBPF 解释器(如果未 JIT 编译)
BPF 程序接收
seccomp_data
结构作为输入,返回一个 32 位动作值。4. 动作处理
根据过滤器返回值执行相应动作:
4.1 SECCOMP_RET_ERRNO
4.2 SECCOMP_RET_TRAP
发送的信号信息:
4.3 SECCOMP_RET_TRACE
4.4 SECCOMP_RET_USER_NOTIF
用户态通知机制:
1. 将系统调用信息发送给用户态监督进程
2. 阻塞当前进程等待响应
3. 根据用户态响应决定如何处理系统调用
4.5 SECCOMP_RET_LOG
4.6 SECCOMP_RET_ALLOW
4.7 SECCOMP_RET_KILL_THREAD / SECCOMP_RET_KILL_PROCESS
5. 日志记录
根据
/proc/sys/kernel/seccomp/actions_logged
配置决定是否记录:审计日志包含:
- 系统调用号
- 信号(如果有)
- Seccomp 动作
- 进程 PID、UID 等
关键数据结构
1. seccomp_filter
生命周期:
-
refs
: 包括直接任务、依赖过滤器、用户通知监听器
- users
: 只包括直接关联的任务
- 当 users
到达 0 时,不能再有新任务关联
- 当 refs
到达 0 时,释放过滤器2. seccomp_data
这是传递给 BPF 程序的数据结构,大小为 64 字节。
3. task_struct.seccomp
4. action_cache
缓存构建(源码:kernel/seccomp.c:825):
对每个系统调用号:
1. 用固定的
nr
和 arch
模拟执行 BPF 程序
2. 如果返回 SECCOMP_RET_ALLOW
,在位图中设置对应位
3. 继承前一个过滤器的缓存(新过滤器只能更严格)缓存使用:
BPF 程序处理
1. cBPF 到 eBPF 转换
转换过程:
1. 第一遍:计算转换后长度
2. 第二遍:实际转换指令并计算跳转偏移
3. 第三遍(如需要):调整跳转偏移
指令映射示例:
2. JIT 编译
启用条件:
- 内核配置
CONFIG_BPF_JIT=y
- 架构支持(x86_64, ARM64, etc.)
- /proc/sys/net/core/bpf_jit_enable
设置优势:
- 原生机器码执行,性能提升 2-4 倍
- 减少指令分发开销
缺点:
- 增加内核攻击面
- 消耗更多内存
3. 解释器执行
如果无法 JIT 编译,使用 eBPF 解释器:
安全机制
1. 验证器检查
基本检查 (
bpf_check_basics_ok
):
- 程序不为空
- 最后一条指令是 RET
- 没有无效指令经典 BPF 检查 (
bpf_check_classic
):
- 没有越界跳转
- 没有后向跳转(防止循环)
- 内存访问合法
- 除数不为零检查Seccomp 特定检查 (
seccomp_check_filter
):
- 只允许白名单中的指令
- 数据访问限制为 seccomp_data
结构
- 强制 4 字节对齐访问2. 长度限制
限制总指令数为 256KB,防止:
- DoS 攻击(过长的执行时间)
- 内存耗尽
3. 权限要求
安装 seccomp 过滤器需要:
no_new_privs
标志:
- 通过 prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)
设置
- 禁止进程及其子进程获得新权限
- 防止特权提升攻击4. 单向门机制
一旦设置 seccomp 模式,无法更改或移除,只能添加更严格的过滤器。
5. 内存屏障
确保多核系统中的内存一致性。
6. 推测执行缓解
默认启用推测执行缓解(如 SSBD),防止 Spectre 类攻击。
深入 Seccomp User Notification 机制
Seccomp 用户态通知 (User Notification) 允许用户空间进程拦截和处理被 seccomp 过滤的系统调用。与传统的
SECCOMP_RET_ERRNO
或 SECCOMP_RET_KILL
不同,SECCOMP_RET_USER_NOTIF
可以将系统调用决策权委托给用户空间的监督进程。被过滤的系统调用会阻塞,等待用户空间监督进程的决策,并将系统调用号、参数、架构等信息传递给用户空间。监督进程可以返回错误码、返回值,或允许系统调用继续执行,也支持通过 SECCOMP_IOCTL_NOTIF_ADDFD
向被监督进程注入文件描述符。整体架构

关键数据结构:
核心数据结构
1. 内核通知对象 (seccomp_knotif)
状态机转换:
- INIT: 通知已创建,等待监督进程读取
- SENT: 监督进程已读取,等待响应
- REPLIED: 监督进程已响应,可以唤醒被监督进程
2. 通知容器 (struct notification)
设计要点:
- 大多数 seccomp 过滤器不使用通知,因此单独分配此结构以节省内存
request
信号量:初始值为 0,每个新通知up()
,读取时down()
next_id
:随机初始化防止 ID 预测攻击
3. FD 注入对象 (seccomp_kaddfd)
4. 用户空间接口结构
seccomp_notif (通知请求)
seccomp_notif_resp (通知响应)
标志位:
SECCOMP_USER_NOTIF_FLAG_CONTINUE
(0x1):允许系统调用继续执行(有 TOCTOU 风险)
seccomp_notif_addfd (FD 注入请求)
完整工作流程
阶段 1:Listener FD 创建
1.1 安装带 NEW_LISTENER 标志的过滤器
用户空间代码:
1.2 内核处理流程
1.3 init_listener 函数
seccomp_notify_ops 文件操作:
阶段 2:系统调用拦截与通知
2.1 触发 SECCOMP_RET_USER_NOTIF
被监督进程执行系统调用 → 匹配 BPF 过滤器 → 返回
SECCOMP_RET_USER_NOTIF
:2.2 seccomp_do_user_notification 详解
这是用户态通知的核心函数,被监督进程的系统调用会在此阻塞。
关键点:
- 通知对象在栈上:
seccomp_knotif n
在被监督进程的内核栈上分配,生命周期与系统调用相同
- 数据指针有效性:
n.data
指向__seccomp_filter
的sd
,在整个通知期间有效
- 多次唤醒:
do-while
循环处理addfd
请求,每次注入 FD 都会唤醒一次
- 信号中断:如果被监督进程收到信号,
wait_for_completion_interruptible
返回错误
阶段 3:监督进程读取通知
3.1 等待通知 (poll/epoll)
监督进程使用
poll
或 epoll
监听 listener fd:3.2 读取通知 (ioctl NOTIF_RECV)
状态转换:
INIT → SENT
阶段 4:监督进程发送响应
4.1 发送响应 (ioctl NOTIF_SEND)
状态转换:
SENT → REPLIED
4.2 验证通知有效性 (ioctl NOTIF_ID_VALID)
监督进程在处理通知前可以验证通知是否仍然有效(被监督进程可能已收到信号退出):
阶段 5:FD 注入机制
5.1 使用场景
FD 注入允许监督进程将自己的文件描述符"传递"给被监督进程,典型场景:
- 容器运行时代理设备访问:容器进程
open("/dev/fuse")
→ 监督进程打开真实设备 → 注入 fd
- 网络代理:容器进程
socket()
→ 监督进程创建 socket 并配置 → 注入 fd
5.2 注入流程 (ioctl NOTIF_ADDFD)
5.3 被监督进程处理 addfd
在
seccomp_do_user_notification
的循环中调用:双向等待机制:
- 监督进程在
seccomp_notify_addfd
中等待kaddfd.completion
- 被监督进程在
seccomp_do_user_notification
中被唤醒,调用seccomp_handle_addfd
安装 fd 并complete()
阶段 6:清理与关闭
6.1 Listener 关闭
当监督进程关闭 listener fd 时:
Android 平台的 Seccomp 使用
在 Android 中,Seccomp 是多层安全机制的一部分,搭配 SELinux、命名空间隔离、UID/GID 沙箱、权限模型 等共同构建应用安全边界。
Android 从 Android 8.0 开始引入了对 App 进程的 Seccomp 过滤器。
设计目标是:
- 减少应用可访问的系统调用数量;
- 阻止利用内核漏洞的攻击面;
- 对特权进程(如 zygote 或 system_server)使用更严格的策略。
参考 bionic/libc/seccom/seccomp_policy.cpp Android 的 Seccomp 机制分为4个类型:
进程类型 | 过滤器类型 | 主要作用 |
普通 App 进程 | App filter | 限定 App 允许的 syscall 集合 |
App Zygote 进程 | App Zygote filter | 更严格,只允许必要的 syscall |
System 进程 | System filter | 系统进程的特定白名单 |
UID/GID 改变辅助过滤器 | SetUid/Gid filter | 限制 setresuid/setresgid 参数范围 |
在 android 系统中,最终可用的 SYSCALL 是由 SYSCALL - BLOCKLIST + ALLOWLIST 组成的,同时会参考
SECCOMP_PRIORITY.TXT
,把高频 syscall 放在 BPF 判断的“快路径”前面,以减少匹配开销。具体的这些文件也可以在 bionic 源码中找到:bionic/libc/
具体的计算逻辑可以参考 genseccomp.py