diff options
| -rw-r--r-- | core/jni/com_android_internal_os_Zygote.cpp | 214 | ||||
| -rw-r--r-- | core/jni/fd_utils.cpp | 165 | ||||
| -rw-r--r-- | core/jni/fd_utils.h | 54 |
3 files changed, 265 insertions, 168 deletions
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp index d05be2ca787b..7c8a52d1432b 100644 --- a/core/jni/com_android_internal_os_Zygote.cpp +++ b/core/jni/com_android_internal_os_Zygote.cpp @@ -71,6 +71,9 @@ using android::String8; using android::base::StringPrintf; using android::base::WriteStringToFile; +#define CREATE_ERROR(...) StringPrintf("%s:%d: ", __FILE__, __LINE__). \ + append(StringPrintf(__VA_ARGS__)) + static pid_t gSystemServerPid = 0; static const char kZygoteClassName[] = "com/android/internal/os/Zygote"; @@ -186,30 +189,32 @@ static void UnsetChldSignalHandler() { // Calls POSIX setgroups() using the int[] object as an argument. // A NULL argument is tolerated. -static void SetGids(JNIEnv* env, jintArray javaGids) { +static bool SetGids(JNIEnv* env, jintArray javaGids, std::string* error_msg) { if (javaGids == NULL) { - return; + return true; } ScopedIntArrayRO gids(env, javaGids); if (gids.get() == NULL) { - RuntimeAbort(env, __LINE__, "Getting gids int array failed"); + *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) { - std::ostringstream oss; - oss << "setgroups failed: " << strerror(errno) << ", gids.size=" << gids.size(); - RuntimeAbort(env, __LINE__, oss.str().c_str()); + *error_msg = CREATE_ERROR("setgroups failed: %s, gids.size=%zu", strerror(errno), gids.size()); + return false; } + + return true; } // 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 // treated as an empty array. -static void SetRLimits(JNIEnv* env, jobjectArray javaRlimits) { +static bool SetRLimits(JNIEnv* env, jobjectArray javaRlimits, std::string* error_msg) { if (javaRlimits == NULL) { - return; + return true; } rlimit rlim; @@ -219,7 +224,8 @@ static void SetRLimits(JNIEnv* env, jobjectArray javaRlimits) { ScopedLocalRef<jobject> javaRlimitObject(env, env->GetObjectArrayElement(javaRlimits, i)); ScopedIntArrayRO javaRlimit(env, reinterpret_cast<jintArray>(javaRlimitObject.get())); if (javaRlimit.size() != 3) { - RuntimeAbort(env, __LINE__, "rlimits array must have a second dimension of size 3"); + *error_msg = CREATE_ERROR("rlimits array must have a second dimension of size 3"); + return false; } rlim.rlim_cur = javaRlimit[1]; @@ -227,11 +233,13 @@ static void SetRLimits(JNIEnv* env, jobjectArray javaRlimits) { int rc = setrlimit(javaRlimit[0], &rlim); if (rc == -1) { - ALOGE("setrlimit(%d, {%ld, %ld}) failed", javaRlimit[0], rlim.rlim_cur, + *error_msg = CREATE_ERROR("setrlimit(%d, {%ld, %ld}) failed", javaRlimit[0], rlim.rlim_cur, rlim.rlim_max); - RuntimeAbort(env, __LINE__, "setrlimit failed"); + return false; } } + + return true; } // The debug malloc library needs to know whether it's the zygote or a child. @@ -259,14 +267,16 @@ static void SetUpSeccompFilter(uid_t uid) { } } -static void EnableKeepCapabilities(JNIEnv* env) { +static bool EnableKeepCapabilities(std::string* error_msg) { int rc = prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0); if (rc == -1) { - RuntimeAbort(env, __LINE__, "prctl(PR_SET_KEEPCAPS) failed"); + *error_msg = CREATE_ERROR("prctl(PR_SET_KEEPCAPS) failed: %s", strerror(errno)); + return false; } + return true; } -static void DropCapabilitiesBoundingSet(JNIEnv* env) { +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) { @@ -274,14 +284,15 @@ static void DropCapabilitiesBoundingSet(JNIEnv* env) { ALOGE("prctl(PR_CAPBSET_DROP) failed with EINVAL. Please verify " "your kernel is compiled with file capabilities support"); } else { - ALOGE("prctl(PR_CAPBSET_DROP, %d) failed: %s", i, strerror(errno)); - RuntimeAbort(env, __LINE__, "prctl(PR_CAPBSET_DROP) failed"); + *error_msg = CREATE_ERROR("prctl(PR_CAPBSET_DROP, %d) failed: %s", i, strerror(errno)); + return false; } } } + return true; } -static void SetInheritable(JNIEnv* env, uint64_t inheritable) { +static bool SetInheritable(uint64_t inheritable, std::string* error_msg) { __user_cap_header_struct capheader; memset(&capheader, 0, sizeof(capheader)); capheader.version = _LINUX_CAPABILITY_VERSION_3; @@ -289,21 +300,23 @@ static void SetInheritable(JNIEnv* env, uint64_t inheritable) { __user_cap_data_struct capdata[2]; if (capget(&capheader, &capdata[0]) == -1) { - ALOGE("capget failed: %s", strerror(errno)); - RuntimeAbort(env, __LINE__, "capget failed"); + *error_msg = CREATE_ERROR("capget failed: %s", strerror(errno)); + return false; } capdata[0].inheritable = inheritable; capdata[1].inheritable = inheritable >> 32; if (capset(&capheader, &capdata[0]) == -1) { - ALOGE("capset(inh=%" PRIx64 ") failed: %s", inheritable, strerror(errno)); - RuntimeAbort(env, __LINE__, "capset failed"); + *error_msg = CREATE_ERROR("capset(inh=%" PRIx64 ") failed: %s", inheritable, strerror(errno)); + return false; } + + return true; } -static void SetCapabilities(JNIEnv* env, uint64_t permitted, uint64_t effective, - uint64_t inheritable) { +static bool SetCapabilities(uint64_t permitted, uint64_t effective, uint64_t inheritable, + std::string* error_msg) { __user_cap_header_struct capheader; memset(&capheader, 0, sizeof(capheader)); capheader.version = _LINUX_CAPABILITY_VERSION_3; @@ -319,18 +332,20 @@ static void SetCapabilities(JNIEnv* env, uint64_t permitted, uint64_t effective, capdata[1].inheritable = inheritable >> 32; if (capset(&capheader, &capdata[0]) == -1) { - ALOGE("capset(perm=%" PRIx64 ", eff=%" PRIx64 ", inh=%" PRIx64 ") failed: %s", permitted, - effective, inheritable, strerror(errno)); - RuntimeAbort(env, __LINE__, "capset failed"); + *error_msg = CREATE_ERROR("capset(perm=%" PRIx64 ", eff=%" PRIx64 ", inh=%" PRIx64 ") " + "failed: %s", permitted, effective, inheritable, strerror(errno)); + return false; } + return true; } -static void SetSchedulerPolicy(JNIEnv* env) { +static bool SetSchedulerPolicy(std::string* error_msg) { errno = -set_sched_policy(0, SP_DEFAULT); if (errno != 0) { - ALOGE("set_sched_policy(0, SP_DEFAULT) failed"); - RuntimeAbort(env, __LINE__, "set_sched_policy(0, SP_DEFAULT) failed"); + *error_msg = CREATE_ERROR("set_sched_policy(0, SP_DEFAULT) failed: %s", strerror(errno)); + return false; } + return true; } static int UnmountTree(const char* path) { @@ -364,7 +379,7 @@ static int UnmountTree(const char* path) { // 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) { + bool force_mount_namespace, std::string* error_msg) { // See storage config details at http://source.android.com/tech/storage/ String8 storageSource; @@ -381,7 +396,7 @@ static bool MountEmulatedStorage(uid_t uid, jint mount_mode, // Create a second private mount namespace for our process if (unshare(CLONE_NEWNS) == -1) { - ALOGW("Failed to unshare(): %s", strerror(errno)); + *error_msg = CREATE_ERROR("Failed to unshare(): %s", strerror(errno)); return false; } @@ -392,7 +407,9 @@ static bool MountEmulatedStorage(uid_t uid, jint mount_mode, if (TEMP_FAILURE_RETRY(mount(storageSource.string(), "/storage", NULL, MS_BIND | MS_REC | MS_SLAVE, NULL)) == -1) { - ALOGW("Failed to mount %s to /storage: %s", storageSource.string(), strerror(errno)); + *error_msg = CREATE_ERROR("Failed to mount %s to /storage: %s", + storageSource.string(), + strerror(errno)); return false; } @@ -400,11 +417,14 @@ static bool MountEmulatedStorage(uid_t uid, jint mount_mode, 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; } if (TEMP_FAILURE_RETRY(mount(userSource.string(), "/storage/self", NULL, MS_BIND, NULL)) == -1) { - ALOGW("Failed to mount %s to /storage/self: %s", userSource.string(), strerror(errno)); + *error_msg = CREATE_ERROR("Failed to mount %s to /storage/self: %s", + userSource.string(), + strerror(errno)); return false; } @@ -436,31 +456,32 @@ static bool NeedsNoRandomizeWorkaround() { // descriptor (if any) is closed via dup2(), replacing it with a valid // (open) descriptor to /dev/null. -static void DetachDescriptors(JNIEnv* env, jintArray fdsToClose) { +static bool DetachDescriptors(JNIEnv* env, jintArray fdsToClose, std::string* error_msg) { if (!fdsToClose) { - return; + return true; } jsize count = env->GetArrayLength(fdsToClose); ScopedIntArrayRO ar(env, fdsToClose); if (ar.get() == NULL) { - RuntimeAbort(env, __LINE__, "Bad fd array"); + *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) { - ALOGE("Failed to open /dev/null: %s", strerror(errno)); - RuntimeAbort(env, __LINE__, "Failed to open /dev/null"); - continue; + *error_msg = std::string("Failed to open /dev/null: ").append(strerror(errno)); + return false; } ALOGV("Switching descriptor %d to /dev/null: %s", ar[i], strerror(errno)); if (dup2(devnull, ar[i]) < 0) { - ALOGE("Failed dup2() on descriptor %d: %s", ar[i], strerror(errno)); - RuntimeAbort(env, __LINE__, "Failed dup2()"); + *error_msg = StringPrintf("Failed dup2() on descriptor %d: %s", ar[i], strerror(errno)); + return false; } close(devnull); } + return true; } void SetThreadName(const char* thread_name) { @@ -495,20 +516,23 @@ void SetThreadName(const char* thread_name) { // The list of open zygote file descriptors. static FileDescriptorTable* gOpenFdTable = NULL; -static void FillFileDescriptorVector(JNIEnv* env, +static bool FillFileDescriptorVector(JNIEnv* env, jintArray java_fds, - std::vector<int>* fds) { + std::vector<int>* fds, + std::string* error_msg) { CHECK(fds != nullptr); if (java_fds != nullptr) { ScopedIntArrayRO ar(env, java_fds); if (ar.get() == nullptr) { - RuntimeAbort(env, __LINE__, "Bad fd array"); + *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; } // Utility routine to fork zygote and specialize the child process. @@ -526,32 +550,53 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra 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) { - ALOGE("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)); - RuntimeAbort(env, __LINE__, "Call to sigprocmask(SIG_BLOCK, { SIGCHLD }) failed."); + 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(); + 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; - FillFileDescriptorVector(env, fdsToIgnore, &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); + gOpenFdTable = FileDescriptorTable::Create(fds_to_ignore, &error_msg); if (gOpenFdTable == NULL) { - RuntimeAbort(env, __LINE__, "Unable to construct file descriptor table."); + fail_fn(error_msg); } - } else if (!gOpenFdTable->Restat(fds_to_ignore)) { - RuntimeAbort(env, __LINE__, "Unable to restat file descriptor table."); + } else if (!gOpenFdTable->Restat(fds_to_ignore, &error_msg)) { + fail_fn(error_msg); } pid_t pid = fork(); @@ -560,17 +605,18 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra PreApplicationInit(); // Clean up any descriptors which must be closed immediately - DetachDescriptors(env, fdsToClose); + 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()) { - RuntimeAbort(env, __LINE__, "Unable to reopen whitelisted descriptors."); + if (!gOpenFdTable->ReopenOrDetach(&error_msg)) { + fail_fn(error_msg); } if (sigprocmask(SIG_UNBLOCK, &sigchld, nullptr) == -1) { - ALOGE("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)); - RuntimeAbort(env, __LINE__, "Call to sigprocmask(SIG_UNBLOCK, { SIGCHLD }) failed."); + fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno))); } // Must be called when the new process still has CAP_SYS_ADMIN. The other alternative is to @@ -580,11 +626,17 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra // Keep capabilities across UID change, unless we're staying root. if (uid != 0) { - EnableKeepCapabilities(env); + if (!EnableKeepCapabilities(&error_msg)) { + fail_fn(error_msg); + } } - SetInheritable(env, permittedCapabilities); - DropCapabilitiesBoundingSet(env); + if (!SetInheritable(permittedCapabilities, &error_msg)) { + fail_fn(error_msg); + } + if (!DropCapabilitiesBoundingSet(&error_msg)) { + fail_fn(error_msg); + } bool use_native_bridge = !is_system_server && (instructionSet != NULL) && android::NativeBridgeAvailable(); @@ -601,8 +653,8 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra ALOGW("Native bridge will not be used because dataDir == NULL."); } - if (!MountEmulatedStorage(uid, mount_external, use_native_bridge)) { - ALOGW("Failed to mount emulated storage: %s", strerror(errno)); + if (!MountEmulatedStorage(uid, mount_external, use_native_bridge, &error_msg)) { + 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. @@ -610,7 +662,7 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra // FUSE hasn't been created yet by init. // In either case, continue without external storage. } else { - RuntimeAbort(env, __LINE__, "Cannot continue without emulated storage"); + fail_fn(error_msg); } } @@ -625,9 +677,14 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra } } - SetGids(env, javaGids); + std::string error_msg; + if (!SetGids(env, javaGids, &error_msg)) { + fail_fn(error_msg); + } - SetRLimits(env, javaRlimits); + if (!SetRLimits(env, javaRlimits, &error_msg)) { + fail_fn(error_msg); + } if (use_native_bridge) { ScopedUtfChars isa_string(env, instructionSet); @@ -637,14 +694,12 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra int rc = setresgid(gid, gid, gid); if (rc == -1) { - ALOGE("setresgid(%d) failed: %s", gid, strerror(errno)); - RuntimeAbort(env, __LINE__, "setresgid failed"); + fail_fn(CREATE_ERROR("setresgid(%d) failed: %s", gid, strerror(errno))); } rc = setresuid(uid, uid, uid); if (rc == -1) { - ALOGE("setresuid(%d) failed: %s", uid, strerror(errno)); - RuntimeAbort(env, __LINE__, "setresuid failed"); + fail_fn(CREATE_ERROR("setresuid(%d) failed: %s", uid, strerror(errno))); } if (NeedsNoRandomizeWorkaround()) { @@ -656,9 +711,14 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra } } - SetCapabilities(env, permittedCapabilities, effectiveCapabilities, permittedCapabilities); + if (!SetCapabilities(permittedCapabilities, effectiveCapabilities, permittedCapabilities, + &error_msg)) { + fail_fn(error_msg); + } - SetSchedulerPolicy(env); + if (!SetSchedulerPolicy(&error_msg)) { + fail_fn(error_msg); + } const char* se_info_c_str = NULL; ScopedUtfChars* se_info = NULL; @@ -666,7 +726,7 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra se_info = new ScopedUtfChars(env, java_se_info); se_info_c_str = se_info->c_str(); if (se_info_c_str == NULL) { - RuntimeAbort(env, __LINE__, "se_info_c_str == NULL"); + fail_fn("se_info_c_str == NULL"); } } const char* se_name_c_str = NULL; @@ -675,14 +735,13 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra se_name = new ScopedUtfChars(env, java_se_name); se_name_c_str = se_name->c_str(); if (se_name_c_str == NULL) { - RuntimeAbort(env, __LINE__, "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) { - ALOGE("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid, - is_system_server, se_info_c_str, se_name_c_str); - RuntimeAbort(env, __LINE__, "selinux_android_setcontext failed"); + 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 @@ -703,15 +762,14 @@ static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArra env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags, is_system_server, is_child_zygote, instructionSet); if (env->ExceptionCheck()) { - RuntimeAbort(env, __LINE__, "Error calling post fork hooks."); + fail_fn("Error calling post fork hooks."); } } else if (pid > 0) { // the parent process // We blocked SIGCHLD prior to a fork, we unblock it here. if (sigprocmask(SIG_UNBLOCK, &sigchld, nullptr) == -1) { - ALOGE("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno)); - RuntimeAbort(env, __LINE__, "Call to sigprocmask(SIG_UNBLOCK, { SIGCHLD }) failed."); + fail_fn(CREATE_ERROR("sigprocmask(SIG_SETMASK, { SIGCHLD }) failed: %s", strerror(errno))); } } return pid; diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp index 2e6058268115..c5904e0e9e5e 100644 --- a/core/jni/fd_utils.cpp +++ b/core/jni/fd_utils.cpp @@ -123,14 +123,57 @@ FileDescriptorWhitelist::FileDescriptorWhitelist() FileDescriptorWhitelist* FileDescriptorWhitelist::instance_ = nullptr; +// Keeps track of all relevant information (flags, offset etc.) of an +// 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); + + // Checks whether the file descriptor associated with this object + // refers to the same description. + bool Restat() const; + + bool ReopenOrDetach(std::string* error_msg) const; + + const int fd; + const struct stat stat; + const std::string file_path; + const int open_flags; + const int fd_flags; + const int fs_flags; + const off_t offset; + const bool is_sock; + + private: + FileDescriptorInfo(int fd); + + FileDescriptorInfo(struct stat stat, const std::string& file_path, int fd, int open_flags, + int fd_flags, int fs_flags, off_t offset); + + // Returns the locally-bound name of the socket |fd|. Returns true + // iff. all of the following hold : + // + // - the socket's sa_family is AF_UNIX. + // - the length of the path is greater than zero (i.e, not an unnamed socket). + // - the first byte of the path isn't zero (i.e, not a socket with an abstract + // address). + static bool GetSocketName(const int fd, std::string* result); + + bool DetachSocket(std::string* error_msg) const; + + DISALLOW_COPY_AND_ASSIGN(FileDescriptorInfo); +}; + // static -FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd) { +FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd, std::string* error_msg) { 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) { - PLOG(ERROR) << "Unable to stat fd " << fd; - return NULL; + *error_msg = android::base::StringPrintf("Unable to stat %d", fd); + return nullptr; } const FileDescriptorWhitelist* whitelist = FileDescriptorWhitelist::Get(); @@ -138,13 +181,15 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd) { if (S_ISSOCK(f_stat.st_mode)) { std::string socket_name; if (!GetSocketName(fd, &socket_name)) { - return NULL; + *error_msg = "Unable to get socket name"; + return nullptr; } if (!whitelist->IsAllowed(socket_name)) { - LOG(ERROR) << "Socket name not whitelisted : " << socket_name - << " (fd=" << fd << ")"; - return NULL; + *error_msg = android::base::StringPrintf("Socket name not whitelisted : %s (fd=%d)", + socket_name.c_str(), + fd); + return nullptr; } return new FileDescriptorInfo(fd); @@ -161,19 +206,22 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd) { // with the child process across forks but those should have been closed // before we got to this point. if (!S_ISCHR(f_stat.st_mode) && !S_ISREG(f_stat.st_mode)) { - LOG(ERROR) << "Unsupported st_mode " << f_stat.st_mode; - return NULL; + *error_msg = android::base::StringPrintf("Unsupported st_mode %u", f_stat.st_mode); + return nullptr; } 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)) { - return NULL; + *error_msg = android::base::StringPrintf("Could not read fd link %s: %s", + fd_path.c_str(), + strerror(errno)); + return nullptr; } if (!whitelist->IsAllowed(file_path)) { - LOG(ERROR) << "Not whitelisted : " << file_path; - return NULL; + *error_msg = std::string("Not whitelisted : ").append(file_path); + return nullptr; } // File descriptor flags : currently on FD_CLOEXEC. We can set these @@ -181,8 +229,11 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd) { // there won't be any races. const int fd_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD)); if (fd_flags == -1) { - PLOG(ERROR) << "Failed fcntl(" << fd << ", F_GETFD)"; - return NULL; + *error_msg = android::base::StringPrintf("Failed fcntl(%d, F_GETFD) (%s): %s", + fd, + file_path.c_str(), + strerror(errno)); + return nullptr; } // File status flags : @@ -199,8 +250,11 @@ FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd) { // their presence and pass them in to open(). int fs_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL)); if (fs_flags == -1) { - PLOG(ERROR) << "Failed fcntl(" << fd << ", F_GETFL)"; - return NULL; + *error_msg = android::base::StringPrintf("Failed fcntl(%d, F_GETFL) (%s): %s", + fd, + file_path.c_str(), + strerror(errno)); + return nullptr; } // File offset : Ignore the offset for non seekable files. @@ -225,9 +279,9 @@ bool FileDescriptorInfo::Restat() const { return f_stat.st_ino == stat.st_ino && f_stat.st_dev == stat.st_dev; } -bool FileDescriptorInfo::ReopenOrDetach() const { +bool FileDescriptorInfo::ReopenOrDetach(std::string* error_msg) const { if (is_sock) { - return DetachSocket(); + return DetachSocket(error_msg); } // NOTE: This might happen if the file was unlinked after being opened. @@ -236,31 +290,49 @@ bool FileDescriptorInfo::ReopenOrDetach() const { const int new_fd = TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags)); if (new_fd == -1) { - PLOG(ERROR) << "Failed open(" << file_path << ", " << open_flags << ")"; + *error_msg = android::base::StringPrintf("Failed open(%s, %i): %s", + file_path.c_str(), + open_flags, + strerror(errno)); return false; } if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFD, fd_flags)) == -1) { close(new_fd); - PLOG(ERROR) << "Failed fcntl(" << new_fd << ", F_SETFD, " << fd_flags << ")"; + *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; } if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFL, fs_flags)) == -1) { close(new_fd); - PLOG(ERROR) << "Failed fcntl(" << new_fd << ", F_SETFL, " << fs_flags << ")"; + *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; } if (offset != -1 && TEMP_FAILURE_RETRY(lseek64(new_fd, offset, SEEK_SET)) == -1) { close(new_fd); - PLOG(ERROR) << "Failed lseek64(" << new_fd << ", SEEK_SET)"; + *error_msg = android::base::StringPrintf("Failed lseek64(%d, SEEK_SET) (%s): %s", + new_fd, + file_path.c_str(), + strerror(errno)); return false; } if (TEMP_FAILURE_RETRY(dup2(new_fd, fd)) == -1) { close(new_fd); - PLOG(ERROR) << "Failed dup2(" << fd << ", " << new_fd << ")"; + *error_msg = android::base::StringPrintf("Failed dup2(%d, %d) (%s): %s", + fd, + new_fd, + file_path.c_str(), + strerror(errno)); return false; } @@ -336,20 +408,22 @@ bool FileDescriptorInfo::GetSocketName(const int fd, std::string* result) { return true; } -bool FileDescriptorInfo::DetachSocket() const { +bool FileDescriptorInfo::DetachSocket(std::string* error_msg) const { const int dev_null_fd = open("/dev/null", O_RDWR); if (dev_null_fd < 0) { - PLOG(ERROR) << "Failed to open /dev/null"; + *error_msg = std::string("Failed to open /dev/null: ").append(strerror(errno)); return false; } if (dup2(dev_null_fd, fd) == -1) { - PLOG(ERROR) << "Failed dup2 on socket descriptor " << fd; + *error_msg = android::base::StringPrintf("Failed dup2 on socket descriptor %d: %s", + fd, + strerror(errno)); return false; } if (close(dev_null_fd) == -1) { - PLOG(ERROR) << "Failed close(" << dev_null_fd << ")"; + *error_msg = android::base::StringPrintf("Failed close(%d): %s", dev_null_fd, strerror(errno)); return false; } @@ -357,11 +431,12 @@ bool FileDescriptorInfo::DetachSocket() const { } // static -FileDescriptorTable* FileDescriptorTable::Create(const std::vector<int>& fds_to_ignore) { +FileDescriptorTable* FileDescriptorTable::Create(const std::vector<int>& fds_to_ignore, + std::string* error_msg) { DIR* d = opendir(kFdPath); - if (d == NULL) { - PLOG(ERROR) << "Unable to open directory " << std::string(kFdPath); - return NULL; + if (d == nullptr) { + *error_msg = std::string("Unable to open directory ").append(kFdPath); + return nullptr; } int dir_fd = dirfd(d); dirent* e; @@ -377,7 +452,7 @@ FileDescriptorTable* FileDescriptorTable::Create(const std::vector<int>& fds_to_ continue; } - FileDescriptorInfo* info = FileDescriptorInfo::CreateFromFd(fd); + FileDescriptorInfo* info = FileDescriptorInfo::CreateFromFd(fd, error_msg); if (info == NULL) { if (closedir(d) == -1) { PLOG(ERROR) << "Unable to close directory"; @@ -388,19 +463,21 @@ FileDescriptorTable* FileDescriptorTable::Create(const std::vector<int>& fds_to_ } if (closedir(d) == -1) { - PLOG(ERROR) << "Unable to close directory"; - return NULL; + *error_msg = "Unable to close directory"; + return nullptr; } return new FileDescriptorTable(open_fd_map); } -bool FileDescriptorTable::Restat(const std::vector<int>& fds_to_ignore) { +bool FileDescriptorTable::Restat(const std::vector<int>& fds_to_ignore, std::string* error_msg) { std::set<int> open_fds; // First get the list of open descriptors. DIR* d = opendir(kFdPath); if (d == NULL) { - PLOG(ERROR) << "Unable to open directory " << std::string(kFdPath); + *error_msg = android::base::StringPrintf("Unable to open directory %s: %s", + kFdPath, + strerror(errno)); return false; } @@ -420,21 +497,21 @@ bool FileDescriptorTable::Restat(const std::vector<int>& fds_to_ignore) { } if (closedir(d) == -1) { - PLOG(ERROR) << "Unable to close directory"; + *error_msg = android::base::StringPrintf("Unable to close directory: %s", strerror(errno)); return false; } - return RestatInternal(open_fds); + return RestatInternal(open_fds, error_msg); } // 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() { +bool FileDescriptorTable::ReopenOrDetach(std::string* error_msg) { 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()) { + if (info == NULL || !info->ReopenOrDetach(error_msg)) { return false; } } @@ -447,7 +524,7 @@ FileDescriptorTable::FileDescriptorTable( : open_fd_map_(map) { } -bool FileDescriptorTable::RestatInternal(std::set<int>& open_fds) { +bool FileDescriptorTable::RestatInternal(std::set<int>& open_fds, std::string* error_msg) { bool error = false; // Iterate through the list of file descriptors we've already recorded @@ -455,6 +532,8 @@ bool FileDescriptorTable::RestatInternal(std::set<int>& open_fds) { // // (a) they continue to be open. // (b) they refer to the same file. + // + // We'll only store the last error message. std::unordered_map<int, FileDescriptorInfo*>::iterator it = open_fd_map_.begin(); while (it != open_fd_map_.end()) { std::set<int>::const_iterator element = open_fds.find(it->first); @@ -475,7 +554,7 @@ bool FileDescriptorTable::RestatInternal(std::set<int>& open_fds) { // The file descriptor refers to a different description. We must // update our entry in the table. delete it->second; - it->second = FileDescriptorInfo::CreateFromFd(*element); + 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 @@ -510,7 +589,7 @@ bool FileDescriptorTable::RestatInternal(std::set<int>& open_fds) { 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); + FileDescriptorInfo* info = FileDescriptorInfo::CreateFromFd(fd, error_msg); if (info == NULL) { // A newly opened file is not on the whitelist. Flag an error and // continue. diff --git a/core/jni/fd_utils.h b/core/jni/fd_utils.h index a39e387fde6c..a3570d7ed1fb 100644 --- a/core/jni/fd_utils.h +++ b/core/jni/fd_utils.h @@ -28,6 +28,8 @@ #include <android-base/macros.h> +class FileDescriptorInfo; + // 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 @@ -66,49 +68,6 @@ class FileDescriptorWhitelist { DISALLOW_COPY_AND_ASSIGN(FileDescriptorWhitelist); }; -// Keeps track of all relevant information (flags, offset etc.) of an -// 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); - - // Checks whether the file descriptor associated with this object - // refers to the same description. - bool Restat() const; - - bool ReopenOrDetach() const; - - const int fd; - const struct stat stat; - const std::string file_path; - const int open_flags; - const int fd_flags; - const int fs_flags; - const off_t offset; - const bool is_sock; - - private: - FileDescriptorInfo(int fd); - - FileDescriptorInfo(struct stat stat, const std::string& file_path, int fd, int open_flags, - int fd_flags, int fs_flags, off_t offset); - - // Returns the locally-bound name of the socket |fd|. Returns true - // iff. all of the following hold : - // - // - the socket's sa_family is AF_UNIX. - // - the length of the path is greater than zero (i.e, not an unnamed socket). - // - the first byte of the path isn't zero (i.e, not a socket with an abstract - // address). - static bool GetSocketName(const int fd, std::string* result); - - bool DetachSocket() const; - - DISALLOW_COPY_AND_ASSIGN(FileDescriptorInfo); -}; - // A FileDescriptorTable is a collection of FileDescriptorInfo objects // keyed by their FDs. class FileDescriptorTable { @@ -116,19 +75,20 @@ class FileDescriptorTable { // Creates a new FileDescriptorTable. This function scans // /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); + static FileDescriptorTable* Create(const std::vector<int>& fds_to_ignore, + std::string* error_msg); - bool Restat(const std::vector<int>& fds_to_ignore); + bool Restat(const std::vector<int>& fds_to_ignore, std::string* error_msg); // 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(); + bool ReopenOrDetach(std::string* error_msg); private: FileDescriptorTable(const std::unordered_map<int, FileDescriptorInfo*>& map); - bool RestatInternal(std::set<int>& open_fds); + bool RestatInternal(std::set<int>& open_fds, std::string* error_msg); static int ParseFd(dirent* e, int dir_fd); |