Recovery Fastbootd
本文面向已经读过 Init Domain、Kernel Domain、Vendor Domain 和 Genfs Contexts 的读者。本文对比 Android 17 的 recovery 与 fastbootd 两个用户空间域:它们如何在 recovery 策略中获得额外权限、如何由 init 启动、怎样访问动态分区和 Virtual A/B,以及为什么 recovery 能执行某些 rootfs/system 文件而正常系统不能。
recovery 和 fastbootd 都是“恢复环境”概念,但不是同一进程:recovery 负责 OTA、擦除、UI 和 sideload;fastbootd 负责用户空间 fastboot 协议和动态分区刷写。两者的宽权限由 recovery_only() 包围,正常 system policy 只保留类型和 neverallow 所需的声明。
1. 两个域
1.1 类型声明
源码文件:system/sepolicy/public/recovery.te、system/sepolicy/public/fastbootd.te
type recovery, domain;
type fastbootd, domain;源码文件:system/sepolicy/private/recovery.te、system/sepolicy/private/fastbootd.te
typeattribute recovery coredomain;
typeattribute fastbootd coredomain;两者都是 process domain 且属于 coredomain,但 private 的实际 allow 只在 recovery policy 中展开。coredomain 不等于拥有所有块设备权限,具体目标仍由 type/class/permission 决定。
1.2 条件编译
源码文件:system/sepolicy/private/recovery.te、system/sepolicy/private/fastbootd.te
recovery_only(`
set_prop(recovery, powerctl_prop)
set_prop(fastbootd, powerctl_prop)
allow recovery dev_type:blk_file rw_file_perms
allow fastbootd dev_type:blk_file rw_file_perms
')recovery_only() 使这些权限只进入 recovery 镜像策略;正常 system 策略不会因为声明了 recovery type 就获得刷写能力。userdebug/eng 还会在 recovery_only 内继续展开额外调试规则。
2. recovery启动
2.1 init transition
源码文件:system/sepolicy/private/init.te
recovery_only(`
domain_trans(init, rootfs, recovery)
domain_trans(init, rootfs, fastbootd)
domain_trans(init, rootfs, adbd)
domain_trans(init, rootfs, servicemanager)
')recovery 镜像中的文件通常得到 rootfs label,因此 init 使用 rootfs 作为 source executable type 的 transition 输入。普通 system 的 /system/bin executable label 和 recovery 的 rootfs label 不是同一条路径。
2.2 rc service
源码文件:bootable/recovery/etc/init.rc
service fastbootd /system/bin/fastbootd
user root
group root
seclabel u:r:fastbootd:s0fastbootd 由 recovery init 直接启动并显式指定 context;它不是正常系统 init 中的 vendor daemon。recovery 主程序通过 property、recovery socket 和 init service 控制 fastbootd 的 start/stop。
源码文件:bootable/recovery/recovery_main.cpp
// Start adbd in recovery for userdebug builds or an unlocked bootloader.
if (should_start_adbd) {
property_set("sys.usb.config", "adb");
}
if (next_recovery_action == Device::ENTER_FASTBOOT) {
property_set("sys.usb.config", "fastboot");
}recovery_main 根据启动原因和设备状态选择 sideload、adb 或 fastboot;USB property 由 recovery policy 的 set_prop 规则约束,真正的 transport 进程可能是 minadbd 或 fastbootd。
2.3 启动时序
3. recovery权限
3.1 分区与文件系统
源码文件:system/sepolicy/private/recovery.te
allow recovery rootfs:dir mounton;
allow recovery tmpfs:dir mounton;
allow recovery { fs_type -debugfs_type }:filesystem ~{ relabelto associate };
allow recovery contextmount_type:filesystem relabelto;
allow recovery file_contexts_file:file r_file_perms;
allow recovery { cache_file cache_recovery_file }:dir create_dir_perms;
allow recovery { cache_file cache_recovery_file }:file create_file_perms;
allow recovery self:process setfscreate;recovery 需要挂载 rootfs/tmpfs、读取 file_contexts、创建 cache/recovery 日志,并用 setfscreate 为 OTA 文件指定创建上下文。filesystem 的排除项和 contextmount_type relabel 约束不能省略,否则会把恢复环境误读为无限 mount/relabel。
3.2 块设备
源码文件:system/sepolicy/private/recovery.te
allow recovery device:dir r_dir_perms;
allow recovery block_device:dir r_dir_perms;
allow recovery { dev_type -apex_dm_device }:blk_file rw_file_perms;
allowxperm recovery { userdata_block_device metadata_block_device cache_block_device }:blk_file ioctl BLKPBSZGET;
allow recovery super_block_device_type:blk_file rw_file_perms;recovery 需要擦除 userdata/cache、读取 metadata 和更新动态分区;apex_dm_device 被从通用 dev_type 写入集合中排除,避免把 APEX device 当作普通块设备。allowxperm 又把关键 ioctl 限定为 BLKPBSZGET 等明确命令。
3.3 HAL与网络
源码文件:system/sepolicy/private/recovery.te
passthrough_hal_client_domain(recovery, hal_bootctl)
binder_use(recovery)
hal_client_domain(recovery, hal_health)
allow recovery self:netlink_route_socket create_socket_perms_no_ioctl;
allow recovery self:global_capability_class_set net_admin;recovery 对 boot control 使用 passthrough HAL,对 health 使用 Binder/AIDL HAL;网络能力用于 recovery 网络和 sideload,不代表 recovery 能查找所有 HAL service。
4. fastbootd权限
4.1 传输与合并
源码文件:system/sepolicy/private/fastbootd.te
allow fastbootd node:tcp_socket node_bind;
allow fastbootd port:tcp_socket name_bind;
allow fastbootd self:tcp_socket { create_socket_perms_no_ioctl listen accept };
set_prop(fastbootd, ctl_snapuserd_prop)
allow fastbootd snapuserd_socket:sock_file write;
allow fastbootd snapuserd:unix_stream_socket connectto;
allow fastbootd dm_user_device:dir r_dir_perms;fastbootd 可以提供 TCP fastboot protocol,也能启动/连接 snapuserd 完成 Virtual A/B merge。socket 和 property 的 owner 是 fastbootd/init;dm_user_device 的访问是动态分区操作链的一部分。
4.2 HAL与USB
源码文件:system/sepolicy/private/fastbootd.te
binder_use(fastbootd)
hal_client_domain(fastbootd, hal_fastboot)
passthrough_hal_client_domain(fastbootd, hal_bootctl)
hal_client_domain(fastbootd, hal_health)
allow fastbootd functionfs:dir search;
allow fastbootd functionfs:file rw_file_perms;
allowxperm fastbootd functionfs:file ioctl { FUNCTIONFS_ENDPOINT_DESC };fastbootd 同时使用 hal_fastboot/hal_health 的 Binder client 和 hal_bootctl passthrough client;FunctionFS endpoint 是 USB fastboot 数据面。三种 client 关系不能被一条 binder_use 替代。
4.3 刷写目标
源码文件:system/sepolicy/private/fastbootd.te
allow fastbootd dm_device:chr_file rw_file_perms;
allow fastbootd dm_device:blk_file rw_file_perms;
allow fastbootd cache_block_device:blk_file rw_file_perms;
allow fastbootd super_block_device_type:blk_file rw_file_perms;
allow fastbootd {
boot_block_device
metadata_block_device
system_block_device
userdata_block_device
}:blk_file { w_file_perms getattr ioctl };
allowxperm fastbootd {
metadata_block_device userdata_block_device
dm_device cache_block_device
}:blk_file ioctl { BLKSECDISCARD BLKDISCARD };fastbootd 的刷写权限覆盖 boot、metadata、system、userdata、super 和 dm 设备;这是 fastboot 用户空间需要的写入集合。该集合受 recovery_only 和 neverallow 共同限制,不能外推到 normal system 的同名 domain。
4.4 GSI与metadata
源码文件:system/sepolicy/private/fastbootd.te
allow fastbootd metadata_file:dir { search getattr mounton };
allow fastbootd gsi_metadata_file_type:dir rw_dir_perms;
allow fastbootd gsi_metadata_file_type:file create_file_perms;
allow fastbootd ota_metadata_file:dir rw_dir_perms;
allow fastbootd ota_metadata_file:file create_file_perms;
allow fastbootd self:capability sys_admin;GSI metadata、OTA metadata 和 device-mapper mount 由 fastbootd 直接管理;sys_admin 仅支持明确 mount/device-mapper 操作,不能据此推导任意 kernel capability。
5. 安全约束
5.1 recovery数据边界
源码文件:system/sepolicy/private/recovery.te
neverallow recovery {
data_file_type
-cache_file
-cache_recovery_file
}:file { no_x_file_perms };recovery 允许少量 cache/recovery 文件访问,但禁止对一般 data_file_type 执行 no_x_file_perms 集合中的访问。注释说明这是“目前主要限制写/执行”的渐进约束,不能把它表述为 recovery 完全不能读取 /data。
5.2 fastbootd边界
源码文件:system/sepolicy/private/fastbootd.te、system/sepolicy/private/domain.te
neverallow fastbootd data_file_type:file { no_x_file_perms };
neverallow { domain recovery_only(`userdebug_or_eng(`-fastbootd')') } contextmount_type:dir_file_class_set { write unlink };
neverallow { domain -recovery -update_engine -fastbootd } {
system_block_device super_block_device_type
}:blk_file { write create };fastbootd 仍受 data 文件执行限制;系统分区写入的 neverallow 只给 recovery、update_engine 和 fastbootd 例外。contextmount_type 的写/删除限制用于阻止其他 domain 修改恢复挂载上下文。
5.3 调试条件
源码文件:system/sepolicy/private/fastbootd.te
userdebug_or_eng(`
allow fastbootd self:process setfscreate;
allow fastbootd self:capability sys_rawio;
allow fastbootd rootfs:file execute_no_trans;
allow fastbootd system_file:file execute_no_trans;
allow fastbootd kernel:system syslog_read;
')userdebug/eng 解锁设备上,fastbootd 可运行 dmesg 等调试 helper 并操作 scratch/overlayfs;user 构建不会展开这些规则。看到“userdebug 能刷写”不能推出 user build 的同样调试路径。
6. 流程与失败
6.1 recovery OTA
源码文件:bootable/recovery/install/install.cpp
bool verify_package(Package* package, RecoveryUI* ui) {
ui->SetBackground(RecoveryUI::INSTALLING_UPDATE);
ui->SetProgressType(RecoveryUI::DETERMINATE);
// Verify the package before applying changes to partitions.
if (!verify_package_compatibility(package)) {
return false;
}
return true;
}OTA 先验证包兼容性,再进入分区写入;SELinux 只约束 recovery 对 cache、metadata、块设备和 HAL 的访问,不能替代签名/版本校验。
6.2 fastboot写入失败
| 现象 | 可能边界 | 首先检查 |
|---|---|---|
| fastbootd service 不存在 | recovery init/service 状态 | bootable/recovery/etc/init.rc |
| USB endpoint 打开失败 | FunctionFS type/ioctl | fastbootd.te、设备节点 label |
| 不能访问 super/dm | blk_file/ioctl/sys_admin | fastbootd.te allowxperm |
| snapuserd merge 失败 | property/socket/dm_user | snapuserd policy 与日志 |
| 分区写入被拒绝 | normal system 或错误 domain | domain neverallow 和 scontext |
| OTA 包校验失败 | recovery 安装逻辑 | install.cpp compatibility/signature |
6.3 状态图
7. 测试与诊断
7.1 recovery测试
源码文件:bootable/recovery/tests/unit/parse_install_logs_test.cpp
TEST(ParseInstallLogsTest, ParseRecoveryUpdateMetrics) {
ASSERT_TRUE(android::base::WriteStringToFile(
"ota.zip\n0\n", last_install.path));
auto metrics = ParseRecoveryUpdateMetrics(lines);
ASSERT_EQ("ota.zip", metrics.package);
}输入是 recovery 安装日志中的包名和状态行;断言解析出的 metrics.package。它验证日志消费者,不证明分区写入权限或 SELinux transition。
源码文件:bootable/recovery/minadbd/minadbd_services_test.cpp
TEST_F(MinadbdServicesTest, SideloadHostService_broken_recovery_socket) {
recovery_socket_.reset();
ASSERT_FALSE(SideloadHostService());
}输入是被关闭的 recovery socket;断言 sideload host service 失败。它证明 minadbd/recovery IPC 的错误处理,不证明 recovery policy 能访问块设备。
7.2 设备命令
# Confirm recovery and fastbootd process contexts.
adb shell 'ps -AZ | grep -E "recovery|fastbootd|minadbd"'
# Inspect executable and block-device labels in recovery-capable builds.
adb shell 'ls -Z /system/bin/fastbootd /dev/block/by-name 2>/dev/null'
# Query the policy edges that authorize flashing.
sesearch -A -s fastbootd -c blk_file
sesearch -A -s fastbootd -c tcp_socket
sesearch -A -s recovery -c blk_file
# Read AVC records for recovery/fastbootd failures.
adb shell 'dmesg | grep "avc: denied" | grep -E "recovery|fastbootd"'ps -AZ 确认 source domain,ls -Z 确认 executable/block target type,sesearch 分离块设备和网络 class,AVC 提供实际 permission。正常系统上没有 recovery process 时,不能用 system mode 的命令输出替代 recovery 实验。
8. 源码导航
system/sepolicy/public/recovery.te、private/recovery.te:recovery 类型、recovery_only 权限和数据边界。system/sepolicy/public/fastbootd.te、private/fastbootd.te:fastbootd USB/TCP、HAL、DM、分区和调试权限。system/sepolicy/private/init.te:rootfs 到 recovery/fastbootd 的 transition。bootable/recovery/etc/init.rc、recovery_main.cpp:service、启动原因、USB property 和 fastboot 切换。bootable/recovery/install/install.cpp:OTA 包验证和写入前流程。system/core/init/selinux.cpp、service.cpp:策略加载、restorecon、seclabel 和 service child。
用 fastbootd 刷写一次动态分区作为练习:先记录 fastbootd 的 scontext,再定位 FunctionFS、dm_device、super_block_device、snapuserd socket 和 HAL 四类 target;如果只改变 recovery_only、只删除 BLKDISCARD ioctl 或只删除 snapuserd socket 权限,应能分别预测普通系统策略不变、分区擦除失败和 Virtual A/B merge 失败。
