2022-05-18 08:55:58 +00:00
|
|
|
#include <base.hpp>
|
|
|
|
#include <magisk.hpp>
|
|
|
|
#include <daemon.hpp>
|
|
|
|
#include <db.hpp>
|
2022-05-20 11:37:37 +00:00
|
|
|
#include <flags.h>
|
2022-05-18 08:55:58 +00:00
|
|
|
|
2022-05-20 05:54:49 +00:00
|
|
|
#include "core.hpp"
|
|
|
|
|
2022-05-18 08:55:58 +00:00
|
|
|
using namespace std;
|
|
|
|
|
2022-05-24 12:21:36 +00:00
|
|
|
#define ENFORCE_SIGNATURE (!MAGISK_DEBUG)
|
|
|
|
|
2022-05-18 08:55:58 +00:00
|
|
|
// These functions will be called on every single zygote process specialization and su request,
|
|
|
|
// so performance is absolutely critical. Most operations should either have its result cached
|
|
|
|
// or simply skipped unless necessary.
|
|
|
|
|
2022-05-29 05:39:44 +00:00
|
|
|
atomic<ino_t> pkg_xml_ino = 0;
|
2022-05-30 06:31:57 +00:00
|
|
|
static atomic_flag skip_mgr_check;
|
2022-05-18 08:55:58 +00:00
|
|
|
|
|
|
|
static pthread_mutex_t pkg_lock = PTHREAD_MUTEX_INITIALIZER;
|
2022-05-20 05:54:49 +00:00
|
|
|
// pkg_lock protects all following variables
|
2022-05-18 08:55:58 +00:00
|
|
|
static int mgr_app_id = -1;
|
|
|
|
static string *mgr_pkg;
|
2022-05-20 11:37:37 +00:00
|
|
|
static string *mgr_cert;
|
2022-05-20 05:54:49 +00:00
|
|
|
static int stub_apk_fd = -1;
|
2022-05-20 11:37:37 +00:00
|
|
|
static const string *default_cert;
|
2022-05-18 08:55:58 +00:00
|
|
|
|
2023-06-14 14:24:42 +00:00
|
|
|
static string read_certificate(int fd, int version) {
|
|
|
|
auto cert = rust::read_certificate(fd, version);
|
|
|
|
return string{cert.begin(), cert.end()};
|
|
|
|
}
|
|
|
|
|
2022-05-30 06:31:57 +00:00
|
|
|
void check_pkg_refresh() {
|
2022-05-18 08:55:58 +00:00
|
|
|
struct stat st{};
|
2022-05-30 06:31:57 +00:00
|
|
|
if (stat("/data/system/packages.xml", &st) == 0 &&
|
|
|
|
pkg_xml_ino.exchange(st.st_ino) != st.st_ino) {
|
|
|
|
skip_mgr_check.clear();
|
|
|
|
skip_pkg_rescan.clear();
|
2022-05-18 08:55:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// app_id = app_no + AID_APP_START
|
|
|
|
// app_no range: [0, 9999]
|
|
|
|
vector<bool> get_app_no_list() {
|
|
|
|
vector<bool> list;
|
|
|
|
auto data_dir = xopen_dir(APP_DATA_DIR);
|
|
|
|
if (!data_dir)
|
|
|
|
return list;
|
|
|
|
dirent *entry;
|
|
|
|
while ((entry = xreaddir(data_dir.get()))) {
|
|
|
|
// For each user
|
|
|
|
int dfd = xopenat(dirfd(data_dir.get()), entry->d_name, O_RDONLY);
|
|
|
|
if (auto dir = xopen_dir(dfd)) {
|
|
|
|
while ((entry = xreaddir(dir.get()))) {
|
|
|
|
// For each package
|
|
|
|
struct stat st{};
|
|
|
|
xfstatat(dfd, entry->d_name, &st, 0);
|
|
|
|
int app_id = to_app_id(st.st_uid);
|
|
|
|
if (app_id >= AID_APP_START && app_id <= AID_APP_END) {
|
|
|
|
int app_no = app_id - AID_APP_START;
|
|
|
|
if (list.size() <= app_no) {
|
|
|
|
list.resize(app_no + 1);
|
|
|
|
}
|
|
|
|
list[app_no] = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
close(dfd);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return list;
|
|
|
|
}
|
|
|
|
|
2022-05-20 05:54:49 +00:00
|
|
|
void preserve_stub_apk() {
|
|
|
|
mutex_guard g(pkg_lock);
|
|
|
|
string stub_path = MAGISKTMP + "/stub.apk";
|
|
|
|
stub_apk_fd = xopen(stub_path.data(), O_RDONLY | O_CLOEXEC);
|
|
|
|
unlink(stub_path.data());
|
2023-06-14 14:24:42 +00:00
|
|
|
auto cert = read_certificate(stub_apk_fd, -1);
|
|
|
|
if (!cert.empty())
|
|
|
|
default_cert = new string(std::move(cert));
|
2022-05-20 11:37:37 +00:00
|
|
|
lseek(stub_apk_fd, 0, SEEK_SET);
|
2022-05-20 05:54:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static void install_stub() {
|
|
|
|
if (stub_apk_fd < 0)
|
|
|
|
return;
|
|
|
|
struct stat st{};
|
|
|
|
fstat(stub_apk_fd, &st);
|
|
|
|
char apk[] = "/data/stub.apk";
|
|
|
|
int dfd = xopen(apk, O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, 0600);
|
|
|
|
xsendfile(dfd, stub_apk_fd, nullptr, st.st_size);
|
|
|
|
lseek(stub_apk_fd, 0, SEEK_SET);
|
|
|
|
close(dfd);
|
|
|
|
install_apk(apk);
|
|
|
|
}
|
|
|
|
|
|
|
|
int get_manager(int user_id, string *pkg, bool install) {
|
2022-05-18 08:55:58 +00:00
|
|
|
mutex_guard g(pkg_lock);
|
|
|
|
|
|
|
|
char app_path[128];
|
|
|
|
struct stat st{};
|
|
|
|
if (mgr_pkg == nullptr)
|
|
|
|
default_new(mgr_pkg);
|
2022-05-20 11:37:37 +00:00
|
|
|
if (mgr_cert == nullptr)
|
|
|
|
default_new(mgr_cert);
|
2022-05-18 08:55:58 +00:00
|
|
|
|
2022-05-24 12:21:36 +00:00
|
|
|
auto check_dyn = [&](int u) -> bool {
|
2022-05-24 12:39:16 +00:00
|
|
|
#if ENFORCE_SIGNATURE
|
2022-09-09 11:29:50 +00:00
|
|
|
ssprintf(app_path, sizeof(app_path),
|
2022-05-24 12:21:36 +00:00
|
|
|
"%s/%d/%s/dyn/current.apk", APP_DATA_DIR, u, mgr_pkg->data());
|
|
|
|
int dyn = open(app_path, O_RDONLY | O_CLOEXEC);
|
2022-06-26 09:08:24 +00:00
|
|
|
if (dyn < 0) {
|
|
|
|
LOGW("pkg: no dyn APK, ignore\n");
|
2022-05-24 12:21:36 +00:00
|
|
|
return false;
|
2022-06-26 09:08:24 +00:00
|
|
|
}
|
2023-06-14 14:24:42 +00:00
|
|
|
auto cert = read_certificate(dyn, MAGISK_VER_CODE);
|
|
|
|
bool mismatch = default_cert && cert != *default_cert;
|
2022-05-24 12:21:36 +00:00
|
|
|
close(dyn);
|
|
|
|
if (mismatch) {
|
|
|
|
LOGE("pkg: dyn APK signature mismatch: %s\n", app_path);
|
|
|
|
clear_pkg(mgr_pkg->data(), u);
|
|
|
|
return false;
|
|
|
|
}
|
2022-05-24 12:39:16 +00:00
|
|
|
#endif
|
2022-05-24 12:21:36 +00:00
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
2022-05-30 06:31:57 +00:00
|
|
|
if (skip_mgr_check.test_and_set()) {
|
2023-01-20 05:11:37 +00:00
|
|
|
if (mgr_app_id >= 0) {
|
|
|
|
// Just need to check whether the app is installed in the user
|
|
|
|
const char *name = mgr_pkg->empty() ? JAVA_PACKAGE_NAME : mgr_pkg->data();
|
|
|
|
ssprintf(app_path, sizeof(app_path), "%s/%d/%s", APP_DATA_DIR, user_id, name);
|
|
|
|
if (access(app_path, F_OK) == 0) {
|
|
|
|
// Always check dyn signature for repackaged app
|
|
|
|
if (!mgr_pkg->empty() && !check_dyn(user_id))
|
Fix race condition when switching root manager
Before this change, the root manager package name is only written into
the database after the repackaged APK is installed. In the time between
the repackaged APK being installed and the package name being written
into the database, if some operation calls `get_manager`, the Magisk
daemon will cache this result and ignore the repackaged APK, even if
the package name is set afterwards, because the cache won't be
invalidated. The result is that the repackaged manager APK will not be
recognized as the root manager, breaking the hide manager feature.
This race condition is more likely to happen when Zygisk is enabled,
because `get_manager` is called with a very high frequency in that case.
To fix the issue, we have to set the new package name into the database
BEFORE installing the repackaged APK. We also stop pruning the
database if the repackaged manager is not found, moving this logic into
the Magisk app. By doing so, we can guarantee that the instant after
the repackaged manager APK is installed, the Magisk daemon will
immediately pick it up and treat it as the root manager.
Another small optimization: when the requester is root, simply bypass
the whole database + manager package check. Since the Magisk app hiding
APK installation proces will call `su` several times to run `pm` under
different UIDs, doing this opimization will reduce the amount of
unnecessary database query + filesystem traversals.
2023-03-06 11:25:59 +00:00
|
|
|
goto ignore;
|
2023-01-20 05:11:37 +00:00
|
|
|
if (pkg) *pkg = name;
|
|
|
|
return user_id * AID_USER_OFFSET + mgr_app_id;
|
|
|
|
} else {
|
2022-05-24 12:21:36 +00:00
|
|
|
goto not_found;
|
2023-01-20 05:11:37 +00:00
|
|
|
}
|
2022-05-18 08:55:58 +00:00
|
|
|
}
|
|
|
|
} else {
|
2022-05-19 09:39:57 +00:00
|
|
|
// Here, we want to actually find the manager app and cache the results.
|
|
|
|
// This means that we check all users, not just the requested user.
|
2022-05-20 11:37:37 +00:00
|
|
|
// Certificates are also verified to prevent manipulation.
|
2022-05-19 09:39:57 +00:00
|
|
|
|
2022-05-18 08:55:58 +00:00
|
|
|
db_strings str;
|
|
|
|
get_db_strings(str, SU_MANAGER);
|
|
|
|
|
2022-05-19 09:39:57 +00:00
|
|
|
vector<int> users;
|
|
|
|
bool collected = false;
|
|
|
|
|
|
|
|
auto collect_users = [&] {
|
|
|
|
if (collected)
|
|
|
|
return;
|
|
|
|
collected = true;
|
|
|
|
auto data_dir = xopen_dir(APP_DATA_DIR);
|
|
|
|
if (!data_dir)
|
|
|
|
return;
|
|
|
|
dirent *entry;
|
|
|
|
while ((entry = xreaddir(data_dir.get()))) {
|
|
|
|
// Only collect users not requested as we've already checked it
|
|
|
|
if (int u = parse_int(entry->d_name); u >= 0 && u != user_id)
|
|
|
|
users.push_back(parse_int(entry->d_name));
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-05-18 08:55:58 +00:00
|
|
|
if (!str[SU_MANAGER].empty()) {
|
2022-05-19 09:39:57 +00:00
|
|
|
// Check the repackaged package name
|
|
|
|
|
2022-05-20 11:37:37 +00:00
|
|
|
bool invalid = false;
|
Fix race condition when switching root manager
Before this change, the root manager package name is only written into
the database after the repackaged APK is installed. In the time between
the repackaged APK being installed and the package name being written
into the database, if some operation calls `get_manager`, the Magisk
daemon will cache this result and ignore the repackaged APK, even if
the package name is set afterwards, because the cache won't be
invalidated. The result is that the repackaged manager APK will not be
recognized as the root manager, breaking the hide manager feature.
This race condition is more likely to happen when Zygisk is enabled,
because `get_manager` is called with a very high frequency in that case.
To fix the issue, we have to set the new package name into the database
BEFORE installing the repackaged APK. We also stop pruning the
database if the repackaged manager is not found, moving this logic into
the Magisk app. By doing so, we can guarantee that the instant after
the repackaged manager APK is installed, the Magisk daemon will
immediately pick it up and treat it as the root manager.
Another small optimization: when the requester is root, simply bypass
the whole database + manager package check. Since the Magisk app hiding
APK installation proces will call `su` several times to run `pm` under
different UIDs, doing this opimization will reduce the amount of
unnecessary database query + filesystem traversals.
2023-03-06 11:25:59 +00:00
|
|
|
auto check_stub_apk = [&](int u) -> bool {
|
2022-09-09 11:29:50 +00:00
|
|
|
ssprintf(app_path, sizeof(app_path),
|
2022-05-19 09:39:57 +00:00
|
|
|
"%s/%d/%s", APP_DATA_DIR, u, str[SU_MANAGER].data());
|
|
|
|
if (stat(app_path, &st) == 0) {
|
2022-05-20 11:37:37 +00:00
|
|
|
int app_id = to_app_id(st.st_uid);
|
|
|
|
|
2023-06-10 08:40:45 +00:00
|
|
|
byte_array<PATH_MAX> apk;
|
|
|
|
find_apk_path(byte_view(str[SU_MANAGER]), apk);
|
|
|
|
int fd = xopen((const char *) apk.buf(), O_RDONLY | O_CLOEXEC);
|
2023-06-14 14:24:42 +00:00
|
|
|
auto cert = read_certificate(fd, -1);
|
2022-05-20 11:37:37 +00:00
|
|
|
close(fd);
|
|
|
|
|
|
|
|
// Verify validity
|
|
|
|
if (str[SU_MANAGER] == *mgr_pkg) {
|
2023-06-14 14:24:42 +00:00
|
|
|
if (app_id != mgr_app_id || cert.empty() || cert != *mgr_cert) {
|
2022-05-20 11:37:37 +00:00
|
|
|
// app ID or cert should never change
|
2023-06-10 08:40:45 +00:00
|
|
|
LOGE("pkg: repackaged APK signature invalid: %s\n", apk.buf());
|
2022-05-20 11:37:37 +00:00
|
|
|
uninstall_pkg(mgr_pkg->data());
|
|
|
|
invalid = true;
|
2022-05-24 12:21:36 +00:00
|
|
|
install = true;
|
2022-05-20 11:37:37 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-19 09:39:57 +00:00
|
|
|
mgr_pkg->swap(str[SU_MANAGER]);
|
2022-05-20 11:37:37 +00:00
|
|
|
mgr_app_id = app_id;
|
|
|
|
mgr_cert->swap(cert);
|
2022-05-19 09:39:57 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
Fix race condition when switching root manager
Before this change, the root manager package name is only written into
the database after the repackaged APK is installed. In the time between
the repackaged APK being installed and the package name being written
into the database, if some operation calls `get_manager`, the Magisk
daemon will cache this result and ignore the repackaged APK, even if
the package name is set afterwards, because the cache won't be
invalidated. The result is that the repackaged manager APK will not be
recognized as the root manager, breaking the hide manager feature.
This race condition is more likely to happen when Zygisk is enabled,
because `get_manager` is called with a very high frequency in that case.
To fix the issue, we have to set the new package name into the database
BEFORE installing the repackaged APK. We also stop pruning the
database if the repackaged manager is not found, moving this logic into
the Magisk app. By doing so, we can guarantee that the instant after
the repackaged manager APK is installed, the Magisk daemon will
immediately pick it up and treat it as the root manager.
Another small optimization: when the requester is root, simply bypass
the whole database + manager package check. Since the Magisk app hiding
APK installation proces will call `su` several times to run `pm` under
different UIDs, doing this opimization will reduce the amount of
unnecessary database query + filesystem traversals.
2023-03-06 11:25:59 +00:00
|
|
|
if (check_stub_apk(user_id)) {
|
2022-05-24 12:21:36 +00:00
|
|
|
if (!check_dyn(user_id))
|
Fix race condition when switching root manager
Before this change, the root manager package name is only written into
the database after the repackaged APK is installed. In the time between
the repackaged APK being installed and the package name being written
into the database, if some operation calls `get_manager`, the Magisk
daemon will cache this result and ignore the repackaged APK, even if
the package name is set afterwards, because the cache won't be
invalidated. The result is that the repackaged manager APK will not be
recognized as the root manager, breaking the hide manager feature.
This race condition is more likely to happen when Zygisk is enabled,
because `get_manager` is called with a very high frequency in that case.
To fix the issue, we have to set the new package name into the database
BEFORE installing the repackaged APK. We also stop pruning the
database if the repackaged manager is not found, moving this logic into
the Magisk app. By doing so, we can guarantee that the instant after
the repackaged manager APK is installed, the Magisk daemon will
immediately pick it up and treat it as the root manager.
Another small optimization: when the requester is root, simply bypass
the whole database + manager package check. Since the Magisk app hiding
APK installation proces will call `su` several times to run `pm` under
different UIDs, doing this opimization will reduce the amount of
unnecessary database query + filesystem traversals.
2023-03-06 11:25:59 +00:00
|
|
|
goto ignore;
|
2022-05-19 09:39:57 +00:00
|
|
|
if (pkg) *pkg = *mgr_pkg;
|
|
|
|
return st.st_uid;
|
2022-05-18 08:55:58 +00:00
|
|
|
}
|
2022-05-20 11:37:37 +00:00
|
|
|
if (!invalid) {
|
|
|
|
collect_users();
|
|
|
|
for (int u : users) {
|
Fix race condition when switching root manager
Before this change, the root manager package name is only written into
the database after the repackaged APK is installed. In the time between
the repackaged APK being installed and the package name being written
into the database, if some operation calls `get_manager`, the Magisk
daemon will cache this result and ignore the repackaged APK, even if
the package name is set afterwards, because the cache won't be
invalidated. The result is that the repackaged manager APK will not be
recognized as the root manager, breaking the hide manager feature.
This race condition is more likely to happen when Zygisk is enabled,
because `get_manager` is called with a very high frequency in that case.
To fix the issue, we have to set the new package name into the database
BEFORE installing the repackaged APK. We also stop pruning the
database if the repackaged manager is not found, moving this logic into
the Magisk app. By doing so, we can guarantee that the instant after
the repackaged manager APK is installed, the Magisk daemon will
immediately pick it up and treat it as the root manager.
Another small optimization: when the requester is root, simply bypass
the whole database + manager package check. Since the Magisk app hiding
APK installation proces will call `su` several times to run `pm` under
different UIDs, doing this opimization will reduce the amount of
unnecessary database query + filesystem traversals.
2023-03-06 11:25:59 +00:00
|
|
|
if (check_stub_apk(u)) {
|
2022-05-20 11:37:37 +00:00
|
|
|
// Found repackaged app, but not installed in the requested user
|
|
|
|
goto not_found;
|
|
|
|
}
|
|
|
|
if (invalid)
|
|
|
|
break;
|
2022-05-19 09:39:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
Fix race condition when switching root manager
Before this change, the root manager package name is only written into
the database after the repackaged APK is installed. In the time between
the repackaged APK being installed and the package name being written
into the database, if some operation calls `get_manager`, the Magisk
daemon will cache this result and ignore the repackaged APK, even if
the package name is set afterwards, because the cache won't be
invalidated. The result is that the repackaged manager APK will not be
recognized as the root manager, breaking the hide manager feature.
This race condition is more likely to happen when Zygisk is enabled,
because `get_manager` is called with a very high frequency in that case.
To fix the issue, we have to set the new package name into the database
BEFORE installing the repackaged APK. We also stop pruning the
database if the repackaged manager is not found, moving this logic into
the Magisk app. By doing so, we can guarantee that the instant after
the repackaged manager APK is installed, the Magisk daemon will
immediately pick it up and treat it as the root manager.
Another small optimization: when the requester is root, simply bypass
the whole database + manager package check. Since the Magisk app hiding
APK installation proces will call `su` several times to run `pm` under
different UIDs, doing this opimization will reduce the amount of
unnecessary database query + filesystem traversals.
2023-03-06 11:25:59 +00:00
|
|
|
// Repackaged app not found, fall through
|
2022-05-19 09:39:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check the original package name
|
|
|
|
|
2022-05-20 11:37:37 +00:00
|
|
|
bool invalid = false;
|
Fix race condition when switching root manager
Before this change, the root manager package name is only written into
the database after the repackaged APK is installed. In the time between
the repackaged APK being installed and the package name being written
into the database, if some operation calls `get_manager`, the Magisk
daemon will cache this result and ignore the repackaged APK, even if
the package name is set afterwards, because the cache won't be
invalidated. The result is that the repackaged manager APK will not be
recognized as the root manager, breaking the hide manager feature.
This race condition is more likely to happen when Zygisk is enabled,
because `get_manager` is called with a very high frequency in that case.
To fix the issue, we have to set the new package name into the database
BEFORE installing the repackaged APK. We also stop pruning the
database if the repackaged manager is not found, moving this logic into
the Magisk app. By doing so, we can guarantee that the instant after
the repackaged manager APK is installed, the Magisk daemon will
immediately pick it up and treat it as the root manager.
Another small optimization: when the requester is root, simply bypass
the whole database + manager package check. Since the Magisk app hiding
APK installation proces will call `su` several times to run `pm` under
different UIDs, doing this opimization will reduce the amount of
unnecessary database query + filesystem traversals.
2023-03-06 11:25:59 +00:00
|
|
|
auto check_apk = [&](int u) -> bool {
|
2022-09-09 11:29:50 +00:00
|
|
|
ssprintf(app_path, sizeof(app_path), "%s/%d/" JAVA_PACKAGE_NAME, APP_DATA_DIR, u);
|
2022-05-18 08:55:58 +00:00
|
|
|
if (stat(app_path, &st) == 0) {
|
2022-05-24 12:39:16 +00:00
|
|
|
#if ENFORCE_SIGNATURE
|
2023-06-10 12:54:29 +00:00
|
|
|
byte_array<PATH_MAX> apk;
|
2023-06-14 10:44:53 +00:00
|
|
|
find_apk_path(byte_view(JAVA_PACKAGE_NAME), apk);
|
2023-06-10 12:54:29 +00:00
|
|
|
int fd = xopen((const char *) apk.buf(), O_RDONLY | O_CLOEXEC);
|
2023-06-14 14:24:42 +00:00
|
|
|
auto cert = read_certificate(fd, MAGISK_VER_CODE);
|
2022-05-20 11:37:37 +00:00
|
|
|
close(fd);
|
|
|
|
if (default_cert && cert != *default_cert) {
|
|
|
|
// Found APK with invalid signature, force replace with stub
|
2023-06-10 12:54:29 +00:00
|
|
|
LOGE("pkg: APK signature mismatch: %s\n", apk.buf());
|
2022-05-20 11:37:37 +00:00
|
|
|
uninstall_pkg(JAVA_PACKAGE_NAME);
|
|
|
|
invalid = true;
|
|
|
|
install = true;
|
|
|
|
return false;
|
|
|
|
}
|
2022-05-24 12:39:16 +00:00
|
|
|
#endif
|
2022-05-19 09:39:57 +00:00
|
|
|
mgr_pkg->clear();
|
2022-05-20 11:37:37 +00:00
|
|
|
mgr_cert->clear();
|
2022-05-19 09:39:57 +00:00
|
|
|
mgr_app_id = to_app_id(st.st_uid);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
};
|
|
|
|
|
Fix race condition when switching root manager
Before this change, the root manager package name is only written into
the database after the repackaged APK is installed. In the time between
the repackaged APK being installed and the package name being written
into the database, if some operation calls `get_manager`, the Magisk
daemon will cache this result and ignore the repackaged APK, even if
the package name is set afterwards, because the cache won't be
invalidated. The result is that the repackaged manager APK will not be
recognized as the root manager, breaking the hide manager feature.
This race condition is more likely to happen when Zygisk is enabled,
because `get_manager` is called with a very high frequency in that case.
To fix the issue, we have to set the new package name into the database
BEFORE installing the repackaged APK. We also stop pruning the
database if the repackaged manager is not found, moving this logic into
the Magisk app. By doing so, we can guarantee that the instant after
the repackaged manager APK is installed, the Magisk daemon will
immediately pick it up and treat it as the root manager.
Another small optimization: when the requester is root, simply bypass
the whole database + manager package check. Since the Magisk app hiding
APK installation proces will call `su` several times to run `pm` under
different UIDs, doing this opimization will reduce the amount of
unnecessary database query + filesystem traversals.
2023-03-06 11:25:59 +00:00
|
|
|
if (check_apk(user_id)) {
|
2022-05-19 09:39:57 +00:00
|
|
|
if (pkg) *pkg = JAVA_PACKAGE_NAME;
|
|
|
|
return st.st_uid;
|
|
|
|
}
|
2022-05-20 11:37:37 +00:00
|
|
|
if (!invalid) {
|
|
|
|
collect_users();
|
|
|
|
for (int u : users) {
|
Fix race condition when switching root manager
Before this change, the root manager package name is only written into
the database after the repackaged APK is installed. In the time between
the repackaged APK being installed and the package name being written
into the database, if some operation calls `get_manager`, the Magisk
daemon will cache this result and ignore the repackaged APK, even if
the package name is set afterwards, because the cache won't be
invalidated. The result is that the repackaged manager APK will not be
recognized as the root manager, breaking the hide manager feature.
This race condition is more likely to happen when Zygisk is enabled,
because `get_manager` is called with a very high frequency in that case.
To fix the issue, we have to set the new package name into the database
BEFORE installing the repackaged APK. We also stop pruning the
database if the repackaged manager is not found, moving this logic into
the Magisk app. By doing so, we can guarantee that the instant after
the repackaged manager APK is installed, the Magisk daemon will
immediately pick it up and treat it as the root manager.
Another small optimization: when the requester is root, simply bypass
the whole database + manager package check. Since the Magisk app hiding
APK installation proces will call `su` several times to run `pm` under
different UIDs, doing this opimization will reduce the amount of
unnecessary database query + filesystem traversals.
2023-03-06 11:25:59 +00:00
|
|
|
if (check_apk(u)) {
|
2022-05-20 11:37:37 +00:00
|
|
|
// Found app, but not installed in the requested user
|
|
|
|
goto not_found;
|
|
|
|
}
|
|
|
|
if (invalid)
|
|
|
|
break;
|
2022-05-18 08:55:58 +00:00
|
|
|
}
|
|
|
|
}
|
2022-05-19 09:39:57 +00:00
|
|
|
}
|
2022-05-18 08:55:58 +00:00
|
|
|
|
2022-05-20 11:37:37 +00:00
|
|
|
// No manager app is found, clear all cached value
|
|
|
|
mgr_app_id = -1;
|
|
|
|
mgr_pkg->clear();
|
|
|
|
mgr_cert->clear();
|
|
|
|
if (install)
|
|
|
|
install_stub();
|
|
|
|
|
2022-05-18 08:55:58 +00:00
|
|
|
not_found:
|
Fix race condition when switching root manager
Before this change, the root manager package name is only written into
the database after the repackaged APK is installed. In the time between
the repackaged APK being installed and the package name being written
into the database, if some operation calls `get_manager`, the Magisk
daemon will cache this result and ignore the repackaged APK, even if
the package name is set afterwards, because the cache won't be
invalidated. The result is that the repackaged manager APK will not be
recognized as the root manager, breaking the hide manager feature.
This race condition is more likely to happen when Zygisk is enabled,
because `get_manager` is called with a very high frequency in that case.
To fix the issue, we have to set the new package name into the database
BEFORE installing the repackaged APK. We also stop pruning the
database if the repackaged manager is not found, moving this logic into
the Magisk app. By doing so, we can guarantee that the instant after
the repackaged manager APK is installed, the Magisk daemon will
immediately pick it up and treat it as the root manager.
Another small optimization: when the requester is root, simply bypass
the whole database + manager package check. Since the Magisk app hiding
APK installation proces will call `su` several times to run `pm` under
different UIDs, doing this opimization will reduce the amount of
unnecessary database query + filesystem traversals.
2023-03-06 11:25:59 +00:00
|
|
|
LOGW("pkg: cannot find %s for user=[%d]\n",
|
|
|
|
mgr_pkg->empty() ? JAVA_PACKAGE_NAME : mgr_pkg->data(), user_id);
|
|
|
|
ignore:
|
2022-05-19 09:39:57 +00:00
|
|
|
if (pkg) pkg->clear();
|
2022-05-18 08:55:58 +00:00
|
|
|
return -1;
|
|
|
|
}
|