diff --git a/kernel/Kconfig b/kernel/Kconfig index 2bd538f1..baf7cc7b 100644 --- a/kernel/Kconfig +++ b/kernel/Kconfig @@ -58,6 +58,15 @@ config KPM This option is suitable for scenarios where you need to force KPM to be enabled. but it may affect system stability. +config KSU_THRONE_TRACKER_ALWAYS_THREADED + bool "Always run throne_tracker in a kthread" + default n + help + Runs throne_tracker in a separate kthread, including the first run. + Significantly decreases boot time, but can cause crowning failure + on some FDE or FBEv1 setups. + If unsure, say n. + choice prompt "KernelSU hook type" depends on KSU diff --git a/kernel/apk_sign.c b/kernel/apk_sign.c index fd272828..f669ea3a 100644 --- a/kernel/apk_sign.c +++ b/kernel/apk_sign.c @@ -19,6 +19,7 @@ #include "klog.h" // IWYU pragma: keep #include "kernel_compat.h" #include "manager_sign.h" +#include "throne_tracker.h" struct sdesc { struct shash_desc shash; @@ -269,6 +270,23 @@ static __always_inline bool check_v2_signature(char *path, bool check_multi_mana bool v3_1_signing_exist = false; int matched_index = -1; int i; + struct path kpath; + if (kern_path(path, 0, &kpath)) + return false; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 5, 0) + if (inode_is_locked(kpath.dentry->d_inode)) +#else + if (mutex_is_locked(&kpath.dentry->d_inode->i_mutex)) +#endif + { + pr_info("%s: inode is locked for %s\n", __func__, path); + path_put(&kpath); + return false; + } + + path_put(&kpath); + struct file *fp = ksu_filp_open_compat(path, O_RDONLY, 0); if (IS_ERR(fp)) { pr_err("open %s error.\n", path); @@ -422,10 +440,42 @@ module_param_cb(ksu_debug_manager_uid, &expected_size_ops, bool is_manager_apk(char *path) { + int tries = 0; + + while (tries++ < 10) { + if (!is_lock_held(path)) + break; + + pr_info("%s: waiting for %s\n", __func__, path); + msleep(100); + } + + // let it go, if retry fails, check_v2_signature will fail to open it anyway + if (tries == 10) { + pr_info("%s: timeout for %s\n", __func__, path); + return false; + } + return check_v2_signature(path, false, NULL); } bool is_dynamic_manager_apk(char *path, int *signature_index) { + int tries = 0; + + while (tries++ < 10) { + if (!is_lock_held(path)) + break; + + pr_info("%s: waiting for %s\n", __func__, path); + msleep(100); + } + + // let it go, if retry fails, check_v2_signature will fail to open it anyway + if (tries == 10) { + pr_info("%s: timeout for %s\n", __func__, path); + return false; + } + return check_v2_signature(path, true, signature_index); } diff --git a/kernel/throne_tracker.c b/kernel/throne_tracker.c index f6074698..d8339c01 100644 --- a/kernel/throne_tracker.c +++ b/kernel/throne_tracker.c @@ -17,8 +17,13 @@ #include "dynamic_manager.h" #include "throne_comm.h" +#include +#include + uid_t ksu_manager_uid = KSU_INVALID_UID; +static struct task_struct *throne_thread; + #define KSU_UID_LIST_PATH "/data/misc/user_uid/uid_list" #define USER_DATA_PATH "/data/user_de/0" #define USER_DATA_PATH_LEN 256 @@ -527,7 +532,7 @@ void search_manager(const char *path, int depth, struct list_head *uid_data) struct file *file; if (!stop) { - file = ksu_filp_open_compat(pos->dirpath, O_RDONLY | O_NOFOLLOW, 0); + file = ksu_filp_open_compat(pos->dirpath, O_RDONLY | O_NOFOLLOW | O_DIRECTORY, 0); if (IS_ERR(file)) { pr_err("Failed to open directory: %s, err: %ld\n", pos->dirpath, PTR_ERR(file)); goto skip_iterate; @@ -586,7 +591,7 @@ static bool is_uid_exist(uid_t uid, char *package, void *data) return exist; } -void track_throne(void) +static void track_throne_function() { struct list_head uid_list; INIT_LIST_HEAD(&uid_list); @@ -667,6 +672,37 @@ out: } } +static int throne_tracker_thread(void *data) +{ + pr_info("%s: pid: %d started\n", __func__, current->pid); + track_throne_function(); + throne_thread = NULL; + smp_mb(); + pr_info("%s: pid: %d exit!\n", __func__, current->pid); + return 0; +} + +void track_throne(void) +{ +#ifndef CONFIG_KSU_THRONE_TRACKER_ALWAYS_THREADED + static bool throne_tracker_first_run __read_mostly = true; + if (unlikely(throne_tracker_first_run)) { + track_throne_function(); + throne_tracker_first_run = false; + return; + } +#endif + smp_mb(); + if (throne_thread != NULL) // single instance lock + return; + + throne_thread = kthread_run(throne_tracker_thread, NULL, "throne_tracker"); + if (IS_ERR(throne_thread)) { + throne_thread = NULL; + return; + } +} + void ksu_throne_tracker_init(void) { // nothing to do diff --git a/kernel/throne_tracker.h b/kernel/throne_tracker.h index 7799a823..e1be3df5 100644 --- a/kernel/throne_tracker.h +++ b/kernel/throne_tracker.h @@ -7,4 +7,37 @@ void ksu_throne_tracker_exit(void); void track_throne(void); +/* + * small helper to check if lock is held + * false - file is stable + * true - file is being deleted/renamed + * possibly optional + * + */ +static bool is_lock_held(const char *path) +{ + struct path kpath; + + // kern_path returns 0 on success + if (kern_path(path, 0, &kpath)) + return true; + + // just being defensive + if (!kpath.dentry) { + path_put(&kpath); + return true; + } + + if (!spin_trylock(&kpath.dentry->d_lock)) { + pr_info("%s: lock held, bail out!\n", __func__); + path_put(&kpath); + return true; + } + // we hold it ourselves here! + + spin_unlock(&kpath.dentry->d_lock); + path_put(&kpath); + return false; +} + #endif