Merge "Refactor native Zygote code."
This commit is contained in:
@@ -20,7 +20,9 @@
|
|||||||
#include <sys/mount.h>
|
#include <sys/mount.h>
|
||||||
#include <linux/fs.h>
|
#include <linux/fs.h>
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
#include <list>
|
#include <list>
|
||||||
|
#include <optional>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@@ -70,6 +72,8 @@
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
using namespace std::placeholders;
|
||||||
|
|
||||||
using android::String8;
|
using android::String8;
|
||||||
using android::base::StringPrintf;
|
using android::base::StringPrintf;
|
||||||
using android::base::WriteStringToFile;
|
using android::base::WriteStringToFile;
|
||||||
@@ -522,12 +526,12 @@ void SetThreadName(const char* thread_name) {
|
|||||||
static FileDescriptorTable* gOpenFdTable = NULL;
|
static FileDescriptorTable* gOpenFdTable = NULL;
|
||||||
|
|
||||||
static bool FillFileDescriptorVector(JNIEnv* env,
|
static bool FillFileDescriptorVector(JNIEnv* env,
|
||||||
jintArray java_fds,
|
jintArray managed_fds,
|
||||||
std::vector<int>* fds,
|
std::vector<int>* fds,
|
||||||
std::string* error_msg) {
|
std::string* error_msg) {
|
||||||
CHECK(fds != nullptr);
|
CHECK(fds != nullptr);
|
||||||
if (java_fds != nullptr) {
|
if (managed_fds != nullptr) {
|
||||||
ScopedIntArrayRO ar(env, java_fds);
|
ScopedIntArrayRO ar(env, managed_fds);
|
||||||
if (ar.get() == nullptr) {
|
if (ar.get() == nullptr) {
|
||||||
*error_msg = "Bad fd array";
|
*error_msg = "Bad fd array";
|
||||||
return false;
|
return false;
|
||||||
@@ -540,32 +544,138 @@ static bool FillFileDescriptorVector(JNIEnv* env,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utility routine to specialize a zygote child process.
|
[[noreturn]]
|
||||||
static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
|
static void ZygoteFailure(JNIEnv* env,
|
||||||
jint runtime_flags, jobjectArray javaRlimits,
|
const char* process_name,
|
||||||
jlong permittedCapabilities, jlong effectiveCapabilities,
|
jstring managed_process_name,
|
||||||
jint mount_external, jstring java_se_info, jstring java_se_name,
|
const std::string& msg) {
|
||||||
bool is_system_server, bool is_child_zygote, jstring instructionSet,
|
std::unique_ptr<ScopedUtfChars> scoped_managed_process_name_ptr = nullptr;
|
||||||
jstring dataDir) {
|
if (managed_process_name != nullptr) {
|
||||||
std::string error_msg;
|
scoped_managed_process_name_ptr.reset(new ScopedUtfChars(env, managed_process_name));
|
||||||
|
if (scoped_managed_process_name_ptr->c_str() != nullptr) {
|
||||||
|
process_name = scoped_managed_process_name_ptr->c_str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
auto fail_fn = [env, java_se_name, is_system_server](const std::string& msg)
|
const std::string& error_msg =
|
||||||
__attribute__ ((noreturn)) {
|
(process_name == nullptr) ? msg : StringPrintf("(%s) %s", process_name, msg.c_str());
|
||||||
const char* se_name_c_str = nullptr;
|
|
||||||
std::unique_ptr<ScopedUtfChars> se_name;
|
env->FatalError(error_msg.c_str());
|
||||||
if (java_se_name != nullptr) {
|
__builtin_unreachable();
|
||||||
se_name.reset(new ScopedUtfChars(env, java_se_name));
|
}
|
||||||
se_name_c_str = se_name->c_str();
|
|
||||||
|
static std::optional<std::string> ExtractJString(JNIEnv* env,
|
||||||
|
const char* process_name,
|
||||||
|
jstring managed_process_name,
|
||||||
|
jstring managed_string) {
|
||||||
|
if (managed_string == nullptr) {
|
||||||
|
return std::optional<std::string>();
|
||||||
|
} else {
|
||||||
|
ScopedUtfChars scoped_string_chars(env, managed_string);
|
||||||
|
|
||||||
|
if (scoped_string_chars.c_str() != nullptr) {
|
||||||
|
return std::optional<std::string>(scoped_string_chars.c_str());
|
||||||
|
} else {
|
||||||
|
ZygoteFailure(env, process_name, managed_process_name, "Failed to extract JString.");
|
||||||
}
|
}
|
||||||
if (se_name_c_str == nullptr && is_system_server) {
|
}
|
||||||
se_name_c_str = "system_server";
|
}
|
||||||
|
|
||||||
|
// Utility routine to fork a zygote.
|
||||||
|
static pid_t ForkCommon(JNIEnv* env, bool is_system_server,
|
||||||
|
jintArray managed_fds_to_close, jintArray managed_fds_to_ignore) {
|
||||||
|
SetSignalHandlers();
|
||||||
|
|
||||||
|
// Block SIGCHLD prior to fork.
|
||||||
|
sigset_t sigchld;
|
||||||
|
sigemptyset(&sigchld);
|
||||||
|
sigaddset(&sigchld, SIGCHLD);
|
||||||
|
|
||||||
|
// Curry a failure function.
|
||||||
|
auto fail_fn = std::bind(ZygoteFailure, env, is_system_server ? "system_server" : "zygote",
|
||||||
|
nullptr, _1);
|
||||||
|
|
||||||
|
// Temporarily block SIGCHLD during forks. The SIGCHLD handler might
|
||||||
|
// log, which would result in the logging FDs we close being reopened.
|
||||||
|
// This would cause failures because the FDs are not whitelisted.
|
||||||
|
//
|
||||||
|
// Note that the zygote process is single threaded at this point.
|
||||||
|
if (sigprocmask(SIG_BLOCK, &sigchld, nullptr) == -1) {
|
||||||
|
fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close any logging related FDs before we start evaluating the list of
|
||||||
|
// file descriptors.
|
||||||
|
__android_log_close();
|
||||||
|
stats_log_close();
|
||||||
|
|
||||||
|
// If this is the first fork for this zygote, create the open FD table. If
|
||||||
|
// it isn't, we just need to check whether the list of open files has changed
|
||||||
|
// (and it shouldn't in the normal case).
|
||||||
|
std::string error_msg;
|
||||||
|
std::vector<int> fds_to_ignore;
|
||||||
|
if (!FillFileDescriptorVector(env, managed_fds_to_ignore, &fds_to_ignore, &error_msg)) {
|
||||||
|
fail_fn(error_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gOpenFdTable == nullptr) {
|
||||||
|
gOpenFdTable = FileDescriptorTable::Create(fds_to_ignore, &error_msg);
|
||||||
|
if (gOpenFdTable == nullptr) {
|
||||||
|
fail_fn(error_msg);
|
||||||
}
|
}
|
||||||
const std::string& error_msg = (se_name_c_str == nullptr)
|
} else if (!gOpenFdTable->Restat(fds_to_ignore, &error_msg)) {
|
||||||
? msg
|
fail_fn(error_msg);
|
||||||
: StringPrintf("(%s) %s", se_name_c_str, msg.c_str());
|
}
|
||||||
env->FatalError(error_msg.c_str());
|
|
||||||
__builtin_unreachable();
|
android_fdsan_error_level fdsan_error_level = android_fdsan_get_error_level();
|
||||||
};
|
|
||||||
|
pid_t pid = fork();
|
||||||
|
|
||||||
|
if (pid == 0) {
|
||||||
|
// The child process.
|
||||||
|
PreApplicationInit();
|
||||||
|
|
||||||
|
// Clean up any descriptors which must be closed immediately
|
||||||
|
if (!DetachDescriptors(env, managed_fds_to_close, &error_msg)) {
|
||||||
|
fail_fn(error_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Re-open all remaining open file descriptors so that they aren't shared
|
||||||
|
// with the zygote across a fork.
|
||||||
|
if (!gOpenFdTable->ReopenOrDetach(&error_msg)) {
|
||||||
|
fail_fn(error_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Turn fdsan back on.
|
||||||
|
android_fdsan_set_error_level(fdsan_error_level);
|
||||||
|
}
|
||||||
|
|
||||||
|
// We blocked SIGCHLD prior to a fork, we unblock it here.
|
||||||
|
if (sigprocmask(SIG_UNBLOCK, &sigchld, nullptr) == -1) {
|
||||||
|
fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)));
|
||||||
|
}
|
||||||
|
return pid;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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, jstring managed_instruction_set,
|
||||||
|
jstring managed_app_data_dir) {
|
||||||
|
auto fail_fn = std::bind(ZygoteFailure, env, is_system_server ? "system_server" : "zygote",
|
||||||
|
managed_nice_name, _1);
|
||||||
|
auto extract_fn = std::bind(ExtractJString, env, is_system_server ? "system_server" : "zygote",
|
||||||
|
managed_nice_name, _1);
|
||||||
|
|
||||||
|
auto se_info = extract_fn(managed_se_info);
|
||||||
|
auto nice_name = extract_fn(managed_nice_name);
|
||||||
|
auto instruction_set = extract_fn(managed_instruction_set);
|
||||||
|
auto app_data_dir = extract_fn(managed_app_data_dir);
|
||||||
|
|
||||||
|
std::string error_msg;
|
||||||
|
|
||||||
// Keep capabilities across UID change, unless we're staying root.
|
// Keep capabilities across UID change, unless we're staying root.
|
||||||
if (uid != 0) {
|
if (uid != 0) {
|
||||||
@@ -574,26 +684,27 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SetInheritable(permittedCapabilities, &error_msg)) {
|
if (!SetInheritable(permitted_capabilities, &error_msg)) {
|
||||||
fail_fn(error_msg);
|
fail_fn(error_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!DropCapabilitiesBoundingSet(&error_msg)) {
|
if (!DropCapabilitiesBoundingSet(&error_msg)) {
|
||||||
fail_fn(error_msg);
|
fail_fn(error_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool use_native_bridge = !is_system_server && (instructionSet != NULL)
|
bool use_native_bridge = !is_system_server &&
|
||||||
&& android::NativeBridgeAvailable();
|
instruction_set.has_value() &&
|
||||||
if (use_native_bridge) {
|
android::NativeBridgeAvailable() &&
|
||||||
ScopedUtfChars isa_string(env, instructionSet);
|
android::NeedsNativeBridge(instruction_set.value().c_str());
|
||||||
use_native_bridge = android::NeedsNativeBridge(isa_string.c_str());
|
|
||||||
}
|
if (use_native_bridge && !app_data_dir.has_value()) {
|
||||||
if (use_native_bridge && dataDir == NULL) {
|
// The app_data_dir variable should never be empty if we need to use a
|
||||||
// dataDir should never be null if we need to use a native bridge.
|
// native bridge. In general, app_data_dir will never be empty for normal
|
||||||
// In general, dataDir will never be null for normal applications. It can only happen in
|
// applications. It can only happen in special cases (for isolated
|
||||||
// special cases (for isolated processes which are not associated with any app). These are
|
// processes which are not associated with any app). These are launched by
|
||||||
// launched by the framework and should not be emulated anyway.
|
// the framework and should not be emulated anyway.
|
||||||
use_native_bridge = false;
|
use_native_bridge = false;
|
||||||
ALOGW("Native bridge will not be used because dataDir == NULL.");
|
ALOGW("Native bridge will not be used because managed_app_data_dir == nullptr.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!MountEmulatedStorage(uid, mount_external, use_native_bridge, &error_msg)) {
|
if (!MountEmulatedStorage(uid, mount_external, use_native_bridge, &error_msg)) {
|
||||||
@@ -622,34 +733,33 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SetGids(env, javaGids, &error_msg)) {
|
if (!SetGids(env, gids, &error_msg)) {
|
||||||
fail_fn(error_msg);
|
fail_fn(error_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SetRLimits(env, javaRlimits, &error_msg)) {
|
if (!SetRLimits(env, rlimits, &error_msg)) {
|
||||||
fail_fn(error_msg);
|
fail_fn(error_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (use_native_bridge) {
|
if (use_native_bridge) {
|
||||||
ScopedUtfChars isa_string(env, instructionSet);
|
// Due to the logic behind use_native_bridge we know that both app_data_dir
|
||||||
ScopedUtfChars data_dir(env, dataDir);
|
// and instruction_set contain values.
|
||||||
android::PreInitializeNativeBridge(data_dir.c_str(), isa_string.c_str());
|
android::PreInitializeNativeBridge(app_data_dir.value().c_str(),
|
||||||
|
instruction_set.value().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
int rc = setresgid(gid, gid, gid);
|
if (setresgid(gid, gid, gid) == -1) {
|
||||||
if (rc == -1) {
|
|
||||||
fail_fn(CREATE_ERROR("setresgid(%d) failed: %s", gid, strerror(errno)));
|
fail_fn(CREATE_ERROR("setresgid(%d) failed: %s", gid, strerror(errno)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must be called when the new process still has CAP_SYS_ADMIN, in this case, before changing
|
// Must be called when the new process still has CAP_SYS_ADMIN, in this case,
|
||||||
// uid from 0, which clears capabilities. The other alternative is to call
|
// before changing uid from 0, which clears capabilities. The other
|
||||||
// prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that breaks SELinux domain transition (see
|
// alternative is to call prctl(PR_SET_NO_NEW_PRIVS, 1) afterward, but that
|
||||||
// b/71859146). As the result, privileged syscalls used below still need to be accessible in
|
// breaks SELinux domain transition (see b/71859146). As the result,
|
||||||
// app process.
|
// privileged syscalls used below still need to be accessible in app process.
|
||||||
SetUpSeccompFilter(uid);
|
SetUpSeccompFilter(uid);
|
||||||
|
|
||||||
rc = setresuid(uid, uid, uid);
|
if (setresuid(uid, uid, uid) == -1) {
|
||||||
if (rc == -1) {
|
|
||||||
fail_fn(CREATE_ERROR("setresuid(%d) failed: %s", uid, strerror(errno)));
|
fail_fn(CREATE_ERROR("setresuid(%d) failed: %s", uid, strerror(errno)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -666,6 +776,7 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGi
|
|||||||
ALOGE("prctl(PR_GET_DUMPABLE) failed: %s", strerror(errno));
|
ALOGE("prctl(PR_GET_DUMPABLE) failed: %s", strerror(errno));
|
||||||
RuntimeAbort(env, __LINE__, "prctl(PR_GET_DUMPABLE) failed");
|
RuntimeAbort(env, __LINE__, "prctl(PR_GET_DUMPABLE) failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dumpable == 2 && uid >= AID_APP) {
|
if (dumpable == 2 && uid >= AID_APP) {
|
||||||
if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) == -1) {
|
if (prctl(PR_SET_DUMPABLE, 0, 0, 0, 0) == -1) {
|
||||||
ALOGE("prctl(PR_SET_DUMPABLE, 0) failed: %s", strerror(errno));
|
ALOGE("prctl(PR_SET_DUMPABLE, 0) failed: %s", strerror(errno));
|
||||||
@@ -682,7 +793,7 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!SetCapabilities(permittedCapabilities, effectiveCapabilities, permittedCapabilities,
|
if (!SetCapabilities(permitted_capabilities, effective_capabilities, permitted_capabilities,
|
||||||
&error_msg)) {
|
&error_msg)) {
|
||||||
fail_fn(error_msg);
|
fail_fn(error_msg);
|
||||||
}
|
}
|
||||||
@@ -691,41 +802,21 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGi
|
|||||||
fail_fn(error_msg);
|
fail_fn(error_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* se_info_c_str = NULL;
|
const char* se_info_ptr = se_info.has_value() ? se_info.value().c_str() : nullptr;
|
||||||
ScopedUtfChars* se_info = NULL;
|
const char* nice_name_ptr = nice_name.has_value() ? nice_name.value().c_str() : nullptr;
|
||||||
if (java_se_info != NULL) {
|
|
||||||
se_info = new ScopedUtfChars(env, java_se_info);
|
if (selinux_android_setcontext(uid, is_system_server, se_info_ptr, nice_name_ptr) == -1) {
|
||||||
se_info_c_str = se_info->c_str();
|
fail_fn(CREATE_ERROR("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed",
|
||||||
if (se_info_c_str == NULL) {
|
uid, is_system_server, se_info_ptr, nice_name_ptr));
|
||||||
fail_fn("se_info_c_str == NULL");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const char* se_name_c_str = NULL;
|
|
||||||
ScopedUtfChars* se_name = NULL;
|
|
||||||
if (java_se_name != NULL) {
|
|
||||||
se_name = new ScopedUtfChars(env, java_se_name);
|
|
||||||
se_name_c_str = se_name->c_str();
|
|
||||||
if (se_name_c_str == NULL) {
|
|
||||||
fail_fn("se_name_c_str == NULL");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str);
|
|
||||||
if (rc == -1) {
|
|
||||||
fail_fn(CREATE_ERROR("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
|
|
||||||
is_system_server, se_info_c_str, se_name_c_str));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make it easier to debug audit logs by setting the main thread's name to the
|
// Make it easier to debug audit logs by setting the main thread's name to the
|
||||||
// nice name rather than "app_process".
|
// nice name rather than "app_process".
|
||||||
if (se_name_c_str == NULL && is_system_server) {
|
if (nice_name.has_value()) {
|
||||||
se_name_c_str = "system_server";
|
SetThreadName(nice_name.value().c_str());
|
||||||
|
} else if (is_system_server) {
|
||||||
|
SetThreadName("system_server");
|
||||||
}
|
}
|
||||||
if (se_name_c_str != NULL) {
|
|
||||||
SetThreadName(se_name_c_str);
|
|
||||||
}
|
|
||||||
|
|
||||||
delete se_info;
|
|
||||||
delete se_name;
|
|
||||||
|
|
||||||
// Unset the SIGCHLD handler, but keep ignoring SIGHUP (rationale in SetSignalHandlers).
|
// Unset the SIGCHLD handler, but keep ignoring SIGHUP (rationale in SetSignalHandlers).
|
||||||
UnsetChldSignalHandler();
|
UnsetChldSignalHandler();
|
||||||
@@ -743,102 +834,13 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGi
|
|||||||
}
|
}
|
||||||
|
|
||||||
env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
|
env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
|
||||||
is_system_server, is_child_zygote, instructionSet);
|
is_system_server, is_child_zygote, managed_instruction_set);
|
||||||
|
|
||||||
if (env->ExceptionCheck()) {
|
if (env->ExceptionCheck()) {
|
||||||
fail_fn("Error calling post fork hooks.");
|
fail_fn("Error calling post fork hooks.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Utility routine to fork zygote and specialize the child process.
|
|
||||||
static pid_t ForkCommon(JNIEnv* env, jstring java_se_name, bool is_system_server,
|
|
||||||
jintArray fdsToClose, jintArray fdsToIgnore) {
|
|
||||||
SetSignalHandlers();
|
|
||||||
|
|
||||||
// Block SIGCHLD prior to fork.
|
|
||||||
sigset_t sigchld;
|
|
||||||
sigemptyset(&sigchld);
|
|
||||||
sigaddset(&sigchld, SIGCHLD);
|
|
||||||
|
|
||||||
auto fail_fn = [env, java_se_name, is_system_server](const std::string& msg)
|
|
||||||
__attribute__ ((noreturn)) {
|
|
||||||
const char* se_name_c_str = nullptr;
|
|
||||||
std::unique_ptr<ScopedUtfChars> se_name;
|
|
||||||
if (java_se_name != nullptr) {
|
|
||||||
se_name.reset(new ScopedUtfChars(env, java_se_name));
|
|
||||||
se_name_c_str = se_name->c_str();
|
|
||||||
}
|
|
||||||
if (se_name_c_str == nullptr && is_system_server) {
|
|
||||||
se_name_c_str = "system_server";
|
|
||||||
}
|
|
||||||
const std::string& error_msg = (se_name_c_str == nullptr)
|
|
||||||
? msg
|
|
||||||
: StringPrintf("(%s) %s", se_name_c_str, msg.c_str());
|
|
||||||
env->FatalError(error_msg.c_str());
|
|
||||||
__builtin_unreachable();
|
|
||||||
};
|
|
||||||
|
|
||||||
// Temporarily block SIGCHLD during forks. The SIGCHLD handler might
|
|
||||||
// log, which would result in the logging FDs we close being reopened.
|
|
||||||
// This would cause failures because the FDs are not whitelisted.
|
|
||||||
//
|
|
||||||
// Note that the zygote process is single threaded at this point.
|
|
||||||
if (sigprocmask(SIG_BLOCK, &sigchld, nullptr) == -1) {
|
|
||||||
fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close any logging related FDs before we start evaluating the list of
|
|
||||||
// file descriptors.
|
|
||||||
__android_log_close();
|
|
||||||
stats_log_close();
|
|
||||||
|
|
||||||
std::string error_msg;
|
|
||||||
|
|
||||||
// If this is the first fork for this zygote, create the open FD table.
|
|
||||||
// If it isn't, we just need to check whether the list of open files has
|
|
||||||
// changed (and it shouldn't in the normal case).
|
|
||||||
std::vector<int> fds_to_ignore;
|
|
||||||
if (!FillFileDescriptorVector(env, fdsToIgnore, &fds_to_ignore, &error_msg)) {
|
|
||||||
fail_fn(error_msg);
|
|
||||||
}
|
|
||||||
if (gOpenFdTable == NULL) {
|
|
||||||
gOpenFdTable = FileDescriptorTable::Create(fds_to_ignore, &error_msg);
|
|
||||||
if (gOpenFdTable == NULL) {
|
|
||||||
fail_fn(error_msg);
|
|
||||||
}
|
|
||||||
} else if (!gOpenFdTable->Restat(fds_to_ignore, &error_msg)) {
|
|
||||||
fail_fn(error_msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
android_fdsan_error_level fdsan_error_level = android_fdsan_get_error_level();
|
|
||||||
|
|
||||||
pid_t pid = fork();
|
|
||||||
|
|
||||||
if (pid == 0) {
|
|
||||||
// The child process.
|
|
||||||
PreApplicationInit();
|
|
||||||
|
|
||||||
// Clean up any descriptors which must be closed immediately
|
|
||||||
if (!DetachDescriptors(env, fdsToClose, &error_msg)) {
|
|
||||||
fail_fn(error_msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Re-open all remaining open file descriptors so that they aren't shared
|
|
||||||
// with the zygote across a fork.
|
|
||||||
if (!gOpenFdTable->ReopenOrDetach(&error_msg)) {
|
|
||||||
fail_fn(error_msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Turn fdsan back on.
|
|
||||||
android_fdsan_set_error_level(fdsan_error_level);
|
|
||||||
}
|
|
||||||
|
|
||||||
// We blocked SIGCHLD prior to a fork, we unblock it here.
|
|
||||||
if (sigprocmask(SIG_UNBLOCK, &sigchld, nullptr) == -1) {
|
|
||||||
fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)));
|
|
||||||
}
|
|
||||||
return pid;
|
|
||||||
}
|
|
||||||
|
|
||||||
static uint64_t GetEffectiveCapabilityMask(JNIEnv* env) {
|
static uint64_t GetEffectiveCapabilityMask(JNIEnv* env) {
|
||||||
__user_cap_header_struct capheader;
|
__user_cap_header_struct capheader;
|
||||||
memset(&capheader, 0, sizeof(capheader));
|
memset(&capheader, 0, sizeof(capheader));
|
||||||
@@ -851,16 +853,82 @@ static uint64_t GetEffectiveCapabilityMask(JNIEnv* env) {
|
|||||||
RuntimeAbort(env, __LINE__, "capget failed");
|
RuntimeAbort(env, __LINE__, "capget failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
return capdata[0].effective |
|
return capdata[0].effective | (static_cast<uint64_t>(capdata[1].effective) << 32);
|
||||||
(static_cast<uint64_t>(capdata[1].effective) << 32);
|
}
|
||||||
|
|
||||||
|
static jlong CalculateCapabilities(JNIEnv* env, jint uid, jint gid, jintArray gids,
|
||||||
|
bool is_child_zygote) {
|
||||||
|
jlong capabilities = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Grant the following capabilities to the Bluetooth user:
|
||||||
|
* - CAP_WAKE_ALARM
|
||||||
|
* - CAP_NET_RAW
|
||||||
|
* - CAP_NET_BIND_SERVICE (for DHCP client functionality)
|
||||||
|
* - CAP_SYS_NICE (for setting RT priority for audio-related threads)
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (multiuser_get_app_id(uid) == AID_BLUETOOTH) {
|
||||||
|
capabilities |= (1LL << CAP_WAKE_ALARM);
|
||||||
|
capabilities |= (1LL << CAP_NET_RAW);
|
||||||
|
capabilities |= (1LL << CAP_NET_BIND_SERVICE);
|
||||||
|
capabilities |= (1LL << CAP_SYS_NICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 gid_index = gids_num; --gids_num >= 0;) {
|
||||||
|
if (native_gid_proxy[gid_index] == AID_WAKELOCK) {
|
||||||
|
gid_wakelock_found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gid_wakelock_found) {
|
||||||
|
capabilities |= (1LL << CAP_BLOCK_SUSPEND);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Grant child Zygote processes the following capabilities:
|
||||||
|
* - CAP_SETUID (change UID of child processes)
|
||||||
|
* - CAP_SETGID (change GID of child processes)
|
||||||
|
* - CAP_SETPCAP (change capabilities of child processes)
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (is_child_zygote) {
|
||||||
|
capabilities |= (1LL << CAP_SETUID);
|
||||||
|
capabilities |= (1LL << CAP_SETGID);
|
||||||
|
capabilities |= (1LL << CAP_SETPCAP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Containers run without some capabilities, so drop any caps that are not
|
||||||
|
* available.
|
||||||
|
*/
|
||||||
|
|
||||||
|
return capabilities & GetEffectiveCapabilityMask(env);
|
||||||
}
|
}
|
||||||
} // anonymous namespace
|
} // anonymous namespace
|
||||||
|
|
||||||
namespace android {
|
namespace android {
|
||||||
|
|
||||||
static void com_android_internal_os_Zygote_nativeSecurityInit(JNIEnv*, jclass) {
|
static void com_android_internal_os_Zygote_nativeSecurityInit(JNIEnv*, jclass) {
|
||||||
// security_getenforce is not allowed on app process. Initialize and cache the value before
|
// security_getenforce is not allowed on app process. Initialize and cache
|
||||||
// zygote forks.
|
// the value before zygote forks.
|
||||||
g_is_security_enforced = security_getenforce();
|
g_is_security_enforced = security_getenforce();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -871,76 +939,33 @@ static void com_android_internal_os_Zygote_nativePreApplicationInit(JNIEnv*, jcl
|
|||||||
static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
|
static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
|
||||||
JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
|
JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
|
||||||
jint runtime_flags, jobjectArray rlimits,
|
jint runtime_flags, jobjectArray rlimits,
|
||||||
jint mount_external, jstring se_info, jstring se_name,
|
jint mount_external, jstring se_info, jstring nice_name,
|
||||||
jintArray fdsToClose, jintArray fdsToIgnore, jboolean is_child_zygote,
|
jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote,
|
||||||
jstring instructionSet, jstring appDataDir) {
|
jstring instruction_set, jstring app_data_dir) {
|
||||||
jlong capabilities = 0;
|
jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
|
||||||
|
|
||||||
// Grant CAP_WAKE_ALARM to the Bluetooth process.
|
pid_t pid = ForkCommon(env, false, fds_to_close, fds_to_ignore);
|
||||||
// Additionally, allow bluetooth to open packet sockets so it can start the DHCP client.
|
|
||||||
// Grant CAP_SYS_NICE to allow Bluetooth to set RT priority for
|
|
||||||
// audio-related threads.
|
|
||||||
// TODO: consider making such functionality an RPC to netd.
|
|
||||||
if (multiuser_get_app_id(uid) == AID_BLUETOOTH) {
|
|
||||||
capabilities |= (1LL << CAP_WAKE_ALARM);
|
|
||||||
capabilities |= (1LL << CAP_NET_RAW);
|
|
||||||
capabilities |= (1LL << CAP_NET_BIND_SERVICE);
|
|
||||||
capabilities |= (1LL << CAP_SYS_NICE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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 != NULL) {
|
|
||||||
jsize gids_num = env->GetArrayLength(gids);
|
|
||||||
ScopedIntArrayRO ar(env, gids);
|
|
||||||
if (ar.get() == NULL) {
|
|
||||||
RuntimeAbort(env, __LINE__, "Bad gids array");
|
|
||||||
}
|
|
||||||
for (int i = 0; i < gids_num; i++) {
|
|
||||||
if (ar[i] == AID_WAKELOCK) {
|
|
||||||
gid_wakelock_found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (gid_wakelock_found) {
|
|
||||||
capabilities |= (1LL << CAP_BLOCK_SUSPEND);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If forking a child zygote process, that zygote will need to be able to change
|
|
||||||
// the UID and GID of processes it forks, as well as drop those capabilities.
|
|
||||||
if (is_child_zygote) {
|
|
||||||
capabilities |= (1LL << CAP_SETUID);
|
|
||||||
capabilities |= (1LL << CAP_SETGID);
|
|
||||||
capabilities |= (1LL << CAP_SETPCAP);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Containers run without some capabilities, so drop any caps that are not
|
|
||||||
// available.
|
|
||||||
capabilities &= GetEffectiveCapabilityMask(env);
|
|
||||||
|
|
||||||
pid_t pid = ForkCommon(env, se_name, false, fdsToClose, fdsToIgnore);
|
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
|
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
|
||||||
capabilities, capabilities,
|
capabilities, capabilities,
|
||||||
mount_external, se_info, se_name, false,
|
mount_external, se_info, nice_name, false,
|
||||||
is_child_zygote == JNI_TRUE, instructionSet, appDataDir);
|
is_child_zygote == JNI_TRUE, instruction_set, app_data_dir);
|
||||||
}
|
}
|
||||||
return pid;
|
return pid;
|
||||||
}
|
}
|
||||||
|
|
||||||
static jint com_android_internal_os_Zygote_nativeForkSystemServer(
|
static jint com_android_internal_os_Zygote_nativeForkSystemServer(
|
||||||
JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
|
JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
|
||||||
jint runtime_flags, jobjectArray rlimits, jlong permittedCapabilities,
|
jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities,
|
||||||
jlong effectiveCapabilities) {
|
jlong effective_capabilities) {
|
||||||
pid_t pid = ForkCommon(env, NULL, true, NULL, NULL);
|
pid_t pid = ForkCommon(env, true,
|
||||||
|
/* managed_fds_to_close= */ nullptr,
|
||||||
|
/* managed_fds_to_ignore= */ nullptr);
|
||||||
if (pid == 0) {
|
if (pid == 0) {
|
||||||
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
|
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
|
||||||
permittedCapabilities, effectiveCapabilities,
|
permitted_capabilities, effective_capabilities,
|
||||||
MOUNT_EXTERNAL_DEFAULT, NULL, NULL, true,
|
MOUNT_EXTERNAL_DEFAULT, nullptr, nullptr, true,
|
||||||
false, NULL, NULL);
|
false, nullptr, nullptr);
|
||||||
} else if (pid > 0) {
|
} else if (pid > 0) {
|
||||||
// The zygote process checks whether the child process has died or not.
|
// The zygote process checks whether the child process has died or not.
|
||||||
ALOGI("System server process %d has been created", pid);
|
ALOGI("System server process %d has been created", pid);
|
||||||
@@ -974,7 +999,7 @@ static void com_android_internal_os_Zygote_nativeAllowFileAcrossFork(
|
|||||||
ScopedUtfChars path_native(env, path);
|
ScopedUtfChars path_native(env, path);
|
||||||
const char* path_cstr = path_native.c_str();
|
const char* path_cstr = path_native.c_str();
|
||||||
if (!path_cstr) {
|
if (!path_cstr) {
|
||||||
RuntimeAbort(env, __LINE__, "path_cstr == NULL");
|
RuntimeAbort(env, __LINE__, "path_cstr == nullptr");
|
||||||
}
|
}
|
||||||
FileDescriptorWhitelist::Get()->Allow(path_cstr);
|
FileDescriptorWhitelist::Get()->Allow(path_cstr);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user