Skip to content

Vendor Domain

追踪 Android 17 vendor_init 与 vendor daemon 从分区策略、init transition 到属性、文件、HAL 和 Binder 隔离。

基于android-17.0.0_r1
AndroidSELinuxvendorTreble源码阅读

Vendor Domain ​

本文面向已经读过 Init Domain、HAL Domain Template 和 Public Private Policy 的读者。本文追踪 Android 17 vendor 进程的真实边界:vendor rc 如何由 init 解释,vendor_init 为什么是单独 domain,vendor daemon 如何通过 *_exec 文件 type 进入自己的 domain,以及 platform/vendor policy 如何共同决定属性、文件、Binder 和 HAL 访问。

vendor domain 不是“所有 /vendor/bin 程序的统一权限”。vendor_init 是 init 执行 vendor rc 命令时使用的受限执行域;具体 daemon(例如 rild 或 hal_audio_default)仍应拥有自己的 domain 和 executable type。

1. 对象边界 ​

1.1 三种身份 ​

对象例子owner主要消费者
vendor_init domainu:r:vendor_init:s0init subcontextvendor rc 内建命令
vendor daemon domainu:r:rild:s0、u:r:hal_audio_default:s0init service/daemonkernel、IPC、HAL
vendor file typevendor_file、rild_execfile_contexts/policyexec、读写和 transition

vendor_init 与 vendor daemon 都属于 vendor 侧运行边界,但不是同一 domain。vendor_init 负责初始化动作,daemon 在 exec 后获得自己的最小权限。

1.2 vendor_init声明 ​

源码文件:system/sepolicy/public/vendor_init.te、system/sepolicy/private/vendor_init.te

text
type vendor_init, domain, mlstrustedsubject;
allow vendor_init init:unix_stream_socket { read write };
allow vendor_init device:dir mounton;
allow vendor_init configfs:dir { mounton create_dir_perms };
allow vendor_init cgroup:dir create_dir_perms;
allow vendor_init cgroup_v2:dir create_dir_perms;
allow vendor_init dev_type:dir create_dir_perms;
allow vendor_init vendor_file_type:dir_file_class_set relabelto;

public 声明让其他 policy 识别 vendor_init;private 才赋予它 init socket、设备目录、configfs、cgroup 和 vendor 文件操作。这里的能力服务于 rc action,不等于 vendor_init 可执行任意 vendor 程序。

1.3 daemon声明 ​

源码文件:system/sepolicy/vendor/rild.te

text
type rild, domain;
hal_server_domain(rild, hal_telephony)
net_domain(rild)
type rild_exec, exec_type, vendor_file_type, file_type;
init_daemon_domain(rild)

rild 是独立 vendor daemon:它有自己的 domain、telephony HAL server attribute、network attribute 和 rild_exec 入口。init 启动后,radio 访问由 rild 规则决定,不会继承 vendor_init 的初始化集合。

2. vendor_init路径 ​

2.1 子上下文 ​

源码文件:system/core/init/subcontext.h、system/core/init/subcontext.cpp

cpp
static constexpr const char kVendorContext[] = "u:r:vendor_init:s0";
static constexpr const char kVendorActionPrefix[] = "/vendor";

vendor subcontext 使用固定 u:r:vendor_init:s0。它与 PID 1 的 init domain 分离,vendor rc 中符合路径和 action 条件的命令由该 context 执行。

2.2 transition约束 ​

源码文件:system/sepolicy/private/init.te、system/sepolicy/private/vendor_init.te

text
domain_trans(init, init_exec, vendor_init)
neverallow domain vendor_init:process dyntransition;
neverallow { domain -init } vendor_init:process transition;
neverallow vendor_init { file_type fs_type -init_exec }:file entrypoint;
neverallow { file_type fs_type }:file execute_no_trans;
neverallow vendor_init service_manager_type:service_manager { add find };
neverallow * vendor_init:process ptrace;

只有 init 能进入 vendor_init;vendor_init 不能发起 dyntransition,也不能把任意文件当 entrypoint,更不能直接注册或查找普通 ServiceManager 服务。需要新 daemon 时应由 init 按专用 executable type 启动。

2.3 属性边界 ​

源码文件:system/sepolicy/private/vendor_init.te

text
set_prop(vendor_init, vendor_default_prop)
set_prop(vendor_init, wifi_hal_prop)
set_prop(vendor_init, vehicle_hal_prop)
set_prop(vendor_init, vendor_security_patch_level_prop)
get_prop(vendor_init, boot_status_prop)
get_prop(vendor_init, ota_prop)
get_prop(vendor_init, device_config_vendor_system_native_prop)

set_prop 的 target 是 property type,不是属性名字字符串。vendor_init 能写哪些属性,取决于 property_contexts 映射与这些宏展开后的 allow;不能把 vendor.* 前缀理解成通配权限。

3. daemon启动 ​

3.1 service输入 ​

源码文件:system/core/init/service.cpp

cpp
std::string scon;
if (!seclabel_.empty()) {
    scon = seclabel_;
} else {
    auto result = ComputeContextFromExecutable(args_[0]);
    if (!result.ok()) {
        return result.error();
    }
    scon = *result;
}

init 在 fork 前决定 service context;rc 显式 seclabel 优先,否则使用 executable file context 计算 process context。vendor daemon 通常依赖 init_daemon_domain 生成自动 transition。

3.2 两个实例 ​

源码文件:system/sepolicy/vendor/hal_audio_default.te、system/sepolicy/vendor/rild.te

text
type hal_audio_default, domain;
hal_server_domain(hal_audio_default, hal_audio)
type hal_audio_default_exec, exec_type, vendor_file_type, file_type;
init_daemon_domain(hal_audio_default)

type rild, domain;
hal_server_domain(rild, hal_telephony)
type rild_exec, exec_type, vendor_file_type, file_type;
init_daemon_domain(rild)

两个 daemon 都由 init 启动,但 executable type、domain 和 HAL attribute 不同。共享 vendor_file_type 只表示分区文件类别,不会共享 audio_device 或 radio 设备权限。

3.3 启动时序 ​

4. policy输入 ​

4.1 vendor集合 ​

源码文件:system/sepolicy/Android.bp

make
vendor_policy = [
    ":se_build_files{.plat_vendor}",
    ":se_build_files{.vendor}",
    ":ashmem_build_file",
];

se_policy_conf {
    name: "vendor_sepolicy.conf",
    srcs: plat_public_policy +
        system_ext_public_policy +
        product_public_policy +
        reqd_mask_policy +
        vendor_policy,
    vendor: true,
    installable: false,
}

vendor policy 能看到 platform/system_ext/product public 和 vendor 实现,不能直接看到 platform private。vendor daemon type 通常在 vendor 目录声明;跨分区使用的 HAL/service type 来自 public。

4.2 executable标签 ​

源码文件:system/sepolicy/private/file_contexts、system/sepolicy/vendor/file_contexts

text
/vendor/bin/hw/.* u:object_r:hal_audio_default_exec:s0
/vendor/bin/rild u:object_r:rild_exec:s0
/vendor/etc/init/.*\.rc u:object_r:vendor_configs_file:s0

路径规则可分布在 platform private、vendor 和设备目录,最终进入 vendor_file_contexts。file type 同时影响 init 的 transition 计算和 daemon 对配置/程序的读取。

4.3 属性与服务 ​

源码文件:system/sepolicy/private/property_contexts、system/sepolicy/public/service.te、system/sepolicy/public/hwservice.te

text
vendor.foo.       u:object_r:vendor_default_prop:s0
ro.vendor.        u:object_r:vendor_default_prop:s0

下面切换到 HAL 名字空间的 service type;它们由 ServiceManager/HwServiceManager 消费。

text
type hal_audio_hwservice, hwservice_manager_type, protected_hwservice;
type hal_audio_service, protected_service, hal_service_type, service_manager_type;

三种 contexts 文件分别解决文件路径、属性名和服务名。vendor daemon 对它们的访问需要各自的 target type/class/permission,不能用 vendor_file_type 代替。

5. 权限边界 ​

5.1 vendor_init文件 ​

源码文件:system/sepolicy/private/vendor_init.te

text
allow vendor_init {
  file_type
  -core_data_file_type
  -exec_type
  -system_file_type
  -vendor_file_type
}:dir { create search getattr open read setattr ioctl write add_name remove_name rmdir relabelfrom };
allow vendor_init {
  file_type
  -core_data_file_type
  -exec_type
  -system_file_type
  -vendor_file_type
}:file { create getattr open read write setattr relabelfrom unlink map };

这两条宽集合同时列出排除项。阅读 file_type 规则时必须把减号项一起解析,否则会把“初始化普通对象”误读为“访问全部文件”。

5.2 daemon文件 ​

源码文件:system/sepolicy/private/hal_audio.te

text
allow hal_audio_server audio_device:dir r_dir_perms;
allow hal_audio_server audio_device:chr_file rw_file_perms;
neverallow hal_audio_server { file_type fs_type }:file execute_no_trans;
neverallow { halserverdomain -hal_audio_server -hal_omx_server } audio_device:chr_file *;

audio server 可以访问 audio_device,但其他 halserverdomain 被排除;同时禁止 audio server 无 transition 执行任意文件。vendor daemon 需要按具体硬件 type 编写最小权限。

5.3 IPC边 ​

源码文件:system/sepolicy/private/hal_audio.te、system/sepolicy/private/hal_wifi_supplicant.te

text
binder_call(hal_audio_client, hal_audio_server)
binder_call(hal_audio_server, hal_audio_client)
hal_attribute_hwservice(hal_audio, hal_audio_hwservice)
hal_attribute_service(hal_audio, hal_audio_service)
binder_call(hal_wifi_supplicant_client, hal_wifi_supplicant_server)
hal_attribute_hwservice(hal_wifi_supplicant, hal_wifi_supplicant_hwservice)

client/server、hwservice_manager、service_manager 和 Binder call 是独立权限层。server 注册成功不表示 client 能 find;find 成功也不表示 Binder call 或 fd 传递成功。

6. 生命周期 ​

6.1 fork与FIFO ​

源码文件:system/core/init/service.cpp

cpp
if (pid == 0) {
    umask(077);
    cgroups_activated.CloseWriteFd();
    setsid_finished.CloseReadFd();
    RunService(descriptors, std::move(cgroups_activated),
               std::move(setsid_finished));
    _exit(127);
} else {
    cgroups_activated.CloseReadFd();
    setsid_finished.CloseWriteFd();
}

父 init 用两个独立 FIFO 协调 cgroup 激活和 setsid 完成;源码注释说明合并 channel 会引入双方 Write 竞态。pid、running flag、restart class 和 cgroup 状态由父侧 Service 对象维护。

6.2 延迟与重复启动 ​

源码文件:system/core/init/service.cpp

cpp
if (is_updatable() && !IsDefaultMountNamespaceReady()) {
    ServiceList::GetInstance().DelayService(*this);
    return Error() << "Cannot start an updatable service before configs are loaded. "
                   << "Queued for execution.";
}
if (pid_) {
    LOG(INFO) << "service '" << name_
              << "' requested start, but it is already running";
    return {};
}

updatable service 可能因 mount namespace 未就绪而延迟;正在运行的 service 重复 start 不会再次 fork。这些是 init 调度状态,不是 SELinux denial。

6.3 清理与恢复 ​

源码文件:system/sepolicy/private/vendor_init.te

text
neverallow domain vendor_init:process dyntransition;
neverallow { domain -init } vendor_init:process transition;
neverallow vendor_init service_manager_type:service_manager { add find };

vendor_init 不能靠运行时 dyntransition 恢复到任意域;需要新进程时,必须修正 rc、executable label、transition 或目标 daemon policy。

7. 失败与诊断 ​

7.1 失败分类 ​

现象owner/边界首先检查
init 报 no domain transitioncontext 计算executable label、entrypoint、transition
child 在 setexeccon 终止rc/service childseclabel 字符串和目标 domain
daemon exec 后退出daemon 初始化property、设备节点、HAL/Binder
service 已启动但 client 找不到managerservice type、client find
Binder call 被拒绝Binder classclient/server binder_call、fd use
vendor policy undefined type构建可见性public/vendor filegroup

7.2 静态查询 ​

sh
# Locate vendor_init transitions and daemon entrypoints.
rg -n 'vendor_init|init_daemon_domain\(|domain_auto_trans\(init' \
  system/sepolicy/private system/sepolicy/vendor

# Check generated vendor policy inputs.
find out/soong/.intermediates/system/sepolicy \
  -name '*vendor_sepolicy*.conf' -o -name 'vendor_sepolicy*.cil'

# Query HAL classes separately.
sesearch -A -s hal_audio_default -c binder
sesearch -A -s hal_audio_default -c hwservice_manager
sesearch -A -s hal_audio_default -c service_manager

第一条定位 transition 和入口 type,第二条确认 vendor policy 输出,第三条区分 Binder、hwservice_manager 和 service_manager。它们不能证明 init rc 已成功启动服务。

7.3 设备观察 ​

sh
# Confirm process contexts.
adb shell 'ps -AZ | grep -E "vendor_init|rild|hal_audio"'

# Confirm executable and config labels.
adb shell 'ls -Z /vendor/bin/rild /vendor/bin/hw'
adb shell 'ls -Z /vendor/etc/init'

# Read denials with source, target, class and permission.
adb shell 'dmesg | grep "avc: denied" | grep -E "rild|hal_audio|vendor_init"'

ps -AZ 证明 process scontext,ls -Z 证明 file/config tcontext,AVC 提供 class/permission。进程不存在时先看 init service 状态和 exec 错误;进程已在正确 domain 但被拒绝时再查目标 type。

7.4 测试边界 ​

Android 17 vendor policy 的反向证据来自 policy 编译、neverallow、Treble 合并和各 HAL 的 CTS/VTS/厂商测试。init 的 parser 测试能验证 seclabel 保存和 service 状态,但不能证明 vendor daemon 硬件访问;VTS 接口通过也不等于每条 vendor file allow 都是最小权限。

8. 源码导航 ​

  1. system/sepolicy/public/vendor_init.te、private/vendor_init.te:vendor_init 类型、初始化权限和 neverallow。
  2. system/sepolicy/private/init.te:init 到 vendor_init 的 transition。
  3. system/sepolicy/vendor/rild.te、vendor/hal_audio_default.te:独立 vendor daemon 的 domain、exec 和 HAL 角色。
  4. system/core/init/subcontext.h、subcontext.cpp:vendor rc 子上下文。
  5. system/core/init/service.cpp:context 计算、fork、FIFO、cgroup、exec、延迟和重复启动。
  6. system/sepolicy/Android.bp:vendor policy 的 public/vendor 输入和 CIL 输出。
  7. system/sepolicy/public/te_macros:HAL、Binder、property 和 transition 宏。

用 rild 或 hal_audio_default 做练习:先找到 /vendor/bin executable label,再展开 init transition;随后分别查询 daemon 的 file、service_manager、hwservice_manager 和 Binder 规则。若替换 executable type、删除 init transition、删掉 client find 或触发 vendor_init neverallow,应能指出失败发生在 exec、服务注册、客户端查找还是策略编译阶段。