Skip to content

Recovery Fastbootd

追踪 Android 17 recovery 与 fastbootd 的策略条件、分区操作、snapuserd、HAL 访问和失败边界。

基于android-17.0.0_r1
AndroidSELinuxrecoveryfastbootdOTA源码阅读

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

text
type recovery, domain;
type fastbootd, domain;

源码文件:system/sepolicy/private/recovery.te、system/sepolicy/private/fastbootd.te

text
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

text
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

text
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

text
service fastbootd /system/bin/fastbootd
    user root
    group root
    seclabel u:r:fastbootd:s0

fastbootd 由 recovery init 直接启动并显式指定 context;它不是正常系统 init 中的 vendor daemon。recovery 主程序通过 property、recovery socket 和 init service 控制 fastbootd 的 start/stop。

源码文件:bootable/recovery/recovery_main.cpp

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

text
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

text
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

text
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

text
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

text
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

text
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

text
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

text
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

text
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

text
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

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/ioctlfastbootd.te、设备节点 label
不能访问 super/dmblk_file/ioctl/sys_adminfastbootd.te allowxperm
snapuserd merge 失败property/socket/dm_usersnapuserd policy 与日志
分区写入被拒绝normal system 或错误 domaindomain neverallow 和 scontext
OTA 包校验失败recovery 安装逻辑install.cpp compatibility/signature

6.3 状态图 ​

7. 测试与诊断 ​

7.1 recovery测试 ​

源码文件:bootable/recovery/tests/unit/parse_install_logs_test.cpp

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

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 设备命令 ​

sh
# 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. 源码导航 ​

  1. system/sepolicy/public/recovery.te、private/recovery.te:recovery 类型、recovery_only 权限和数据边界。
  2. system/sepolicy/public/fastbootd.te、private/fastbootd.te:fastbootd USB/TCP、HAL、DM、分区和调试权限。
  3. system/sepolicy/private/init.te:rootfs 到 recovery/fastbootd 的 transition。
  4. bootable/recovery/etc/init.rc、recovery_main.cpp:service、启动原因、USB property 和 fastboot 切换。
  5. bootable/recovery/install/install.cpp:OTA 包验证和写入前流程。
  6. 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 失败。