11、SELinux策略调试工具:avc log解析,audit2allow工具使用,setenforce/permissive模式切换,dmesg与logcat配合

说实话,SELinux 策略调试是很多 Android 工程师的噩梦。我刚入行那会儿,一看到 avc: denied 就头皮发麻。后来摸清了门道,才发现其实就那么几板斧——avc log 解析、audit2allow 自动生成策略、setenforce 临时切换模式,再加上 dmesg 和 logcat 配合定位。今天我把这些工具串起来讲,保证你听完就能上手。

核心思路:先放行(permissive),再分析(avc log),最后固化(te 文件)。千万别一上来就写策略,那是给自己挖坑。

11.1 avc log 解析——读懂内核的“拒绝信”

avc log 是 SELinux 的审计日志。每次访问被拒绝,内核都会打印一条记录。你想想看,这就像系统在跟你说:“嘿,有人想干坏事,被我拦下来了!”

一条典型的 avc log 长这样:

type=1400 audit(123456.789:0): avc: denied { read } for pid=1234 comm="mediaserver" name="config.xml" dev="mmcblk0p25" ino=5678 scontext=u:r:mediaserver:s0 tcontext=u:object_r:system_file:s0 tclass=file permissive=0

我来拆解一下关键字段:

字段 含义 我的经验
denied { read } 被拒绝的权限 可能是 read/write/execute 等
scontext 发起访问的主体(源上下文) 比如 mediaserver 进程
tcontext 被访问的客体(目标上下文) 比如 system_file 类型的文件
tclass 客体的类别 file/dir/socket 等
permissive=0 当前是否强制模式 0=强制,1=宽容

我在项目中遇到过最坑的一次:avc log 里明明有 denied 记录,但系统就是不打印。后来发现是内核的 audit 缓冲区满了,日志被丢弃了。嗯,这里要注意——avc log 不是 100% 可靠的,如果拒绝太频繁,内核会丢日志。

小技巧:adb shell dmesg | grep avc 抓取所有 avc 日志。如果日志太多,可以加 | tail -n 50 只看最后 50 条。

11.2 audit2allow——自动生成策略的“偷懒神器”

audit2allow 是我最喜欢的工具。说白了,它就是个翻译器——把 avc log 翻译成 te 文件里的 allow 语句。

基本用法:

# 从 dmesg 提取 avc log,生成策略
adb shell dmesg | grep avc | audit2allow -p policy.conf

# 或者从 logcat 提取
adb logcat -b events | grep avc | audit2allow -p policy.conf

输出示例:

#============= mediaserver ==============
allow mediaserver system_file:file read;

我曾经犯过一个错误:直接把 audit2allow 生成的策略全部塞进 te 文件。结果系统是能跑了,但安全策略形同虚设。你想想看,audit2allow 只是把拒绝的权限补上,它不会帮你判断这个权限是否合理。

警告:audit2allow 生成的策略一定要人工审核。特别是涉及 unlabeledinit 上下文的规则,很可能是因为文件上下文没打对,而不是真的需要加权限。

11.3 setenforce/permissive 模式切换——调试时的“免死金牌”

调试 SELinux 策略时,我最常用的命令就是 setenforce。它允许你在强制模式和宽容模式之间切换,不用重启系统。

# 查看当前模式
getenforce

# 切换到宽容模式(只记录不阻止)
setenforce 0

# 切换回强制模式
setenforce 1

我个人习惯是:先 setenforce 0 让系统跑起来,同时抓取 avc log。等所有功能都验证通过后,再 setenforce 1 验证策略是否完整。

但要注意——setenforce 0 只是临时生效,重启后恢复。如果你需要永久修改,得改 /sys/fs/selinux/enforce 或者内核启动参数。

避坑指南:我曾经在生产环境的设备上忘了切回强制模式,结果跑了三天才发现。从那以后,我每次调试完都会用 adb shell getenforce 确认一下模式。

11.4 dmesg 与 logcat 配合——双管齐下定位问题

avc log 主要出现在 dmesg 里,但有些场景下 logcat 也能抓到。我建议两个一起看,互相印证。

为什么?因为 dmesg 是内核日志,记录的是最原始的 avc 拒绝信息。而 logcat 是用户空间日志,有些应用层框架(比如 servicemanager)会二次封装 avc 错误,打印出更友好的提示。

举个例子:

# dmesg 里的原始记录
avc: denied { read } for pid=1234 comm="mediaserver" ...

# logcat 里的应用层提示
E/SELinux ( 1234): SELinux: denied read for path=/data/config.xml

我在项目中遇到过:dmesg 里明明没有 avc 日志,但 logcat 里报 SELinux 错误。后来发现是某个服务自己捕获了 avc 异常,然后打印了自定义日志。所以,两个渠道都要看,别只盯着一个。

配合技巧:同时开两个终端窗口,一个跑 adb logcat -b events | grep avc,另一个跑 adb shell dmesg -w | grep avc。这样能实时看到两边的日志。

11.5 实战流程——从拒绝到放行

说了这么多,我们来走一遍完整流程。假设你的 App 在启动时崩溃,怀疑是 SELinux 的问题。

  1. 确认模式:adb shell getenforce,如果是 Enforcing,先切到 Permissive。
  2. 复现问题:重新启动 App,触发崩溃路径。
  3. 抓取日志:adb shell dmesg | grep avc > avc.log,同时 adb logcat -b events | grep avc >> avc.log
  4. 生成策略:cat avc.log | audit2allow -p policy.conf > new.te
  5. 审核策略:打开 new.te,逐条检查。问自己三个问题:
    • 这个权限真的需要吗?
    • 有没有更细粒度的控制方式?
    • 会不会引入安全风险?
  6. 编译部署:将审核后的策略加入对应的 te 文件,重新编译 bootimage,刷机验证。
  7. 回归测试:切回 Enforcing 模式,确保所有功能正常。

核心原则:永远不要在 Enforcing 模式下调试新策略。先放行,再分析,最后固化。这是 SELinux 调试的黄金法则。

11.6 知识体系总览

下面这张图总结了本章的核心逻辑,我建议你保存下来,调试时对照着看:

SELinux 策略调试工具知识体系 avc log 解析 读懂内核拒绝信 audit2allow 自动生成策略 setenforce 模式切换 dmesg + logcat 双通道定位 人工审核 + 策略合并 逐条检查,避免过度授权 最终输出:.te 策略文件 编译进 bootimage,刷机验证 调试黄金流程 Permissive 模式 → 复现问题 → 抓取 avc log → audit2allow → 人工审核 → Enforcing 验证 注意:avc log 可能因内核缓冲区溢出而丢失,建议多次复现确保日志完整

这张图把四个工具串成了一个闭环。你从 avc log 入手,用 audit2allow 生成策略,用 setenforce 切换模式验证,用 dmesg 和 logcat 双通道确认。每一步都有对应的工具,每一步都有坑要避。

好了,这一章的内容就到这里。记住我说的:先放行,再分析,最后固化。这是 SELinux 调试的不二法门。


公众号:蓝海资料掘金营,微信deep3321