From a2211e29091508b745b7874519be125369375681 Mon Sep 17 00:00:00 2001 From: Faris <90097027+rsuntk@users.noreply.github.com> Date: Sun, 9 Nov 2025 07:35:42 +0700 Subject: [PATCH] kernel, manager: Track upstream changes (#195) * These commits are carefully picked from upstream (tiann/KernelSU) - Picked range: https://github.com/tiann/KernelSU/compare/8c5f485f270126fa2a08997d89a5f733f10ff40f..e5f43a3427381a1ec65d1f3bc88580b7c3f50b95 Signed-off-by: Faris Co-authored-by: Wang Han <416810799@qq.com> Co-authored-by: TwinbornPlate75 <3342733415@qq.com> Co-authored-by: KOWX712 Co-authored-by: Ylarod Co-authored-by: YuKongA <70465933+YuKongA@users.noreply.github.com> Co-authored-by: backslashxx <118538522+backslashxx@users.noreply.github.com> Co-authored-by: 5ec1cff <56485584+5ec1cff@users.noreply.github.com> Co-authored-by: weishu --- kernel/.gitignore | 22 +++ kernel/Makefile | 13 +- kernel/allowlist.c | 85 +++++++---- kernel/allowlist.h | 8 +- kernel/core_hook.c | 112 +++++++++++++- kernel/feature.h | 1 + kernel/kernel_compat.c | 69 +-------- kernel/kernel_compat.h | 6 +- kernel/ksu.c | 21 +-- kernel/ksu.h | 4 +- kernel/ksud.c | 31 +++- kernel/manager.h | 2 - kernel/selinux/rules.c | 7 + kernel/selinux/selinux.c | 100 +++++++------ kernel/selinux/selinux.h | 9 +- kernel/selinux/selinux_defs.h | 6 + kernel/sucompat.c | 274 ++++++++++++++++++++++++++-------- kernel/sucompat.h | 40 ++++- kernel/supercalls.c | 11 +- kernel/throne_tracker.c | 1 + 20 files changed, 565 insertions(+), 257 deletions(-) create mode 100644 kernel/.gitignore diff --git a/kernel/.gitignore b/kernel/.gitignore new file mode 100644 index 00000000..20d68ae5 --- /dev/null +++ b/kernel/.gitignore @@ -0,0 +1,22 @@ +.cache/ +.thinlto-cache/ +compile_commands.json +*.ko +*.o +*.mod +*.lds +*.mod.o +.*.o* +.*.mod* +*.ko* +*.mod.c +*.symvers* +*.order +.*.ko.cmd +.tmp_versions/ +libs/ +obj/ + +CLAUDE.md +.ddk-version +.vscode/settings.json \ No newline at end of file diff --git a/kernel/Makefile b/kernel/Makefile index 6451e091..a0f36e23 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -118,10 +118,15 @@ endif ifeq ($(shell grep -q "inode_security_struct\s\+\*selinux_inode" $(srctree)/security/selinux/include/objsec.h; echo $$?),0) ccflags-y += -DKSU_OPTIONAL_SELINUX_INODE endif +ifeq ($(shell grep -q "task_security_struct\s\+\*selinux_cred" $(srctree)/security/selinux/include/objsec.h; echo $$?),0) +ccflags-y += -DKSU_OPTIONAL_SELINUX_CRED +endif -# Check if __poll_t is available -ifeq ($(shell grep -q "__poll_t" $(srctree)/include/linux/poll.h; echo $$?),1) -ccflags-y += -DKSU_NEED_POLL_T_DEF +# Check if __poll_t exists in linux or uapi types headers (handles backports) +ifneq ($(shell grep -q "__poll_t" \ + $(srctree)/include/linux/types.h \ + $(srctree)/include/uapi/linux/types.h; echo $$?),0) +ccflags-y += -DKSU_NO___POLL_T endif ifeq ($(shell grep -q "anon_inode_getfd_secure" $(srctree)/fs/anon_inodes.c; echo $$?),0) @@ -178,7 +183,7 @@ else $(info -- KPM is disabled) endif -ccflags-y += -Wno-implicit-function-declaration -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat +ccflags-y += -Wno-strict-prototypes -Wno-int-conversion -Wno-gcc-compat ccflags-y += -Wno-declaration-after-statement -Wno-unused-function -Wno-missing-prototypes # Keep a new line here!! Because someone may append config diff --git a/kernel/allowlist.c b/kernel/allowlist.c index 1a1cf9a0..a0af8676 100644 --- a/kernel/allowlist.c +++ b/kernel/allowlist.c @@ -1,3 +1,5 @@ +#include +#include #include #include #include @@ -8,6 +10,11 @@ #include #include #include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) +#include +#else +#include +#endif #if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 14, 0) #include #endif @@ -93,10 +100,7 @@ static uint8_t allow_list_bitmap[PAGE_SIZE] __read_mostly __aligned(PAGE_SIZE); #define KERNEL_SU_ALLOWLIST "/data/adb/ksu/.allowlist" -static struct work_struct ksu_save_work; -static struct work_struct ksu_load_work; - -bool persistent_allow_list(void); +void persistent_allow_list(void); void ksu_show_allow_list(void) { @@ -231,9 +235,9 @@ out: } else { if (profile->allow_su) { /* - * 1024 apps with uid higher than BITMAP_UID_MAX - * registered to request superuser? - */ + * 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); @@ -270,11 +274,6 @@ bool __ksu_is_allow_uid(uid_t uid) { int i; - if (unlikely(uid == 0)) { - // already root, but only allow our domain. - return is_ksu_domain(); - } - if (forbid_system_uid(uid)) { // do not bother going through the list if it's system return false; @@ -299,6 +298,15 @@ bool __ksu_is_allow_uid(uid_t uid) return false; } +bool __ksu_is_allow_uid_for_current(uid_t uid) +{ + if (unlikely(uid == 0)) { + // already root, but only allow our domain. + return is_ksu_domain(); + } + return __ksu_is_allow_uid(uid); +} + bool ksu_uid_should_umount(uid_t uid) { struct app_profile profile = { .current_uid = uid }; @@ -360,7 +368,7 @@ bool ksu_get_allow_list(int *array, int *length, bool allow) return true; } -void do_save_allow_list(struct work_struct *work) +static void do_persistent_allow_list(struct callback_head *_cb) { u32 magic = FILE_MAGIC; u32 version = FILE_FORMAT_VERSION; @@ -368,11 +376,13 @@ void do_save_allow_list(struct work_struct *work) struct list_head *pos = NULL; loff_t off = 0; + mutex_lock(&allowlist_mutex); struct file *fp = ksu_filp_open_compat( KERNEL_SU_ALLOWLIST, O_WRONLY | O_CREAT | O_TRUNC, 0644); if (IS_ERR(fp)) { pr_err("save_allow_list create file failed: %ld\n", PTR_ERR(fp)); + mutex_unlock(&allowlist_mutex); return; } @@ -391,7 +401,7 @@ void do_save_allow_list(struct work_struct *work) list_for_each (pos, &allow_list) { p = list_entry(pos, struct perm_data, list); - pr_info("save allow list, name: %s uid: %d, allow: %d\n", + pr_info("save allow list, name: %s uid :%d, allow: %d\n", p->profile.key, p->profile.current_uid, p->profile.allow_su); @@ -401,9 +411,38 @@ void do_save_allow_list(struct work_struct *work) exit: filp_close(fp, 0); + mutex_unlock(&allowlist_mutex); } -void do_load_allow_list(struct work_struct *work) +void persistent_allow_list(void) +{ + struct task_struct *tsk; + + tsk = get_pid_task(find_vpid(1), PIDTYPE_PID); + if (!tsk) { + pr_err("save_allow_list find init task err\n"); + return; + } + + struct callback_head *cb = + kzalloc(sizeof(struct callback_head), GFP_KERNEL); + if (!cb) { + pr_err("save_allow_list alloc cb err\b"); + goto put_task; + } + cb->func = do_persistent_allow_list; + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 8) + task_work_add(tsk, cb, TWA_RESUME); +#else + task_work_add(tsk, cb, true); +#endif + +put_task: + put_task_struct(tsk); +} + +void ksu_load_allow_list(void) { loff_t off = 0; ssize_t ret = 0; @@ -494,17 +533,6 @@ void ksu_prune_allowlist(bool (*is_uid_valid)(uid_t, char *, void *), } } -// make sure allow list works cross boot -bool persistent_allow_list(void) -{ - return ksu_queue_work(&ksu_save_work); -} - -bool ksu_load_allow_list(void) -{ - return ksu_queue_work(&ksu_load_work); -} - void ksu_allowlist_init(void) { int i; @@ -517,9 +545,6 @@ void ksu_allowlist_init(void) INIT_LIST_HEAD(&allow_list); - INIT_WORK(&ksu_save_work, do_save_allow_list); - INIT_WORK(&ksu_load_work, do_load_allow_list); - init_default_profiles(); } @@ -528,7 +553,7 @@ void ksu_allowlist_exit(void) struct perm_data *np = NULL; struct perm_data *n = NULL; - do_save_allow_list(NULL); + persistent_allow_list(); // free allowlist mutex_lock(&allowlist_mutex); diff --git a/kernel/allowlist.h b/kernel/allowlist.h index 298caf14..b9d845f1 100644 --- a/kernel/allowlist.h +++ b/kernel/allowlist.h @@ -8,13 +8,19 @@ void ksu_allowlist_init(void); void ksu_allowlist_exit(void); -bool ksu_load_allow_list(void); +void ksu_load_allow_list(void); void ksu_show_allow_list(void); +// Check if the uid is in allow list bool __ksu_is_allow_uid(uid_t uid); #define ksu_is_allow_uid(uid) unlikely(__ksu_is_allow_uid(uid)) +// Check if the uid is in allow list, or current is ksu domain root +bool __ksu_is_allow_uid_for_current(uid_t uid); +#define ksu_is_allow_uid_for_current(uid) \ + unlikely(__ksu_is_allow_uid_for_current(uid)) + bool ksu_get_allow_list(int *array, int *length, bool allow); void ksu_prune_allowlist(bool (*is_uid_exist)(uid_t, char *, void *), diff --git a/kernel/core_hook.c b/kernel/core_hook.c index 85c68867..2a0375e7 100644 --- a/kernel/core_hook.c +++ b/kernel/core_hook.c @@ -1,3 +1,10 @@ +#include +#include +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 11, 0) +#include +#else +#include +#endif #include #include #include @@ -22,7 +29,6 @@ #include #include #include -#include #ifndef KSU_HAS_PATH_UMOUNT #include // sys_umount (<4.17) & ksys_umount (4.17+) #endif @@ -42,6 +48,7 @@ #include "throne_comm.h" #include "kernel_compat.h" #include "supercalls.h" +#include "sucompat.h" bool ksu_module_mounted __read_mostly = false; @@ -56,6 +63,7 @@ bool ksu_is_compat __read_mostly = false; #endif static bool ksu_kernel_umount_enabled = true; +static bool ksu_enhanced_security_enabled = false; static int kernel_umount_feature_get(u64 *value) { @@ -78,13 +86,34 @@ static const struct ksu_feature_handler kernel_umount_handler = { .set_handler = kernel_umount_feature_set, }; +static int enhanced_security_feature_get(u64 *value) +{ + *value = ksu_enhanced_security_enabled ? 1 : 0; + return 0; +} + +static int enhanced_security_feature_set(u64 value) +{ + bool enable = value != 0; + ksu_enhanced_security_enabled = enable; + pr_info("enhanced_security: set to %d\n", enable); + return 0; +} + +static const struct ksu_feature_handler enhanced_security_handler = { + .feature_id = KSU_FEATURE_ENHANCED_SECURITY, + .name = "enhanced_security", + .get_handler = enhanced_security_feature_get, + .set_handler = enhanced_security_feature_set, +}; + static inline bool is_allow_su(void) { if (is_manager()) { // we are manager, allow! return true; } - return ksu_is_allow_uid(current_uid().val); + return ksu_is_allow_uid_for_current(current_uid().val); } static inline bool is_unsupported_uid(uid_t uid) @@ -179,6 +208,10 @@ static void disable_seccomp(struct task_struct *tsk) void escape_to_root(void) { struct cred *cred; +#ifdef KSU_SHOULD_USE_NEW_TP + struct task_struct *p = current; + struct task_struct *t; +#endif cred = prepare_creds(); if (!cred) { @@ -229,8 +262,15 @@ void escape_to_root(void) spin_unlock_irq(¤t->sighand->siglock); setup_selinux(profile->selinux_domain); + +#ifdef KSU_SHOULD_USE_NEW_TP + for_each_thread (p, t) { + ksu_set_task_tracepoint_flag(t); + } +#endif } +extern void ext4_unregister_sysfs(struct super_block *sb); void nuke_ext4_sysfs(void) { #ifdef CONFIG_EXT4_FS @@ -244,12 +284,13 @@ void nuke_ext4_sysfs(void) struct super_block *sb = path.dentry->d_inode->i_sb; const char *name = sb->s_type->name; if (strcmp(name, "ext4") != 0) { - pr_info("skipping s_type: %s\n", name); + pr_info("nuke_module: skipping s_type: %s\n", name); path_put(&path); return; } ext4_unregister_sysfs(sb); + pr_info("nuke_module: ext4 sysfs unregistered.n\n"); path_put(&path); #endif } @@ -285,6 +326,7 @@ static bool should_umount(struct path *path) #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) || \ defined(KSU_HAS_PATH_UMOUNT) +extern int path_umount(struct path *path, int flags); #define ksu_umount_mnt(__unused, path, flags) (path_umount(path, flags)) #else static int ksu_sys_umount(const char *mnt, int flags) @@ -384,6 +426,14 @@ static void umount_tw_func(struct callback_head *cb) } #endif +// force_sig kcompat, TODO: move it out of core_hook.c +// https://elixir.bootlin.com/linux/v5.3-rc1/source/kernel/signal.c#L1613 +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 3, 0) +#define __force_sig(sig) force_sig(sig) +#else +#define __force_sig(sig) force_sig(sig, current) +#endif + int ksu_handle_setuid(struct cred *new, const struct cred *old) { if (!new || !old) { @@ -396,9 +446,39 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old) if (0 != old_uid.val) { // old process is not root, ignore it. + if (ksu_enhanced_security_enabled) { + // disallow any non-ksu domain escalation from non-root to root! + if (unlikely(new_uid.val) == 0) { + if (!is_ksu_domain()) { + pr_warn("find suspicious EoP: %d %s, from %d to %d\n", + current->pid, current->comm, + old_uid.val, new_uid.val); + __force_sig(SIGKILL); + return 0; + } + } + // disallow appuid decrease to any other uid if it is allowed to su + if (is_appuid(old_uid)) { + if (new_uid.val < old_uid.val && + !ksu_is_allow_uid_for_current( + old_uid.val)) { + pr_warn("find suspicious EoP: %d %s, from %d to %d\n", + current->pid, current->comm, + old_uid.val, new_uid.val); + __force_sig(SIGKILL); + return 0; + } + } + } return 0; } +#ifdef KSU_SHOULD_USE_NEW_TP + if (new_uid.val == 2000 && ksu_su_compat_enabled) { + ksu_set_task_tracepoint_flag(current); + } +#endif + if (!is_appuid(new_uid) || is_unsupported_uid(new_uid.val)) { // pr_info("handle setuid ignore non application or isolated uid: %d\n", new_uid.val); return 0; @@ -417,10 +497,11 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old) spin_lock_irq(¤t->sighand->siglock); ksu_seccomp_allow_cache(current->seccomp.filter, __NR_reboot); spin_unlock_irq(¤t->sighand->siglock); + ksu_set_task_tracepoint_flag(current); return 0; } - if (ksu_is_allow_uid(new_uid.val)) { + if (ksu_is_allow_uid_for_current(new_uid.val)) { if (current->seccomp.mode == SECCOMP_MODE_FILTER && current->seccomp.filter) { spin_lock_irq(¤t->sighand->siglock); @@ -428,9 +509,17 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old) __NR_reboot); spin_unlock_irq(¤t->sighand->siglock); } + if (ksu_su_compat_enabled) { + ksu_set_task_tracepoint_flag(current); + } + } else { + if (ksu_su_compat_enabled) { + // Disable syscall tracepoint sucompat for non-allowed processes + ksu_clear_task_tracepoint_flag(current); + } } #else - if (ksu_is_allow_uid(new_uid.val)) { + if (ksu_is_allow_uid_for_current(new_uid.val)) { spin_lock_irq(¤t->sighand->siglock); disable_seccomp(current); spin_unlock_irq(¤t->sighand->siglock); @@ -465,7 +554,7 @@ int ksu_handle_setuid(struct cred *new, const struct cred *old) // check old process's selinux context, if it is not zygote, ignore it! // because some su apps may setuid to untrusted_app but they are in global mount namespace // when we umount for such process, that is a disaster! - if (!is_zygote(old->security)) { + if (!is_zygote(old)) { pr_info("handle umount ignore non zygote child: %d\n", current->pid); return 0; @@ -706,6 +795,10 @@ void __init ksu_core_init(void) if (ksu_register_feature_handler(&kernel_umount_handler)) { pr_err("Failed to register umount feature handler\n"); } + + if (ksu_register_feature_handler(&enhanced_security_handler)) { + pr_err("Failed to register enhanced security feature handler\n"); + } } void ksu_core_exit(void) @@ -723,13 +816,20 @@ void __init ksu_core_init(void) if (ksu_register_feature_handler(&kernel_umount_handler)) { pr_err("Failed to register umount feature handler\n"); } + + if (ksu_register_feature_handler(&enhanced_security_handler)) { + pr_err("Failed to register enhanced security feature handler\n"); + } } void ksu_core_exit(void) { ksu_unregister_feature_handler(KSU_FEATURE_KERNEL_UMOUNT); + ksu_uid_exit(); ksu_throne_comm_exit(); + + ksu_unregister_feature_handler(KSU_FEATURE_ENHANCED_SECURITY); } #endif diff --git a/kernel/feature.h b/kernel/feature.h index a5de137a..ad38d05e 100644 --- a/kernel/feature.h +++ b/kernel/feature.h @@ -6,6 +6,7 @@ enum ksu_feature_id { KSU_FEATURE_SU_COMPAT = 0, KSU_FEATURE_KERNEL_UMOUNT = 1, + KSU_FEATURE_ENHANCED_SECURITY = 2, KSU_FEATURE_MAX }; diff --git a/kernel/kernel_compat.c b/kernel/kernel_compat.c index 53cd2ff0..1020581c 100644 --- a/kernel/kernel_compat.c +++ b/kernel/kernel_compat.c @@ -18,6 +18,7 @@ #include #include +extern int install_session_keyring_to_cred(struct cred *, struct key *); struct key *init_session_keyring = NULL; static inline int install_session_keyring(struct key *keyring) { @@ -38,50 +39,6 @@ static inline int install_session_keyring(struct key *keyring) } #endif -extern struct task_struct init_task; - -// mnt_ns context switch for environment that android_init->nsproxy->mnt_ns != init_task.nsproxy->mnt_ns, such as WSA -struct ksu_ns_fs_saved { - struct nsproxy *ns; - struct fs_struct *fs; -}; - -static void ksu_save_ns_fs(struct ksu_ns_fs_saved *ns_fs_saved) -{ - ns_fs_saved->ns = current->nsproxy; - ns_fs_saved->fs = current->fs; -} - -static void ksu_load_ns_fs(struct ksu_ns_fs_saved *ns_fs_saved) -{ - current->nsproxy = ns_fs_saved->ns; - current->fs = ns_fs_saved->fs; -} - -static bool android_context_saved_checked = false; -static bool android_context_saved_enabled = false; -static struct ksu_ns_fs_saved android_context_saved; - -void ksu_android_ns_fs_check(void) -{ - if (android_context_saved_checked) - return; - android_context_saved_checked = true; - task_lock(current); - if (current->nsproxy && current->fs && - current->nsproxy->mnt_ns != init_task.nsproxy->mnt_ns) { - android_context_saved_enabled = true; -#ifdef CONFIG_KSU_DEBUG - pr_info("android context saved enabled due to init mnt_ns(%p) != android mnt_ns(%p)\n", - current->nsproxy->mnt_ns, init_task.nsproxy->mnt_ns); -#endif - ksu_save_ns_fs(&android_context_saved); - } else { - pr_info("android context saved disabled\n"); - } - task_unlock(current); -} - struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode) { #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \ @@ -92,27 +49,7 @@ struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode) install_session_keyring(init_session_keyring); } #endif - // switch mnt_ns even if current is not wq_worker, to ensure what we open is the correct file in android mnt_ns, rather than user created mnt_ns - struct ksu_ns_fs_saved saved; - if (android_context_saved_enabled) { -#ifdef CONFIG_KSU_DEBUG - pr_info("start switch current nsproxy and fs to android context\n"); -#endif - task_lock(current); - ksu_save_ns_fs(&saved); - ksu_load_ns_fs(&android_context_saved); - task_unlock(current); - } - struct file *fp = filp_open(filename, flags, mode); - if (android_context_saved_enabled) { - task_lock(current); - ksu_load_ns_fs(&saved); - task_unlock(current); -#ifdef CONFIG_KSU_DEBUG - pr_info("switch current nsproxy and fs back to saved successfully\n"); -#endif - } - return fp; + return filp_open(filename, flags, mode); } ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count, @@ -300,4 +237,4 @@ void ksu_seccomp_allow_cache(struct seccomp_filter *filter, int nr) #endif } -#endif \ No newline at end of file +#endif diff --git a/kernel/kernel_compat.h b/kernel/kernel_compat.h index 20f12b0a..8b6033dc 100644 --- a/kernel/kernel_compat.h +++ b/kernel/kernel_compat.h @@ -27,6 +27,10 @@ #endif #endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) && defined(KSU_KPROBE_HOOK) +#define KSU_SHOULD_USE_NEW_TP +#endif + extern long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr, long count); @@ -38,7 +42,7 @@ extern long ksu_strncpy_from_user_retry(char *dst, defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND) extern struct key *init_session_keyring; #endif -extern void ksu_android_ns_fs_check(void); + extern struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode); extern ssize_t ksu_kernel_read_compat(struct file *p, void *buf, size_t count, diff --git a/kernel/ksu.c b/kernel/ksu.c index 55ca87b9..d48594f8 100644 --- a/kernel/ksu.c +++ b/kernel/ksu.c @@ -5,7 +5,6 @@ #include #include #include /* LINUX_VERSION_CODE, KERNEL_VERSION macros */ -#include #include "allowlist.h" #include "arch.h" @@ -18,14 +17,10 @@ #include "ksud.h" #include "supercalls.h" -static struct workqueue_struct *ksu_workqueue; - -bool ksu_queue_work(struct work_struct *work) -{ - return queue_work(ksu_workqueue, work); -} - - +extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr, + void *argv, void *envp, int *flags); +extern int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, + void *argv, void *envp, int *flags); int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv, void *envp, int *flags) { @@ -55,17 +50,13 @@ int __init kernelsu_init(void) ksu_core_init(); - ksu_workqueue = alloc_ordered_workqueue("kernelsu_work_queue", 0); - ksu_allowlist_init(); ksu_throne_tracker_init(); ksu_sucompat_init(); -#ifdef KSU_KPROBES_HOOK ksu_ksud_init(); -#endif #ifdef MODULE #ifndef CONFIG_KSU_DEBUG @@ -75,6 +66,7 @@ int __init kernelsu_init(void) return 0; } +extern void ksu_observer_exit(void); void kernelsu_exit(void) { ksu_allowlist_exit(); @@ -85,13 +77,12 @@ void kernelsu_exit(void) destroy_workqueue(ksu_workqueue); -#ifdef KSU_KPROBES_HOOK ksu_ksud_exit(); -#endif ksu_sucompat_exit(); ksu_core_exit(); + ksu_feature_exit(); } diff --git a/kernel/ksu.h b/kernel/ksu.h index 098044d4..1e3fc4e8 100644 --- a/kernel/ksu.h +++ b/kernel/ksu.h @@ -95,8 +95,7 @@ struct app_profile { }; }; -bool ksu_queue_work(struct work_struct *work); - +#if 0 static inline int startswith(char *s, char *prefix) { return strncmp(s, prefix, strlen(prefix)); @@ -110,5 +109,6 @@ static inline int endswith(const char *s, const char *t) return 1; return strcmp(s + slen - tlen, t); } +#endif #endif \ No newline at end of file diff --git a/kernel/ksud.c b/kernel/ksud.c index e6cf5835..359b9648 100644 --- a/kernel/ksud.c +++ b/kernel/ksud.c @@ -1,3 +1,6 @@ +#include +#include +#include #include #include #include @@ -26,6 +29,8 @@ #include "ksud.h" #include "kernel_compat.h" #include "selinux/selinux.h" +#include "manager.h" +#include "sucompat.h" static const char KERNEL_SU_RC[] = "\n" @@ -79,6 +84,7 @@ void on_post_fs_data(void) done = true; pr_info("%s!\n", __func__); ksu_load_allow_list(); + ksu_mark_running_process(); ksu_observer_init(); // sanity check, this may influence the performance stop_input_hook(); @@ -102,6 +108,13 @@ struct user_arg_ptr { } ptr; }; +static void on_post_fs_data_cbfun(struct callback_head *cb) +{ + on_post_fs_data(); +} + +static struct callback_head on_post_fs_data_cb = { .func = on_post_fs_data_cbfun }; + // since _ksud handler only uses argv and envp for comparisons // this can probably work // adapted from ksu_handle_execveat_ksud @@ -146,7 +159,6 @@ static int ksu_handle_bprm_ksud(const char *filename, const char *argv1, const c pr_info("%s: /system/bin/init second_stage executed\n", __func__); apply_kernelsu_rules(); init_second_stage_executed = true; - ksu_android_ns_fs_check(); } } @@ -157,7 +169,6 @@ static int ksu_handle_bprm_ksud(const char *filename, const char *argv1, const c pr_info("%s: /init --second-stage executed\n", __func__); apply_kernelsu_rules(); init_second_stage_executed = true; - ksu_android_ns_fs_check(); } } @@ -184,15 +195,25 @@ static int ksu_handle_bprm_ksud(const char *filename, const char *argv1, const c pr_info("%s: /init +envp: INIT_SECOND_STAGE executed\n", __func__); apply_kernelsu_rules(); init_second_stage_executed = true; - ksu_android_ns_fs_check(); } } first_app_process: if (first_app_process && !memcmp(filename, app_process, sizeof(app_process) - 1)) { first_app_process = false; - pr_info("%s: exec app_process, /data prepared, second_stage: %d\n", __func__, init_second_stage_executed); - on_post_fs_data(); + pr_info("exec app_process, /data prepared, second_stage: %d\n", + init_second_stage_executed); + struct task_struct *init_task; + rcu_read_lock(); + init_task = rcu_dereference(current->real_parent); + if (init_task) { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 7, 8) + task_work_add(init_task, &on_post_fs_data_cb, TWA_RESUME); +#else + task_work_add(init_task, &on_post_fs_data_cb, true); +#endif + } + rcu_read_unlock(); stop_execve_hook(); } diff --git a/kernel/manager.h b/kernel/manager.h index 454409ae..4270c7a4 100644 --- a/kernel/manager.h +++ b/kernel/manager.h @@ -40,6 +40,4 @@ static inline void ksu_invalidate_manager_uid(void) } int ksu_observer_init(void); -void ksu_observer_exit(void); - #endif diff --git a/kernel/selinux/rules.c b/kernel/selinux/rules.c index 9d84e042..b855f7f0 100644 --- a/kernel/selinux/rules.c +++ b/kernel/selinux/rules.c @@ -186,6 +186,13 @@ static int get_object(char *buf, char __user *user_object, size_t buf_sz, return 0; } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 4, 0) || \ + !defined(KSU_COMPAT_USE_SELINUX_STATE) +extern int avc_ss_reset(u32 seqno); +#else +extern int avc_ss_reset(struct selinux_avc *avc, u32 seqno); +#endif + // reset avc cache table, otherwise the new rules will not take effect if already denied static void reset_avc_cache(void) { diff --git a/kernel/selinux/selinux.c b/kernel/selinux/selinux.c index a8b70f7c..f34e1093 100644 --- a/kernel/selinux/selinux.c +++ b/kernel/selinux/selinux.c @@ -1,4 +1,7 @@ -#include +#include "linux/cred.h" +#include "linux/sched.h" +#include "linux/security.h" +#include "linux/version.h" #include "selinux_defs.h" #include "../klog.h" // IWYU pragma: keep @@ -24,14 +27,12 @@ static int transive_to_domain(const char *domain) pr_info("security_secctx_to_secid %s -> sid: %d, error: %d\n", domain, sid, error); } - if (!error) { tsec->sid = sid; tsec->create_sid = 0; tsec->keycreate_sid = 0; tsec->sockcreate_sid = 0; } - return error; } @@ -93,65 +94,68 @@ static inline u32 current_sid(void) } #endif -bool is_ksu_domain(void) +#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 14, 0) +struct lsm_context { + char *context; + u32 len; +}; + +static int __security_secid_to_secctx(u32 secid, struct lsm_context *cp) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 14, 0) - struct lsm_context ctx; + return security_secid_to_secctx(secid, &cp->context, &cp->len); +} +static void __security_release_secctx(struct lsm_context *cp) +{ + return security_release_secctx(cp->context, cp->len); +} #else - char *domain; - u32 seclen; -#endif - bool result; -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 14, 0) - int err = security_secid_to_secctx(current_sid(), &ctx); -#else - int err = security_secid_to_secctx(current_sid(), &domain, &seclen); +#define __security_secid_to_secctx security_secid_to_secctx +#define __security_release_secctx security_release_secctx #endif - if (err) { +bool is_task_ksu_domain(const struct cred *cred) +{ + struct lsm_context ctx; + bool result; + if (!cred) { return false; } - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 14, 0) - result = strncmp(KERNEL_SU_DOMAIN, ctx.context, ctx.len) == 0; - security_release_secctx(&ctx); -#else - result = strncmp(KERNEL_SU_DOMAIN, domain, seclen) == 0; - security_release_secctx(domain, seclen); -#endif - return result; -} - -bool is_zygote(void *sec) -{ - struct task_security_struct *tsec = (struct task_security_struct *)sec; + const struct task_security_struct *tsec = __selinux_cred(cred); if (!tsec) { return false; } -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 14, 0) - struct lsm_context ctx; -#else - char *domain; - u32 seclen; -#endif - bool result; - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 14, 0) - int err = security_secid_to_secctx(tsec->sid, &ctx); -#else - int err = security_secid_to_secctx(tsec->sid, &domain, &seclen); -#endif + int err = __security_secid_to_secctx(tsec->sid, &ctx); if (err) { return false; } + result = strncmp(KERNEL_SU_DOMAIN, ctx.context, ctx.len) == 0; + __security_release_secctx(&ctx); + return result; +} -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 14, 0) +bool is_ksu_domain(void) +{ + current_sid(); + return is_task_ksu_domain(current_cred()); +} + +bool is_zygote(const struct cred *cred) +{ + if (!cred) { + return false; + } + const struct task_security_struct *tsec = __selinux_cred(cred); + if (!tsec) { + return false; + } + struct lsm_context ctx; + bool result; + int err = __security_secid_to_secctx(tsec->sid, &ctx); + if (err) { + return false; + } result = strncmp("u:r:zygote:s0", ctx.context, ctx.len) == 0; - security_release_secctx(&ctx); -#else - result = strncmp("u:r:zygote:s0", domain, seclen) == 0; - security_release_secctx(domain, seclen); -#endif + __security_release_secctx(&ctx); return result; } diff --git a/kernel/selinux/selinux.h b/kernel/selinux/selinux.h index 7c6acb05..1384147a 100644 --- a/kernel/selinux/selinux.h +++ b/kernel/selinux/selinux.h @@ -1,8 +1,9 @@ #ifndef __KSU_H_SELINUX #define __KSU_H_SELINUX -#include -#include +#include "linux/types.h" +#include "linux/version.h" +#include "linux/sched.h" #if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0)) || \ defined(KSU_COMPAT_HAS_SELINUX_STATE) @@ -13,11 +14,13 @@ void setup_selinux(const char *); void setenforce(bool); +bool is_task_ksu_domain(const struct cred *cred); + bool getenforce(void); bool is_ksu_domain(void); -bool is_zygote(void *cred); +bool is_zygote(const struct cred *cred); void apply_kernelsu_rules(void); diff --git a/kernel/selinux/selinux_defs.h b/kernel/selinux/selinux_defs.h index 11ec5a4d..68081fd3 100644 --- a/kernel/selinux/selinux_defs.h +++ b/kernel/selinux/selinux_defs.h @@ -33,4 +33,10 @@ #define __setenforce(val) #endif +#ifdef KSU_OPTIONAL_SELINUX_CRED +#define __selinux_cred(cred) (selinux_cred(cred)) +#else +#define __selinux_cred(cred) (cred->security) +#endif + #endif diff --git a/kernel/sucompat.c b/kernel/sucompat.c index f4d41243..90514624 100644 --- a/kernel/sucompat.c +++ b/kernel/sucompat.c @@ -30,8 +30,80 @@ static const char su[] = SU_PATH; static const char ksud_path[] = KSUD_PATH; +bool ksu_su_compat_enabled __read_mostly = true; -static bool ksu_su_compat_enabled __read_mostly = true; +#ifdef KSU_SHOULD_USE_NEW_TP + +#include "linux/compiler.h" +#include "linux/printk.h" +#include "selinux/selinux.h" + +#include +#include +#include +#include + +void ksu_mark_running_process(void) +{ + struct task_struct *p, *t; + read_lock(&tasklist_lock); + for_each_process_thread (p, t) { + if (!t->mm) { // only user processes + continue; + } + int uid = task_uid(t).val; + bool ksu_root_process = + uid == 0 && is_task_ksu_domain(get_task_cred(t)); + if (ksu_root_process || ksu_is_allow_uid(uid)) { + ksu_set_task_tracepoint_flag(t); + pr_info("sucompat: mark process: pid:%d, uid: %d, comm:%s\n", + t->pid, uid, t->comm); + } + } + read_unlock(&tasklist_lock); +} + +static void handle_process_mark(bool mark) +{ + struct task_struct *p, *t; + read_lock(&tasklist_lock); + for_each_process_thread (p, t) { + if (mark) + ksu_set_task_tracepoint_flag(t); + else + ksu_clear_task_tracepoint_flag(t); + } + read_unlock(&tasklist_lock); +} + +static void mark_all_process(void) +{ + handle_process_mark(true); + pr_info("sucompat: mark all user process done!\n"); +} + +static void unmark_all_process(void) +{ + handle_process_mark(false); + pr_info("sucompat: unmark all user process done!\n"); +} +#else +void ksu_mark_running_process(void) +{ +} + +static void handle_process_mark(bool mark) +{ +} + +static void mark_all_process(void) +{ +} + +static void unmark_all_process(void) +{ +} +#endif static int su_compat_feature_get(u64 *value) { @@ -93,7 +165,7 @@ static inline bool __is_su_allowed(const void *ptr_to_check) if (!ksu_su_compat_enabled) return false; #endif - if (likely(!ksu_is_allow_uid(current_uid().val))) + if (!ksu_is_allow_uid_for_current(current_uid().val)) return false; if (unlikely(!ptr_to_check)) @@ -209,86 +281,150 @@ int __ksu_handle_devpts(struct inode *inode) return 0; } -#ifdef KSU_KPROBES_HOOK +#ifdef KSU_SHOULD_USE_NEW_TP +#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS -static int faccessat_handler_pre(struct kprobe *p, struct pt_regs *regs) +// Tracepoint probe for sys_enter +static void sucompat_sys_enter_handler(void *data, struct pt_regs *regs, + long id) { - struct pt_regs *real_regs = PT_REAL_REGS(regs); - int *dfd = (int *)&PT_REGS_PARM1(real_regs); - const char __user **filename_user = - (const char **)&PT_REGS_PARM2(real_regs); - int *mode = (int *)&PT_REGS_PARM3(real_regs); + // Handle newfstatat + if (unlikely(id == __NR_newfstatat)) { + int *dfd = (int *)&PT_REGS_PARM1(regs); + const char __user **filename_user = + (const char __user **)&PT_REGS_PARM2(regs); + int *flags = (int *)&PT_REGS_SYSCALL_PARM4(regs); + ksu_handle_stat(dfd, filename_user, flags); + return; + } - return ksu_handle_faccessat(dfd, filename_user, mode, NULL); + // Handle faccessat + if (unlikely(id == __NR_faccessat)) { + int *dfd = (int *)&PT_REGS_PARM1(regs); + const char __user **filename_user = + (const char __user **)&PT_REGS_PARM2(regs); + int *mode = (int *)&PT_REGS_PARM3(regs); + ksu_handle_faccessat(dfd, filename_user, mode, NULL); + return; + } + + // Handle execve + if (unlikely(id == __NR_execve)) { + const char __user **filename_user = + (const char __user **)&PT_REGS_PARM1(regs); + ksu_handle_execve_sucompat(AT_FDCWD, filename_user, NULL, NULL, + NULL); + return; + } } -static int newfstatat_handler_pre(struct kprobe *p, struct pt_regs *regs) +#endif // CONFIG_HAVE_SYSCALL_TRACEPOINTS + +#ifdef CONFIG_KRETPROBES + +static struct kretprobe *init_kretprobe(const char *name, + kretprobe_handler_t handler) { - struct pt_regs *real_regs = PT_REAL_REGS(regs); - int *dfd = (int *)&PT_REGS_PARM1(real_regs); - const char __user **filename_user = - (const char **)&PT_REGS_PARM2(real_regs); - int *flags = (int *)&PT_REGS_SYSCALL_PARM4(real_regs); - - return ksu_handle_stat(dfd, filename_user, flags); -} - -static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs) -{ - struct pt_regs *real_regs = PT_REAL_REGS(regs); - const char __user **filename_user = - (const char **)&PT_REGS_PARM1(real_regs); - - return ksu_handle_execve_sucompat(AT_FDCWD, filename_user, NULL, NULL, - NULL); -} - -#ifdef CONFIG_COMPAT -static struct kprobe *su_kps[5]; -#else -static struct kprobe *su_kps[3]; -#endif - -static struct kprobe *init_kprobe(const char *name, - kprobe_pre_handler_t handler) -{ - struct kprobe *kp = kzalloc(sizeof(struct kprobe), GFP_KERNEL); - if (!kp) + struct kretprobe *rp = kzalloc(sizeof(struct kretprobe), GFP_KERNEL); + if (!rp) return NULL; - kp->symbol_name = name; - kp->pre_handler = handler; + rp->kp.symbol_name = name; + rp->handler = handler; + rp->data_size = 0; + rp->maxactive = 0; - int ret = register_kprobe(kp); - pr_info("sucompat: register_%s kprobe: %d\n", name, ret); + int ret = register_kretprobe(rp); + pr_info("sucompat: register_%s kretprobe: %d\n", name, ret); if (ret) { - kfree(kp); + kfree(rp); return NULL; } - return kp; + return rp; } -static void destroy_kprobe(struct kprobe **kp_ptr) +static void destroy_kretprobe(struct kretprobe **rp_ptr) { - struct kprobe *kp = *kp_ptr; - if (!kp) + struct kretprobe *rp = *rp_ptr; + if (!rp) return; - unregister_kprobe(kp); + unregister_kretprobe(rp); synchronize_rcu(); - kfree(kp); - *kp_ptr = NULL; + kfree(rp); + *rp_ptr = NULL; } + +static int tracepoint_reg_count = 0; +static DEFINE_SPINLOCK(tracepoint_reg_lock); + +static int syscall_regfunc_handler(struct kretprobe_instance *ri, + struct pt_regs *regs) +{ + unsigned long flags; + spin_lock_irqsave(&tracepoint_reg_lock, flags); + if (tracepoint_reg_count < 1) { + // while install our tracepoint, mark our processes + unmark_all_process(); + ksu_mark_running_process(); + } else { + // while installing other tracepoint, mark all processes + mark_all_process(); + } + tracepoint_reg_count++; + spin_unlock_irqrestore(&tracepoint_reg_lock, flags); + return 0; +} + +static int syscall_unregfunc_handler(struct kretprobe_instance *ri, + struct pt_regs *regs) +{ + unsigned long flags; + spin_lock_irqsave(&tracepoint_reg_lock, flags); + if (tracepoint_reg_count <= 1) { + // while uninstall our tracepoint, unmark all processes + unmark_all_process(); + } else { + // while uninstalling other tracepoint, mark our processes + unmark_all_process(); + ksu_mark_running_process(); + } + tracepoint_reg_count--; + spin_unlock_irqrestore(&tracepoint_reg_lock, flags); + return 0; +} + +static struct kretprobe *syscall_regfunc_rp = NULL; +static struct kretprobe *syscall_unregfunc_rp = NULL; +#endif #endif void ksu_sucompat_enable(void) { -#ifdef KSU_KPROBES_HOOK - su_kps[0] = init_kprobe(SYS_EXECVE_SYMBOL, execve_handler_pre); - su_kps[1] = init_kprobe(SYS_FACCESSAT_SYMBOL, faccessat_handler_pre); - su_kps[2] = init_kprobe(SYS_NEWFSTATAT_SYMBOL, newfstatat_handler_pre); -#ifdef CONFIG_COMPAT - su_kps[3] = init_kprobe(SYS_EXECVE_COMPAT_SYMBOL, execve_handler_pre); - su_kps[4] = init_kprobe(SYS_FSTATAT64_SYMBOL, newfstatat_handler_pre); +#ifdef KSU_SHOULD_USE_NEW_TP + int ret; + pr_info("sucompat: ksu_sucompat_enable called\n"); + +#ifdef CONFIG_KRETPROBES + // Register kretprobe for syscall_regfunc + syscall_regfunc_rp = + init_kretprobe("syscall_regfunc", syscall_regfunc_handler); + // Register kretprobe for syscall_unregfunc + syscall_unregfunc_rp = + init_kretprobe("syscall_unregfunc", syscall_unregfunc_handler); +#endif + +#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS + ret = register_trace_sys_enter(sucompat_sys_enter_handler, NULL); +#ifndef CONFIG_KRETPROBES + unmark_all_process(); + ksu_mark_running_process(); +#endif + if (ret) { + pr_err("sucompat: failed to register sys_enter tracepoint: %d\n", + ret); + } else { + pr_info("sucompat: sys_enter tracepoint registered\n"); + } #endif #else ksu_su_compat_enabled = true; @@ -298,11 +434,18 @@ void ksu_sucompat_enable(void) void ksu_sucompat_disable(void) { -#ifdef KSU_KPROBES_HOOK - int i; - for (i = 0; i < ARRAY_SIZE(su_kps); i++) { - destroy_kprobe(&su_kps[i]); - } +#ifdef KSU_SHOULD_USE_NEW_TP + pr_info("sucompat: ksu_sucompat_disable called\n"); +#ifdef CONFIG_HAVE_SYSCALL_TRACEPOINTS + unregister_trace_sys_enter(sucompat_sys_enter_handler, NULL); + tracepoint_synchronize_unregister(); + pr_info("sucompat: sys_enter tracepoint unregistered\n"); +#endif + +#ifdef CONFIG_KRETPROBES + destroy_kretprobe(&syscall_regfunc_rp); + destroy_kretprobe(&syscall_unregfunc_rp); +#endif #else ksu_su_compat_enabled = false; pr_info("deinit sucompat\n"); @@ -315,6 +458,7 @@ void ksu_sucompat_init(void) if (ksu_register_feature_handler(&su_compat_handler)) { pr_err("Failed to register su_compat feature handler\n"); } + if (ksu_su_compat_enabled) { ksu_sucompat_enable(); } diff --git a/kernel/sucompat.h b/kernel/sucompat.h index f967bac8..149aab7a 100644 --- a/kernel/sucompat.h +++ b/kernel/sucompat.h @@ -3,15 +3,47 @@ #include #include +extern bool ksu_su_compat_enabled; + void ksu_sucompat_init(void); void ksu_sucompat_exit(void); void ksu_sucompat_enable(void); void ksu_sucompat_disable(void); -extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr, - void *argv, void *envp, int *flags); +void ksu_mark_running_process(void); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 10, 0) && defined(KSU_KPROBE_HOOK) + +#include + +static inline void ksu_set_task_tracepoint_flag(struct task_struct *t) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) + set_task_syscall_work(t, SYSCALL_TRACEPOINT); +#else + set_tsk_thread_flag(t, TIF_SYSCALL_TRACEPOINT); +#endif +} + +static inline void ksu_clear_task_tracepoint_flag(struct task_struct *t) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) + clear_task_syscall_work(t, SYSCALL_TRACEPOINT); +#else + clear_tsk_thread_flag(t, TIF_SYSCALL_TRACEPOINT); +#endif +} +#else +static inline void ksu_set_task_tracepoint_flag(struct task_struct *t) +{ + return; +} + +static inline void ksu_clear_task_tracepoint_flag(struct task_struct *t) +{ + return; +} +#endif -extern int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, - void *argv, void *envp, int *flags); #endif diff --git a/kernel/supercalls.c b/kernel/supercalls.c index e913ba8f..f35abac3 100644 --- a/kernel/supercalls.c +++ b/kernel/supercalls.c @@ -50,8 +50,9 @@ bool always_allow(void) bool allowed_for_su(void) { - bool is_allowed = is_manager() || ksu_is_allow_uid_for_current(current_uid().val); - return is_allowed; + bool is_allowed = + is_manager() || ksu_is_allow_uid_for_current(current_uid().val); + return is_allowed; } static void init_uid_scanner(void) @@ -219,7 +220,7 @@ static int do_uid_granted_root(void __user *arg) return -EFAULT; } - cmd.granted = ksu_is_allow_uid_for_current(cmd.uid); + cmd.granted = ksu_is_allow_uid_for_current(cmd.uid); if (copy_to_user(arg, &cmd, sizeof(cmd))) { pr_err("uid_granted_root: copy_to_user failed\n"); @@ -677,7 +678,7 @@ int ksu_install_fd(void) // Install fd fd_install(fd, filp); - pr_info("ksu fd installed: %d for pid %d\n", fd, current->pid); + pr_info("ksu fd[%d] installed for %s/%d\n", fd, current->comm, current->pid); return fd; -} \ No newline at end of file +} diff --git a/kernel/throne_tracker.c b/kernel/throne_tracker.c index 177eed7d..aec00a6a 100644 --- a/kernel/throne_tracker.c +++ b/kernel/throne_tracker.c @@ -11,6 +11,7 @@ #include "allowlist.h" #include "klog.h" // IWYU pragma: keep #include "ksu.h" +#include "apk_sign.h" #include "manager.h" #include "throne_tracker.h" #include "kernel_compat.h"