Zygote安全限制
Zygote 的安全模型不是一次 setuid()。一个 spawn 请求先由 ZygoteConnection.processCommand() 根据 Unix peer credentials 检查 uid、invoke-with 和 capability 参数,再进入 Zygote.forkAndSpecialize();native SpecializeCommon 在子进程中按顺序关闭继承 fd、设置 rlimit、切换 SELinux context、裁剪 capability、安装 seccomp,并最后把进程变成目标身份。
1. Java命令校验
源码文件:frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java、frameworks/base/core/java/com/android/internal/os/Zygote.java
if (parsedArgs.mPermittedCapabilities != 0
|| parsedArgs.mEffectiveCapabilities != 0) {
throw new ZygoteSecurityException(
"Client may not specify capabilities: permitted="
+ Long.toHexString(parsedArgs.mPermittedCapabilities)
+ ", effective="
+ Long.toHexString(parsedArgs.mEffectiveCapabilities));
}
Zygote.applyUidSecurityPolicy(parsedArgs, peer);
Zygote.applyInvokeWithSecurityPolicy(parsedArgs, peer);
Zygote.applyDebuggerSystemProperty(parsedArgs);
Zygote.applyInvokeWithSystemProperty(parsedArgs);普通客户端不能在 spawn 请求里直接指定 capability;uid/gid 和 invoke-with 先根据 socket peer credentials 校验。debugger system properties 只调整允许的 runtime flags,不是给请求方任意加权限。
2. UID/GID策略
源码文件:frameworks/base/core/java/com/android/internal/os/Zygote.java
static void applyUidSecurityPolicy(
ZygoteArguments args, Credentials peer)
throws ZygoteSecurityException {
if (args.mUidSpecified
&& args.mUid < minChildUid(peer)) {
throw new ZygoteSecurityException(
"System UID may not launch process with UID < "
+ Process.SYSTEM_UID);
}
if (!args.mUidSpecified) {
args.mUid = peer.getUid();
args.mUidSpecified = true;
}
if (!args.mGidSpecified) {
args.mGid = peer.getGid();
args.mGidSpecified = true;
}
}
static void applyInvokeWithSecurityPolicy(
ZygoteArguments args, Credentials peer)
throws ZygoteSecurityException {
int peerUid = peer.getUid();
if (args.mInvokeWith != null && peerUid != 0
&& (args.mRuntimeFlags
& (Zygote.DEBUG_ENABLE_JDWP
| Zygote.DEBUG_ENABLE_PTRACE)) == 0) {
throw new ZygoteSecurityException(
"Peer is permitted to specify an explicit invoke-with "
+ "wrapper command only for debuggable applications.");
}
}未显式指定 uid/gid 时继承发起者 credentials;显式 uid 不能低于允许的最小 child UID。invoke-with 允许 root,非 root 只有在 JDWP/ptrace 调试标志已启用时才能使用。这个限制在 fork 前生效,拒绝时不会产生子进程。
3. 继承FD与rlimit
源码文件:frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java
int[] fdsToClose = { -1, -1 };
FileDescriptor fd = mSocket.getFileDescriptor();
if (fd != null) fdsToClose[0] = fd.getInt$();
FileDescriptor zygoteFd =
zygoteServer.getZygoteSocketFileDescriptor();
if (zygoteFd != null) fdsToClose[1] = zygoteFd.getInt$();
pid = Zygote.forkAndSpecialize(
parsedArgs.mUid, parsedArgs.mGid, parsedArgs.mGids,
parsedArgs.mRuntimeFlags, rlimits,
parsedArgs.mMountExternal, parsedArgs.mSeInfo,
parsedArgs.mNiceName, fdsToClose, fdsToIgnore, ...);子进程必须在切换到应用 UID 和权限前关闭 session socket 与 Zygote server socket,防止应用继承控制面 fd。invoke-with 还会创建 CLOEXEC 管道,并把 pipe fd 放入 fdsToIgnore,避免 fd 表检查误判。rlimit 则作为二维数组传给 native specialization,不是由 Java 进程事后补设。
4. Native特化
源码文件:frameworks/base/core/jni/com_android_internal_os_Zygote.cpp
static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid,
jintArray gids, jint runtime_flags, jobjectArray rlimits,
jlong permitted_capabilities, jlong effective_capabilities,
...)
{
// 解析并关闭/替换子进程不应继承的 fd。
...
// 设置 uid/gid、groups、rlimit、SELinux context、capability。
...
// 根据 runtime flags 安装 app/system seccomp policy。
...
}SpecializeCommon 是 native 安全状态的 owner。源码把大量动作集中在 fork 后的 child 路径;任何不可恢复的安全设置失败通过 fail_fn/RuntimeAbort 终止子进程,而不是让它以部分特权继续运行。
5. SELinux与能力
源码文件: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));
}SELinux context 由 uid、是否 SystemServer、seInfo 和进程名共同决定;不是 Java --nice-name 单独决定。capability 路径先设置 keepcaps、裁剪 bounding set,再调用 capset 写 permitted/effective/inheritable 集合。Zygote 的父进程保留创建能力,目标子进程必须完成裁剪后才进入正常代码。
6. Seccomp策略
if ((runtime_flags & RuntimeFlags::DEBUG_ENABLE_JDWP) != 0) {
...
}
if (is_system_server) {
set_system_seccomp_filter();
} else if (is_app_zygote) {
set_app_zygote_seccomp_filter();
} else {
set_app_seccomp_filter();
}不同进程角色选择不同 seccomp policy;setenforce 0 等调试环境可能禁用过滤,但不能把调试设备行为当作生产默认。seccomp 安装失败会让 specialization 失败,生效时机在子进程执行目标 Java main/native main 之前。
7. 失败边界
安全设置有明确的先后关系:Java 拒绝发生在 fork 前;fd 关闭和 uid/gid 变更发生在 child;SELinux、capability、seccomp 失败不能返回给父进程后继续使用半安全 child。ZygoteConnection 在父端异常时关闭控制 socket,让客户端立即得到失败,不把错误留成超时。
8. 导航与后续
# Java 侧命令校验、uid/invoke-with、fd 列表和 fork 调用。
rg -n "applyUidSecurityPolicy|applyInvokeWithSecurityPolicy|fdsToClose|forkAndSpecialize" \
frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java \
frameworks/base/core/java/com/android/internal/os/Zygote.java
# Native specialization、SELinux、capability、seccomp 和失败终止。
rg -n "SpecializeCommon|selinux_android_setcontext|capset|PR_CAPBSET_DROP|seccomp|RuntimeAbort|fail_fn" \
frameworks/base/core/jni/com_android_internal_os_Zygote.cpp下一篇将进入 SystemServer 的 fork 后处理和 RuntimeInit 接缝;不会重复本文的 uid/gid、SELinux、capability 和 seccomp 细节。
