Permit CAP_SYS_NICE for virtualmachine groups
Grant CAP_SYS_NICE to CapInh/CapPrm/CapBnd for processes that can spawn
VMs. This enables processes to execve on binaries with elevated
capabilities if its file capability bits are set. This does not grant
capability to the parent process(that spawns the VM) as the effective
bits are not set.
This allows for VMs to be able to tune for better performance.
Bug: 322197421
Test: Booted device and processes and checked that the correct
capabilities are given.
Change-Id: I9ba974495383494d24a564c66249c78176eebca9
Merged-In: I9ba974495383494d24a564c66249c78176eebca9
Merged-In: I8cc08506fa6af831fdc607246df11e06fead9f1c
Signed-off-by: David Dai <davidai@google.com>
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index 9c40a28..63a6f4c 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -665,8 +665,9 @@
}
}
-static void DropCapabilitiesBoundingSet(fail_fn_t fail_fn) {
+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("prctl(PR_CAPBSET_DROP) failed with EINVAL. Please verify "
@@ -678,6 +679,27 @@
}
}
+static bool MatchGid(JNIEnv* env, jintArray gids, jint gid, jint gid_to_find) {
+ if (gid == gid_to_find) return true;
+
+ if (gids == nullptr) return false;
+
+ jsize gids_num = env->GetArrayLength(gids);
+ ScopedIntArrayRO native_gid_proxy(env, gids);
+
+ if (native_gid_proxy.get() == nullptr) {
+ RuntimeAbort(env, __LINE__, "Bad gids array");
+ }
+
+ for (int gids_index = 0; gids_index < gids_num; ++gids_index) {
+ if (native_gid_proxy[gids_index] == gid_to_find) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
static void SetInheritable(uint64_t inheritable, fail_fn_t fail_fn) {
__user_cap_header_struct capheader;
memset(&capheader, 0, sizeof(capheader));
@@ -1742,9 +1764,9 @@
// Utility routine to specialize a zygote child process.
static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids, jint runtime_flags,
jobjectArray rlimits, jlong permitted_capabilities,
- jlong effective_capabilities, jint mount_external,
- jstring managed_se_info, jstring managed_nice_name,
- bool is_system_server, bool is_child_zygote,
+ jlong effective_capabilities, jlong bounding_capabilities,
+ jint mount_external, jstring managed_se_info,
+ jstring managed_nice_name, bool is_system_server, bool is_child_zygote,
jstring managed_instruction_set, jstring managed_app_data_dir,
bool is_top_app, jobjectArray pkg_data_info_list,
jobjectArray allowlisted_data_info_list, bool mount_data_dirs,
@@ -1758,6 +1780,9 @@
auto instruction_set = extract_fn(managed_instruction_set);
auto app_data_dir = extract_fn(managed_app_data_dir);
+ // Permit bounding capabilities
+ permitted_capabilities |= bounding_capabilities;
+
// Keep capabilities across UID change, unless we're staying root.
if (uid != 0) {
EnableKeepCapabilities(fail_fn);
@@ -1765,7 +1790,7 @@
SetInheritable(permitted_capabilities, fail_fn);
- DropCapabilitiesBoundingSet(fail_fn);
+ DropCapabilitiesBoundingSet(fail_fn, bounding_capabilities);
bool need_pre_initialize_native_bridge = !is_system_server && instruction_set.has_value() &&
android::NativeBridgeAvailable() &&
@@ -2028,6 +2053,23 @@
return capdata[0].effective | (static_cast<uint64_t>(capdata[1].effective) << 32);
}
+static jlong CalculateBoundingCapabilities(JNIEnv* env, jint uid, jint gid, jintArray gids) {
+ jlong capabilities = 0;
+
+ /*
+ * Grant CAP_SYS_NICE to CapInh/CapPrm/CapBnd for processes that can spawn
+ * VMs. This enables processes to execve on binaries with elevated
+ * capabilities if its file capability bits are set. This does not grant
+ * capability to the parent process(that spawns the VM) as the effective
+ * bits are not set.
+ */
+ if (MatchGid(env, gids, gid, AID_VIRTUALMACHINE)) {
+ capabilities |= (1LL << CAP_SYS_NICE);
+ }
+
+ return capabilities;
+}
+
static jlong CalculateCapabilities(JNIEnv* env, jint uid, jint gid, jintArray gids,
bool is_child_zygote) {
jlong capabilities = 0;
@@ -2061,26 +2103,7 @@
* Grant CAP_BLOCK_SUSPEND to processes that belong to GID "wakelock"
*/
- bool gid_wakelock_found = false;
- if (gid == AID_WAKELOCK) {
- gid_wakelock_found = true;
- } else if (gids != nullptr) {
- jsize gids_num = env->GetArrayLength(gids);
- ScopedIntArrayRO native_gid_proxy(env, gids);
-
- if (native_gid_proxy.get() == nullptr) {
- RuntimeAbort(env, __LINE__, "Bad gids array");
- }
-
- for (int gids_index = 0; gids_index < gids_num; ++gids_index) {
- if (native_gid_proxy[gids_index] == AID_WAKELOCK) {
- gid_wakelock_found = true;
- break;
- }
- }
- }
-
- if (gid_wakelock_found) {
+ if (MatchGid(env, gids, gid, AID_WAKELOCK)) {
capabilities |= (1LL << CAP_BLOCK_SUSPEND);
}
@@ -2357,6 +2380,7 @@
jobjectArray pkg_data_info_list, jobjectArray allowlisted_data_info_list,
jboolean mount_data_dirs, jboolean mount_storage_dirs) {
jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
+ jlong bounding_capabilities = CalculateBoundingCapabilities(env, uid, gid, gids);
if (UNLIKELY(managed_fds_to_close == nullptr)) {
zygote::ZygoteFailure(env, "zygote", nice_name,
@@ -2395,10 +2419,10 @@
if (pid == 0) {
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits, capabilities, capabilities,
- mount_external, se_info, nice_name, false, is_child_zygote == JNI_TRUE,
- instruction_set, app_data_dir, is_top_app == JNI_TRUE, pkg_data_info_list,
- allowlisted_data_info_list, mount_data_dirs == JNI_TRUE,
- mount_storage_dirs == JNI_TRUE);
+ bounding_capabilities, mount_external, se_info, nice_name, false,
+ is_child_zygote == JNI_TRUE, instruction_set, app_data_dir,
+ is_top_app == JNI_TRUE, pkg_data_info_list, allowlisted_data_info_list,
+ mount_data_dirs == JNI_TRUE, mount_storage_dirs == JNI_TRUE);
}
return pid;
}
@@ -2431,7 +2455,7 @@
// System server prcoess does not need data isolation so no need to
// know pkg_data_info_list.
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits, permitted_capabilities,
- effective_capabilities, MOUNT_EXTERNAL_DEFAULT, nullptr, nullptr, true,
+ effective_capabilities, 0, MOUNT_EXTERNAL_DEFAULT, nullptr, nullptr, true,
false, nullptr, nullptr, /* is_top_app= */ false,
/* pkg_data_info_list */ nullptr,
/* allowlisted_data_info_list */ nullptr, false, false);
@@ -2588,12 +2612,13 @@
jobjectArray allowlisted_data_info_list, jboolean mount_data_dirs,
jboolean mount_storage_dirs) {
jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
+ jlong bounding_capabilities = CalculateBoundingCapabilities(env, uid, gid, gids);
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits, capabilities, capabilities,
- mount_external, se_info, nice_name, false, is_child_zygote == JNI_TRUE,
- instruction_set, app_data_dir, is_top_app == JNI_TRUE, pkg_data_info_list,
- allowlisted_data_info_list, mount_data_dirs == JNI_TRUE,
- mount_storage_dirs == JNI_TRUE);
+ bounding_capabilities, mount_external, se_info, nice_name, false,
+ is_child_zygote == JNI_TRUE, instruction_set, app_data_dir,
+ is_top_app == JNI_TRUE, pkg_data_info_list, allowlisted_data_info_list,
+ mount_data_dirs == JNI_TRUE, mount_storage_dirs == JNI_TRUE);
}
/**