SELinux策略链
本文面向已经理解 Linux 进程、文件和基本系统调用,能够使用 rg 阅读 AOSP 文本策略的读者。建议先读 C++调用链 了解 native 进程入口,再读 搜索调用链 了解如何从一个符号反查定义、调用者和测试。
本文不把 .te 语法做成词典,而是回答一个可以沿源码验证的问题:system_server 为什么能够执行 /system/bin/perfetto,执行后为什么进入 perfetto domain,而不是继续留在 system_server;如果新增一个未经允许的 transition,究竟在哪个阶段失败? 案例同时覆盖运行时访问拒绝和编译期 neverallow 拒绝。读完后,读者应能从一个策略对象名找到标签来源、主体 domain、规则 owner、进程切换点和验证入口。
1. 策略对象
SELinux 规则描述的是一个四元关系:主体 domain、客体 type、对象 class 和 permission。以本文案例为例,system_server 是执行 execve() 的主体,perfetto_exec 是 /system/bin/perfetto 的文件标签,perfetto 是切换后的进程 domain,process transition 是内核检查的权限。
| 概念 | 本文案例 | 来源 |
|---|---|---|
| 主体 domain | system_server | system_server.te |
| 可执行文件 type | perfetto_exec | file_contexts、perfetto.te |
| 新进程 domain | perfetto | perfetto.te |
| 目录/文件 type | perfetto_traces_data_file | file_contexts、数据类型声明 |
| 规则关系 | domain_auto_trans、allow、neverallow | .te 与宏定义 |
一个 AVC 日志里的 scontext、tcontext、tclass 和 { permission },正好对应这组对象。排查时不要先写 allow;先确认日志中的主体和客体是否真的是你以为的进程和路径。
2. 标签入口
2.1 文件标签
system/sepolicy/private/file_contexts 把绝对路径映射为 perfetto_exec:
源码文件:system/sepolicy/private/file_contexts
/system/bin/perfetto u:object_r:perfetto_exec:s0
/system/bin/traced u:object_r:traced_exec:s0
/system/bin/trace_redactor u:object_r:trace_redactor_exec:s0因此策略里的 perfetto_exec 不是可执行文件名,也不是进程名;它是 inode 的 SELinux type。文件被移动到其他路径、路径匹配规则被覆盖,或者镜像中的 file context 没有重新标记时,后面的 .te 规则即使正确也可能匹配不到。
2.2 Domain声明
private/perfetto.te 定义执行文件 type 和进程 domain:
源码文件:system/sepolicy/private/perfetto.te
type perfetto, domain, coredomain;
type perfetto_exec, system_file_type, exec_type, file_type;
type perfetto_tmpfs, file_type;
tmpfs_domain(perfetto);
init_daemon_domain(perfetto)domain、exec_type 等是 attribute。它们让通用宏可以一次作用于一组 type;真正排查权限时仍要把 attribute 展开回具体类型。 init_daemon_domain(perfetto) 给出了由 init 启动时需要的基础 domain 关系,但它不会自动允许 system_server 执行该文件。
2.3 目录标签
同一个 file_contexts 文件还标记 trace 输出目录:
源码文件:system/sepolicy/private/file_contexts
/data/misc/perfetto-traces(/.*)? u:object_r:perfetto_traces_data_file:s0
/data/misc/perfetto-traces/profiling(/.*)? u:object_r:perfetto_traces_profiling_data_file:s0
/data/misc/perfetto-configs(/.*)? u:object_r:perfetto_configs_data_file:s0这解释了为什么日志中的目标 type 比路径更重要:内核做的是 perfetto domain 对 perfetto_traces_data_file 的访问判断,而不是对字符串路径的判断。
3. 转换规则
3.1 调用方授权
system_server.te 明确允许 system_server 使用 perfetto 的文件描述符、读写管道,并声明 domain transition:
源码文件:system/sepolicy/private/system_server.te
allow system_server perfetto_traces_data_file:file { read getattr };
allow system_server perfetto:fd use;
domain_auto_trans(system_server, perfetto_exec, perfetto);
allow system_server perfetto:fifo_file { read write };
allow system_server perfetto_traces_profiling_data_file:dir rw_dir_perms;
allow system_server perfetto_traces_profiling_data_file:file create_file_perms;
allow system_server perfetto_traces_data_file:dir search;
allow system_server perfetto:process signal;domain_auto_trans 不是普通的 allow 别名。它表达“source domain 执行带有 entrypoint type 的文件时,切换到目标 domain”。宏展开还会加入执行文件、进程 transition 和 entrypoint 所需的基础关系;因此阅读时必须继续打开宏定义,而不能只凭宏名推断权限集合。
3.2 宏展开
domain_auto_trans 定义在公共宏文件中。它把 transition、entrypoint 和执行所需的关系组合起来:
源码文件:system/sepolicy/public/te_macros
define(`domain_auto_trans', `
domain_trans($1, $2, $3)
type_transition $1 $2:process $3;
`)
define(`domain_trans', `
allow $1 $2:file { getattr open read execute map };
allow $1 $3:process transition;
allow $3 $2:file entrypoint;
allow $1 $3:process siginh;
allow $1 $3:process rlimitinh;
`)实际版本的宏还会根据构建条件组合更多关系;上面代码用于展示阅读方向:宏名必须回到 te_macros,再回到展开后的 source、target、class 和 permission。
3.3 执行结果
当 system_server 执行 /system/bin/perfetto 时,内核先按文件标签得到 perfetto_exec,再匹配 type_transition,将新进程 domain 设置为 perfetto。这就是为什么后续对 trace 文件的访问规则写在 perfetto.te,而不是全部继续写给 system_server。
4. 运行时访问
4.1 Trace文件
切换后的 perfetto domain 在自己的策略文件中获得 trace 目录和文件权限:
allow perfetto perfetto_traces_data_file:dir rw_dir_perms;
allow perfetto perfetto_traces_data_file:file create_file_perms;
allow perfetto perfetto_traces_bugreport_data_file:file create_file_perms;
allow perfetto perfetto_traces_profiling_data_file:dir rw_dir_perms;
allow perfetto perfetto_traces_profiling_data_file:file create_file_perms;create_file_perms 和 rw_dir_perms 也是宏。要判断某条 AVC 是否被覆盖,必须知道 class 是 dir 还是 file,以及宏展开是否包含日志中请求的 permission。把目录权限复制到文件规则通常是错误修复。
4.2 Binder与Socket
Perfetto 还通过宏连接 traced 和 system_server:
源码文件:system/sepolicy/private/perfetto.te
unix_socket_connect(perfetto, traced_consumer, traced)
perfetto_producer(perfetto)
binder_use(perfetto)
binder_call(perfetto, system_server)
allow perfetto tracingproxy_service:service_manager find;
allow perfetto incident_service:service_manager find;
binder_call(perfetto, incidentd)这些宏分别涉及 Unix socket、producer fd、Binder 使用和 service manager 查找。它们的 owner 是 perfetto,不是被连接的 traced 或 incidentd。若日志的 scontext 是 system_server,直接添加 allow perfetto ... 不会修复该日志。
4.3 观测与静默
dontaudit 只控制日志,不授予权限:
源码文件:system/sepolicy/private/perfetto.te
dontaudit perfetto adbd:unix_stream_socket getattr;
dontauditxperm perfetto adbd:unix_stream_socket ioctl unpriv_tty_ioctls;
dontauditxperm perfetto shell:fifo_file ioctl unpriv_tty_ioctls;如果操作仍被拒绝,dontaudit 不能让它成功;它只会让特定拒绝不再产生 AVC。故障排查阶段不能为了让日志安静而盲目添加 dontaudit。
5. 编译约束
5.1 Neverallow
system_server 的策略明确限制 transition 目标:
源码文件:system/sepolicy/private/system_server.te
neverallow system_server {
domain -clatd -crash_dump -perfetto -trace_redactor
}:process transition;
neverallow system_server *:process dyntransition;这条规则不是运行时拒绝,而是策略编译时的断言。它允许已经列入白名单的 perfetto 和 trace_redactor,同时禁止新增任意 domain transition。 dyntransition 则对 system_server 全部禁止。
5.2 目录边界
同一文件还限制 system_server 对 trace 目录只能 search:
源码文件:system/sepolicy/private/system_server.te
neverallow system_server perfetto_traces_data_file:dir ~search;因此 system_server 可以把文件描述符交给其他组件,但不能直接打开、创建、删除该目录中的对象。这个限制和前面的 allow system_server ...:dir search 是一对“允许最小操作 + 禁止其余操作”的策略关系。
5.3 构建目标
system/sepolicy/Android.bp 定义 se_neverallow_test,把多分区 policy 源文件组合后执行 neverallow 检查;同时还生成只包含 platform public/private neverallow 的 general_sepolicy.conf,供 CTS 对设备策略做检查:
源码文件:system/sepolicy/Android.bp
se_neverallow_test {
name: "sepolicy_neverallows",
defaults: ["se_policy_conf_flags_defaults"],
srcs: plat_public_policy +
plat_private_policy +
system_ext_public_policy +
system_ext_private_policy +
product_public_policy +
product_private_policy +
vendor_policy +
[":se_build_files{.odm}"],
}
se_policy_conf {
name: "general_sepolicy.conf",
build_variant: "user",
cts: true,
only_neverallow_rules: true,
}所以“编译通过”至少包含两个不同事实:策略能够被工具解析和生成;合并后的规则没有违反 neverallow。运行设备上的 AVC 则是另一层事实,不能互相替代。
6. 目录边界
Treble 使策略源分成 public/private、system_ext、product、vendor 等边界。 system/sepolicy/public 主要导出 vendor 可引用的 type 和 attribute;文件中的注释明确禁止在 public 类型声明文件中直接添加 allow、neverallow 或 dontaudit。实现规则通常放入对应的 private .te 文件。
system/sepolicy/
├── public/ # 导出的类型、属性和宏接口
├── private/ # platform 内部规则与 neverallow
├── vendor/ # AOSP 默认 vendor 规则
├── prebuilts/api/ # 公开策略快照与兼容映射
├── compat/ # 跨版本兼容 CIL
└── tools/ # checkfc、sepolicy-analyze 等工具阅读 perfetto_exec 时,它的声明在 private,说明该 type 不是 vendor 的稳定策略 API。vendor 想引用平台 public type,必须经过导出的属性或兼容映射;不能因为本地源码能搜到 private 文件,就把它当成跨分区契约。
7. 拒绝定位
7.1 AVC字段
看到类似日志时,先保留完整字段,不要只复制 permission:
avc: denied { read } for pid=1234 comm="perfetto"
path="/data/misc/perfetto-traces/config"
scontext=u:r:perfetto:s0
tcontext=u:object_r:perfetto_configs_data_file:s0
tclass=file permissive=0scontext 指出应搜索哪个 domain 的规则,tcontext 指出目标 type,tclass 区分 file、dir、fd、process 等对象类,permissive=0 表示该拒绝实际阻断了操作。路径只是帮助核对标签的线索。
7.2 双向搜索
推荐先按 type 搜,再按主体和 class 收窄:
rg -n "perfetto_configs_data_file|perfetto_traces_data_file" \
system/sepolicy/public system/sepolicy/private
rg -n "allow perfetto|dontaudit perfetto|neverallow perfetto" \
system/sepolicy/public system/sepolicy/private
rg -n "perfetto_exec|type_transition|domain_auto_trans" \
system/sepolicy/private system/sepolicy/public找到宏后继续查定义和调用方:
rg -n "define(.*domain_auto_trans|define(.*perfetto_producer" \
system/sepolicy/public/te_macros
rg -n "domain_auto_trans\\(system_server|perfetto_producer\\(" \
system/sepolicy/private这样可以区分“规则直接写在案例文件中”“规则由宏提供”“规则来自通用 domain attribute”三种情况。
7.3 修复顺序
修复一个拒绝时,按以下顺序检查:
| 顺序 | 问题 | 定位依据 |
|---|---|---|
| 1 | source domain 是否正确 | scontext、进程上下文 |
| 2 | 目标路径的 type 是否正确 | tcontext、file_contexts |
| 3 | class 和 permission 是否对应 | AVC 字段、宏展开 |
| 4 | 是否已有 allow 被 neverallow 限制 | 同主体/目标规则、neverallow |
| 5 | 是否应该修复标签或调用方 | 路径设计、owner、最小权限 |
| 6 | 规则加入哪个分区目录 | public/private/vendor 边界 |
audit2allow 只能把日志转换成候选 allow,不能判断标签设计、domain transition、neverallow 或分区 API。它适合作为搜索提示,不是修复结论。
8. 策略拒绝
8.1 策略测试
sepolicy_neverallows 是 Android.bp 中的 se_neverallow_test 目标,输入是合并后的多分区策略源。它验证的是编译期安全不变量:新增规则不能让受保护主体获得被禁止的关系。
Treble 测试脚本 system/sepolicy/tests/treble_sepolicy_tests.py 还检查 public policy 的新增和删除是否有兼容映射。它证明的是跨版本 public API 稳定性,不证明某个运行时 AVC 会消失。
8.2 案例范围
从这些源码关系可以直接得到:
/system/bin/perfetto被标记为perfetto_exec;system_server被显式允许转入perfetto;perfetto获得 trace、Binder、socket 和 config 访问;- system_server 的 transition 和 trace 目录访问受
neverallow限制。
本文没有声称:任意设备都加载了完全相同的 vendor policy;没有声称所有 Perfetto 运行时错误都是 SELinux;也没有用未执行的设备实验证明某一条 AVC 已经在硬件上复现。
9. 阅读验证
在 Android 源码 checkout 中,可以用下面的命令从路径复述完整链路:
rg -n "system/bin/perfetto|perfetto_traces" \
system/sepolicy/private/file_contexts
rg -n "type perfetto|domain_auto_trans|allow perfetto|neverallow" \
system/sepolicy/private/perfetto.te \
system/sepolicy/private/system_server.te
rg -n "define(.*domain_auto_trans|define(.*domain_trans" \
system/sepolicy/public/te_macros
rg -n "se_neverallow_test|general_sepolicy.conf|only_neverallow_rules" \
system/sepolicy/Android.bp然后回答四个问题:哪个文件给 /system/bin/perfetto 打标签?哪个规则决定 system_server 执行后进入什么 domain?trace 目录为什么由 perfetto 写而不是由 system_server 写?新增一个 transition 为什么可能在编译期就失败?如果只能回答“加一条 allow”,还需要继续区分主体、客体、class、permission 和生效阶段。
10. 边界
SELinux 排查的核心不是把 AVC 文本粘贴进 allow,而是确认一条策略关系的所有权和生效阶段:file_contexts 决定客体 type,.te 和宏决定允许或禁止的关系,Soong 负责合并与编译,neverallow 在编译期阻止危险扩展,内核在运行时依据加载策略返回允许或拒绝。
以 Perfetto 为例,最小权限设计让 system_server 只负责启动、传递 fd 和管理生命周期,真正读写 trace 的工作由 perfetto domain 承担。这个分工同时解释了正常路径、编译失败路径和运行时 AVC 路径,也提供了继续阅读其他 daemon 策略时可以复用的源码顺序。
