Skip to content

进程Capability设置

深入 Zygote 的 permitted、effective、inheritable、bounding capability 设置及 UID 切换前后的顺序。

基于android-17.0.0_r1
AndroidZygoteCapabilityLinux源码阅读

进程Capability设置 ​

Linux capability 不是一个整数开关。Zygote specialization 同时处理 permitted、effective、inheritable 和 bounding set,并且必须在 UID 切换前完成关键准备。SystemServer 的 capability 候选由 forkSystemServer() 提供;普通应用请求不能直接指定 capability;native SpecializeCommon() 先保持跨 UID 能力、设置 inheritable、裁剪 bounding set,再在接近最终身份时写入 permitted/effective/inheritable。

1. SystemServer能力 ​

源码文件:frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

java
long capabilities =
        (1L << OsConstants.CAP_IPC_LOCK) |
        (1L << OsConstants.CAP_KILL) |
        (1L << OsConstants.CAP_NET_ADMIN) |
        (1L << OsConstants.CAP_NET_BIND_SERVICE) |
        (1L << OsConstants.CAP_NET_BROADCAST) |
        (1L << OsConstants.CAP_NET_RAW) |
        (1L << OsConstants.CAP_SYS_NICE) |
        (1L << OsConstants.CAP_SYS_PTRACE) |
        (1L << OsConstants.CAP_SYS_TIME) |
        (1L << OsConstants.CAP_SYS_TTY_CONFIG) |
        (1L << OsConstants.CAP_WAKE_ALARM) |
        (1L << OsConstants.CAP_BLOCK_SUSPEND) |
        (1L << OsConstants.CAP_MAC_ADMIN);

StructCapUserData[] data = Os.capget(header);
capabilities &= Integer.toUnsignedLong(data[0].effective)
        | (Integer.toUnsignedLong(data[1].effective) << 32);

SystemServer 先列出需要的 capability,再与当前 Zygote effective set 取交集。容器或启动环境已删除的能力不会凭参数重新出现;capget() 失败会阻止 fork。

2. 四组Capability ​

  • permitted:进程允许重新获得的能力上限;
  • effective:当前实际参与权限检查的能力;
  • inheritable:exec/子进程继承关系中的能力集合;
  • bounding:后续 permitted/effective 能力不允许超过的全局上限。

SetCapabilities(permitted, effective, inheritable) 只写前三组;bounding 通过 prctl(PR_CAPBSET_DROP) 逐项删除。bounding 一旦删除不可在该进程中恢复,因此必须在确认目标策略后执行。

3. Keepcaps与继承 ​

源码文件:frameworks/base/core/jni/com_android_internal_os_Zygote.cpp

cpp
static void EnableKeepCapabilities(
        fail_fn_t fail_fn) {
    if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) == -1) {
        fail_fn(CREATE_ERROR(
                "prctl(PR_SET_KEEPCAPS) failed: %s",
                strerror(errno)));
    }
}

static void SetInheritable(
        uint64_t inheritable, fail_fn_t fail_fn) {
    __user_cap_header_struct header = {};
    header.version = _LINUX_CAPABILITY_VERSION_3;
    __user_cap_data_struct data[2];
    if (capget(&header, &data[0]) == -1) {
        fail_fn(CREATE_ERROR("capget failed"));
    }
    data[0].inheritable = inheritable;
    data[1].inheritable = inheritable >> 32;
    if (capset(&header, &data[0]) == -1) {
        fail_fn(CREATE_ERROR("capset inheritable failed"));
    }
}

目标 uid 非 root 时,PR_SET_KEEPCAPS 防止 setuid 清空 permitted;inheritable 则建立 exec/后续派生所需的继承集合。两者属于 UID 切换前的准备,不能放到 setresuid() 之后。

4. BoundingSet裁剪 ​

cpp
static void DropCapabilitiesBoundingSet(
        fail_fn_t fail_fn,
        jlong bounding_capabilities) {
    for (int i = 0;
            prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0;
            i++) {
        if ((1LL << i) & bounding_capabilities) {
            continue;
        }
        if (prctl(PR_CAPBSET_DROP, i, 0, 0, 0) == -1) {
            if (errno == EINVAL) {
                ALOGE("kernel lacks file capabilities support");
            } else {
                fail_fn(CREATE_ERROR(
                        "PR_CAPBSET_DROP failed: %s",
                        strerror(errno)));
            }
        }
    }
}

循环读取内核支持的 capability 编号,保留目标 bounding mask 中的 bit,删除其他 bit。EINVAL 会记录内核能力支持问题;其他错误终止 specialization。bounding mask 不是 effective mask,删除它不会立即改变当前 effective,但会限制后续获取。

5. 最终capset ​

cpp
static void SetCapabilities(
        uint64_t permitted,
        uint64_t effective,
        uint64_t inheritable,
        fail_fn_t fail_fn) {
    __user_cap_header_struct header = {};
    header.version = _LINUX_CAPABILITY_VERSION_3;
    __user_cap_data_struct data[2] = {};
    data[0].effective = effective;
    data[1].effective = effective >> 32;
    data[0].permitted = permitted;
    data[1].permitted = permitted >> 32;
    data[0].inheritable = inheritable;
    data[1].inheritable = inheritable >> 32;
    if (capset(&header, &data[0]) == -1) {
        fail_fn(CREATE_ERROR("capset final set failed"));
    }
}

最终写入要求 permitted/effective 不超过 bounding 和内核允许范围。capset() 失败时 fail_fn 终止子进程,不能继续执行 Java main;否则进程可能以部分 capability 运行,造成权限和功能状态不一致。

6. 特化顺序 ​

源码文件:frameworks/base/core/jni/com_android_internal_os_Zygote.cpp

cpp
permitted_capabilities |= bounding_capabilities;
if (uid != 0) {
    EnableKeepCapabilities(fail_fn);
}
SetInheritable(permitted_capabilities, fail_fn);
DropCapabilitiesBoundingSet(
        fail_fn, bounding_capabilities);

SetGids(env, gids, is_child_zygote, fail_fn);
SetRLimits(env, rlimits, fail_fn);
if (setresgid(gid, gid, gid) == -1) {
    fail_fn(CREATE_ERROR("setresgid failed"));
}
SetUpSeccompFilter(uid, is_child_zygote);
SetSchedulerPolicy(fail_fn, is_top_app);
if (setresuid(uid, uid, uid) == -1) {
    fail_fn(CREATE_ERROR("setresuid failed"));
}
SetCapabilities(permitted_capabilities,
        effective_capabilities,
        permitted_capabilities, fail_fn);

顺序的核心是:先准备能力继承与 bounding,再设置组/rlimit,趁仍有特权装 seccomp 和调度策略,最后切换 uid,并写入最终 capability。文章不把 permitted_capabilities |= bounding_capabilities 解释为“给进程增加任意能力”:前面的 Java/Native policy 和当前 bounding 共同限制最终结果。

7. 角色差异 ​

SystemServer capability 候选来自 forkSystemServer(),并在 native 中以 is_system_server=true 走专用 post-fork hook;应用 capability 通常为零,普通客户端不能通过 Zygote 命令自由指定。app zygote/child zygote 还会清理 inherited groups、选择不同 seccomp policy,不能套用 SystemServer 的集合。

8. 失败与导航 ​

  • PR_SET_KEEPCAPS/capget/capset 失败:终止 specialization;
  • bounding drop 失败:除特定 EINVAL 记录外,其他错误终止;
  • UID 切换前 seccomp/scheduler 失败:不能延后到应用 main;
  • 最终 capability 超过 bounding:内核拒绝 capset;
  • 把 effective 当 permitted:会误判当前可用权限;
  • 把 capability 设置成功当 SELinux 允许:两者是独立安全层。
bash
# Java/SystemServer capability 候选与传递。
rg -n "CAP_IPC_LOCK|CAP_MAC_ADMIN|capget|--capabilities|forkSystemServer" \
  frameworks/base/core/java/com/android/internal/os/ZygoteInit.java

# native capability 四组、UID顺序和失败函数。
rg -n "EnableKeepCapabilities|SetInheritable|DropCapabilitiesBoundingSet|SetCapabilities|SpecializeCommon|setresuid|setresgid|SetUpSeccompFilter" \
  frameworks/base/core/jni/com_android_internal_os_Zygote.cpp

下一篇将分析 capability 与 SELinux/seccomp 的组合边界,不重复本文的四组集合和 capset 顺序。