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.
This commit is contained in:
topjohnwu
2023-03-06 03:25:59 -08:00
parent 1aade8f8a8
commit a1ce6f5f12
4 changed files with 23 additions and 19 deletions
+8 -3
View File
@@ -167,8 +167,13 @@ void prune_su_access() {
static shared_ptr<su_info> get_su_info(unsigned uid) {
LOGD("su: request from uid=[%d]\n", uid);
shared_ptr<su_info> info;
if (uid == AID_ROOT) {
auto info = make_shared<su_info>(uid);
info->access = SILENT_SU_ACCESS;
return info;
}
shared_ptr<su_info> info;
{
mutex_guard lock(cache_lock);
if (!cached || cached->uid != uid || !cached->is_fresh())
@@ -183,8 +188,8 @@ static shared_ptr<su_info> get_su_info(unsigned uid) {
// Not cached, get data from database
info->check_db();
// If it's root or the manager, allow it silently
if (info->uid == AID_ROOT || to_app_id(info->uid) == to_app_id(info->mgr_uid)) {
// If it's the manager, allow it silently
if (to_app_id(info->uid) == to_app_id(info->mgr_uid)) {
info->access = SILENT_SU_ACCESS;
return info;
}