问题
起因是视频播放器播放一个视频没有声音,实际上视频播放的是raw资源自带的默认视频,所有视频资源都放在 /data/link/ac/videocomfort/video 下边
从log来看发现检测文件存在返回结果为false,我们查了系统目录下是有资源的,首先就怀疑权限问题
SELinux关键日志
检查读文件前后的log,发现SELinux警告日志
12-05 10:24:30.386 4845 4845 W cedes.wellbeing: type=1400 audit(0.0:189): avc: denied { search } for name="/" dev="vdw" ino=2 scontext=u:r:platform_app:s0:c523,c768 tcontext=u:object_r:unlabeled:s0 tclass=dir permissive=0log各个字段的含义
在宽容模式下验证
为了确认的确是SELinux权限导致的问题,我们将SELinux设置为宽容模式
adb shell setenforce 0然后,我们观察到了一些SELinux警告输出
24249:12-05 11:32:18.797 11038 11038 I ExoPlayer:Loade: type=1400 audit(0.0:1217): avc: denied { search } for name="/" dev="vdw" ino=2 scontext=u:r:platform_app:s0:c522,c768 tcontext=u:object_r:unlabeled:s0 tclass=dir permissive=1
24250:12-05 11:32:18.797 11038 11038 I ExoPlayer:Loade: type=1400 audit(0.0:1218): avc: denied { read } for name="energy_full_screen.mp4" dev="vdw" ino=25 scontext=u:r:platform_app:s0:c522,c768 tcontext=u:object_r:unlabeled:s0 tclass=file permissive=1
24251:12-05 11:32:18.797 11038 11038 I ExoPlayer:Loade: type=1400 audit(0.0:1219): avc: denied { open } for path="/mnt/system_data/video_comfort_a/videocomfort/video/energy_full_screen.mp4" dev="vdw" ino=25 scontext=u:r:platform_app:s0:c522,c768 tcontext=u:object_r:unlabeled:s0 tclass=file permissive=1
24252:12-05 11:32:18.797 11038 11038 I ExoPlayer:Loade: type=1400 audit(0.0:1220): avc: denied { getattr } for path="/mnt/system_data/video_comfort_a/videocomfort/video/energy_full_screen.mp4" dev="vdw" ino=25 scontext=u:r:platform_app:s0:c522,c768 tcontext=u:object_r:unlabeled:s0 tclass=file permissive=1开启宽容模式后有警告log输出,但是所有访问都会通过,log中permissive=1表示宽容模式,首先输出对根目录执行search操作的denied警告,然后是read,open的警告
检查代码
我们在openVideo传入的是绝对路径为什么在search/根目录呢?因为代码是这样写的
//path=/data/link/ac/videocomfort/xx.mp4
override fun openVideo(path: String) {
if (isFileExist(path)) {
...
} else {
...
}
}
fun isFileExist(filePath: String?) = filePath?.let { File(filePath).exists() }.orFalse()
isFileExist触发了
解析符号链接:需要从根目录
/开始遍历路径, 这个根目录不是Linux根目录而是设备挂载的根目录:/mnt/system_data/video_comfort_a,mount命令可以输出挂载点检查权限:需要访问路径上的每一个目录
SELinux检查:访问
vdw设备的根目录时被拒绝,vdw设备在mount中没有找到,但是根据后面警告日志中的子目录所在的dev也是vdw,所以这个vdw很明显就是挂载点指向的设备
从上面的SELinux log tcontext=u:object_r:unlabeled:s0 tclass=dir 看到目标目录的SELinux上下文是一个unlabeled标签
检查SELinux te规则配置
te规则
在vendor/sepolicy/ac.te中我们的权限规则配置为
allow platform_app software_update_link_data_file:dir { read getattr search open };
allow platform_app software_update_link_data_file:file { read getattr open };
allow platform_app software_update_link_data_file:lnk_file { read getattr open };
allow platform_app system_data_dir:dir { read getattr search open };
allow platform_app system_data_dir:file { read getattr open };
allow platform_app system_data_dir:lnk_file { read getattr open };
allow platform_app apk_data_file:dir { read getattr search open };
allow platform_app apk_data_file:file { read getattr open };上面得规则表明platform_app 对label为software_update_link_data_file的目录/文件具有搜索读写权限
file_contexts配置
目标目录配置 vendor/sepolicy/file_contexts为
/mnt/system_data/video_comfort_a(/.*)? u:object_r:software_update_link_data_file:s0
/mnt/system_data/video_comfort_b(/.*)? u:object_r:software_update_link_data_file:s0
表明目标目录的tcontext应该是 u:object_r:software_update_link_data_file:s0 ,而log中显示为 tcontext=u:object_r:unlabeled:s0
检查文件的context
用命令行显示文件SELinux上下文
ls -laZ /mnt/system_data/
#正常输出
drwxr-xr-x 2 root system u:object_r:software_update_link_data_file:s0 40 1970-01-01 08:00 video_comfort_a
异常输出
drwxr-xr-x 2 root system u:object_r:unlabeled:s0 40 1970-01-01 08:00 video_comfort_a上边显示了两种情况下的context,第5个字段为SELinux context
从上面的分析来看,权限拒绝的原因不是SELinux权限规则配置错误,而是文件夹的SELinux context 丢失,变成了unlabeled,导致权限校验失败
验证
为了验证这个猜想,我们尝试在正常可以访问的设备上来修改文件夹的context为unlabeled
adb root
adb remount
// 将dev下面的设备重新挂载到system_data目录下,挂载方式为读写
adb shell mount -o remount,rw /dev/block/video_comfort_a /mnt/system_data/video_comfort_a
//修改SELinux context
chcon -R u:object_r:unlabeled:s0 /mnt/system_data/video_comfort_a/
//查看context
ls -lZ /mnt/system_data/
//查看SELinux拒绝访问的log
logcat |grep avc
//恢复context
chcon -R u:object_r:software_update_link_data_file:s0 /mnt/system_data/video_comfort_a/
上面步骤里的重新挂载需要确认挂载点,因为直接remount之后依旧提示
chcon: 'video_comfort_a' to u:object_r:system_file:s0: Read-only file system
我们通过 mount |grep video_comfort_a 输出了挂载点的消息
/dev/block/video_comfort_a on /mnt/system_data/video_comfort_a type ext4 (ro,seclabel,nosuid,nodev,noexec,relatime)
将上述的context改为unlabeled之后,logcat立即就有avc : denied输出
这个验证可以明确我们SELinux 规则是配置正确的,根本原因是因为刷机方式不同导致设备挂载后的SELinux context变更为unlabled.
检查挂载点的配置文件
查找挂载点配置文件
cd /vendor/etc/
find ./ -name "*fstab*"
#output
./fstab.gen4.qcom
./fstab.qcom查看fstab.qcom文件,是否包含分区的挂载配置,经过检查,我们发现/dev/block/video_comfort_a挂载行被注释
这个文件的在aosp中的配置位于 /device/vendor/qcom或者/platform/vendor/qcom
检查SELinux label规则是否生效
cd /vendor/etc/
grep -rin 'video_comfort'
#output
./fstab.qcom:54:#/dev/block/video_comfort_a /mnt/system_data/video_comfort_a ext4 rw,noatime,noexec defaults
./selinux/vendor_file_contexts:720:/mnt/system_data/video_comfort_a(/.*)? u:object_r:software_update_link_data_file:s0
./selinux/vendor_file_contexts:1031:/dev/block/video_comfort u:object_r:userdata_block_device:s0上面的输出表示,在file_context文件中配置的label: software_update_link_data_file是生效的
尝试修复label
在上面的输出中看到了挂载信息被注释掉的行,所以问题的原因很可能是跟挂载方式有关系,尝试将挂载的注释放开,重启设备label恢复
进一步排查发现,被注释掉的原因是挂载的功能由OTA或者应用自己负责挂载,另外了解到两种不同的刷机方式
第一种是低层次的初始刷机,会刷掉系统所有信息,此时我们挂载的路径是没有SELinux label的
第二种是full upgrade全量刷机,所有需要升级的地方都会升级,刷完之后有label
所以最终来看这个应该还是和挂载方式有关,另外从bsp开发了解到,两套刷机方式有不同的SELinux策略,位于不同的代码库,所以两种刷机的结果表现不一致,放开上面的注释是一种解决办法