Android-SELinux 策略调试实战:从 AVC 日志到策略修复

Android SELinux 策略调试实战:从 AVC 日志到策略修复

本文记录了一次完整的 Android 车载系统 SELinux 策略调试过程,涵盖 AVC denied 日志解读、TE 规则编写、MLS 约束排查、neverallow 冲突处理等典型场景。适合做车机/Android 系统开发的工程师参考。


一、背景

在 Android 车载系统的开发中,自定义 vendor 域(如 trace_copytrace_deletelog)在运行 shell 脚本(lscprmfindchmodawk 等)时,频繁触发 SELinux AVC denied 日志。本文从零开始,逐步将这些日志转换为 SELinux 策略语句,并解决过程中遇到的 MLS 约束、mlstrustedsubject 语法错误、app_data_file neverallow 风险等问题。


二、AVC 日志如何读懂

一条典型的 AVC denied 日志格式如下:

bash 复制代码
01-01 08:19:08.013 24575 24575 W ls : type=1400 audit(0.0:495): avc: denied { search } for name="0" dev="dm-44" ino=250 scontext=u:r:trace_copy:s0 tcontext=u:object_r:system_data_file:s0:c512,c768 tclass=dir permissive=0

关键字段解读:

字段 含义 示例值
denied 被拒绝的权限 { search }
name 目标对象名 "0"(即 /data/user/0
dev 所在块设备 dm-44(userdata)
scontext 主体(source)的安全上下文 u:r:trace_copy:s0
tcontext 客体(target)的安全上下文 u:object_r:system_data_file:s0:c512,c768
tclass 对象类 dir
permissive 是否 permissive 模式 0(强制模式,真拒绝)

转换公式

xml 复制代码
allow <scontext的domain> <tcontext的type>:<tclass> <perm>;

注意:scontext/tcontext 里的 :s0:c512,c768 这部分是 MLS 类别,不写进 allow 规则。allow 只按 type 匹配。


三、第一批 AVC:基础 TE 规则

日志

ini 复制代码
avc: denied { search } for name="0" tcontext=u:object_r:system_data_file:s0:c512,c768 tclass=dir
avc: denied { setattr } for name="wkk" tcontext=u:object_r:mnt_media_rw_file tclass=dir
avc: denied { read } for name="wkk" tcontext=u:object_r:mnt_media_rw_file tclass=dir

转换结果

bash 复制代码
allow trace_copy system_data_file:dir search;
allow trace_copy mnt_media_rw_file:dir { read setattr };

说明

  • name="0"/data/user/0,type 为 system_data_file
  • name="wkk" → U 盘挂载点下的目录,type 为 mnt_media_rw_file
  • setattrchmod 触发,readls 触发

四、CAP_FSETID:capability 类的权限

日志

ini 复制代码
avc: denied { fsetid } for capability=4 scontext=u:r:trace_copy:s0 tcontext=u:r:trace_copy:s0 tclass=capability

转换结果

css 复制代码
allow trace_copy self:capability fsetid;

说明

  • capability=4CAP_FSETID
  • chmod 修改带 setgid/setuid 位的文件时,内核需要清除这些位,此时校验 CAP_FSETID
  • tcontext == scontext 时,规范写法用 self

五、MLS 约束:加了 allow 还是被拒

问题现象

已经加了 allow trace_copy system_data_file:dir search;,但同样的 AVC 仍然出现

根本原因

看 scontext 和 tcontext 的 MLS 部分:

ini 复制代码
scontext=u:r:trace_copy:s0                    ← 主体:s0,无类别
tcontext=u:object_r:system_data_file:s0:c512,c768  ← 客体:s0,带 c512,c768

SELinux 在 Android 上有两层校验

  1. TE 层(Type Enforcement)allow 规则 → 已通过
  2. MLS 层(mlsconstrain) :检查主体级别是否支配客体级别

MLS 约束大致形如:

ini 复制代码
mlsconstrain dir search ( l1 dom l2 or t1 == mlstrustedsubject );

主体 s0:{} 不支配 客体 s0:{c512,c768}(空集不是 {c512,c768} 的超集)→ MLS 拒绝

c512,c768user 0 的 per-user 隔离类别,用于把不同用户的数据互相隔离。

解决方案

将域标记为 MLS 可信主体:

ini 复制代码
typeattribute trace_copy mlstrustedsubject;

踩坑:mlstrustedsubject 语法错误

错误写法 (会编译报错 syntax error):

ini 复制代码
mlstrustedsubject trace_copy;     ← 错!这是 m4 宏,没加括号

正确写法(二选一):

ini 复制代码
# 方式 A:m4 宏调用(带括号)
mlstrustedsubject(trace_copy)
​
# 方式 B:直接 typeattribute(推荐,最不容易出错)
typeattribute trace_copy mlstrustedsubject;

mlstrustedsubjectsystem/sepolicy/public/te_macros 中是一个 m4 宏,展开后就是 typeattribute ... mlstrustedsubject;。vendor .te 可以直接引用该 attribute。

如何确认是 MLS 在拦

bash 复制代码
# 1. 确认 TE 规则已编进 policy
adb shell sesearch -A -s trace_copy -t system_data_file -c dir -p search /sys/fs/selinux/policy
​
# 2. 临时切 permissive 验证
adb shell setenforce 0
# 跑一次 ls/cp,若成功且 AVC 变 permissive=1 → 策略问题
adb shell setenforce 1

六、execute_no_trans:执行系统二进制

日志

ini 复制代码
avc: denied { execute_no_trans } for path="/system/bin/awk" tcontext=u:object_r:system_file tclass=file

转换结果

lua 复制代码
allow trace_deletelog system_file:file { read getattr open execute execute_no_trans };

说明

  • execute_no_trans = 执行该文件但不发生域转换(awk 在当前域内运行)
  • 通常配套需要 executereadgetattropen

七、unlabeled:未打标签的文件

日志

ini 复制代码
avc: denied { rmdir } for name="kernel" tcontext=u:object_r:unlabeled tclass=dir
avc: denied { read } for name="dropbox" tcontext=u:object_r:unlabeled tclass=lnk_file
avc: denied { getattr } for path="/la_log/vendor/log/android/anr" tcontext=u:object_r:unlabeled tclass=lnk_file
avc: denied { unlink } for name="anr" tcontext=u:object_r:unlabeled tclass=lnk_file

转换结果

bash 复制代码
allow trace_deletelog unlabeled:lnk_file { read getattr unlink };
allow trace_deletelog unlabeled:dir { search read getattr write rmdir remove_name };
allow trace_deletelog unlabeled:file { unlink getattr };

说明

  • tcontext=u:object_r:unlabeled 表示该文件/目录没有 file_contexts 规则匹配 ,落到默认 unlabeled type
  • 本例中 /la_log/vendor/fawvw/log/android/ 下的 anrdropboxtombstones 等符号链接都是 unlabeled
  • dev=mmcblk0p29 是独立持久分区(la_log),不是 userdata(dm-44)

风险与建议

unlabeled 放权限安全收敛性差unlabeled 是全局兜底 type,给它放权等于对所有未标 label 的对象放权)。

推荐做法 :给 /la_log 路径打专用 label:

bash 复制代码
# file_contexts
/la_log(/.*)?    u:object_r:la_log_file:s0
bash 复制代码
# trace_deletelog.te
allow trace_deletelog la_log_file:lnk_file { read getattr unlink };
allow trace_deletelog la_log_file:dir { search read getattr write rmdir remove_name };
allow trace_deletelog la_log_file:file { read getattr unlink };

八、rootfs:根目录遍历

日志

ini 复制代码
avc: denied { read } for name="/" dev="dm-0" tcontext=u:object_r:rootfs tclass=dir
avc: denied { open } for path="/" dev="dm-0" tcontext=u:object_r:rootfs tclass=dir

转换结果

python 复制代码
allow trace_deletelog rootfs:dir { read search getattr open };

说明

  • find / 从根目录开始遍历,需要 read(读目录项)和 open(打开目录)
  • 配套补齐 searchgetattr

九、app_data_file:App 私有数据目录

日志

ini 复制代码
avc: denied { search } for name="com.vehicle.navi.app" tcontext=u:object_r:app_data_file:s0:c512,c768 tclass=dir
avc: denied { getattr } for path="/data/user_de/0/com.vehicle.navi.app/files/J01/BaiduMapAutoSDK/log" tcontext=u:object_r:app_data_file:s0:c512,c768 tclass=dir
avc: denied { read } for name="log" tcontext=u:object_r:app_data_file:s0:c512,c768 tclass=dir

转换结果

perl 复制代码
allow trace_deletelog app_data_file:dir { search getattr read open write remove_name rmdir };
allow trace_deletelog app_data_file:file { read getattr open unlink };
allow trace_deletelog app_data_file:lnk_file { read getattr unlink };

neverallow 风险

app_data_file 是 AOSP 重点保护 的 type,system/sepolicy 里有大量 neverallow:

bash 复制代码
neverallow { domain -appdomain -installd -system_app -platform_app ... } app_data_file:dir search;

自定义 vendor 域不在白名单里,直接加 allow 可能编译失败

方案权衡

方案 说明 代价
A. 直接加 allow 先试编译,看是否触发 neverallow 可能编译失败
B. 改脚本绕开 trace_deletelog 不碰 app 私有目录 需改脚本逻辑
C. 换共享路径 让 app 把 trace 写到 /data/vendor/trace 需改 app 代码
D. 加 neverallow 例外 在 neverallow 里加 -trace_deletelog 侵入 AOSP

本案例中方案 A 编译通过(该项目的 sepolicy 对 app_data_file:dir search 限制比纯 AOSP 宽松)。


十、完整策略汇总

trace_copy.te

lua 复制代码
allow trace_copy system_data_file:dir search;
allow trace_copy mnt_media_rw_file:dir { read setattr };
allow trace_copy mnt_media_rw_file:file { create read write getattr setattr open unlink rename };
allow trace_copy self:capability fsetid;
typeattribute trace_copy mlstrustedsubject;

trace_deletelog.te

perl 复制代码
# 1. 执行系统二进制
allow trace_deletelog system_file:file { read getattr open execute execute_no_trans };
​
# 2. /la_log 分区(当前 unlabeled)
allow trace_deletelog unlabeled:lnk_file { read getattr unlink };
allow trace_deletelog unlabeled:dir { search read getattr write rmdir remove_name };
allow trace_deletelog unlabeled:file { unlink getattr };
​
# 3. 根目录遍历
allow trace_deletelog rootfs:dir { read search getattr open };
​
# 4. /data/user/0(TE + MLS)
allow trace_deletelog system_data_file:dir search;
typeattribute trace_deletelog mlstrustedsubject;
​
# 5. app 私有数据目录
allow trace_deletelog app_data_file:dir { search getattr read open write remove_name rmdir };
allow trace_deletelog app_data_file:file { read getattr open unlink };
allow trace_deletelog app_data_file:lnk_file { read getattr unlink };

十一、调试方法论总结

1. AVC 日志转换三步法

  1. 提取 scontext(domain)、tcontext(type)、tclassdenied(perm)
  2. 套用公式:allow <domain> <type>:<class> <perm>;
  3. 去重合并同类规则

2. 加了 allow 还是报?排查 MLS

ini 复制代码
scontext=u:r:xxx:s0              ← 无类别
tcontext=u:object_r:yyy:s0:c512,c768  ← 带类别

→ 加 typeattribute xxx mlstrustedsubject;

3. 编译报 syntax error?检查宏语法

  • mlstrustedsubject xxx; → 错
  • mlstrustedsubject(xxx) → 对
  • typeattribute xxx mlstrustedsubject; → 对(推荐)

4. 编译报 neverallow?评估方案权衡

  • 先试编译,看报错的具体 neverallow 规则
  • 优先考虑绕开(改脚本/换路径),其次才考虑加例外

5. 配套权限预判

基于命令的典型行为预判可能需要的权限,一次补齐:

命令 典型权限
ls dir: search read getattr open
cp dir: search; file: read getattr open
rm -r dir: search read getattr write rmdir remove_name; file: unlink getattr
find dir: read search getattr open
chmod dir/file: setattr; self:capability fsetid
执行二进制 file: execute execute_no_trans read getattr open

6. unlabeled 的规范化

bash 复制代码
临时方案:allow xxx unlabeled:... ;
规范方案:定义专用 type + file_contexts + allow 专用 type

十二、附录:相关 Android 知识点

/data/data 与 /data/user/0 的区别

  • 单用户(user 0) :两者等价,/data/user/0/data/data 的多用户化别名(符号链接或 bind mount)
  • 多用户/data/user/<id> 才是标准路径,/data/data 只对应主用户
  • 验证方法:ls -di /data/data /data/user/0(inode 相同 → 同一物理目录)

BOOT_IMAGE_STORAGE_REQUIREMENT

arduino 复制代码
private static final long BOOT_IMAGE_STORAGE_REQUIREMENT = DataUnit.MEBIBYTES.toBytes(250);
  • 判断 /data 分区剩余空间是否够 boot image 使用的阈值(250 MiB)
  • 与用户提示文案 low_internal_storage_view_text_no_boot 里硬编码的 "250MB" 对应
  • 不影响 data 分区大小,只决定告警时机

结语

SELinux 策略调试是一个迭代收敛的过程:贴 AVC → 转 allow → 编译 → 刷机 → 再贴 AVC。关键经验:

  1. 先解决 TE 层,再解决 MLS 层 ------mlstrustedsubject 是万能钥匙
  2. 注意宏语法 ------typeattribute 比 m4 宏更不容易出错
  3. 配套权限一次补齐------避免反复编译刷机
  4. unlabeled 要规范化------临时放行后记得补 file_contexts
  5. neverallow 是红线------优先绕开,不轻易加例外
相关推荐
todoitbo1 小时前
飞算JavaAI的多租户权限隔离实测
java·springboot·ai编程·java开发·飞算javaai·java代码生成
星空1 小时前
Springboot复习
java·spring boot·spring
Dicky-_-zhang1 小时前
大模型部署架构:从推理引擎到弹性扩缩容的工程实践
java·jvm
vHelios2 小时前
【电商项目】商品搜索开发复盘(1):根据需求拆解搜索接口的设计逻辑
java·微服务·es
Dovis(誓平步青云)2 小时前
DevEco Studio 6.1.1 Windows 安装实录:从下载校验到首次启动
android·开发语言·数据库·人工智能·windows·harmonyos
极创信息2 小时前
国产化信创适配认证高频术语:信创适配、软件自主可控、国产化率、代码溯源率、代码自主率、代码开源率是什么?
java·python·struts·eclipse·开源·php·hibernate
Data_Journal2 小时前
什么是 CAPTCHA,它是如何工作的?
java·大数据·服务器·前端·数据库
Zender Han2 小时前
Flutter 自适应(Adaptive)与响应式(Responsive)设计实践:官方推荐方案详解
android·flutter·ios
mqiqe2 小时前
响应式流中的错误处理:Project Reactor 异常治理全体系
java·架构