Skip to content

SELinux策略链

以 system_server 启动 Perfetto 为例,追踪 file_contexts、domain_auto_trans、allow、neverallow 与策略测试。

基于android-17.0.0_r1
AndroidSELinuxsepolicy源码阅读

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 是内核检查的权限。

概念本文案例来源
主体 domainsystem_serversystem_server.te
可执行文件 typeperfetto_execfile_contexts、perfetto.te
新进程 domainperfettoperfetto.te
目录/文件 typeperfetto_traces_data_filefile_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

text
/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

text
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

text
/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

text
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

text
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 目录和文件权限:

text
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

text
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

text
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

text
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

text
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

make
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 文件。

text
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:

text
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=0

scontext 指出应搜索哪个 domain 的规则,tcontext 指出目标 type,tclass 区分 file、dir、fd、process 等对象类,permissive=0 表示该拒绝实际阻断了操作。路径只是帮助核对标签的线索。

7.2 双向搜索 ​

推荐先按 type 搜,再按主体和 class 收窄:

bash
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

找到宏后继续查定义和调用方:

bash
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 修复顺序 ​

修复一个拒绝时,按以下顺序检查:

顺序问题定位依据
1source domain 是否正确scontext、进程上下文
2目标路径的 type 是否正确tcontext、file_contexts
3class 和 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 中,可以用下面的命令从路径复述完整链路:

bash
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 策略时可以复用的源码顺序。