Hook improvements (take 2) (#563)
Hi @tiann. Thanks for the great project, I had great fun playing around with it. This PR mainly tries to further minimize the possible delays caused by KernelSU hooking. There are 3 major changes: - Processes with 0 < UID < 2000 are blocked straight-up before going through the allow_list. I don't see any need for such processes to be interested in root, and this allows returning early before going through a more expensive lookup. If there's an expected breakage due to this change, I'll remove it. Let me know. - A page-sized (4K) bitmap is added. This allows O(1) lookup for UID <= 32767. This speeds up `ksu_is_allow_uid()` by about 4.8x by sacrificing a 4K memory. IMHO, a good trade-off. Most notably, this reduces the 99.999% result previously from worrying milliseconds scale to microseconds scale. For UID > 32767, another page-sized (4K) sequential array is used to cache allow_list. Compared to the previous PR #557, this new approach gives another nice 25% performance boost in average, 63-96% boost in worst cases. Benchmark results are available at https://docs.google.com/spreadsheets/d/1w_tO1zRLPNMFRer49pL1TQfL6ndEhilRrDU1XFIcWXY/edit?usp=sharing Thanks! --------- Signed-off-by: Juhyung Park <qkrwngud825@gmail.com>
This commit is contained in:
@@ -29,6 +29,38 @@ static DEFINE_MUTEX(allowlist_mutex);
|
||||
static struct root_profile default_root_profile;
|
||||
static struct non_root_profile default_non_root_profile;
|
||||
|
||||
static int allow_list_arr[PAGE_SIZE / sizeof(int)] __read_mostly __aligned(PAGE_SIZE);
|
||||
static int allow_list_pointer __read_mostly = 0;
|
||||
|
||||
static void remove_uid_from_arr(uid_t uid)
|
||||
{
|
||||
int *temp_arr;
|
||||
int i, j;
|
||||
|
||||
if (allow_list_pointer == 0)
|
||||
return;
|
||||
|
||||
temp_arr = kmalloc(sizeof(allow_list_arr), GFP_KERNEL);
|
||||
if (temp_arr == NULL) {
|
||||
pr_err("%s: unable to allocate memory\n", __func__);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = j = 0; i < allow_list_pointer; i++) {
|
||||
if (allow_list_arr[i] == uid)
|
||||
continue;
|
||||
temp_arr[j++] = allow_list_arr[i];
|
||||
}
|
||||
|
||||
allow_list_pointer = j;
|
||||
|
||||
for (; j < ARRAY_SIZE(allow_list_arr); j++)
|
||||
temp_arr[j] = -1;
|
||||
|
||||
memcpy(&allow_list_arr, temp_arr, PAGE_SIZE);
|
||||
kfree(temp_arr);
|
||||
}
|
||||
|
||||
static void init_default_profiles()
|
||||
{
|
||||
default_root_profile.uid = 0;
|
||||
@@ -51,6 +83,9 @@ struct perm_data {
|
||||
|
||||
static struct list_head allow_list;
|
||||
|
||||
static uint8_t allow_list_bitmap[PAGE_SIZE] __read_mostly __aligned(PAGE_SIZE);
|
||||
#define BITMAP_UID_MAX ((sizeof(allow_list_bitmap) * BITS_PER_BYTE) - 1)
|
||||
|
||||
#define KERNEL_SU_ALLOWLIST "/data/adb/ksu/.allowlist"
|
||||
|
||||
static struct work_struct ksu_save_work;
|
||||
@@ -110,6 +145,11 @@ static bool profile_valid(struct app_profile *profile)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (profile->current_uid < 2000) {
|
||||
pr_err("uid lower than 2000 is unsupported: %d\n", profile->current_uid);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (profile->version < KSU_APP_PROFILE_VER) {
|
||||
pr_info("Unsupported profile version: %d\n", profile->version);
|
||||
return false;
|
||||
@@ -147,7 +187,7 @@ bool ksu_set_app_profile(struct app_profile *profile, bool persist)
|
||||
// found it, just override it all!
|
||||
memcpy(&p->profile, profile, sizeof(*profile));
|
||||
result = true;
|
||||
goto exit;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,9 +210,31 @@ bool ksu_set_app_profile(struct app_profile *profile, bool persist)
|
||||
profile->nrp_config.profile.umount_modules);
|
||||
}
|
||||
list_add_tail(&p->list, &allow_list);
|
||||
|
||||
out:
|
||||
if (profile->current_uid <= BITMAP_UID_MAX) {
|
||||
if (profile->allow_su)
|
||||
allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] |= 1 << (profile->current_uid % BITS_PER_BYTE);
|
||||
else
|
||||
allow_list_bitmap[profile->current_uid / BITS_PER_BYTE] &= ~(1 << (profile->current_uid % BITS_PER_BYTE));
|
||||
} else {
|
||||
if (profile->allow_su) {
|
||||
/*
|
||||
* 1024 apps with uid higher than BITMAP_UID_MAX
|
||||
* registered to request superuser?
|
||||
*/
|
||||
if (allow_list_pointer >= ARRAY_SIZE(allow_list_arr)) {
|
||||
pr_err("too many apps registered\n");
|
||||
WARN_ON(1);
|
||||
return false;
|
||||
}
|
||||
allow_list_arr[allow_list_pointer++] = profile->current_uid;
|
||||
} else {
|
||||
remove_uid_from_arr(profile->current_uid);
|
||||
}
|
||||
}
|
||||
result = true;
|
||||
|
||||
exit:
|
||||
// check if the default profiles is changed, cache it to a single struct to accelerate access.
|
||||
if (unlikely(!strcmp(profile->key, "$"))) {
|
||||
// set default non root profile
|
||||
@@ -192,21 +254,26 @@ exit:
|
||||
return result;
|
||||
}
|
||||
|
||||
bool ksu_is_allow_uid(uid_t uid)
|
||||
bool __ksu_is_allow_uid(uid_t uid)
|
||||
{
|
||||
struct perm_data *p = NULL;
|
||||
struct list_head *pos = NULL;
|
||||
int i;
|
||||
|
||||
if (uid == 0) {
|
||||
if (unlikely(uid == 0)) {
|
||||
// already root, but only allow our domain.
|
||||
return is_ksu_domain();
|
||||
}
|
||||
|
||||
list_for_each (pos, &allow_list) {
|
||||
p = list_entry(pos, struct perm_data, list);
|
||||
// pr_info("is_allow_uid uid :%d, allow: %d\n", p->uid, p->allow);
|
||||
if (uid == p->profile.current_uid) {
|
||||
return p->profile.allow_su;
|
||||
if (uid < 2000) {
|
||||
// do not bother going through the list if it's system
|
||||
return false;
|
||||
}
|
||||
|
||||
if (likely(uid <= BITMAP_UID_MAX)) {
|
||||
return !!(allow_list_bitmap[uid / BITS_PER_BYTE] & (1 << (uid % BITS_PER_BYTE)));
|
||||
} else {
|
||||
for (i = 0; i < allow_list_pointer; i++) {
|
||||
if (allow_list_arr[i] == uid)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,6 +453,9 @@ void ksu_prune_allowlist(bool (*is_uid_exist)(uid_t, void *), void *data)
|
||||
modified = true;
|
||||
pr_info("prune uid: %d\n", uid);
|
||||
list_del(&np->list);
|
||||
allow_list_bitmap[uid / BITS_PER_BYTE] &= ~(1 << (uid % BITS_PER_BYTE));
|
||||
remove_uid_from_arr(uid);
|
||||
smp_mb();
|
||||
kfree(np);
|
||||
}
|
||||
}
|
||||
@@ -409,6 +479,14 @@ bool ksu_load_allow_list(void)
|
||||
|
||||
void ksu_allowlist_init(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
BUILD_BUG_ON(sizeof(allow_list_bitmap) != PAGE_SIZE);
|
||||
BUILD_BUG_ON(sizeof(allow_list_arr) != PAGE_SIZE);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(allow_list_arr); i++)
|
||||
allow_list_arr[i] = -1;
|
||||
|
||||
INIT_LIST_HEAD(&allow_list);
|
||||
|
||||
INIT_WORK(&ksu_save_work, do_save_allow_list);
|
||||
|
||||
Reference in New Issue
Block a user