summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Chris Wailes <chriswailes@google.com> 2019-01-10 16:55:32 -0800
committer Chris Wailes <chriswailes@google.com> 2019-01-22 16:07:27 -0800
commitaa1c962ded6bfdf0dd4ed2152320f75459311e93 (patch)
treedcdba8136725d5dda3bb980f83e76a0ad049de41
parente54b7b549871bf2f475b04a6ad3a4289d55e02cc (diff)
Implemented native functions and types for blastula management.
This patch adds native support for spawning and managing blastula pools, as well as several code cleanups and modernizations. Changes includes: * A function to fork blastulas * A table for managing blastula-related data * Functions for adding and removing blastula data from the aforementioned table * Switching from NULL to nullptr * Replacing string-passing error handling with a curried failure function * Utility functions for handling managed objects * JNI functions for blastula pool management Change-Id: I12cd9f2c87a2e3c00d64b683edf3631e29a51551 Topic: zygot-prefork Test: make & flash & launch apps & check log for messages Bug: 68253328
-rw-r--r--core/java/com/android/internal/os/Zygote.java47
-rw-r--r--core/jni/com_android_internal_os_Zygote.cpp1102
-rw-r--r--core/jni/fd_utils.cpp273
-rw-r--r--core/jni/fd_utils.h11
4 files changed, 935 insertions, 498 deletions
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index 705bae4f24ef..b28f4cdd5a87 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -126,7 +126,7 @@ public final class Zygote {
private Zygote() {}
/** Called for some security initialization before any fork. */
- native static void nativeSecurityInit();
+ static native void nativeSecurityInit();
/**
* Forks a new VM instance. The current VM must have been started
@@ -170,9 +170,9 @@ public final class Zygote {
// Resets nice priority for zygote process.
resetNicePriority();
int pid = nativeForkAndSpecialize(
- uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
- fdsToIgnore, startChildZygote, instructionSet, appDataDir, packageName,
- packagesForUid, visibleVolIds);
+ uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
+ fdsToIgnore, startChildZygote, instructionSet, appDataDir, packageName,
+ packagesForUid, visibleVolIds);
// Enable tracing as soon as possible for the child process.
if (pid == 0) {
Trace.setTracingEnabled(true, runtimeFlags);
@@ -184,15 +184,20 @@ public final class Zygote {
return pid;
}
- native private static int nativeForkAndSpecialize(int uid, int gid, int[] gids,int runtimeFlags,
- int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
- int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir,
- String packageName, String[] packagesForUid, String[] visibleVolIds);
+ private static native int nativeForkAndSpecialize(int uid, int gid, int[] gids,
+ int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
+ int[] fdsToClose, int[] fdsToIgnore, boolean startChildZygote, String instructionSet,
+ String appDataDir, String packageName, String[] packagesForUid, String[] visibleVolIds);
+
+ private static native void nativeSpecializeBlastula(int uid, int gid, int[] gids,
+ int runtimeFlags, int[][] rlimits, int mountExternal, String seInfo, String niceName,
+ boolean startChildZygote, String instructionSet, String appDataDir, String packageName,
+ String[] packagesForUid, String[] visibleVolIds);
/**
* Called to do any initialization before starting an application.
*/
- native static void nativePreApplicationInit();
+ static native void nativePreApplicationInit();
/**
* Special method to start the system server process. In addition to the
@@ -223,7 +228,8 @@ public final class Zygote {
// Resets nice priority for zygote process.
resetNicePriority();
int pid = nativeForkSystemServer(
- uid, gid, gids, runtimeFlags, rlimits, permittedCapabilities, effectiveCapabilities);
+ uid, gid, gids, runtimeFlags, rlimits,
+ permittedCapabilities, effectiveCapabilities);
// Enable tracing as soon as we enter the system_server.
if (pid == 0) {
Trace.setTracingEnabled(true, runtimeFlags);
@@ -232,13 +238,13 @@ public final class Zygote {
return pid;
}
- native private static int nativeForkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
+ private static native int nativeForkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, long permittedCapabilities, long effectiveCapabilities);
/**
* Lets children of the zygote inherit open file descriptors to this path.
*/
- native protected static void nativeAllowFileAcrossFork(String path);
+ protected static native void nativeAllowFileAcrossFork(String path);
/**
* Installs a seccomp filter that limits setresuid()/setresgid() to the passed-in range
@@ -251,7 +257,22 @@ public final class Zygote {
* Zygote unmount storage space on initializing.
* This method is called once.
*/
- native protected static void nativeUnmountStorageOnInit();
+ protected static native void nativeUnmountStorageOnInit();
+
+ protected static native void nativeGetSocketFDs(boolean isPrimary);
+
+ private static native int nativeGetBlastulaPoolCount();
+
+ private static native int nativeGetBlastulaPoolEventFD();
+
+ private static native int nativeForkBlastula(int readPipeFD,
+ int writePipeFD,
+ int[] sessionSocketRawFDs);
+
+ private static native int[] nativeGetBlastulaPipeFDs();
+
+ private static native boolean nativeRemoveBlastulaTableEntry(int blastulaPID);
+
private static void callPostForkSystemServerHooks() {
// SystemServer specific post fork hooks run before child post fork hooks.
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index e81b6276ef5f..702800827f26 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -29,13 +29,17 @@
#include <sys/mount.h>
#include <linux/fs.h>
+#include <array>
+#include <atomic>
#include <functional>
#include <list>
#include <optional>
#include <sstream>
#include <string>
+#include <string_view>
#include <android/fdsan.h>
+#include <arpa/inet.h>
#include <fcntl.h>
#include <grp.h>
#include <inttypes.h>
@@ -46,9 +50,11 @@
#include <stdlib.h>
#include <sys/capability.h>
#include <sys/cdefs.h>
+#include <sys/eventfd.h>
#include <sys/personality.h>
#include <sys/prctl.h>
#include <sys/resource.h>
+#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
@@ -56,10 +62,11 @@
#include <sys/wait.h>
#include <unistd.h>
-#include "android-base/logging.h"
+#include <android-base/logging.h>
#include <android-base/properties.h>
#include <android-base/file.h>
#include <android-base/stringprintf.h>
+#include <android-base/unique_fd.h>
#include <cutils/fs.h>
#include <cutils/multiuser.h>
#include <private/android_filesystem_config.h>
@@ -81,6 +88,9 @@
namespace {
+// TODO (chriswailes): Add a function to initialize native Zygote data.
+// TODO (chriswailes): Fix mixed indentation style (2 and 4 spaces).
+
using namespace std::placeholders;
using android::String8;
@@ -92,6 +102,9 @@ using android::base::GetBoolProperty;
#define CREATE_ERROR(...) StringPrintf("%s:%d: ", __FILE__, __LINE__). \
append(StringPrintf(__VA_ARGS__))
+// This type is duplicated in fd_utils.h
+typedef const std::function<void(std::string)>& fail_fn_t;
+
static pid_t gSystemServerPid = 0;
static const char kIsolatedStorage[] = "persist.sys.isolated_storage";
@@ -103,6 +116,152 @@ static jmethodID gCallPostForkChildHooks;
static bool g_is_security_enforced = true;
+/**
+ * The maximum number of characters (not including a null terminator) that a
+ * process name may contain.
+ */
+static constexpr size_t MAX_NAME_LENGTH = 15;
+
+/**
+ * The prefix string for environmental variables storing socket FDs created by
+ * init.
+ */
+
+static constexpr std::string_view ANDROID_SOCKET_PREFIX("ANDROID_SOCKET_");
+
+/**
+ * The file descriptor for the Zygote socket opened by init.
+ */
+
+static int gZygoteSocketFD = -1;
+
+/**
+ * The file descriptor for the Blastula pool socket opened by init.
+ */
+
+static int gBlastulaPoolSocketFD = -1;
+
+/**
+ * The number of Blastulas currently in this Zygote's pool.
+ */
+static std::atomic_uint32_t gBlastulaPoolCount = 0;
+
+/**
+ * Event file descriptor used to communicate reaped blastulas to the
+ * ZygoteServer.
+ */
+static int gBlastulaPoolEventFD = -1;
+
+/**
+ * The maximum value that the gBlastulaPoolMax variable may take. This value
+ * is a mirror of Zygote.BLASTULA_POOL_MAX_LIMIT
+ */
+static constexpr int BLASTULA_POOL_MAX_LIMIT = 10;
+
+/**
+ * A helper class containing accounting information for Blastulas.
+ */
+class BlastulaTableEntry {
+ public:
+ struct EntryStorage {
+ int32_t pid;
+ int32_t read_pipe_fd;
+
+ bool operator!=(const EntryStorage& other) {
+ return pid != other.pid || read_pipe_fd != other.read_pipe_fd;
+ }
+ };
+
+ private:
+ static constexpr EntryStorage INVALID_ENTRY_VALUE = {-1, -1};
+
+ std::atomic<EntryStorage> mStorage;
+ static_assert(decltype(mStorage)::is_always_lock_free);
+
+ public:
+ constexpr BlastulaTableEntry() : mStorage(INVALID_ENTRY_VALUE) {}
+
+ /**
+ * If the provided PID matches the one stored in this entry, the entry will
+ * be invalidated and the associated file descriptor will be closed. If the
+ * PIDs don't match nothing will happen.
+ *
+ * @param pid The ID of the process who's entry we want to clear.
+ * @return True if the entry was cleared; false otherwise
+ */
+ bool ClearForPID(int32_t pid) {
+ EntryStorage storage = mStorage.load();
+
+ if (storage.pid == pid) {
+ /*
+ * There are three possible outcomes from this compare-and-exchange:
+ * 1) It succeeds, in which case we close the FD
+ * 2) It fails and the new value is INVALID_ENTRY_VALUE, in which case
+ * the entry has already been cleared.
+ * 3) It fails and the new value isn't INVALID_ENTRY_VALUE, in which
+ * case the entry has already been cleared and re-used.
+ *
+ * In all three cases the goal of the caller has been met and we can
+ * return true.
+ */
+ if (mStorage.compare_exchange_strong(storage, INVALID_ENTRY_VALUE)) {
+ close(storage.read_pipe_fd);
+ }
+
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * @return A copy of the data stored in this entry.
+ */
+ std::optional<EntryStorage> GetValues() {
+ EntryStorage storage = mStorage.load();
+
+ if (storage != INVALID_ENTRY_VALUE) {
+ return storage;
+ } else {
+ return std::nullopt;
+ }
+ }
+
+ /**
+ * Sets the entry to the given values if it is currently invalid.
+ *
+ * @param pid The process ID for the new entry.
+ * @param read_pipe_fd The read end of the blastula control pipe for this
+ * process.
+ * @return True if the entry was set; false otherwise.
+ */
+ bool SetIfInvalid(int32_t pid, int32_t read_pipe_fd) {
+ EntryStorage new_value_storage;
+
+ new_value_storage.pid = pid;
+ new_value_storage.read_pipe_fd = read_pipe_fd;
+
+ EntryStorage expected = INVALID_ENTRY_VALUE;
+
+ return mStorage.compare_exchange_strong(expected, new_value_storage);
+ }
+};
+
+/**
+ * A table containing information about the Blastulas currently in the pool.
+ *
+ * Multiple threads may be attempting to modify the table, either from the
+ * signal handler or from the ZygoteServer poll loop. Atomic loads/stores in
+ * the BlastulaTableEntry class prevent data races during these concurrent
+ * operations.
+ */
+static std::array<BlastulaTableEntry, BLASTULA_POOL_MAX_LIMIT> gBlastulaTable;
+
+/**
+ * The list of open zygote file descriptors.
+ */
+static FileDescriptorTable* gOpenFdTable = nullptr;
+
// Must match values in com.android.internal.os.Zygote.
enum MountExternalKind {
MOUNT_EXTERNAL_NONE = 0,
@@ -119,6 +278,9 @@ enum RuntimeFlags : uint32_t {
DEBUG_ENABLE_JDWP = 1,
};
+// Forward declaration so we don't have to move the signal handler.
+static bool RemoveBlastulaTableEntry(pid_t blastula_pid);
+
static void RuntimeAbort(JNIEnv* env, int line, const char* msg) {
std::ostringstream oss;
oss << __FILE__ << ":" << line << ": " << msg;
@@ -129,6 +291,7 @@ static void RuntimeAbort(JNIEnv* env, int line, const char* msg) {
static void SigChldHandler(int /*signal_number*/) {
pid_t pid;
int status;
+ int64_t blastulas_removed = 0;
// It's necessary to save and restore the errno during this function.
// Since errno is stored per thread, changing it here modifies the errno
@@ -162,6 +325,11 @@ static void SigChldHandler(int /*signal_number*/) {
ALOGE("Exit zygote because system server (%d) has terminated", pid);
kill(getpid(), SIGKILL);
}
+
+ // Check to see if the PID is in the blastula pool and remove it if it is.
+ if (RemoveBlastulaTableEntry(pid)) {
+ ++blastulas_removed;
+ }
}
// Note that we shouldn't consider ECHILD an error because
@@ -170,6 +338,15 @@ static void SigChldHandler(int /*signal_number*/) {
ALOGW("Zygote SIGCHLD error in waitpid: %s", strerror(errno));
}
+ if (blastulas_removed > 0) {
+ if (write(gBlastulaPoolEventFD, &blastulas_removed, sizeof(blastulas_removed)) == -1) {
+ // If this write fails something went terribly wrong. We will now kill
+ // the zygote and let the system bring it back up.
+ ALOGE("Zygote failed to write to blastula pool event FD: %s", strerror(errno));
+ kill(getpid(), SIGKILL);
+ }
+ }
+
errno = saved_errno;
}
@@ -194,13 +371,13 @@ static void SetSignalHandlers() {
struct sigaction sig_chld = {};
sig_chld.sa_handler = SigChldHandler;
- if (sigaction(SIGCHLD, &sig_chld, NULL) < 0) {
+ if (sigaction(SIGCHLD, &sig_chld, nullptr) < 0) {
ALOGW("Error setting SIGCHLD handler: %s", strerror(errno));
}
struct sigaction sig_hup = {};
sig_hup.sa_handler = SIG_IGN;
- if (sigaction(SIGHUP, &sig_hup, NULL) < 0) {
+ if (sigaction(SIGHUP, &sig_hup, nullptr) < 0) {
ALOGW("Error setting SIGHUP handler: %s", strerror(errno));
}
}
@@ -211,64 +388,57 @@ static void UnsetChldSignalHandler() {
memset(&sa, 0, sizeof(sa));
sa.sa_handler = SIG_DFL;
- if (sigaction(SIGCHLD, &sa, NULL) < 0) {
+ if (sigaction(SIGCHLD, &sa, nullptr) < 0) {
ALOGW("Error unsetting SIGCHLD handler: %s", strerror(errno));
}
}
// Calls POSIX setgroups() using the int[] object as an argument.
-// A NULL argument is tolerated.
-static bool SetGids(JNIEnv* env, jintArray javaGids, std::string* error_msg) {
- if (javaGids == NULL) {
- return true;
+// A nullptr argument is tolerated.
+static void SetGids(JNIEnv* env, jintArray managed_gids, fail_fn_t fail_fn) {
+ if (managed_gids == nullptr) {
+ return;
}
- ScopedIntArrayRO gids(env, javaGids);
- if (gids.get() == NULL) {
- *error_msg = CREATE_ERROR("Getting gids int array failed");
- return false;
- }
- int rc = setgroups(gids.size(), reinterpret_cast<const gid_t*>(&gids[0]));
- if (rc == -1) {
- *error_msg = CREATE_ERROR("setgroups failed: %s, gids.size=%zu", strerror(errno), gids.size());
- return false;
+ ScopedIntArrayRO gids(env, managed_gids);
+ if (gids.get() == nullptr) {
+ fail_fn(CREATE_ERROR("Getting gids int array failed"));
}
- return true;
+ if (setgroups(gids.size(), reinterpret_cast<const gid_t*>(&gids[0])) == -1) {
+ fail_fn(CREATE_ERROR("setgroups failed: %s, gids.size=%zu", strerror(errno), gids.size()));
+ }
}
// Sets the resource limits via setrlimit(2) for the values in the
// two-dimensional array of integers that's passed in. The second dimension
-// contains a tuple of length 3: (resource, rlim_cur, rlim_max). NULL is
+// contains a tuple of length 3: (resource, rlim_cur, rlim_max). nullptr is
// treated as an empty array.
-static bool SetRLimits(JNIEnv* env, jobjectArray javaRlimits, std::string* error_msg) {
- if (javaRlimits == NULL) {
- return true;
+static void SetRLimits(JNIEnv* env, jobjectArray managed_rlimits, fail_fn_t fail_fn) {
+ if (managed_rlimits == nullptr) {
+ return;
}
rlimit rlim;
memset(&rlim, 0, sizeof(rlim));
- for (int i = 0; i < env->GetArrayLength(javaRlimits); ++i) {
- ScopedLocalRef<jobject> javaRlimitObject(env, env->GetObjectArrayElement(javaRlimits, i));
- ScopedIntArrayRO javaRlimit(env, reinterpret_cast<jintArray>(javaRlimitObject.get()));
- if (javaRlimit.size() != 3) {
- *error_msg = CREATE_ERROR("rlimits array must have a second dimension of size 3");
- return false;
+ for (int i = 0; i < env->GetArrayLength(managed_rlimits); ++i) {
+ ScopedLocalRef<jobject>
+ managed_rlimit_object(env, env->GetObjectArrayElement(managed_rlimits, i));
+ ScopedIntArrayRO rlimit_handle(env, reinterpret_cast<jintArray>(managed_rlimit_object.get()));
+
+ if (rlimit_handle.size() != 3) {
+ fail_fn(CREATE_ERROR("rlimits array must have a second dimension of size 3"));
}
- rlim.rlim_cur = javaRlimit[1];
- rlim.rlim_max = javaRlimit[2];
+ rlim.rlim_cur = rlimit_handle[1];
+ rlim.rlim_max = rlimit_handle[2];
- int rc = setrlimit(javaRlimit[0], &rlim);
- if (rc == -1) {
- *error_msg = CREATE_ERROR("setrlimit(%d, {%ld, %ld}) failed", javaRlimit[0], rlim.rlim_cur,
- rlim.rlim_max);
- return false;
+ if (setrlimit(rlimit_handle[0], &rlim) == -1) {
+ fail_fn(CREATE_ERROR("setrlimit(%d, {%ld, %ld}) failed",
+ rlimit_handle[0], rlim.rlim_cur, rlim.rlim_max));
}
}
-
- return true;
}
static void EnableDebugger() {
@@ -335,32 +505,26 @@ static void SetUpSeccompFilter(uid_t uid, bool is_child_zygote) {
}
}
-static bool EnableKeepCapabilities(std::string* error_msg) {
- int rc = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
- if (rc == -1) {
- *error_msg = CREATE_ERROR("prctl(PR_SET_KEEPCAPS) failed: %s", strerror(errno));
- return false;
+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)));
}
- return true;
}
-static bool DropCapabilitiesBoundingSet(std::string* error_msg) {
- for (int i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) {
- int rc = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
- if (rc == -1) {
+static void DropCapabilitiesBoundingSet(fail_fn_t fail_fn) {
+ for (int i = 0; prctl(PR_CAPBSET_READ, i, 0, 0, 0) >= 0; i++) {;
+ if (prctl(PR_CAPBSET_DROP, i, 0, 0, 0) == -1) {
if (errno == EINVAL) {
ALOGE("prctl(PR_CAPBSET_DROP) failed with EINVAL. Please verify "
"your kernel is compiled with file capabilities support");
} else {
- *error_msg = CREATE_ERROR("prctl(PR_CAPBSET_DROP, %d) failed: %s", i, strerror(errno));
- return false;
+ fail_fn(CREATE_ERROR("prctl(PR_CAPBSET_DROP, %d) failed: %s", i, strerror(errno)));
}
}
}
- return true;
}
-static bool SetInheritable(uint64_t inheritable, std::string* error_msg) {
+static void SetInheritable(uint64_t inheritable, fail_fn_t fail_fn) {
__user_cap_header_struct capheader;
memset(&capheader, 0, sizeof(capheader));
capheader.version = _LINUX_CAPABILITY_VERSION_3;
@@ -368,23 +532,19 @@ static bool SetInheritable(uint64_t inheritable, std::string* error_msg) {
__user_cap_data_struct capdata[2];
if (capget(&capheader, &capdata[0]) == -1) {
- *error_msg = CREATE_ERROR("capget failed: %s", strerror(errno));
- return false;
+ fail_fn(CREATE_ERROR("capget failed: %s", strerror(errno)));
}
capdata[0].inheritable = inheritable;
capdata[1].inheritable = inheritable >> 32;
if (capset(&capheader, &capdata[0]) == -1) {
- *error_msg = CREATE_ERROR("capset(inh=%" PRIx64 ") failed: %s", inheritable, strerror(errno));
- return false;
+ fail_fn(CREATE_ERROR("capset(inh=%" PRIx64 ") failed: %s", inheritable, strerror(errno)));
}
-
- return true;
}
-static bool SetCapabilities(uint64_t permitted, uint64_t effective, uint64_t inheritable,
- std::string* error_msg) {
+static void SetCapabilities(uint64_t permitted, uint64_t effective, uint64_t inheritable,
+ fail_fn_t fail_fn) {
__user_cap_header_struct capheader;
memset(&capheader, 0, sizeof(capheader));
capheader.version = _LINUX_CAPABILITY_VERSION_3;
@@ -400,27 +560,23 @@ static bool SetCapabilities(uint64_t permitted, uint64_t effective, uint64_t inh
capdata[1].inheritable = inheritable >> 32;
if (capset(&capheader, &capdata[0]) == -1) {
- *error_msg = CREATE_ERROR("capset(perm=%" PRIx64 ", eff=%" PRIx64 ", inh=%" PRIx64 ") "
- "failed: %s", permitted, effective, inheritable, strerror(errno));
- return false;
+ fail_fn(CREATE_ERROR("capset(perm=%" PRIx64 ", eff=%" PRIx64 ", inh=%" PRIx64 ") "
+ "failed: %s", permitted, effective, inheritable, strerror(errno)));
}
- return true;
}
-static bool SetSchedulerPolicy(std::string* error_msg) {
+static void SetSchedulerPolicy(fail_fn_t fail_fn) {
errno = -set_sched_policy(0, SP_DEFAULT);
if (errno != 0) {
- *error_msg = CREATE_ERROR("set_sched_policy(0, SP_DEFAULT) failed: %s", strerror(errno));
- return false;
+ fail_fn(CREATE_ERROR("set_sched_policy(0, SP_DEFAULT) failed: %s", strerror(errno)));
}
- return true;
}
static int UnmountTree(const char* path) {
size_t path_len = strlen(path);
FILE* fp = setmntent("/proc/mounts", "r");
- if (fp == NULL) {
+ if (fp == nullptr) {
ALOGE("Error opening /proc/mounts: %s", strerror(errno));
return -errno;
}
@@ -429,7 +585,7 @@ static int UnmountTree(const char* path) {
// reverse order to give us the best chance of success.
std::list<std::string> toUnmount;
mntent* mentry;
- while ((mentry = getmntent(fp)) != NULL) {
+ while ((mentry = getmntent(fp)) != nullptr) {
if (strncmp(mentry->mnt_dir, path, path_len) == 0) {
toUnmount.push_front(std::string(mentry->mnt_dir));
}
@@ -444,56 +600,55 @@ static int UnmountTree(const char* path) {
return 0;
}
-static bool createPkgSandbox(uid_t uid, const std::string& package_name, std::string* error_msg) {
+static void CreatePkgSandbox(uid_t uid, const std::string& package_name, fail_fn_t fail_fn) {
// Create /mnt/user/0/package/<package-name>
userid_t user_id = multiuser_get_user_id(uid);
std::string pkg_sandbox_dir = StringPrintf("/mnt/user/%d", user_id);
if (fs_prepare_dir(pkg_sandbox_dir.c_str(), 0751, AID_ROOT, AID_ROOT) != 0) {
- *error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", pkg_sandbox_dir.c_str());
- return false;
+ fail_fn(CREATE_ERROR("fs_prepare_dir failed on %s", pkg_sandbox_dir.c_str()));
}
+
StringAppendF(&pkg_sandbox_dir, "/package");
if (fs_prepare_dir(pkg_sandbox_dir.c_str(), 0700, AID_ROOT, AID_ROOT) != 0) {
- *error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", pkg_sandbox_dir.c_str());
- return false;
+ fail_fn(CREATE_ERROR("fs_prepare_dir failed on %s", pkg_sandbox_dir.c_str()));
}
+
StringAppendF(&pkg_sandbox_dir, "/%s", package_name.c_str());
if (fs_prepare_dir(pkg_sandbox_dir.c_str(), 0755, uid, uid) != 0) {
- *error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", pkg_sandbox_dir.c_str());
- return false;
+ fail_fn(CREATE_ERROR("fs_prepare_dir failed on %s", pkg_sandbox_dir.c_str()));
}
- return true;
}
-static bool bindMount(const std::string& sourceDir, const std::string& targetDir,
- std::string* error_msg) {
- if (TEMP_FAILURE_RETRY(mount(sourceDir.c_str(), targetDir.c_str(),
- nullptr, MS_BIND | MS_REC, nullptr)) == -1) {
- *error_msg = CREATE_ERROR("Failed to mount %s to %s: %s",
- sourceDir.c_str(), targetDir.c_str(), strerror(errno));
- return false;
+static void BindMount(const std::string& sourceDir, const std::string& targetDir,
+ fail_fn_t fail_fn) {
+ if (TEMP_FAILURE_RETRY(mount(sourceDir.c_str(), targetDir.c_str(), nullptr,
+ MS_BIND | MS_REC, nullptr)) == -1) {
+ fail_fn(CREATE_ERROR("Failed to mount %s to %s: %s",
+ sourceDir.c_str(), targetDir.c_str(), strerror(errno)));
}
- if (TEMP_FAILURE_RETRY(mount(nullptr, targetDir.c_str(),
- nullptr, MS_SLAVE | MS_REC, nullptr)) == -1) {
- *error_msg = CREATE_ERROR("Failed to set MS_SLAVE for %s", targetDir.c_str());
- return false;
+
+ if (TEMP_FAILURE_RETRY(mount(nullptr, targetDir.c_str(), nullptr,
+ MS_SLAVE | MS_REC, nullptr)) == -1) {
+ fail_fn(CREATE_ERROR("Failed to set MS_SLAVE for %s", targetDir.c_str()));
}
- return true;
}
-static bool mountPkgSpecificDir(const std::string& mntSourceRoot,
- const std::string& mntTargetRoot, const std::string& packageName,
- const char* dirName, std::string* error_msg) {
+static void MountPkgSpecificDir(const std::string& mntSourceRoot,
+ const std::string& mntTargetRoot,
+ const std::string& packageName,
+ const char* dirName,
+ fail_fn_t fail_fn) {
std::string mntSourceDir = StringPrintf("%s/Android/%s/%s",
mntSourceRoot.c_str(), dirName, packageName.c_str());
std::string mntTargetDir = StringPrintf("%s/Android/%s/%s",
mntTargetRoot.c_str(), dirName, packageName.c_str());
- return bindMount(mntSourceDir, mntTargetDir, error_msg);
+
+ BindMount(mntSourceDir, mntTargetDir, fail_fn);
}
-static bool preparePkgSpecificDirs(const std::vector<std::string>& packageNames,
- const std::vector<std::string>& volumeLabels, bool mountAllObbs,
- userid_t userId, std::string* error_msg) {
+static void PreparePkgSpecificDirs(const std::vector<std::string>& packageNames,
+ const std::vector<std::string>& volumeLabels,
+ bool mountAllObbs, userid_t userId, fail_fn_t fail_fn) {
for (auto& label : volumeLabels) {
std::string mntSource = StringPrintf("/mnt/runtime/write/%s", label.c_str());
std::string mntTarget = StringPrintf("/storage/%s", label.c_str());
@@ -501,28 +656,29 @@ static bool preparePkgSpecificDirs(const std::vector<std::string>& packageNames,
StringAppendF(&mntSource, "/%d", userId);
StringAppendF(&mntTarget, "/%d", userId);
}
+
for (auto& package : packageNames) {
- mountPkgSpecificDir(mntSource, mntTarget, package, "data", error_msg);
- mountPkgSpecificDir(mntSource, mntTarget, package, "media", error_msg);
+ MountPkgSpecificDir(mntSource, mntTarget, package, "data", fail_fn);
+ MountPkgSpecificDir(mntSource, mntTarget, package, "media", fail_fn);
if (!mountAllObbs) {
- mountPkgSpecificDir(mntSource, mntTarget, package, "obb", error_msg);
+ MountPkgSpecificDir(mntSource, mntTarget, package, "obb", fail_fn);
}
}
+
if (mountAllObbs) {
StringAppendF(&mntSource, "/Android/obb");
StringAppendF(&mntTarget, "/Android/obb");
- bindMount(mntSource, mntTarget, error_msg);
+ BindMount(mntSource, mntTarget, fail_fn);
}
}
- return true;
}
// Create a private mount namespace and bind mount appropriate emulated
// storage for the given user.
-static bool MountEmulatedStorage(uid_t uid, jint mount_mode,
- bool force_mount_namespace, std::string* error_msg, const std::string& package_name,
+static void MountEmulatedStorage(uid_t uid, jint mount_mode,
+ bool force_mount_namespace, const std::string& package_name,
const std::vector<std::string>& packages_for_uid,
- const std::vector<std::string>& visible_vol_ids) {
+ const std::vector<std::string>& visible_vol_ids, fail_fn_t fail_fn) {
// See storage config details at http://source.android.com/tech/storage/
String8 storageSource;
@@ -534,18 +690,17 @@ static bool MountEmulatedStorage(uid_t uid, jint mount_mode,
storageSource = "/mnt/runtime/write";
} else if (mount_mode == MOUNT_EXTERNAL_NONE && !force_mount_namespace) {
// Sane default of no storage visible
- return true;
+ return;
}
// Create a second private mount namespace for our process
if (unshare(CLONE_NEWNS) == -1) {
- *error_msg = CREATE_ERROR("Failed to unshare(): %s", strerror(errno));
- return false;
+ fail_fn(CREATE_ERROR("Failed to unshare(): %s", strerror(errno)));
}
// Handle force_mount_namespace with MOUNT_EXTERNAL_NONE.
if (mount_mode == MOUNT_EXTERNAL_NONE) {
- return true;
+ return;
}
if (GetBoolProperty(kIsolatedStorageSnapshot, GetBoolProperty(kIsolatedStorage, false))) {
@@ -553,66 +708,64 @@ static bool MountEmulatedStorage(uid_t uid, jint mount_mode,
storageSource = (mount_mode == MOUNT_EXTERNAL_FULL)
? "/mnt/runtime/full" : "/mnt/runtime/write";
if (TEMP_FAILURE_RETRY(mount(storageSource.string(), "/storage",
- NULL, MS_BIND | MS_REC | MS_SLAVE, NULL)) == -1) {
- *error_msg = CREATE_ERROR("Failed to mount %s to /storage: %s",
- storageSource.string(),
- strerror(errno));
- return false;
+ NULL, MS_BIND | MS_REC | MS_SLAVE, NULL)) == -1) {
+ fail_fn(CREATE_ERROR("Failed to mount %s to /storage: %s",
+ storageSource.string(),
+ strerror(errno)));
}
// Mount user-specific symlink helper into place
userid_t user_id = multiuser_get_user_id(uid);
const String8 userSource(String8::format("/mnt/user/%d", user_id));
if (fs_prepare_dir(userSource.string(), 0751, 0, 0) == -1) {
- *error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", userSource.string());
- return false;
+ fail_fn(CREATE_ERROR("fs_prepare_dir failed on %s (%s)",
+ userSource.string(), strerror(errno)));
}
- if (TEMP_FAILURE_RETRY(mount(userSource.string(), "/storage/self",
- NULL, MS_BIND, NULL)) == -1) {
- *error_msg = CREATE_ERROR("Failed to mount %s to /storage/self: %s",
- userSource.string(),
- strerror(errno));
- return false;
+
+ if (TEMP_FAILURE_RETRY(mount(userSource.string(), "/storage/self", nullptr, MS_BIND,
+ nullptr)) == -1) {
+ fail_fn(CREATE_ERROR("Failed to mount %s to /storage/self: %s",
+ userSource.string(),
+ strerror(errno)));
}
} else {
if (package_name.empty()) {
- return true;
+ return;
}
+
userid_t user_id = multiuser_get_user_id(uid);
- std::string pkgSandboxDir = StringPrintf("/mnt/user/%d/package/%s",
- user_id, package_name.c_str());
+ std::string pkgSandboxDir =
+ StringPrintf("/mnt/user/%d/package/%s", user_id, package_name.c_str());
struct stat sb;
bool sandboxAlreadyCreated = true;
if (TEMP_FAILURE_RETRY(lstat(pkgSandboxDir.c_str(), &sb)) == -1) {
if (errno == ENOENT) {
ALOGD("Sandbox not yet created for %s", pkgSandboxDir.c_str());
sandboxAlreadyCreated = false;
- if (!createPkgSandbox(uid, package_name, error_msg)) {
- return false;
- }
+ CreatePkgSandbox(uid, package_name, fail_fn);
} else {
- ALOGE("Failed to lstat %s", pkgSandboxDir.c_str());
- return false;
+ fail_fn(CREATE_ERROR("Failed to lstat %s: %s",
+ pkgSandboxDir.c_str(), strerror(errno)));
}
}
+
if (TEMP_FAILURE_RETRY(mount(pkgSandboxDir.c_str(), "/storage",
- nullptr, MS_BIND | MS_REC | MS_SLAVE, nullptr)) == -1) {
- *error_msg = CREATE_ERROR("Failed to mount %s to /storage: %s",
- pkgSandboxDir.c_str(), strerror(errno));
- return false;
+ nullptr, MS_BIND | MS_REC | MS_SLAVE, nullptr)) == -1) {
+ fail_fn(CREATE_ERROR("Failed to mount %s to /storage: %s",
+ pkgSandboxDir.c_str(), strerror(errno)));
}
+
if (access("/storage/obb_mount", F_OK) == 0) {
if (mount_mode != MOUNT_EXTERNAL_INSTALLER) {
remove("/storage/obb_mount");
}
} else {
if (mount_mode == MOUNT_EXTERNAL_INSTALLER) {
- int fd = TEMP_FAILURE_RETRY(open("/storage/obb_mount",
- O_RDWR | O_CREAT, 0660));
+ int fd =
+ TEMP_FAILURE_RETRY(open("/storage/obb_mount", O_RDWR | O_CREAT, 0660));
if (fd == -1) {
- *error_msg = CREATE_ERROR("Couldn't create /storage/obb_mount: %s",
- strerror(errno));
- return false;
+ fail_fn(CREATE_ERROR("Couldn't create /storage/obb_mount: %s",
+ strerror(errno)));
}
close(fd);
}
@@ -621,38 +774,32 @@ static bool MountEmulatedStorage(uid_t uid, jint mount_mode,
// pkg specific directories. Otherwise, leave as is and bind mounts will be taken
// care of by vold later.
if (sandboxAlreadyCreated) {
- if (!preparePkgSpecificDirs(packages_for_uid, visible_vol_ids,
- mount_mode == MOUNT_EXTERNAL_INSTALLER, user_id, error_msg)) {
- return false;
- }
+ PreparePkgSpecificDirs(packages_for_uid, visible_vol_ids,
+ mount_mode == MOUNT_EXTERNAL_INSTALLER, user_id, fail_fn);
}
}
} else {
- if (TEMP_FAILURE_RETRY(mount(storageSource.string(), "/storage",
- NULL, MS_BIND | MS_REC | MS_SLAVE, NULL)) == -1) {
- *error_msg = CREATE_ERROR("Failed to mount %s to /storage: %s",
- storageSource.string(),
- strerror(errno));
- return false;
+ if (TEMP_FAILURE_RETRY(mount(storageSource.string(), "/storage", nullptr,
+ MS_BIND | MS_REC | MS_SLAVE, nullptr)) == -1) {
+ fail_fn(CREATE_ERROR("Failed to mount %s to /storage: %s",
+ storageSource.string(),
+ strerror(errno)));
}
// Mount user-specific symlink helper into place
userid_t user_id = multiuser_get_user_id(uid);
const String8 userSource(String8::format("/mnt/user/%d", user_id));
if (fs_prepare_dir(userSource.string(), 0751, 0, 0) == -1) {
- *error_msg = CREATE_ERROR("fs_prepare_dir failed on %s", userSource.string());
- return false;
+ fail_fn(CREATE_ERROR("fs_prepare_dir failed on %s",
+ userSource.string()));
}
+
if (TEMP_FAILURE_RETRY(mount(userSource.string(), "/storage/self",
- NULL, MS_BIND, NULL)) == -1) {
- *error_msg = CREATE_ERROR("Failed to mount %s to /storage/self: %s",
- userSource.string(),
- strerror(errno));
- return false;
+ nullptr, MS_BIND, nullptr)) == -1) {
+ fail_fn(CREATE_ERROR("Failed to mount %s to /storage/self: %s",
+ userSource.string(), strerror(errno)));
}
}
-
- return true;
}
static bool NeedsNoRandomizeWorkaround() {
@@ -680,55 +827,45 @@ static bool NeedsNoRandomizeWorkaround() {
// descriptor (if any) is closed via dup2(), replacing it with a valid
// (open) descriptor to /dev/null.
-static bool DetachDescriptors(JNIEnv* env, jintArray fdsToClose, std::string* error_msg) {
- if (!fdsToClose) {
- return true;
- }
- jsize count = env->GetArrayLength(fdsToClose);
- ScopedIntArrayRO ar(env, fdsToClose);
- if (ar.get() == NULL) {
- *error_msg = "Bad fd array";
- return false;
- }
- jsize i;
- int devnull;
- for (i = 0; i < count; i++) {
- devnull = open("/dev/null", O_RDWR);
- if (devnull < 0) {
- *error_msg = std::string("Failed to open /dev/null: ").append(strerror(errno));
- return false;
+static void DetachDescriptors(JNIEnv* env,
+ const std::vector<int>& fds_to_close,
+ fail_fn_t fail_fn) {
+
+ if (fds_to_close.size() > 0) {
+ android::base::unique_fd devnull_fd(open("/dev/null", O_RDWR));
+ if (devnull_fd == -1) {
+ fail_fn(std::string("Failed to open /dev/null: ").append(strerror(errno)));
}
- ALOGV("Switching descriptor %d to /dev/null: %s", ar[i], strerror(errno));
- if (dup2(devnull, ar[i]) < 0) {
- *error_msg = StringPrintf("Failed dup2() on descriptor %d: %s", ar[i], strerror(errno));
- return false;
+
+ for (int fd : fds_to_close) {
+ ALOGV("Switching descriptor %d to /dev/null", fd);
+ if (dup2(devnull_fd, fd) == -1) {
+ fail_fn(StringPrintf("Failed dup2() on descriptor %d: %s", fd, strerror(errno)));
+ }
}
- close(devnull);
}
- return true;
}
-void SetThreadName(const char* thread_name) {
+void SetThreadName(const std::string& thread_name) {
bool hasAt = false;
bool hasDot = false;
- const char* s = thread_name;
- while (*s) {
- if (*s == '.') {
+
+ for (const char str_el : thread_name) {
+ if (str_el == '.') {
hasDot = true;
- } else if (*s == '@') {
+ } else if (str_el == '@') {
hasAt = true;
}
- s++;
}
- const int len = s - thread_name;
- if (len < 15 || hasAt || !hasDot) {
- s = thread_name;
- } else {
- s = thread_name + len - 15;
+
+ const char* name_start_ptr = thread_name.c_str();
+ if (thread_name.length() >= MAX_NAME_LENGTH && !hasAt && hasDot) {
+ name_start_ptr += thread_name.length() - MAX_NAME_LENGTH;
}
+
// pthread_setname_np fails rather than truncating long strings.
char buf[16]; // MAX_TASK_COMM_LEN=16 is hard-coded into bionic
- strlcpy(buf, s, sizeof(buf)-1);
+ strlcpy(buf, name_start_ptr, sizeof(buf) - 1);
errno = pthread_setname_np(pthread_self(), buf);
if (errno != 0) {
ALOGW("Unable to set the name of current thread to '%s': %s", buf, strerror(errno));
@@ -737,28 +874,16 @@ void SetThreadName(const char* thread_name) {
android::base::SetDefaultTag(buf);
}
-// The list of open zygote file descriptors.
-static FileDescriptorTable* gOpenFdTable = NULL;
-
-static bool FillFileDescriptorVector(JNIEnv* env,
- jintArray managed_fds,
- std::vector<int>* fds,
- std::string* error_msg) {
- CHECK(fds != nullptr);
- if (managed_fds != nullptr) {
- ScopedIntArrayRO ar(env, managed_fds);
- if (ar.get() == nullptr) {
- *error_msg = "Bad fd array";
- return false;
- }
- fds->reserve(ar.size());
- for (size_t i = 0; i < ar.size(); ++i) {
- fds->push_back(ar[i]);
- }
- }
- return true;
-}
-
+/**
+ * A failure function used to report fatal errors to the managed runtime. This
+ * function is often curried with the process name information and then passed
+ * to called functions.
+ *
+ * @param env Managed runtime environment
+ * @param process_name A native representation of the process name
+ * @param managed_process_name A managed representation of the process name
+ * @param msg The error message to be reported
+ */
[[noreturn]]
static void ZygoteFailure(JNIEnv* env,
const char* process_name,
@@ -779,12 +904,25 @@ static void ZygoteFailure(JNIEnv* env,
__builtin_unreachable();
}
+/**
+ * A helper method for converting managed strings to native strings. A fatal
+ * error is generated if a problem is encountered in extracting a non-null
+ * string.
+ *
+ * @param env Managed runtime environment
+ * @param process_name A native representation of the process name
+ * @param managed_process_name A managed representation of the process name
+ * @param managed_string The managed string to extract
+ *
+ * @return An empty option if the managed string is null. A optional-wrapped
+ * string otherwise.
+ */
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>();
+ return std::nullopt;
} else {
ScopedUtfChars scoped_string_chars(env, managed_string);
@@ -796,16 +934,125 @@ static std::optional<std::string> ExtractJString(JNIEnv* env,
}
}
-// Utility routine to fork a zygote.
+/**
+ * A helper method for converting managed string arrays to native vectors. A
+ * fatal error is generated if a problem is encountered in extracting a non-null array.
+ *
+ * @param env Managed runtime environment
+ * @param process_name A native representation of the process name
+ * @param managed_process_name A managed representation of the process name
+ * @param managed_array The managed integer array to extract
+ *
+ * @return An empty option if the managed array is null. A optional-wrapped
+ * vector otherwise.
+ */
+static std::optional<std::vector<int>> ExtractJIntArray(JNIEnv* env,
+ const char* process_name,
+ jstring managed_process_name,
+ jintArray managed_array) {
+ if (managed_array == nullptr) {
+ return std::nullopt;
+ } else {
+ ScopedIntArrayRO managed_array_handle(env, managed_array);
+
+ if (managed_array_handle.get() != nullptr) {
+ std::vector<int> native_array;
+ native_array.reserve(managed_array_handle.size());
+
+ for (size_t array_index = 0; array_index < managed_array_handle.size(); ++array_index) {
+ native_array.push_back(managed_array_handle[array_index]);
+ }
+
+ return std::move(native_array);
+
+ } else {
+ ZygoteFailure(env, process_name, managed_process_name, "Failed to extract JIntArray.");
+ }
+ }
+}
+
+/**
+ * A helper method for converting managed string arrays to native vectors. A
+ * fatal error is generated if a problem is encountered in extracting a non-null array.
+ *
+ * @param env Managed runtime environment
+ * @param process_name A native representation of the process name
+ * @param managed_process_name A managed representation of the process name
+ * @param managed_array The managed string array to extract
+ *
+ * @return An empty option if the managed array is null. A optional-wrapped
+ * vector otherwise.
+ */
+static std::optional<std::vector<std::string>> ExtractJStringArray(JNIEnv* env,
+ const char* process_name,
+ jstring managed_process_name,
+ jobjectArray managed_array) {
+ if (managed_array == nullptr) {
+ return std::nullopt;
+ } else {
+ jsize element_count = env->GetArrayLength(managed_array);
+ std::vector<std::string> native_string_vector;
+ native_string_vector.reserve(element_count);
+
+ for (jsize array_index = 0; array_index < element_count; ++array_index) {
+ jstring managed_string = (jstring) env->GetObjectArrayElement(managed_array, array_index);
+ auto native_string = ExtractJString(env, process_name, managed_process_name, managed_string);
+
+ if (LIKELY(native_string.has_value())) {
+ native_string_vector.emplace_back(std::move(native_string.value()));
+ } else {
+ ZygoteFailure(env, process_name, managed_process_name,
+ "Null string found in managed string array.");
+ }
+ }
+
+ return std::move(native_string_vector);
+ }
+}
+
+/**
+ * A utility function for blocking signals.
+ *
+ * @param signum Signal number to block
+ * @param fail_fn Fatal error reporting function
+ *
+ * @see ZygoteFailure
+ */
+static void BlockSignal(int signum, fail_fn_t fail_fn) {
+ sigset_t sigs;
+ sigemptyset(&sigs);
+ sigaddset(&sigs, signum);
+
+ if (sigprocmask(SIG_BLOCK, &sigs, nullptr) == -1) {
+ fail_fn(CREATE_ERROR("Failed to block signal %s: %s", strsignal(signum), strerror(errno)));
+ }
+}
+
+
+/**
+ * A utility function for unblocking signals.
+ *
+ * @param signum Signal number to unblock
+ * @param fail_fn Fatal error reporting function
+ *
+ * @see ZygoteFailure
+ */
+static void UnblockSignal(int signum, fail_fn_t fail_fn) {
+ sigset_t sigs;
+ sigemptyset(&sigs);
+ sigaddset(&sigs, signum);
+
+ if (sigprocmask(SIG_UNBLOCK, &sigs, nullptr) == -1) {
+ fail_fn(CREATE_ERROR("Failed to un-block signal %s: %s", strsignal(signum), strerror(errno)));
+ }
+}
+
+// Utility routine to fork a process from the zygote.
static pid_t ForkCommon(JNIEnv* env, bool is_system_server,
- jintArray managed_fds_to_close, jintArray managed_fds_to_ignore) {
+ const std::vector<int>& fds_to_close,
+ const std::vector<int>& 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);
@@ -815,9 +1062,7 @@ static pid_t ForkCommon(JNIEnv* env, bool is_system_server,
// 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)));
- }
+ BlockSignal(SIGCHLD, fail_fn);
// Close any logging related FDs before we start evaluating the list of
// file descriptors.
@@ -827,19 +1072,10 @@ static pid_t ForkCommon(JNIEnv* env, bool is_system_server,
// 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);
- }
- } else if (!gOpenFdTable->Restat(fds_to_ignore, &error_msg)) {
- fail_fn(error_msg);
+ gOpenFdTable = FileDescriptorTable::Create(fds_to_ignore, fail_fn);
+ } else {
+ gOpenFdTable->Restat(fds_to_ignore, fail_fn);
}
android_fdsan_error_level fdsan_error_level = android_fdsan_get_error_level();
@@ -851,24 +1087,18 @@ static pid_t ForkCommon(JNIEnv* env, bool is_system_server,
PreApplicationInit();
// Clean up any descriptors which must be closed immediately
- if (!DetachDescriptors(env, managed_fds_to_close, &error_msg)) {
- fail_fn(error_msg);
- }
+ DetachDescriptors(env, fds_to_close, fail_fn);
// 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);
- }
+ gOpenFdTable->ReopenOrDetach(fail_fn);
// 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)));
- }
+ UnblockSignal(SIGCHLD, fail_fn);
return pid;
}
@@ -883,10 +1113,9 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
jstring managed_app_data_dir, jstring managed_package_name,
jobjectArray managed_pacakges_for_uid,
jobjectArray managed_visible_vol_ids) {
- 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);
+ const char* process_name = is_system_server ? "system_server" : "zygote";
+ auto fail_fn = std::bind(ZygoteFailure, env, process_name, managed_nice_name, _1);
+ auto extract_fn = std::bind(ExtractJString, env, process_name, managed_nice_name, _1);
auto se_info = extract_fn(managed_se_info);
auto nice_name = extract_fn(managed_nice_name);
@@ -894,22 +1123,14 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
auto app_data_dir = extract_fn(managed_app_data_dir);
auto package_name = extract_fn(managed_package_name);
- std::string error_msg;
-
// Keep capabilities across UID change, unless we're staying root.
if (uid != 0) {
- if (!EnableKeepCapabilities(&error_msg)) {
- fail_fn(error_msg);
- }
+ EnableKeepCapabilities(fail_fn);
}
- if (!SetInheritable(permitted_capabilities, &error_msg)) {
- fail_fn(error_msg);
- }
+ SetInheritable(permitted_capabilities, fail_fn);
- if (!DropCapabilitiesBoundingSet(&error_msg)) {
- fail_fn(error_msg);
- }
+ DropCapabilitiesBoundingSet(fail_fn);
bool use_native_bridge = !is_system_server &&
instruction_set.has_value() &&
@@ -934,56 +1155,21 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
}
}
- std::vector<std::string> packages_for_uid;
- if (managed_pacakges_for_uid != nullptr) {
- jsize count = env->GetArrayLength(managed_pacakges_for_uid);
- for (jsize package_index = 0; package_index < count; ++package_index) {
- jstring managed_package_for_uid =
- (jstring) env->GetObjectArrayElement(managed_pacakges_for_uid, package_index);
+ std::vector<std::string> packages_for_uid =
+ ExtractJStringArray(env, process_name, managed_nice_name, managed_pacakges_for_uid).
+ value_or(std::vector<std::string>());
- auto package_for_uid = extract_fn(managed_package_for_uid);
- if (LIKELY(package_for_uid.has_value())) {
- packages_for_uid.emplace_back(std::move(package_for_uid.value()));
- } else {
- fail_fn("Null string found in managed packages_for_uid.");
- }
- }
- }
-
- std::vector<std::string> visible_vol_ids;
- if (managed_visible_vol_ids != nullptr) {
- jsize count = env->GetArrayLength(managed_visible_vol_ids);
- for (jsize vol_id_index = 0; vol_id_index < count; ++vol_id_index) {
- jstring managed_visible_vol_id =
- (jstring) env->GetObjectArrayElement(managed_visible_vol_ids, vol_id_index);
-
- auto visible_vol_id = extract_fn(managed_visible_vol_id);
- if (LIKELY(visible_vol_id.has_value())) {
- visible_vol_ids.emplace_back(std::move(visible_vol_id.value()));
- } else {
- fail_fn("Null string found in managed visible_vol_ids.");
- }
- }
- }
+ std::vector<std::string> visible_vol_ids =
+ ExtractJStringArray(env, process_name, managed_nice_name, managed_visible_vol_ids).
+ value_or(std::vector<std::string>());
- if (!MountEmulatedStorage(uid, mount_external, use_native_bridge, &error_msg,
- package_name.value(), packages_for_uid, visible_vol_ids)) {
- ALOGW("Failed to mount emulated storage: %s (%s)", error_msg.c_str(), strerror(errno));
- if (errno == ENOTCONN || errno == EROFS) {
- // When device is actively encrypting, we get ENOTCONN here
- // since FUSE was mounted before the framework restarted.
- // When encrypted device is booting, we get EROFS since
- // FUSE hasn't been created yet by init.
- // In either case, continue without external storage.
- } else {
- fail_fn(error_msg);
- }
- }
+ MountEmulatedStorage(uid, mount_external, use_native_bridge, package_name.value(),
+ packages_for_uid, visible_vol_ids, fail_fn);
// If this zygote isn't root, it won't be able to create a process group,
// since the directory is owned by root.
if (!is_system_server && getuid() == 0) {
- int rc = createProcessGroup(uid, getpid());
+ const int rc = createProcessGroup(uid, getpid());
if (rc == -EROFS) {
ALOGW("createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?");
} else if (rc != 0) {
@@ -991,13 +1177,8 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
}
}
- if (!SetGids(env, gids, &error_msg)) {
- fail_fn(error_msg);
- }
-
- if (!SetRLimits(env, rlimits, &error_msg)) {
- fail_fn(error_msg);
- }
+ SetGids(env, gids, fail_fn);
+ SetRLimits(env, rlimits, fail_fn);
if (use_native_bridge) {
// Due to the logic behind use_native_bridge we know that both app_data_dir
@@ -1056,14 +1237,9 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
}
}
- if (!SetCapabilities(permitted_capabilities, effective_capabilities, permitted_capabilities,
- &error_msg)) {
- fail_fn(error_msg);
- }
+ SetCapabilities(permitted_capabilities, effective_capabilities, permitted_capabilities, fail_fn);
- if (!SetSchedulerPolicy(&error_msg)) {
- fail_fn(error_msg);
- }
+ SetSchedulerPolicy(fail_fn);
const char* se_info_ptr = se_info.has_value() ? se_info.value().c_str() : nullptr;
const char* nice_name_ptr = nice_name.has_value() ? nice_name.value().c_str() : nullptr;
@@ -1076,7 +1252,7 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
// Make it easier to debug audit logs by setting the main thread's name to the
// nice name rather than "app_process".
if (nice_name.has_value()) {
- SetThreadName(nice_name.value().c_str());
+ SetThreadName(nice_name.value());
} else if (is_system_server) {
SetThreadName("system_server");
}
@@ -1089,6 +1265,7 @@ static void SpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray gids,
if (env->ExceptionCheck()) {
fail_fn("Error calling post fork system server hooks.");
}
+
// TODO(oth): Remove hardcoded label here (b/117874058).
static const char* kSystemServerLabel = "u:r:system_server:s0";
if (selinux_android_setcon(kSystemServerLabel) != 0) {
@@ -1192,6 +1369,74 @@ static jlong CalculateCapabilities(JNIEnv* env, jint uid, jint gid, jintArray gi
return capabilities & GetEffectiveCapabilityMask(env);
}
+
+/**
+ * Adds the given information about a newly created blastula to the Zygote's
+ * blastula table.
+ *
+ * @param blastula_pid Process ID of the newly created blastula
+ * @param read_pipe_fd File descriptor for the read end of the blastula
+ * reporting pipe. Used in the ZygoteServer poll loop to track blastula
+ * specialization.
+ */
+static void AddBlastulaTableEntry(pid_t blastula_pid, int read_pipe_fd) {
+ static int sBlastulaTableInsertIndex = 0;
+
+ int search_index = sBlastulaTableInsertIndex;
+
+ do {
+ if (gBlastulaTable[search_index].SetIfInvalid(blastula_pid, read_pipe_fd)) {
+ // Start our next search right after where we finished this one.
+ sBlastulaTableInsertIndex = (search_index + 1) % gBlastulaTable.size();
+
+ return;
+ }
+
+ search_index = (search_index + 1) % gBlastulaTable.size();
+ } while (search_index != sBlastulaTableInsertIndex);
+
+ // Much like money in the banana stand, there should always be an entry
+ // in the blastula table.
+ __builtin_unreachable();
+}
+
+/**
+ * Invalidates the entry in the BlastulaTable corresponding to the provided
+ * process ID if it is present. If an entry was removed the blastula pool
+ * count is decremented.
+ *
+ * @param blastula_pid Process ID of the blastula entry to invalidate
+ * @return True if an entry was invalidated; false otherwise
+ */
+static bool RemoveBlastulaTableEntry(pid_t blastula_pid) {
+ for (BlastulaTableEntry& entry : gBlastulaTable) {
+ if (entry.ClearForPID(blastula_pid)) {
+ --gBlastulaPoolCount;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * @return A vector of the read pipe FDs for each of the active blastulas.
+ */
+std::vector<int> MakeBlastulaPipeReadFDVector() {
+ std::vector<int> fd_vec;
+ fd_vec.reserve(gBlastulaTable.size());
+
+ for (BlastulaTableEntry& entry : gBlastulaTable) {
+ auto entry_values = entry.GetValues();
+
+ if (entry_values.has_value()) {
+ fd_vec.push_back(entry_values.value().read_pipe_fd);
+ }
+ }
+
+ return fd_vec;
+}
+
} // anonymous namespace
namespace android {
@@ -1210,12 +1455,35 @@ static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
jint runtime_flags, jobjectArray rlimits,
jint mount_external, jstring se_info, jstring nice_name,
- jintArray fds_to_close, jintArray fds_to_ignore, jboolean is_child_zygote,
+ jintArray managed_fds_to_close, jintArray managed_fds_to_ignore, jboolean is_child_zygote,
jstring instruction_set, jstring app_data_dir, jstring package_name,
jobjectArray packages_for_uid, jobjectArray visible_vol_ids) {
jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
+ if (UNLIKELY(managed_fds_to_close == nullptr)) {
+ ZygoteFailure(env, "zygote", nice_name, "Zygote received a null fds_to_close vector.");
+ }
+
+ std::vector<int> fds_to_close =
+ ExtractJIntArray(env, "zygote", nice_name, managed_fds_to_close).value();
+ std::vector<int> fds_to_ignore =
+ ExtractJIntArray(env, "zygote", nice_name, managed_fds_to_ignore)
+ .value_or(std::vector<int>());
+
+ std::vector<int> blastula_pipes = MakeBlastulaPipeReadFDVector();
+
+ fds_to_close.insert(fds_to_close.end(), blastula_pipes.begin(), blastula_pipes.end());
+ fds_to_ignore.insert(fds_to_ignore.end(), blastula_pipes.begin(), blastula_pipes.end());
+
+// fds_to_close.push_back(gBlastulaPoolSocketFD);
+
+ if (gBlastulaPoolEventFD != -1) {
+ fds_to_close.push_back(gBlastulaPoolEventFD);
+ fds_to_ignore.push_back(gBlastulaPoolEventFD);
+ }
+
pid_t pid = ForkCommon(env, false, fds_to_close, fds_to_ignore);
+
if (pid == 0) {
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
capabilities, capabilities,
@@ -1230,9 +1498,19 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer(
JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities,
jlong effective_capabilities) {
+ std::vector<int> fds_to_close(MakeBlastulaPipeReadFDVector()),
+ fds_to_ignore(fds_to_close);
+
+// fds_to_close.push_back(gBlastulaPoolSocketFD);
+
+ if (gBlastulaPoolEventFD != -1) {
+ fds_to_close.push_back(gBlastulaPoolEventFD);
+ fds_to_ignore.push_back(gBlastulaPoolEventFD);
+ }
+
pid_t pid = ForkCommon(env, true,
- /* managed_fds_to_close= */ nullptr,
- /* managed_fds_to_ignore= */ nullptr);
+ fds_to_close,
+ fds_to_ignore);
if (pid == 0) {
SpecializeCommon(env, uid, gid, gids, runtime_flags, rlimits,
permitted_capabilities, effective_capabilities,
@@ -1266,6 +1544,52 @@ static jint com_android_internal_os_Zygote_nativeForkSystemServer(
return pid;
}
+/**
+ * A JNI function that forks a blastula from the Zygote while ensuring proper
+ * file descriptor hygiene.
+ *
+ * @param env Managed runtime environment
+ * @param read_pipe_fd The read FD for the blastula reporting pipe. Manually closed by blastlas
+ * in managed code.
+ * @param write_pipe_fd The write FD for the blastula reporting pipe. Manually closed by the
+ * zygote in managed code.
+ * @param managed_session_socket_fds A list of anonymous session sockets that must be ignored by
+ * the FD hygiene code and automatically "closed" in the new blastula.
+ * @return
+ */
+static jint com_android_internal_os_Zygote_nativeForkBlastula(JNIEnv* env, jclass,
+ jint read_pipe_fd, jint write_pipe_fd, jintArray managed_session_socket_fds) {
+ std::vector<int> fds_to_close(MakeBlastulaPipeReadFDVector()),
+ fds_to_ignore(fds_to_close);
+
+ std::vector<int> session_socket_fds =
+ ExtractJIntArray(env, "blastula", nullptr, managed_session_socket_fds)
+ .value_or(std::vector<int>());
+
+ // The Blastula Pool Event FD is created during the initialization of the
+ // blastula pool and should always be valid here.
+
+ fds_to_close.push_back(gZygoteSocketFD);
+ fds_to_close.push_back(gBlastulaPoolEventFD);
+ fds_to_close.insert(fds_to_close.end(), session_socket_fds.begin(), session_socket_fds.end());
+
+ fds_to_ignore.push_back(gZygoteSocketFD);
+ fds_to_ignore.push_back(gBlastulaPoolSocketFD);
+ fds_to_ignore.push_back(gBlastulaPoolEventFD);
+ fds_to_ignore.push_back(read_pipe_fd);
+ fds_to_ignore.push_back(write_pipe_fd);
+ fds_to_ignore.insert(fds_to_ignore.end(), session_socket_fds.begin(), session_socket_fds.end());
+
+ pid_t blastula_pid = ForkCommon(env, /* is_system_server= */ false, fds_to_close, fds_to_ignore);
+
+ if (blastula_pid != 0) {
+ ++gBlastulaPoolCount;
+ AddBlastulaTableEntry(blastula_pid, read_pipe_fd);
+ }
+
+ return blastula_pid;
+}
+
static void com_android_internal_os_Zygote_nativeAllowFileAcrossFork(
JNIEnv* env, jclass, jstring path) {
ScopedUtfChars path_native(env, path);
@@ -1331,6 +1655,121 @@ static void com_android_internal_os_Zygote_nativeInstallSeccompUidGidFilter(
*/
}
+/**
+ * Called from a blastula to specialize the process for a specific application.
+ *
+ * @param env Managed runtime environment
+ * @param uid User ID of the new application
+ * @param gid Group ID of the new application
+ * @param gids Extra groups that the process belongs to
+ * @param runtime_flags Flags for changing the behavior of the managed runtime
+ * @param rlimits Resource limits
+ * @param mount_external The mode (read/write/normal) that external storage will be mounted with
+ * @param se_info SELinux policy information
+ * @param nice_name New name for this process
+ * @param is_child_zygote If the process is to become a WebViewZygote
+ * @param instruction_set The instruction set expected/requested by the new application
+ * @param app_data_dir Path to the application's data directory
+ */
+static void com_android_internal_os_Zygote_nativeSpecializeBlastula(
+ JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
+ jint runtime_flags, jobjectArray rlimits,
+ jint mount_external, jstring se_info, jstring nice_name,
+ jboolean is_child_zygote, jstring instruction_set, jstring app_data_dir,
+ jstring package_name, jobjectArray packages_for_uid, jobjectArray visible_vol_ids) {
+ jlong capabilities = CalculateCapabilities(env, uid, gid, gids, is_child_zygote);
+
+ 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,
+ package_name, packages_for_uid, visible_vol_ids);
+}
+
+/**
+ * A helper method for fetching socket file descriptors that were opened by init from the
+ * environment.
+ *
+ * @param env Managed runtime environment
+ * @param is_primary If this process is the primary or secondary Zygote; used to compute the name
+ * of the environment variable storing the file descriptors.
+ */
+static void com_android_internal_os_Zygote_nativeGetSocketFDs(JNIEnv* env, jclass,
+ jboolean is_primary) {
+ std::string android_socket_prefix(ANDROID_SOCKET_PREFIX);
+ std::string env_var_name = android_socket_prefix + (is_primary ? "zygote" : "zygote_secondary");
+ char* env_var_val = getenv(env_var_name.c_str());
+
+ if (env_var_val != nullptr) {
+ gZygoteSocketFD = atoi(env_var_val);
+ ALOGV("Zygote:zygoteSocketFD = %d", gZygoteSocketFD);
+ } else {
+ ALOGE("Unable to fetch Zygote socket file descriptor");
+ }
+
+ env_var_name = android_socket_prefix + (is_primary ? "blastula_pool" : "blastula_pool_secondary");
+ env_var_val = getenv(env_var_name.c_str());
+
+ if (env_var_val != nullptr) {
+ gBlastulaPoolSocketFD = atoi(env_var_val);
+ ALOGV("Zygote:blastulaPoolSocketFD = %d", gBlastulaPoolSocketFD);
+ } else {
+ ALOGE("Unable to fetch Blastula pool socket file descriptor");
+ }
+}
+
+/**
+ * @param env Managed runtime environment
+ * @return A managed array of raw file descriptors for the read ends of the blastula reporting
+ * pipes.
+ */
+static jintArray com_android_internal_os_Zygote_nativeGetBlastulaPipeFDs(JNIEnv* env, jclass) {
+ std::vector<int> blastula_fds = MakeBlastulaPipeReadFDVector();
+
+ jintArray managed_blastula_fds = env->NewIntArray(blastula_fds.size());
+ env->SetIntArrayRegion(managed_blastula_fds, 0, blastula_fds.size(), blastula_fds.data());
+
+ return managed_blastula_fds;
+}
+
+/**
+ * A JNI wrapper around RemoveBlastulaTableEntry.
+ *
+ * @param env Managed runtime environment
+ * @param blastula_pid Process ID of the blastula entry to invalidate
+ * @return True if an entry was invalidated; false otherwise.
+ */
+static jboolean com_android_internal_os_Zygote_nativeRemoveBlastulaTableEntry(JNIEnv* env, jclass,
+ jint blastula_pid) {
+ return RemoveBlastulaTableEntry(blastula_pid);
+}
+
+/**
+ * Creates the blastula pool event FD if it doesn't exist and returns it. This is used by the
+ * ZygoteServer poll loop to know when to re-fill the blastula pool.
+ *
+ * @param env Managed runtime environment
+ * @return A raw event file descriptor used to communicate (from the signal handler) when the
+ * Zygote receives a SIGCHLD for a blastula
+ */
+static jint com_android_internal_os_Zygote_nativeGetBlastulaPoolEventFD(JNIEnv* env, jclass) {
+ if (gBlastulaPoolEventFD == -1) {
+ if ((gBlastulaPoolEventFD = eventfd(0, 0)) == -1) {
+ ZygoteFailure(env, "zygote", nullptr, StringPrintf("Unable to create eventfd: %s", strerror(errno)));
+ }
+ }
+
+ return gBlastulaPoolEventFD;
+}
+
+/**
+ * @param env Managed runtime environment
+ * @return The number of blastulas currently in the blastula pool
+ */
+static jint com_android_internal_os_Zygote_nativeGetBlastulaPoolCount(JNIEnv* env, jclass) {
+ return gBlastulaPoolCount;
+}
+
static const JNINativeMethod gMethods[] = {
{ "nativeSecurityInit", "()V",
(void *) com_android_internal_os_Zygote_nativeSecurityInit },
@@ -1346,7 +1785,22 @@ static const JNINativeMethod gMethods[] = {
{ "nativePreApplicationInit", "()V",
(void *) com_android_internal_os_Zygote_nativePreApplicationInit },
{ "nativeInstallSeccompUidGidFilter", "(II)V",
- (void *) com_android_internal_os_Zygote_nativeInstallSeccompUidGidFilter }
+ (void *) com_android_internal_os_Zygote_nativeInstallSeccompUidGidFilter },
+ { "nativeForkBlastula", "(II[I)I",
+ (void *) com_android_internal_os_Zygote_nativeForkBlastula },
+ { "nativeSpecializeBlastula",
+ "(II[II[[IILjava/lang/String;Ljava/lang/String;ZLjava/lang/String;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V",
+ (void *) com_android_internal_os_Zygote_nativeSpecializeBlastula },
+ { "nativeGetSocketFDs", "(Z)V",
+ (void *) com_android_internal_os_Zygote_nativeGetSocketFDs },
+ { "nativeGetBlastulaPipeFDs", "()[I",
+ (void *) com_android_internal_os_Zygote_nativeGetBlastulaPipeFDs },
+ { "nativeRemoveBlastulaTableEntry", "(I)Z",
+ (void *) com_android_internal_os_Zygote_nativeRemoveBlastulaTableEntry },
+ { "nativeGetBlastulaPoolEventFD", "()I",
+ (void *) com_android_internal_os_Zygote_nativeGetBlastulaPoolEventFD },
+ { "nativeGetBlastulaPoolCount", "()I",
+ (void *) com_android_internal_os_Zygote_nativeGetBlastulaPoolCount }
};
int register_com_android_internal_os_Zygote(JNIEnv* env) {
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
index 3661ff235222..53dde80edd89 100644
--- a/core/jni/fd_utils.cpp
+++ b/core/jni/fd_utils.cpp
@@ -136,15 +136,14 @@ FileDescriptorWhitelist* FileDescriptorWhitelist::instance_ = nullptr;
// open zygote file descriptor.
class FileDescriptorInfo {
public:
- // Create a FileDescriptorInfo for a given file descriptor. Returns
- // |NULL| if an error occurred.
- static FileDescriptorInfo* CreateFromFd(int fd, std::string* error_msg);
+ // Create a FileDescriptorInfo for a given file descriptor.
+ static FileDescriptorInfo* CreateFromFd(int fd, fail_fn_t fail_fn);
// Checks whether the file descriptor associated with this object
// refers to the same description.
- bool Restat() const;
+ bool RefersToSameFile() const;
- bool ReopenOrDetach(std::string* error_msg) const;
+ void ReopenOrDetach(fail_fn_t fail_fn) const;
const int fd;
const struct stat stat;
@@ -170,19 +169,18 @@ class FileDescriptorInfo {
// address).
static bool GetSocketName(const int fd, std::string* result);
- bool DetachSocket(std::string* error_msg) const;
+ void DetachSocket(fail_fn_t fail_fn) const;
DISALLOW_COPY_AND_ASSIGN(FileDescriptorInfo);
};
// static
-FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd, std::string* error_msg) {
+FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd, fail_fn_t fail_fn) {
struct stat f_stat;
// This should never happen; the zygote should always have the right set
// of permissions required to stat all its open files.
if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
- *error_msg = android::base::StringPrintf("Unable to stat %d", fd);
- return nullptr;
+ fail_fn(android::base::StringPrintf("Unable to stat %d", fd));
}
const FileDescriptorWhitelist* whitelist = FileDescriptorWhitelist::Get();
@@ -190,15 +188,13 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd, std::string* error_
if (S_ISSOCK(f_stat.st_mode)) {
std::string socket_name;
if (!GetSocketName(fd, &socket_name)) {
- *error_msg = "Unable to get socket name";
- return nullptr;
+ fail_fn("Unable to get socket name");
}
if (!whitelist->IsAllowed(socket_name)) {
- *error_msg = android::base::StringPrintf("Socket name not whitelisted : %s (fd=%d)",
- socket_name.c_str(),
- fd);
- return nullptr;
+ fail_fn(android::base::StringPrintf("Socket name not whitelisted : %s (fd=%d)",
+ socket_name.c_str(),
+ fd));
}
return new FileDescriptorInfo(fd);
@@ -211,26 +207,35 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd, std::string* error_
// S_ISDIR : Not supported. (We could if we wanted to, but it's unused).
// S_ISLINK : Not supported.
// S_ISBLK : Not supported.
- // S_ISFIFO : Not supported. Note that the zygote uses pipes to communicate
- // with the child process across forks but those should have been closed
- // before we got to this point.
+ // S_ISFIFO : Not supported. Note that the Zygote and blastulas use pipes to
+ // communicate with the child processes across forks but those should have been
+ // added to the redirection exemption list.
if (!S_ISCHR(f_stat.st_mode) && !S_ISREG(f_stat.st_mode)) {
- *error_msg = android::base::StringPrintf("Unsupported st_mode %u", f_stat.st_mode);
- return nullptr;
+ std::string mode = "Unknown";
+
+ if (S_ISDIR(f_stat.st_mode)) {
+ mode = "DIR";
+ } else if (S_ISLNK(f_stat.st_mode)) {
+ mode = "LINK";
+ } else if (S_ISBLK(f_stat.st_mode)) {
+ mode = "BLOCK";
+ } else if (S_ISFIFO(f_stat.st_mode)) {
+ mode = "FIFO";
+ }
+
+ fail_fn(android::base::StringPrintf("Unsupported st_mode for FD %d: %s", fd, mode.c_str()));
}
std::string file_path;
const std::string fd_path = android::base::StringPrintf("/proc/self/fd/%d", fd);
if (!android::base::Readlink(fd_path, &file_path)) {
- *error_msg = android::base::StringPrintf("Could not read fd link %s: %s",
- fd_path.c_str(),
- strerror(errno));
- return nullptr;
+ fail_fn(android::base::StringPrintf("Could not read fd link %s: %s",
+ fd_path.c_str(),
+ strerror(errno)));
}
if (!whitelist->IsAllowed(file_path)) {
- *error_msg = std::string("Not whitelisted : ").append(file_path);
- return nullptr;
+ fail_fn(std::string("Not whitelisted : ").append(file_path));
}
// File descriptor flags : currently on FD_CLOEXEC. We can set these
@@ -238,11 +243,10 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd, std::string* error_
// there won't be any races.
const int fd_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD));
if (fd_flags == -1) {
- *error_msg = android::base::StringPrintf("Failed fcntl(%d, F_GETFD) (%s): %s",
- fd,
- file_path.c_str(),
- strerror(errno));
- return nullptr;
+ fail_fn(android::base::StringPrintf("Failed fcntl(%d, F_GETFD) (%s): %s",
+ fd,
+ file_path.c_str(),
+ strerror(errno)));
}
// File status flags :
@@ -259,11 +263,10 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd, std::string* error_
// their presence and pass them in to open().
int fs_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));
if (fs_flags == -1) {
- *error_msg = android::base::StringPrintf("Failed fcntl(%d, F_GETFL) (%s): %s",
- fd,
- file_path.c_str(),
- strerror(errno));
- return nullptr;
+ fail_fn(android::base::StringPrintf("Failed fcntl(%d, F_GETFL) (%s): %s",
+ fd,
+ file_path.c_str(),
+ strerror(errno)));
}
// File offset : Ignore the offset for non seekable files.
@@ -278,7 +281,7 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd, std::string* error_
return new FileDescriptorInfo(f_stat, file_path, fd, open_flags, fd_flags, fs_flags, offset);
}
-bool FileDescriptorInfo::Restat() const {
+bool FileDescriptorInfo::RefersToSameFile() const {
struct stat f_stat;
if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
PLOG(ERROR) << "Unable to restat fd " << fd;
@@ -288,9 +291,9 @@ bool FileDescriptorInfo::Restat() const {
return f_stat.st_ino == stat.st_ino && f_stat.st_dev == stat.st_dev;
}
-bool FileDescriptorInfo::ReopenOrDetach(std::string* error_msg) const {
+void FileDescriptorInfo::ReopenOrDetach(fail_fn_t fail_fn) const {
if (is_sock) {
- return DetachSocket(error_msg);
+ return DetachSocket(fail_fn);
}
// NOTE: This might happen if the file was unlinked after being opened.
@@ -299,57 +302,50 @@ bool FileDescriptorInfo::ReopenOrDetach(std::string* error_msg) const {
const int new_fd = TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags));
if (new_fd == -1) {
- *error_msg = android::base::StringPrintf("Failed open(%s, %i): %s",
- file_path.c_str(),
- open_flags,
- strerror(errno));
- return false;
+ fail_fn(android::base::StringPrintf("Failed open(%s, %i): %s",
+ file_path.c_str(),
+ open_flags,
+ strerror(errno)));
}
if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFD, fd_flags)) == -1) {
close(new_fd);
- *error_msg = android::base::StringPrintf("Failed fcntl(%d, F_SETFD, %d) (%s): %s",
- new_fd,
- fd_flags,
- file_path.c_str(),
- strerror(errno));
- return false;
+ fail_fn(android::base::StringPrintf("Failed fcntl(%d, F_SETFD, %d) (%s): %s",
+ new_fd,
+ fd_flags,
+ file_path.c_str(),
+ strerror(errno)));
}
if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFL, fs_flags)) == -1) {
close(new_fd);
- *error_msg = android::base::StringPrintf("Failed fcntl(%d, F_SETFL, %d) (%s): %s",
- new_fd,
- fs_flags,
- file_path.c_str(),
- strerror(errno));
- return false;
+ fail_fn(android::base::StringPrintf("Failed fcntl(%d, F_SETFL, %d) (%s): %s",
+ new_fd,
+ fs_flags,
+ file_path.c_str(),
+ strerror(errno)));
}
if (offset != -1 && TEMP_FAILURE_RETRY(lseek64(new_fd, offset, SEEK_SET)) == -1) {
close(new_fd);
- *error_msg = android::base::StringPrintf("Failed lseek64(%d, SEEK_SET) (%s): %s",
- new_fd,
- file_path.c_str(),
- strerror(errno));
- return false;
+ fail_fn(android::base::StringPrintf("Failed lseek64(%d, SEEK_SET) (%s): %s",
+ new_fd,
+ file_path.c_str(),
+ strerror(errno)));
}
- int dupFlags = (fd_flags & FD_CLOEXEC) ? O_CLOEXEC : 0;
- if (TEMP_FAILURE_RETRY(dup3(new_fd, fd, dupFlags)) == -1) {
+ int dup_flags = (fd_flags & FD_CLOEXEC) ? O_CLOEXEC : 0;
+ if (TEMP_FAILURE_RETRY(dup3(new_fd, fd, dup_flags)) == -1) {
close(new_fd);
- *error_msg = android::base::StringPrintf("Failed dup3(%d, %d, %d) (%s): %s",
- fd,
- new_fd,
- dupFlags,
- file_path.c_str(),
- strerror(errno));
- return false;
+ fail_fn(android::base::StringPrintf("Failed dup3(%d, %d, %d) (%s): %s",
+ fd,
+ new_fd,
+ dup_flags,
+ file_path.c_str(),
+ strerror(errno)));
}
close(new_fd);
-
- return true;
}
FileDescriptorInfo::FileDescriptorInfo(int fd) :
@@ -375,7 +371,6 @@ FileDescriptorInfo::FileDescriptorInfo(struct stat stat, const std::string& file
is_sock(false) {
}
-// static
bool FileDescriptorInfo::GetSocketName(const int fd, std::string* result) {
sockaddr_storage ss;
sockaddr* addr = reinterpret_cast<sockaddr*>(&ss);
@@ -419,86 +414,75 @@ bool FileDescriptorInfo::GetSocketName(const int fd, std::string* result) {
return true;
}
-bool FileDescriptorInfo::DetachSocket(std::string* error_msg) const {
+void FileDescriptorInfo::DetachSocket(fail_fn_t fail_fn) const {
const int dev_null_fd = open("/dev/null", O_RDWR);
if (dev_null_fd < 0) {
- *error_msg = std::string("Failed to open /dev/null: ").append(strerror(errno));
- return false;
+ fail_fn(std::string("Failed to open /dev/null: ").append(strerror(errno)));
}
if (dup2(dev_null_fd, fd) == -1) {
- *error_msg = android::base::StringPrintf("Failed dup2 on socket descriptor %d: %s",
- fd,
- strerror(errno));
- return false;
+ fail_fn(android::base::StringPrintf("Failed dup2 on socket descriptor %d: %s",
+ fd,
+ strerror(errno)));
}
if (close(dev_null_fd) == -1) {
- *error_msg = android::base::StringPrintf("Failed close(%d): %s", dev_null_fd, strerror(errno));
- return false;
+ fail_fn(android::base::StringPrintf("Failed close(%d): %s", dev_null_fd, strerror(errno)));
}
-
- return true;
}
// static
FileDescriptorTable* FileDescriptorTable::Create(const std::vector<int>& fds_to_ignore,
- std::string* error_msg) {
- DIR* d = opendir(kFdPath);
- if (d == nullptr) {
- *error_msg = std::string("Unable to open directory ").append(kFdPath);
- return nullptr;
+ fail_fn_t fail_fn) {
+ DIR* proc_fd_dir = opendir(kFdPath);
+ if (proc_fd_dir == nullptr) {
+ fail_fn(std::string("Unable to open directory ").append(kFdPath));
}
- int dir_fd = dirfd(d);
- dirent* e;
+
+ int dir_fd = dirfd(proc_fd_dir);
+ dirent* dir_entry;
std::unordered_map<int, FileDescriptorInfo*> open_fd_map;
- while ((e = readdir(d)) != NULL) {
- const int fd = ParseFd(e, dir_fd);
+ while ((dir_entry = readdir(proc_fd_dir)) != nullptr) {
+ const int fd = ParseFd(dir_entry, dir_fd);
if (fd == -1) {
continue;
}
+
if (std::find(fds_to_ignore.begin(), fds_to_ignore.end(), fd) != fds_to_ignore.end()) {
LOG(INFO) << "Ignoring open file descriptor " << fd;
continue;
}
- FileDescriptorInfo* info = FileDescriptorInfo::CreateFromFd(fd, error_msg);
- if (info == NULL) {
- if (closedir(d) == -1) {
- PLOG(ERROR) << "Unable to close directory";
- }
- return NULL;
- }
- open_fd_map[fd] = info;
+ open_fd_map[fd] = FileDescriptorInfo::CreateFromFd(fd, fail_fn);
}
- if (closedir(d) == -1) {
- *error_msg = "Unable to close directory";
- return nullptr;
+ if (closedir(proc_fd_dir) == -1) {
+ fail_fn("Unable to close directory");
}
+
return new FileDescriptorTable(open_fd_map);
}
-bool FileDescriptorTable::Restat(const std::vector<int>& fds_to_ignore, std::string* error_msg) {
+void FileDescriptorTable::Restat(const std::vector<int>& fds_to_ignore, fail_fn_t fail_fn) {
std::set<int> open_fds;
// First get the list of open descriptors.
- DIR* d = opendir(kFdPath);
- if (d == NULL) {
- *error_msg = android::base::StringPrintf("Unable to open directory %s: %s",
- kFdPath,
- strerror(errno));
- return false;
+ DIR* proc_fd_dir = opendir(kFdPath);
+ if (proc_fd_dir == nullptr) {
+ fail_fn(android::base::StringPrintf("Unable to open directory %s: %s",
+ kFdPath,
+ strerror(errno)));
}
- int dir_fd = dirfd(d);
- dirent* e;
- while ((e = readdir(d)) != NULL) {
- const int fd = ParseFd(e, dir_fd);
+ int dir_fd = dirfd(proc_fd_dir);
+ dirent* dir_entry;
+ while ((dir_entry = readdir(proc_fd_dir)) != nullptr) {
+ const int fd = ParseFd(dir_entry, dir_fd);
if (fd == -1) {
continue;
}
+
if (std::find(fds_to_ignore.begin(), fds_to_ignore.end(), fd) != fds_to_ignore.end()) {
LOG(INFO) << "Ignoring open file descriptor " << fd;
continue;
@@ -507,27 +491,24 @@ bool FileDescriptorTable::Restat(const std::vector<int>& fds_to_ignore, std::str
open_fds.insert(fd);
}
- if (closedir(d) == -1) {
- *error_msg = android::base::StringPrintf("Unable to close directory: %s", strerror(errno));
- return false;
+ if (closedir(proc_fd_dir) == -1) {
+ fail_fn(android::base::StringPrintf("Unable to close directory: %s", strerror(errno)));
}
- return RestatInternal(open_fds, error_msg);
+ RestatInternal(open_fds, fail_fn);
}
-// Reopens all file descriptors that are contained in the table. Returns true
-// if all descriptors were successfully re-opened or detached, and false if an
-// error occurred.
-bool FileDescriptorTable::ReopenOrDetach(std::string* error_msg) {
+// Reopens all file descriptors that are contained in the table.
+void FileDescriptorTable::ReopenOrDetach(fail_fn_t fail_fn) {
std::unordered_map<int, FileDescriptorInfo*>::const_iterator it;
for (it = open_fd_map_.begin(); it != open_fd_map_.end(); ++it) {
const FileDescriptorInfo* info = it->second;
- if (info == NULL || !info->ReopenOrDetach(error_msg)) {
- return false;
+ if (info == nullptr) {
+ return;
+ } else {
+ info->ReopenOrDetach(fail_fn);
}
}
-
- return true;
}
FileDescriptorTable::FileDescriptorTable(
@@ -535,9 +516,7 @@ FileDescriptorTable::FileDescriptorTable(
: open_fd_map_(map) {
}
-bool FileDescriptorTable::RestatInternal(std::set<int>& open_fds, std::string* error_msg) {
- bool error = false;
-
+void FileDescriptorTable::RestatInternal(std::set<int>& open_fds, fail_fn_t fail_fn) {
// Iterate through the list of file descriptors we've already recorded
// and check whether :
//
@@ -560,28 +539,18 @@ bool FileDescriptorTable::RestatInternal(std::set<int>& open_fds, std::string* e
} else {
// The entry from the file descriptor table is still open. Restat
// it and check whether it refers to the same file.
- const bool same_file = it->second->Restat();
- if (!same_file) {
+ if (!it->second->RefersToSameFile()) {
// The file descriptor refers to a different description. We must
// update our entry in the table.
delete it->second;
- it->second = FileDescriptorInfo::CreateFromFd(*element, error_msg);
- if (it->second == NULL) {
- // The descriptor no longer no longer refers to a whitelisted file.
- // We flag an error and remove it from the list of files we're
- // tracking.
- error = true;
- it = open_fd_map_.erase(it);
- } else {
- // Successfully restatted the file, move on to the next open FD.
- ++it;
- }
+ it->second = FileDescriptorInfo::CreateFromFd(*element, fail_fn);
} else {
// It's the same file. Nothing to do here. Move on to the next open
// FD.
- ++it;
}
+ ++it;
+
// Finally, remove the FD from the set of open_fds. We do this last because
// |element| will not remain valid after a call to erase.
open_fds.erase(element);
@@ -600,25 +569,15 @@ bool FileDescriptorTable::RestatInternal(std::set<int>& open_fds, std::string* e
std::set<int>::const_iterator it;
for (it = open_fds.begin(); it != open_fds.end(); ++it) {
const int fd = (*it);
- FileDescriptorInfo* info = FileDescriptorInfo::CreateFromFd(fd, error_msg);
- if (info == NULL) {
- // A newly opened file is not on the whitelist. Flag an error and
- // continue.
- error = true;
- } else {
- // Track the newly opened file.
- open_fd_map_[fd] = info;
- }
+ open_fd_map_[fd] = FileDescriptorInfo::CreateFromFd(fd, fail_fn);
}
}
-
- return !error;
}
// static
-int FileDescriptorTable::ParseFd(dirent* e, int dir_fd) {
+int FileDescriptorTable::ParseFd(dirent* dir_entry, int dir_fd) {
char* end;
- const int fd = strtol(e->d_name, &end, 10);
+ const int fd = strtol(dir_entry->d_name, &end, 10);
if ((*end) != '\0') {
return -1;
}
diff --git a/core/jni/fd_utils.h b/core/jni/fd_utils.h
index 09022a2e2408..2caf1575981a 100644
--- a/core/jni/fd_utils.h
+++ b/core/jni/fd_utils.h
@@ -30,6 +30,9 @@
class FileDescriptorInfo;
+// This type is duplicated in com_android_internal_os_Zygote.cpp
+typedef const std::function<void(std::string)>& fail_fn_t;
+
// Whitelist of open paths that the zygote is allowed to keep open.
//
// In addition to the paths listed in kPathWhitelist in file_utils.cpp, and
@@ -76,19 +79,19 @@ class FileDescriptorTable {
// /proc/self/fd for the list of open file descriptors and collects
// information about them. Returns NULL if an error occurs.
static FileDescriptorTable* Create(const std::vector<int>& fds_to_ignore,
- std::string* error_msg);
+ fail_fn_t fail_fn);
- bool Restat(const std::vector<int>& fds_to_ignore, std::string* error_msg);
+ void Restat(const std::vector<int>& fds_to_ignore, fail_fn_t fail_fn);
// Reopens all file descriptors that are contained in the table. Returns true
// if all descriptors were successfully re-opened or detached, and false if an
// error occurred.
- bool ReopenOrDetach(std::string* error_msg);
+ void ReopenOrDetach(fail_fn_t fail_fn);
private:
explicit FileDescriptorTable(const std::unordered_map<int, FileDescriptorInfo*>& map);
- bool RestatInternal(std::set<int>& open_fds, std::string* error_msg);
+ void RestatInternal(std::set<int>& open_fds, fail_fn_t fail_fn);
static int ParseFd(dirent* e, int dir_fd);