Specialize does not need to close logd_fd

This commit is contained in:
topjohnwu 2022-09-06 03:01:39 -07:00
parent 6e299018a4
commit 4e14dab60a

View File

@ -33,6 +33,7 @@ enum {
SERVER_FORK_AND_SPECIALIZE, SERVER_FORK_AND_SPECIALIZE,
DO_REVERT_UNMOUNT, DO_REVERT_UNMOUNT,
CAN_UNLOAD_ZYGISK, CAN_UNLOAD_ZYGISK,
SKIP_CLOSE_LOGD_FD,
FLAG_MAX FLAG_MAX
}; };
@ -41,6 +42,8 @@ enum {
void name##_pre(); \ void name##_pre(); \
void name##_post(); void name##_post();
#define MAX_FD_SIZE 1024
struct HookContext { struct HookContext {
JNIEnv *env; JNIEnv *env;
union { union {
@ -55,12 +58,12 @@ struct HookContext {
int pid; int pid;
bitset<FLAG_MAX> flags; bitset<FLAG_MAX> flags;
uint32_t info_flags; uint32_t info_flags;
bitset<MAX_FD_SIZE> allowed_fds;
HookContext() : env(nullptr), args{nullptr}, process(nullptr), pid(-1), info_flags(0) {} HookContext() : env(nullptr), args{nullptr}, process(nullptr), pid(-1), info_flags(0) {}
static void pre_specialize_start(DIR *dir, bitset<1024> &allowed_fds); void setup_fd_ignore();
void pre_specialize_end(DIR *dir, bitset<1024> &allowed_fds); void sanitize_fds();
bool setup_fd_ignore(bitset<1024> *allowed_fds = nullptr);
void run_modules_pre(const vector<int> &fds); void run_modules_pre(const vector<int> &fds);
void run_modules_post(); void run_modules_post();
@ -149,7 +152,6 @@ DCL_HOOK_FUNC(int, jniRegisterNativeMethods,
} }
// Skip actual fork and return cached result if applicable // Skip actual fork and return cached result if applicable
// Also unload first stage zygisk if necessary
DCL_HOOK_FUNC(int, fork) { DCL_HOOK_FUNC(int, fork) {
return (g_ctx && g_ctx->pid >= 0) ? g_ctx->pid : old_fork(); return (g_ctx && g_ctx->pid >= 0) ? g_ctx->pid : old_fork();
} }
@ -174,12 +176,18 @@ DCL_HOOK_FUNC(int, unshare, int flags) {
return res; return res;
} }
// A place to clean things up before zygote evaluates fd table // Close logd_fd if necessary to prevent crashing
// For more info, check comments in zygisk_log_write
DCL_HOOK_FUNC(void, android_log_close) { DCL_HOOK_FUNC(void, android_log_close) {
if (g_ctx == nullptr || !g_ctx->flags[APP_SPECIALIZE]) { if (g_ctx == nullptr || g_ctx->pid > 0) {
// Close fd to prevent crashing. // This happens either in the zygote daemon or during nativeForkApp, nativeForkUsap etc.
// For more info, check comments in zygisk_log_write.
close(logd_fd.exchange(-1)); close(logd_fd.exchange(-1));
// Do NOT revert back to android logging
} else if (!g_ctx->flags[SKIP_CLOSE_LOGD_FD]) {
close(logd_fd.exchange(-1));
// Switch to plain old android logging because we cannot talk
// to magiskd to fetch our log pipe afterwards anyways.
android_logging();
} }
old_android_log_close(); old_android_log_close();
} }
@ -381,79 +389,96 @@ uint32_t ZygiskModule::getFlags() {
// ----------------------------------------------------------------- // -----------------------------------------------------------------
bool HookContext::setup_fd_ignore(bitset<1024> *allowed_fds) { void HookContext::setup_fd_ignore() {
if (flags[APP_SPECIALIZE]) { if (flags[APP_FORK_AND_SPECIALIZE]) {
if (args.app->fds_to_ignore == nullptr) { if (args.app->fds_to_ignore == nullptr) {
// The field fds_to_ignore don't exist if // The field fds_to_ignore don't exist before Android 8.0, which FDs are not checked
// - Running before Android 8.0 flags[SKIP_CLOSE_LOGD_FD] = true;
// - Called by nativeSpecializeAppProcess return;
// In either case, FDs are not checked, so we can simply skip
return true;
} }
bool success = false; bool success = false;
if (jintArray fdsToIgnore = *args.app->fds_to_ignore) { if (jintArray fdsToIgnore = *args.app->fds_to_ignore) {
int *arr = env->GetIntArrayElements(fdsToIgnore, nullptr); int *arr = env->GetIntArrayElements(fdsToIgnore, nullptr);
int len = env->GetArrayLength(fdsToIgnore); int len = env->GetArrayLength(fdsToIgnore);
if (allowed_fds) {
for (int i = 0; i < len; ++i) { for (int i = 0; i < len; ++i) {
int fd = arr[i]; int fd = arr[i];
if (fd >= 0 && fd < 1024) { if (fd >= 0 && fd < MAX_FD_SIZE) {
(*allowed_fds)[fd] = true; allowed_fds[fd] = true;
}
} }
} }
jintArray newFdList = nullptr; jintArray newFdList = nullptr;
if (logd_fd >= 0 && (newFdList = env->NewIntArray(len + 1))) { if (logd_fd >= 0 && (newFdList = env->NewIntArray(len + 1))) {
success = true;
env->SetIntArrayRegion(newFdList, 0, len, arr); env->SetIntArrayRegion(newFdList, 0, len, arr);
int fd = logd_fd; int fd = logd_fd;
env->SetIntArrayRegion(newFdList, len, 1, &fd); env->SetIntArrayRegion(newFdList, len, 1, &fd);
*args.app->fds_to_ignore = newFdList; *args.app->fds_to_ignore = newFdList;
success = true;
} }
env->ReleaseIntArrayElements(fdsToIgnore, arr, JNI_ABORT); env->ReleaseIntArrayElements(fdsToIgnore, arr, JNI_ABORT);
} else { } else {
jintArray newFdList = nullptr; jintArray newFdList = nullptr;
if (logd_fd >= 0 && (newFdList = env->NewIntArray(1))) { if (logd_fd >= 0 && (newFdList = env->NewIntArray(1))) {
success = true;
int fd = logd_fd; int fd = logd_fd;
env->SetIntArrayRegion(newFdList, 0, 1, &fd); env->SetIntArrayRegion(newFdList, 0, 1, &fd);
*args.app->fds_to_ignore = newFdList; *args.app->fds_to_ignore = newFdList;
success = true;
} }
} }
return success; flags[SKIP_CLOSE_LOGD_FD] = success;
} }
return false;
} }
void HookContext::pre_specialize_start(DIR *dir, bitset<1024> &allowed_fds) { void HookContext::sanitize_fds() {
// Record all open fds setup_fd_ignore();
for (dirent *entry; (entry = xreaddir(dir));) {
// Close all forbidden fds to prevent crashing
auto dir = xopen_dir("/proc/self/fd");
rewinddir(dir.get());
int dfd = dirfd(dir.get());
for (dirent *entry; (entry = xreaddir(dir.get()));) {
int fd = parse_int(entry->d_name); int fd = parse_int(entry->d_name);
if (fd < 0 || fd >= 1024) { if ((fd < 0 || fd >= MAX_FD_SIZE || !allowed_fds[fd]) && fd != dfd) {
close(fd);
}
}
}
int sigmask(int how, int signum) {
sigset_t set;
sigemptyset(&set);
sigaddset(&set, signum);
return sigprocmask(how, &set, nullptr);
}
void HookContext::fork_pre() {
g_ctx = this;
// Do our own fork before loading any 3rd party code
// First block SIGCHLD, unblock after original fork is done
sigmask(SIG_BLOCK, SIGCHLD);
pid = old_fork();
if (pid == 0) {
// Record all open fds
auto dir = xopen_dir("/proc/self/fd");
for (dirent *entry; (entry = xreaddir(dir.get()));) {
int fd = parse_int(entry->d_name);
if (fd < 0 || fd >= MAX_FD_SIZE) {
close(fd); close(fd);
continue; continue;
} }
allowed_fds[fd] = true; allowed_fds[fd] = true;
} }
} // The dirfd should not be allowed
allowed_fds[dirfd(dir.get())] = false;
void HookContext::pre_specialize_end(DIR *dir, bitset<1024> &allowed_fds) {
bool disable_zygisk_logging = !setup_fd_ignore(&allowed_fds);
// Close all forbidden fds to prevent crashing
rewinddir(dir);
for (dirent *entry; (entry = xreaddir(dir));) {
int fd = parse_int(entry->d_name);
if (fd < 0 || fd >= 1024 || !allowed_fds[fd]) {
close(fd);
} }
} }
if (disable_zygisk_logging) { void HookContext::fork_post() {
close(logd_fd.exchange(-1)); // Unblock SIGCHLD in case the original method didn't
android_logging(); sigmask(SIG_UNBLOCK, SIGCHLD);
} g_ctx = nullptr;
unload_zygisk();
} }
void HookContext::run_modules_pre(const vector<int> &fds) { void HookContext::run_modules_pre(const vector<int> &fds) {
@ -528,12 +553,10 @@ void HookContext::nativeSpecializeAppProcess_pre() {
ZLOGV("pre forkAndSpecialize [%s]\n", process); ZLOGV("pre forkAndSpecialize [%s]\n", process);
} else { } else {
ZLOGV("pre specialize [%s]\n", process); ZLOGV("pre specialize [%s]\n", process);
// App specialize does not check FD
flags[SKIP_CLOSE_LOGD_FD] = true;
} }
bitset<1024> allowed_fds;
auto dir = open_dir("/proc/self/fd");
pre_specialize_start(dir.get(), allowed_fds);
vector<int> module_fds; vector<int> module_fds;
int fd = remote_get_info(args.app->uid, process, &info_flags, module_fds); int fd = remote_get_info(args.app->uid, process, &info_flags, module_fds);
if ((info_flags & UNMOUNT_MASK) == UNMOUNT_MASK) { if ((info_flags & UNMOUNT_MASK) == UNMOUNT_MASK) {
@ -543,8 +566,6 @@ void HookContext::nativeSpecializeAppProcess_pre() {
run_modules_pre(module_fds); run_modules_pre(module_fds);
} }
close(fd); close(fd);
pre_specialize_end(dir.get(), allowed_fds);
} }
void HookContext::nativeSpecializeAppProcess_post() { void HookContext::nativeSpecializeAppProcess_post() {
@ -577,10 +598,6 @@ void HookContext::nativeForkSystemServer_pre() {
ZLOGV("pre forkSystemServer\n"); ZLOGV("pre forkSystemServer\n");
bitset<1024> allowed_fds;
auto dir = open_dir("/proc/self/fd");
pre_specialize_start(dir.get(), allowed_fds);
vector<int> module_fds; vector<int> module_fds;
int fd = remote_get_info(1000, "system_server", &info_flags, module_fds); int fd = remote_get_info(1000, "system_server", &info_flags, module_fds);
if (fd >= 0) { if (fd >= 0) {
@ -602,7 +619,7 @@ void HookContext::nativeForkSystemServer_pre() {
close(fd); close(fd);
} }
pre_specialize_end(dir.get(), allowed_fds); sanitize_fds();
} }
void HookContext::nativeForkSystemServer_post() { void HookContext::nativeForkSystemServer_post() {
@ -618,8 +635,9 @@ void HookContext::nativeForkAndSpecialize_pre() {
flags[APP_FORK_AND_SPECIALIZE] = true; flags[APP_FORK_AND_SPECIALIZE] = true;
if (pid == 0) { if (pid == 0) {
nativeSpecializeAppProcess_pre(); nativeSpecializeAppProcess_pre();
} else if (!setup_fd_ignore()) { sanitize_fds();
close(logd_fd.exchange(-1)); } else {
setup_fd_ignore();
} }
} }
@ -630,28 +648,6 @@ void HookContext::nativeForkAndSpecialize_post() {
fork_post(); fork_post();
} }
int sigmask(int how, int signum) {
sigset_t set;
sigemptyset(&set);
sigaddset(&set, signum);
return sigprocmask(how, &set, nullptr);
}
// Do our own fork before loading any 3rd party code
// First block SIGCHLD, unblock after original fork is done
void HookContext::fork_pre() {
g_ctx = this;
sigmask(SIG_BLOCK, SIGCHLD);
pid = old_fork();
}
// Unblock SIGCHLD in case the original method didn't
void HookContext::fork_post() {
sigmask(SIG_UNBLOCK, SIGCHLD);
g_ctx = nullptr;
unload_zygisk();
}
} // namespace } // namespace
static bool hook_refresh() { static bool hook_refresh() {