Zygote Domain
本文面向已经读过 Init Domain、System Server Domain 和 seapp_contexts 的读者。本文聚焦 zygote 作为进程孵化器时的 SELinux owner、资源继承和子进程隔离,不把 Java 类预加载、应用启动协议和所有 app domain 的策略混为一谈。
读完后,读者应能从 zygote rc service 找到 init 到 zygote 的 transition,从 fork 请求追到 native specialize,再区分 system_server、普通 app、webview_zygote 和 app_zygote 的目标域;同时能解释为什么 zygote 需要 mount、execmem、setcurrent 和 seccomp 相关权限,却不能任意 transition 或 ptrace 其他进程。
1. 启动与职责
1.1 zygote类型
源码文件:system/sepolicy/public/zygote.te
type zygote, domain;
type zygote_exec, system_file_type, exec_type, file_type;
type zygote_tmpfs, file_type;源码文件:system/sepolicy/private/zygote.te
typeattribute zygote coredomain;
typeattribute zygote mlstrustedsubject;
init_daemon_domain(zygote)
tmpfs_domain(zygote)zygote_exec 是 init execve 时使用的文件 type;zygote 是进程 domain;zygote_tmpfs 是 zygote 在 tmpfs 上创建或执行代码的对象 type。coredomain 和 mlstrustedsubject 只描述 policy 属性,不会自动授予 fork、mount 或 setuid 权限。
1.2 rc入口
源码文件:system/core/rootdir/init.zygote64.rc
service zygote /system/bin/app_process64 -Xzygote /system/bin \
--zygote --start-system-server --socket-name=zygote
class main
socket zygote stream 660 root systeminit 的 Service 对象拥有该 service 的 class、命令行和 socket。init 执行 app_process64 时,file_contexts 返回 zygote_exec,init_daemon_domain(zygote) 提供 init 到 zygote 的自动 transition。服务进入 zygote 后,命令行由 ZygoteInit.main 解析。
1.3 Java主循环
源码文件:frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
if (!enableLazyPreload) {
bootTimingsTraceLog.traceBegin("ZygotePreload");
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
SystemClock.uptimeMillis());
preload(bootTimingsTraceLog);
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
SystemClock.uptimeMillis());
bootTimingsTraceLog.traceEnd();
}
gcAndFinalize();
Zygote.initNativeState(isPrimaryZygote);
ZygoteHooks.stopZygoteNoThreadCreation();
zygoteServer = new ZygoteServer(isPrimaryZygote);
if (startSystemServer) {
Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
if (r != null) {
r.run();
return;
}
}
caller = zygoteServer.runSelectLoop(abiList);zygote 先预加载 classes/resources/shared libraries,再做 GC 和 native state 初始化,最后进入 command socket select loop。startSystemServer 分支在同一进程里 fork child;父 zygote 回到 select loop,child 返回 Runnable 并执行 SystemServer。
2. Fork目标
2.1 目标域
源码文件:system/sepolicy/private/zygote.te
allow zygote self:process setcurrent;
allow zygote system_server_startup:process dyntransition;
allow zygote appdomain:process dyntransition;
allow zygote webview_zygote:process dyntransition;
allow zygote app_zygote:process dyntransition;zygote 只能切到四类目标集合。system_server 先进入 system_server_startup;普通 app 进入 appdomain 的具体 seapp domain;WebView 和 child zygote 使用专用 domain。setcurrent 允许 zygote 在 native specialization 中调用 setcon,但目标 type 仍受 policy 集合约束。
2.2 system_server
源码文件:frameworks/base/core/jni/com_android_internal_os_Zygote.cpp
if (selinux_android_setcontext(
uid, is_system_server, se_info_ptr, nice_name_ptr) == -1) {
fail_fn(CREATE_ERROR(
"selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed",
uid, is_system_server, se_info_ptr, nice_name_ptr));
}
if (is_system_server) {
env->CallStaticVoidMethod(
gZygoteClass, gCallPostForkSystemServerHooks, runtime_flags);
static const char* kSystemServerLabel = "u:r:system_server:s0";
if (selinux_android_setcon(kSystemServerLabel) != 0) {
fail_fn(CREATE_ERROR("selinux_android_setcon(%s)",
kSystemServerLabel));
}
}system_server 的最终标签是固定 u:r:system_server:s0;普通 app 则由 se_info、uid 和 seapp_contexts selector 计算。system_server_startup 负责 AOT/JIT 资源和 setcon 权限,最终 domain 通过显式 setcon 接管。
2.3 普通app分支
源码文件:frameworks/base/core/jni/com_android_internal_os_Zygote.cpp
const char* se_info_ptr = se_info.has_value() ? se_info.value().c_str() : nullptr;
if (selinux_android_setcontext(
uid, is_system_server, se_info_ptr, nice_name_ptr) == -1) {
fail_fn(CREATE_ERROR(
"selinux_android_setcontext failed for uid %d", uid));
}
if (is_child_zygote) {
initUnsolSocketToSystemServer();
}
env->CallStaticVoidMethod(
gZygoteClass, gCallPostForkChildHooks, runtime_flags,
is_system_server, is_child_zygote, managed_instruction_set);普通 app 的 se_info 来自 PackageManager,包含 seinfo、target SDK、用户和其他 selector;selinux_android_setcontext 将它交给 libselinux,最终得到 untrusted_app、platform_app、isolated_app 等具体 domain。child hook 只在 context 设置成功后调用。
3. Native切换
3.1 参数顺序
源码文件:frameworks/base/core/java/android/os/ZygoteProcess.java
argsForZygote.add("--runtime-args");
argsForZygote.add("--setuid=" + uid);
argsForZygote.add("--setgid=" + gid);
argsForZygote.add("--runtime-flags=" + runtimeFlags);
argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
if (niceName != null) {
argsForZygote.add("--nice-name=" + niceName);
}
if (seInfo != null) {
argsForZygote.add("--seinfo=" + seInfo);
}
if (appDataDir != null) {
argsForZygote.add("--app-data-dir=" + appDataDir);
}
if (startChildZygote) {
argsForZygote.add("--start-child-zygote");
}ActivityManager/ProcessList 通过 ZygoteProcess 把 uid、runtime flags、target SDK、seInfo、data dir 和 child-zygote 标志编码为 socket 请求。zygote 读取同一协议后,native 和 Java 两层共同决定 specialization 参数。
3.2 安全限制
源码文件:frameworks/base/core/jni/com_android_internal_os_Zygote.cpp
SetInheritable(permitted_capabilities, fail_fn);
DropCapabilitiesBoundingSet(fail_fn, bounding_capabilities);
if (getuid() == 0) {
const int rc = createProcessGroup(uid, getpid());
if (rc != 0) {
fail_fn(CREATE_ERROR("createProcessGroup failed"));
}
}
SetGids(env, gids, is_child_zygote, fail_fn);
SetRLimits(env, rlimits, fail_fn);
SetUpSeccompFilter(uid, is_child_zygote);
SetSchedulerPolicy(fail_fn, is_top_app);
if (setresuid(uid, uid, uid) == -1) {
fail_fn(CREATE_ERROR("setresuid failed"));
}specialize 的顺序有安全原因:先设置继承 capability、创建 process group、设置 groups/rlimits,再安装 seccomp 和 scheduler policy,最后 setresuid。丢弃 bounding set 后,子进程不能重新获得被移除的 capability。
3.3 seccomp选择
源码文件:frameworks/base/core/jni/com_android_internal_os_Zygote.cpp
static void SetUpSeccompFilter(uid_t uid, bool is_child_zygote) {
if (!gIsSecurityEnforced) {
ALOGI("seccomp disabled by setenforce 0");
return;
}
if (uid >= AID_APP_START) {
if (is_child_zygote) {
set_app_zygote_seccomp_filter();
} else {
set_app_seccomp_filter();
}
} else {
set_system_seccomp_filter();
}
}uid 决定 system/app filter,is_child_zygote 再区分 app zygote filter。setenforce 0 会跳过 seccomp 安装,但不等于 SELinux policy 被删除;这是运行模式条件,不能用 permissive 实验推断 enforcing 的系统调用限制。
4. 数据隔离
4.1 zygote策略
源码文件:system/sepolicy/private/zygote.te
allow zygote {
system_userdir_file
system_data_file
user_profile_root_file
user_profile_data_file
media_rw_data_file
properties_device
}:dir { mounton search };
allow zygote mirror_data_file:dir search;
allow zygote tmpfs:dir { create_dir_perms mounton };
allow zygote tmpfs:{ dir lnk_file } relabelfrom;
allow zygote system_userdir_file:dir relabelto;
allow zygote system_data_file:{ dir lnk_file } relabelto;zygote 在 fork 后为 app mount namespace 隔离 CE/DE、profile、media 和 property override 目录。它需要 mounton、search、relabelto 等权限,但这些动作发生在 child mount namespace;不会让 zygote domain 直接成为 app 对象的长期 owner。
4.2 native隔离调用
源码文件:frameworks/base/core/jni/com_android_internal_os_Zygote.cpp
ensureInAppMountNamespace(fail_fn);
if (mount_data_dirs) {
appid_t appId = multiuser_get_app_id(uid);
if (appId >= AID_SDK_SANDBOX_PROCESS_START &&
appId <= AID_SDK_SANDBOX_PROCESS_END) {
isolateSdkSandboxData(env, pkg_data_info_list, uid,
process_name, managed_nice_name, fail_fn);
}
isolateAppData(env, pkg_data_info_list,
allowlisted_data_info_list, uid,
process_name, managed_nice_name, fail_fn);
isolateJitProfile(env, pkg_data_info_list, uid,
process_name, managed_nice_name, fail_fn);
}
if (mount_storage_dirs) {
BindMountStorageDirs(env, pkg_data_info_list, uid,
process_name, managed_nice_name, fail_fn);
}mount_data_dirs 和 mount_storage_dirs 来自 ZygoteProcess 的启动参数。普通 app 可能需要 app data、JIT profile 和 storage bind mount;system_server 不传 app data isolation list,因此不走这段 app 隔离逻辑。
4.3 资源继承
源码文件:system/sepolicy/private/zygote.te
allow zygote zygote:memfd_file execute;
allow zygote zygote_tmpfs:file execute;
allow zygote ashmem_libcutils_device:chr_file execute;
allow zygote idmap_exec:file rx_file_perms;
allow zygote dex2oat_exec:file rx_file_perms;
allow zygote apex_art_data_file:file { r_file_perms execute };这些权限支持预加载代码、JIT memory、idmap/dex2oat 和 ART APEX 产物。子进程是否能继续执行这些对象取决于它自己的 domain;zygote 拥有 execute 不等于 appdomain 自动拥有同样 execute。
5. 安全约束
5.1 目标域白名单
源码文件:system/sepolicy/private/zygote.te、system/sepolicy/private/system_server.te
allow zygote system_server_startup:process dyntransition;
allow zygote appdomain:process dyntransition;
allow zygote webview_zygote:process dyntransition;
allow zygote app_zygote:process dyntransition;
neverallow system_server *:process dyntransition;
neverallow system_server {
domain -clatd -crash_dump -perfetto -trace_redactor
}:process transition;zygote 是切换发起者;system_server 切换完成后不能再随意切域。system_server 的少量 transition 例外由崩溃转储、profiling 或网络配置等明确消费者使用。
5.2 proc与进程控制
源码文件:system/sepolicy/private/zygote.te
allow zygote appdomain:dir { getattr search };
allow zygote appdomain:file { r_file_perms };
allow zygote system_server:process { getpgid setpgid };
allow zygote appdomain:process { getpgid setpgid };
allow zygote webview_zygote:process { getpgid setpgid };
allow zygote app_zygote:process { getpgid setpgid };zygote 需要读取 app proc 目录、调整 child process group,并把 system_server 加入 peer group。这里没有对 appdomain 的 signal/kill 泛化权限;应用进程的进程管理由 system_server 等 domain 负责。
5.3 neverallow
源码文件:system/sepolicy/private/zygote.te
neverallow zygote ~{
self
system_server_startup
appdomain
webview_zygote
app_zygote
}:process dyntransition;
neverallow zygote app_data_file_type:dir ~getattr;第一条把 dyntransition 目标限制在 allowlist,第二条限制 zygote 对 app data 目录的操作集合。neverallow 在策略编译时检查最终展开结果,运行时不会产生一条“拒绝日志”来替代编译失败。
6. 失败与测试
6.1 fork失败
源码文件:frameworks/base/core/jni/com_android_internal_os_Zygote.cpp
pid_t pid = zygote::ForkCommon(env, true,
fds_to_close,
fds_to_ignore,
true);
if (pid == 0) {
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
permitted_capabilities, effective_capabilities,
0, MOUNT_EXTERNAL_DEFAULT, nullptr, nullptr, true,
false, nullptr, nullptr, false,
nullptr, nullptr, false, false, false);
} else if (pid > 0) {
gSystemServerPid = pid;
}fork 返回小于零时由上层 fail_fn/异常路径处理;child 在 specialization 任一阶段失败会 abort。父 zygote 只有确认 pid 后继续监听,不会把 child 的 partial state 当成已启动服务。
6.2 Java测试
源码文件:frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java
private Runnable handleChildProc(ZygoteArguments parsedArgs,
FileDescriptor pipeFd, boolean isZygote) {
closeSocket();
Zygote.setAppProcessName(parsedArgs, TAG);
if (parsedArgs.mInvokeWith != null) {
WrapperInit.execApplication(parsedArgs.mInvokeWith,
parsedArgs.mNiceName, parsedArgs.mTargetSdkVersion,
VMRuntime.getCurrentInstructionSet(),
pipeFd, parsedArgs.mRemainingArgs);
throw new IllegalStateException(
"WrapperInit.execApplication unexpectedly returned");
} else {
if (!isZygote) {
return ZygoteInit.zygoteInit(
parsedArgs.mTargetSdkVersion,
parsedArgs.mDisabledCompatChanges,
parsedArgs.mEnabledCompatChanges,
parsedArgs.mRemainingArgs, null);
} else {
return ZygoteInit.childZygoteInit(
parsedArgs.mRemainingArgs);
}
}
}输入是已由 native fork/specialize 处理的 ZygoteArguments;普通 child 进入 zygoteInit,child zygote 进入 childZygoteInit,wrapper 返回属于异常。该测试/代码路径证明 Java 后处理分支,不证明 kernel dyntransition 已允许。
6.3 设备诊断
# Confirm the parent and child process contexts.
adb shell 'ps -AZ | grep -E "zygote|system_server|webview_zygote|app_zygote"'
# Verify executable labels and init transition input.
adb shell 'ls -Z /system/bin/app_process32 /system/bin/app_process64'
rg -n 'init_daemon_domain\(zygote\)|zygote_exec|dyntransition' \
system/sepolicy/public system/sepolicy/private
# Check seccomp and SELinux failures separately.
adb shell 'logcat -b all | grep -E "seccomp|setcontext|setcon|SpecializeCommon"'
# Inspect process-group and data-isolation symptoms.
adb shell 'dumpsys activity processes | grep -E "pid=|processName="'ps -AZ 验证最终 domain;ls -Z 与 policy 搜索验证 init→zygote 输入;日志区分 seccomp/setcontext;ActivityManager 输出用于关联 pid 和 app process。任何一条命令都不能单独证明全部 specialization 步骤成功。
7. 源码导航
- system/sepolicy/public/zygote.te、system/sepolicy/private/zygote.te:类型、dyntransition、数据隔离、seccomp 前置权限和 neverallow。
- frameworks/base/core/java/com/android/internal/os/ZygoteInit.java:预加载、forkSystemServer 和 select loop。
- frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java:socket 请求后的 child/parent 处理。
- frameworks/base/core/java/android/os/ZygoteProcess.java:启动参数、seInfo、mount isolation 和 socket 协议。
- frameworks/base/core/jni/com_android_internal_os_Zygote.cpp:SpecializeCommon、能力、seccomp、setcontext 和 child hooks。
- system/sepolicy/private/system_server_startup.te、system_server.te:startup → system_server 交接和最终约束。
以普通 app 启动为练习:从 ZygoteProcess 的 seInfo、uid、data isolation 参数开始,追到 SpecializeCommon 的 seccomp、mount、setresuid 和 selinux_android_setcontext,再对照 zygote 的 appdomain dyntransition 与目标 app 的 seapp_contexts 规则。若只改了 file_contexts、只删除 dyntransition 或只关闭 seccomp,应能分别预测 init transition、kernel domain switch 和系统调用过滤哪一层发生变化。
