Merge changes I14df050f,I55c30f78,I269eae0f

* changes:
  Frameworks: Refactor FileDescriptorTable errors
  Frameworks: Add app name to zygote abort
  Frameworks: Refactor zygote abort code
This commit is contained in:
Treehugger Robot
2018-03-14 15:35:41 +00:00
committed by Gerrit Code Review
3 changed files with 265 additions and 168 deletions

View File

@@ -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;

View File

@@ -119,14 +119,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();
@@ -134,13 +177,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);
@@ -157,19 +202,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
@@ -177,8 +225,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 :
@@ -195,8 +246,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.
@@ -221,9 +275,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.
@@ -232,31 +286,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;
}
@@ -332,20 +404,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;
}
@@ -353,11 +427,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;
@@ -373,7 +448,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";
@@ -384,19 +459,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;
}
@@ -416,21 +493,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;
}
}
@@ -443,7 +520,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
@@ -451,6 +528,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);
@@ -471,7 +550,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
@@ -506,7 +585,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.

View File

@@ -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);