From eebde9be16429d352dc1d9afcf787a7b96bc7e4a Mon Sep 17 00:00:00 2001 From: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com> Date: Fri, 12 Sep 2025 14:37:39 +0800 Subject: [PATCH] kernel: handle optional backport for selinux_inode * For supporting kernel 4.19 with 5.10 bpf backports. Co-authored-by: rsuntk --- kernel/Makefile | 20 +- kernel/kernel_compat.c | 31 +- kernel/kernel_compat.h | 34 +- kernel/ksu.c | 5 +- kernel/ksu.h | 2 +- kernel/ksud.c | 762 ++++++++++++++++++++-------------------- kernel/selinux/rules.c | 1 - kernel/sucompat.c | 2 +- kernel/throne_tracker.h | 4 +- 9 files changed, 429 insertions(+), 432 deletions(-) diff --git a/kernel/Makefile b/kernel/Makefile index f4436ee3..ff753b39 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -105,27 +105,14 @@ else $(info -- KPM is disabled) endif -# SELinux 驱动程序检查 +# SELinux drivers check ifeq ($(shell grep -q "current_sid(void)" $(srctree)/security/selinux/include/objsec.h; echo $$?),0) ccflags-y += -DKSU_COMPAT_HAS_CURRENT_SID endif - ifeq ($(shell grep -q "struct selinux_state " $(srctree)/security/selinux/include/security.h; echo $$?),0) ccflags-y += -DKSU_COMPAT_HAS_SELINUX_STATE endif -# 该功能在 linux 5.0-rc1 中引入 -ifeq ($(shell grep -q "get_cred_rcu" $(srctree)/include/linux/cred.h; echo $$?),0) -ccflags-y += -DKSU_COMPAT_HAS_GET_CRED_RCU -else -ifeq ($(shell grep -q "atomic_long_t\s\+usage" $(srctree)/include/linux/cred.h; echo $$?),0) -ccflags-y += -DKSU_COMPAT_ATOMIC_LONG -endif -ifeq ($(shell grep -q "int\s\+non_rcu" $(srctree)/include/linux/cred.h; echo $$?),0) -ccflags-y += -DKSU_COMPAT_HAS_NONCONST_CRED -endif -endif - # Handle optional backports ifeq ($(shell grep -q "strncpy_from_user_nofault" $(srctree)/include/linux/uaccess.h; echo $$?),0) ccflags-y += -DKSU_OPTIONAL_STRNCPY @@ -136,6 +123,10 @@ endif ifeq ($(shell grep "ssize_t kernel_write" $(srctree)/fs/read_write.c | grep -q "const void" ; echo $$?),0) ccflags-y += -DKSU_OPTIONAL_KERNEL_WRITE endif +ifeq ($(shell grep -q "inode_security_struct\s\+\*selinux_inode" $(srctree)/security/selinux/include/objsec.h; echo $$?),0) +$(info -- KernelSU: kernel has selinux_inode.) +ccflags-y += -DKSU_OPTIONAL_SELINUX_INODE +endif ifeq ($(shell grep -q "int\s\+path_umount" $(srctree)/fs/namespace.c; echo $$?),0) ccflags-y += -DKSU_HAS_PATH_UMOUNT ifneq ($(shell grep -Eq "^int path_umount" $(srctree)/fs/internal.h; echo $$?),0) @@ -143,7 +134,6 @@ $(shell sed -i '/^extern void __init mnt_init/a int path_umount(struct path *pat $(info -- KernelSU: SusFS: Adding 'int path_umount(struct path *path, int flags);' to $(srctree)/fs/internal.h) endif endif - # Checks Samsung UH drivers ifeq ($(shell grep -q "CONFIG_KDP_CRED" $(srctree)/kernel/cred.c; echo $$?),0) ccflags-y += -DSAMSUNG_UH_DRIVER_EXIST diff --git a/kernel/kernel_compat.c b/kernel/kernel_compat.c index a1daa9de..cd54794b 100644 --- a/kernel/kernel_compat.c +++ b/kernel/kernel_compat.c @@ -79,7 +79,6 @@ void ksu_android_ns_fs_check(void) 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) || \ @@ -179,3 +178,33 @@ long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr, return ret; } #endif + +long ksu_strncpy_from_user_retry(char *dst, const void __user *unsafe_addr, + long count) +{ + long ret; + + ret = ksu_strncpy_from_user_nofault(dst, unsafe_addr, count); + if (likely(ret >= 0)) + return ret; + + // we faulted! fallback to slow path + if (unlikely(!ksu_access_ok(unsafe_addr, count))) { +#ifdef CONFIG_KSU_DEBUG + pr_err("%s: faulted!\n", __func__); +#endif + return -EFAULT; + } + + // why we don't do like how strncpy_from_user_nofault? + ret = strncpy_from_user(dst, unsafe_addr, count); + + if (ret >= count) { + ret = count; + dst[ret - 1] = '\0'; + } else if (likely(ret >= 0)) { + ret++; + } + + return ret; +} diff --git a/kernel/kernel_compat.h b/kernel/kernel_compat.h index cdb275f2..08f7b2ad 100644 --- a/kernel/kernel_compat.h +++ b/kernel/kernel_compat.h @@ -4,30 +4,9 @@ #include #include #include -#include #include "ss/policydb.h" #include "linux/key.h" - -// for kernel without get_cred_rcu -#ifndef KSU_COMPAT_HAS_GET_CRED_RCU -static inline const struct cred *get_cred_rcu(const struct cred *cred) -{ - struct cred *nonconst_cred = (struct cred *) cred; - if (!cred) - return NULL; -#ifdef KSU_COMPAT_ATOMIC_LONG - if (!atomic_long_inc_not_zero(&nonconst_cred->usage)) -#else - if (!atomic_inc_not_zero(&nonconst_cred->usage)) -#endif - return NULL; - validate_creds(cred); -#ifdef KSU_COMPAT_HAS_NONCONST_CRED - nonconst_cred->non_rcu = 0; -#endif - return cred; -} -#endif +#include /** * list_count_nodes - count the number of nodes in a list @@ -82,8 +61,13 @@ static inline __maybe_unused size_t list_count_nodes(const struct list_head *hea extern long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr, long count); +extern long ksu_strncpy_from_user_retry(char *dst, + const void __user *unsafe_addr, + long count); -#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND) +#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \ + defined(CONFIG_IS_HW_HISI) || \ + defined(CONFIG_KSU_ALLOWLIST_WORKAROUND) extern struct key *init_session_keyring; #endif @@ -96,9 +80,9 @@ extern ssize_t ksu_kernel_write_compat(struct file *p, const void *buf, size_t count, loff_t *pos); #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 0, 0) -#define ksu_access_ok(addr, size) (access_ok(addr, size)) +#define ksu_access_ok(addr, size) access_ok(addr, size) #else -#define ksu_access_ok(addr, size) (access_ok(VERIFY_READ, addr, size)) +#define ksu_access_ok(addr, size) access_ok(VERIFY_READ, addr, size) #endif #endif diff --git a/kernel/ksu.c b/kernel/ksu.c index 03801198..7ec8bfb5 100644 --- a/kernel/ksu.c +++ b/kernel/ksu.c @@ -66,7 +66,7 @@ extern void ksu_trace_unregister(); int __init kernelsu_init(void) { pr_info("kernelsu.enabled=%d\n", - get_ksu_state()); + (int)get_ksu_state()); #ifdef CONFIG_KSU_CMDLINE if (!get_ksu_state()) { @@ -74,6 +74,7 @@ int __init kernelsu_init(void) return 0; } #endif + #ifdef CONFIG_KSU_DEBUG pr_alert("*************************************************************"); pr_alert("** NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE NOTICE **"); @@ -101,7 +102,7 @@ int __init kernelsu_init(void) #ifdef CONFIG_KSU_KPROBES_HOOK ksu_ksud_init(); #else - pr_alert("KPROBES is disabled, KernelSU may not work, please check https://kernelsu.org/guide/how-to-integrate-for-non-gki.html"); + pr_debug("init ksu driver\n"); #endif #ifdef CONFIG_KSU_TRACEPOINT_HOOK diff --git a/kernel/ksu.h b/kernel/ksu.h index c7c6de97..64163b87 100644 --- a/kernel/ksu.h +++ b/kernel/ksu.h @@ -178,4 +178,4 @@ static inline int endswith(const char *s, const char *t) return strcmp(s + slen - tlen, t); } -#endif \ No newline at end of file +#endif diff --git a/kernel/ksud.c b/kernel/ksud.c index 37bb0d76..ef687506 100644 --- a/kernel/ksud.c +++ b/kernel/ksud.c @@ -27,23 +27,30 @@ #include "kernel_compat.h" #include "selinux/selinux.h" -#define KERNEL_VERSION_5_10 KERNEL_VERSION(5, 10, 0) +bool ksu_is_compat __read_mostly = false; // let it here static const char KERNEL_SU_RC[] = - "\n" - "on post-fs-data\n" - " start logd\n" - " exec u:r:su:s0 root -- " KSUD_PATH " post-fs-data\n" - "\n" - "on nonencrypted\n" - " exec u:r:su:s0 root -- " KSUD_PATH " services\n" - "\n" - "on property:vold.decrypt=trigger_restart_framework\n" - " exec u:r:su:s0 root -- " KSUD_PATH " services\n" - "\n" - "on property:sys.boot_completed=1\n" - " exec u:r:su:s0 root -- " KSUD_PATH " boot-completed\n" - "\n"; + "\n" + + "on post-fs-data\n" + " start logd\n" + // We should wait for the post-fs-data finish + " exec u:r:su:s0 root -- " KSUD_PATH " post-fs-data\n" + "\n" + + "on nonencrypted\n" + " exec u:r:su:s0 root -- " KSUD_PATH " services\n" + "\n" + + "on property:vold.decrypt=trigger_restart_framework\n" + " exec u:r:su:s0 root -- " KSUD_PATH " services\n" + "\n" + + "on property:sys.boot_completed=1\n" + " exec u:r:su:s0 root -- " KSUD_PATH " boot-completed\n" + "\n" + + "\n"; static void stop_vfs_read_hook(void); static void stop_execve_hook(void); @@ -68,24 +75,21 @@ u32 ksu_devpts_sid; // Detect whether it is on or not static bool is_boot_phase = true; -#ifdef CONFIG_COMPAT -bool ksu_is_compat __read_mostly = false; -#endif - void on_post_fs_data(void) { - static bool done = false; - if (done) { - pr_info("%s already done\n", __func__); - return; - } - done = true; - pr_info("%s!\n", __func__); - ksu_load_allow_list(); - stop_input_hook(); + static bool done = false; + if (done) { + pr_info("%s already done\n", __func__); + return; + } + done = true; + pr_info("%s!\n", __func__); + ksu_load_allow_list(); + // sanity check, this may influence the performance + stop_input_hook(); - ksu_devpts_sid = ksu_get_devpts_sid(); - pr_info("devpts sid: %d\n", ksu_devpts_sid); + ksu_devpts_sid = ksu_get_devpts_sid(); + pr_info("devpts sid: %d\n", ksu_devpts_sid); // End of boot state is_boot_phase = false; @@ -94,36 +98,36 @@ void on_post_fs_data(void) #define MAX_ARG_STRINGS 0x7FFFFFFF struct user_arg_ptr { #ifdef CONFIG_COMPAT - bool is_compat; + bool is_compat; #endif - union { - const char __user *const __user *native; + union { + const char __user *const __user *native; #ifdef CONFIG_COMPAT - const compat_uptr_t __user *compat; + const compat_uptr_t __user *compat; #endif - } ptr; + } ptr; }; static const char __user *get_user_arg_ptr(struct user_arg_ptr argv, int nr) { - const char __user *native; + const char __user *native; #ifdef CONFIG_COMPAT - if (unlikely(argv.is_compat)) { - compat_uptr_t compat; + if (unlikely(argv.is_compat)) { + compat_uptr_t compat; - if (get_user(compat, argv.ptr.compat + nr)) - return ERR_PTR(-EFAULT); + if (get_user(compat, argv.ptr.compat + nr)) + return ERR_PTR(-EFAULT); - ksu_is_compat = true; - return compat_ptr(compat); - } + ksu_is_compat = true; + return compat_ptr(compat); + } #endif - if (get_user(native, argv.ptr.native + nr)) - return ERR_PTR(-EFAULT); + if (get_user(native, argv.ptr.native + nr)) + return ERR_PTR(-EFAULT); - return native; + return native; } /* @@ -137,156 +141,156 @@ static const char __user *get_user_arg_ptr(struct user_arg_ptr argv, int nr) static int __maybe_unused count(struct user_arg_ptr argv, int max) { - int i = 0; + int i = 0; - if (argv.ptr.native != NULL) { - for (;;) { - const char __user *p = get_user_arg_ptr(argv, i); + if (argv.ptr.native != NULL) { + for (;;) { + const char __user *p = get_user_arg_ptr(argv, i); - if (!p) - break; + if (!p) + break; - if (IS_ERR(p)) - return -EFAULT; + if (IS_ERR(p)) + return -EFAULT; - if (i >= max) - return -E2BIG; - ++i; + if (i >= max) + return -E2BIG; + ++i; - if (fatal_signal_pending(current)) - return -ERESTARTNOHAND; - cond_resched(); - } - } - return i; + if (fatal_signal_pending(current)) + return -ERESTARTNOHAND; + cond_resched(); + } + } + return i; } // IMPORTANT NOTE: the call from execve_handler_pre WON'T provided correct value for envp and flags in GKI version int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, - struct user_arg_ptr *argv, - struct user_arg_ptr *envp, int *flags) + struct user_arg_ptr *argv, + struct user_arg_ptr *envp, int *flags) { #ifndef CONFIG_KSU_KPROBES_HOOK - if (!ksu_execveat_hook) { - return 0; - } + if (!ksu_execveat_hook) { + return 0; + } #endif - struct filename *filename; + struct filename *filename; - static const char app_process[] = "/system/bin/app_process"; - static bool first_app_process = true; + static const char app_process[] = "/system/bin/app_process"; + static bool first_app_process = true; - /* This applies to versions Android 10+ */ - static const char system_bin_init[] = "/system/bin/init"; - /* This applies to versions between Android 6 ~ 9 */ - static const char old_system_init[] = "/init"; - static bool init_second_stage_executed = false; + /* This applies to versions Android 10+ */ + static const char system_bin_init[] = "/system/bin/init"; + /* This applies to versions between Android 6 ~ 9 */ + static const char old_system_init[] = "/init"; + static bool init_second_stage_executed = false; - if (!filename_ptr) - return 0; + if (!filename_ptr) + return 0; - filename = *filename_ptr; - if (IS_ERR(filename)) { - return 0; - } + filename = *filename_ptr; + if (IS_ERR(filename)) { + return 0; + } - if (unlikely(!memcmp(filename->name, system_bin_init, - sizeof(system_bin_init) - 1) && - argv)) { - // /system/bin/init executed - int argc = count(*argv, MAX_ARG_STRINGS); - pr_info("/system/bin/init argc: %d\n", argc); - if (argc > 1 && !init_second_stage_executed) { - const char __user *p = get_user_arg_ptr(*argv, 1); - if (p && !IS_ERR(p)) { - char first_arg[16]; - ksu_strncpy_from_user_nofault( - first_arg, p, sizeof(first_arg)); - pr_info("/system/bin/init first arg: %s\n", - first_arg); - if (!strcmp(first_arg, "second_stage")) { - pr_info("/system/bin/init second_stage executed\n"); - apply_kernelsu_rules(); - init_second_stage_executed = true; - ksu_android_ns_fs_check(); - } - } else { - pr_err("/system/bin/init parse args err!\n"); - } - } - } else if (unlikely(!memcmp(filename->name, old_system_init, - sizeof(old_system_init) - 1) && - argv)) { - // /init executed - int argc = count(*argv, MAX_ARG_STRINGS); - pr_info("/init argc: %d\n", argc); - if (argc > 1 && !init_second_stage_executed) { - /* This applies to versions between Android 6 ~ 7 */ - const char __user *p = get_user_arg_ptr(*argv, 1); - if (p && !IS_ERR(p)) { - char first_arg[16]; - ksu_strncpy_from_user_nofault( - first_arg, p, sizeof(first_arg)); - pr_info("/init first arg: %s\n", first_arg); - if (!strcmp(first_arg, "--second-stage")) { - pr_info("/init second_stage executed\n"); - apply_kernelsu_rules(); - init_second_stage_executed = true; - ksu_android_ns_fs_check(); - } - } else { - pr_err("/init parse args err!\n"); - } - } else if (argc == 1 && !init_second_stage_executed && envp) { - /* This applies to versions between Android 8 ~ 9 */ - int envc = count(*envp, MAX_ARG_STRINGS); - if (envc > 0) { - int n; - for (n = 1; n <= envc; n++) { - const char __user *p = - get_user_arg_ptr(*envp, n); - if (!p || IS_ERR(p)) { - continue; - } - char env[256]; - // Reading environment variable strings from user space - if (ksu_strncpy_from_user_nofault( - env, p, sizeof(env)) < 0) - continue; - // Parsing environment variable names and values - char *env_name = env; - char *env_value = strchr(env, '='); - if (env_value == NULL) - continue; - // Replace equal sign with string terminator - *env_value = '\0'; - env_value++; - // Check if the environment variable name and value are matching - if (!strcmp(env_name, - "INIT_SECOND_STAGE") && - (!strcmp(env_value, "1") || - !strcmp(env_value, "true"))) { - pr_info("/init second_stage executed\n"); - apply_kernelsu_rules(); - init_second_stage_executed = - true; - ksu_android_ns_fs_check(); - } - } - } - } - } + if (unlikely(!memcmp(filename->name, system_bin_init, + sizeof(system_bin_init) - 1) && + argv)) { + // /system/bin/init executed + int argc = count(*argv, MAX_ARG_STRINGS); + pr_info("/system/bin/init argc: %d\n", argc); + if (argc > 1 && !init_second_stage_executed) { + const char __user *p = get_user_arg_ptr(*argv, 1); + if (p && !IS_ERR(p)) { + char first_arg[16]; + ksu_strncpy_from_user_retry( + first_arg, p, sizeof(first_arg)); + pr_info("/system/bin/init first arg: %s\n", + first_arg); + if (!strcmp(first_arg, "second_stage")) { + pr_info("/system/bin/init second_stage executed\n"); + apply_kernelsu_rules(); + init_second_stage_executed = true; + ksu_android_ns_fs_check(); + } + } else { + pr_err("/system/bin/init parse args err!\n"); + } + } + } else if (unlikely(!memcmp(filename->name, old_system_init, + sizeof(old_system_init) - 1) && + argv)) { + // /init executed + int argc = count(*argv, MAX_ARG_STRINGS); + pr_info("/init argc: %d\n", argc); + if (argc > 1 && !init_second_stage_executed) { + /* This applies to versions between Android 6 ~ 7 */ + const char __user *p = get_user_arg_ptr(*argv, 1); + if (p && !IS_ERR(p)) { + char first_arg[16]; + ksu_strncpy_from_user_retry( + first_arg, p, sizeof(first_arg)); + pr_info("/init first arg: %s\n", first_arg); + if (!strcmp(first_arg, "--second-stage")) { + pr_info("/init second_stage executed\n"); + apply_kernelsu_rules(); + init_second_stage_executed = true; + ksu_android_ns_fs_check(); + } + } else { + pr_err("/init parse args err!\n"); + } + } else if (argc == 1 && !init_second_stage_executed && envp) { + /* This applies to versions between Android 8 ~ 9 */ + int envc = count(*envp, MAX_ARG_STRINGS); + if (envc > 0) { + int n; + for (n = 1; n <= envc; n++) { + const char __user *p = + get_user_arg_ptr(*envp, n); + if (!p || IS_ERR(p)) { + continue; + } + char env[256]; + // Reading environment variable strings from user space + if (ksu_strncpy_from_user_retry( + env, p, sizeof(env)) < 0) + continue; + // Parsing environment variable names and values + char *env_name = env; + char *env_value = strchr(env, '='); + if (env_value == NULL) + continue; + // Replace equal sign with string terminator + *env_value = '\0'; + env_value++; + // Check if the environment variable name and value are matching + if (!strcmp(env_name, + "INIT_SECOND_STAGE") && + (!strcmp(env_value, "1") || + !strcmp(env_value, "true"))) { + pr_info("/init second_stage executed\n"); + apply_kernelsu_rules(); + init_second_stage_executed = + true; + ksu_android_ns_fs_check(); + } + } + } + } + } - if (unlikely(first_app_process && !memcmp(filename->name, app_process, - sizeof(app_process) - 1))) { - first_app_process = false; - pr_info("exec app_process, /data prepared, second_stage: %d\n", - init_second_stage_executed); - on_post_fs_data(); // we keep this for old ksud - stop_execve_hook(); - } + if (unlikely(first_app_process && !memcmp(filename->name, app_process, + sizeof(app_process) - 1))) { + first_app_process = false; + pr_info("exec app_process, /data prepared, second_stage: %d\n", + init_second_stage_executed); + on_post_fs_data(); // we keep this for old ksud + stop_execve_hook(); + } - return 0; + return 0; } static ssize_t (*orig_read)(struct file *, char __user *, size_t, loff_t *); @@ -295,265 +299,256 @@ static struct file_operations fops_proxy; static ssize_t read_count_append = 0; static ssize_t read_proxy(struct file *file, char __user *buf, size_t count, - loff_t *pos) + loff_t *pos) { - bool first_read = file->f_pos == 0; - ssize_t ret = orig_read(file, buf, count, pos); - if (first_read) { - pr_info("read_proxy append %ld + %ld\n", ret, - read_count_append); - ret += read_count_append; - } - return ret; + bool first_read = file->f_pos == 0; + ssize_t ret = orig_read(file, buf, count, pos); + if (first_read) { + pr_info("read_proxy append %ld + %ld\n", ret, + read_count_append); + ret += read_count_append; + } + return ret; } static ssize_t read_iter_proxy(struct kiocb *iocb, struct iov_iter *to) { - bool first_read = iocb->ki_pos == 0; - ssize_t ret = orig_read_iter(iocb, to); - if (first_read) { - pr_info("read_iter_proxy append %ld + %ld\n", ret, - read_count_append); - ret += read_count_append; - } - return ret; + bool first_read = iocb->ki_pos == 0; + ssize_t ret = orig_read_iter(iocb, to); + if (first_read) { + pr_info("read_iter_proxy append %ld + %ld\n", ret, + read_count_append); + ret += read_count_append; + } + return ret; } int ksu_handle_vfs_read(struct file **file_ptr, char __user **buf_ptr, - size_t *count_ptr, loff_t **pos) + size_t *count_ptr, loff_t **pos) { #ifndef CONFIG_KSU_KPROBES_HOOK - if (!ksu_vfs_read_hook) { - return 0; - } + if (!ksu_vfs_read_hook) { + return 0; + } #endif - struct file *file; - char __user *buf; - size_t count; + struct file *file; + char __user *buf; + size_t count; - if (strcmp(current->comm, "init")) { - // we are only interest in `init` process - return 0; - } + if (strcmp(current->comm, "init")) { + // we are only interest in `init` process + return 0; + } - file = *file_ptr; - if (IS_ERR(file)) { - return 0; - } + file = *file_ptr; + if (IS_ERR(file)) { + return 0; + } - if (!d_is_reg(file->f_path.dentry)) { - return 0; - } + if (!d_is_reg(file->f_path.dentry)) { + return 0; + } - const char *short_name = file->f_path.dentry->d_name.name; - if (strcmp(short_name, "atrace.rc")) { - // we are only interest `atrace.rc` file name file - return 0; - } - char path[256]; - char *dpath = d_path(&file->f_path, path, sizeof(path)); + const char *short_name = file->f_path.dentry->d_name.name; + if (strcmp(short_name, "atrace.rc")) { + // we are only interest `atrace.rc` file name file + return 0; + } + char path[256]; + char *dpath = d_path(&file->f_path, path, sizeof(path)); - if (IS_ERR(dpath)) { - return 0; - } + if (IS_ERR(dpath)) { + return 0; + } - if (strcmp(dpath, "/system/etc/init/atrace.rc")) { - return 0; - } + if (strcmp(dpath, "/system/etc/init/atrace.rc")) { + return 0; + } - // we only process the first read - static bool rc_inserted = false; - if (rc_inserted) { - // we don't need this kprobe, unregister it! - stop_vfs_read_hook(); - return 0; - } - rc_inserted = true; + // we only process the first read + static bool rc_inserted = false; + if (rc_inserted) { + // we don't need this kprobe, unregister it! + stop_vfs_read_hook(); + return 0; + } + rc_inserted = true; - // now we can sure that the init process is reading - // `/system/etc/init/atrace.rc` - buf = *buf_ptr; - count = *count_ptr; + // now we can sure that the init process is reading + // `/system/etc/init/atrace.rc` + buf = *buf_ptr; + count = *count_ptr; - size_t rc_count = strlen(KERNEL_SU_RC); + size_t rc_count = strlen(KERNEL_SU_RC); - pr_info("vfs_read: %s, comm: %s, count: %zu, rc_count: %zu\n", dpath, - current->comm, count, rc_count); + pr_info("vfs_read: %s, comm: %s, count: %zu, rc_count: %zu\n", dpath, + current->comm, count, rc_count); - if (count < rc_count) { - pr_err("count: %zu < rc_count: %zu\n", count, rc_count); - return 0; - } + if (count < rc_count) { + pr_err("count: %zu < rc_count: %zu\n", count, rc_count); + return 0; + } - size_t ret = copy_to_user(buf, KERNEL_SU_RC, rc_count); - if (ret) { - pr_err("copy ksud.rc failed: %zu\n", ret); - return 0; - } + size_t ret = copy_to_user(buf, KERNEL_SU_RC, rc_count); + if (ret) { + pr_err("copy ksud.rc failed: %zu\n", ret); + return 0; + } - // we've succeed to insert ksud.rc, now we need to proxy the read and modify the result! - // But, we can not modify the file_operations directly, because it's in read-only memory. - // We just replace the whole file_operations with a proxy one. - memcpy(&fops_proxy, file->f_op, sizeof(struct file_operations)); - orig_read = file->f_op->read; - if (orig_read) { - fops_proxy.read = read_proxy; - } - orig_read_iter = file->f_op->read_iter; - if (orig_read_iter) { - fops_proxy.read_iter = read_iter_proxy; - } - // replace the file_operations - file->f_op = &fops_proxy; - read_count_append = rc_count; + // we've succeed to insert ksud.rc, now we need to proxy the read and modify the result! + // But, we can not modify the file_operations directly, because it's in read-only memory. + // We just replace the whole file_operations with a proxy one. + memcpy(&fops_proxy, file->f_op, sizeof(struct file_operations)); + orig_read = file->f_op->read; + if (orig_read) { + fops_proxy.read = read_proxy; + } + orig_read_iter = file->f_op->read_iter; + if (orig_read_iter) { + fops_proxy.read_iter = read_iter_proxy; + } + // replace the file_operations + file->f_op = &fops_proxy; + read_count_append = rc_count; - *buf_ptr = buf + rc_count; - *count_ptr = count - rc_count; + *buf_ptr = buf + rc_count; + *count_ptr = count - rc_count; - return 0; + return 0; } int ksu_handle_sys_read(unsigned int fd, char __user **buf_ptr, - size_t *count_ptr) + size_t *count_ptr) { - struct file *file = fget(fd); - if (!file) { - return 0; - } - int result = ksu_handle_vfs_read(&file, buf_ptr, count_ptr, NULL); - fput(file); - return result; + struct file *file = fget(fd); + if (!file) { + return 0; + } + int result = ksu_handle_vfs_read(&file, buf_ptr, count_ptr, NULL); + fput(file); + return result; } static unsigned int volumedown_pressed_count = 0; static bool is_volumedown_enough(unsigned int count) { - return count >= 3; + return count >= 3; } int ksu_handle_input_handle_event(unsigned int *type, unsigned int *code, - int *value) + int *value) { #ifndef CONFIG_KSU_KPROBES_HOOK - if (!ksu_input_hook) { - return 0; - } + if (!ksu_input_hook) { + return 0; + } #endif - if (*type == EV_KEY && *code == KEY_VOLUMEDOWN) { - int val = *value; - pr_info("KEY_VOLUMEDOWN val: %d\n", val); - if (val && is_boot_phase) { // Accumulates only during the power-up phase - volumedown_pressed_count += 1; - if (is_volumedown_enough(volumedown_pressed_count)) { - stop_input_hook(); - } - } - } + if (*type == EV_KEY && *code == KEY_VOLUMEDOWN) { + int val = *value; + pr_info("KEY_VOLUMEDOWN val: %d\n", val); + if (val && is_boot_phase) { + // key pressed, count it + volumedown_pressed_count += 1; + if (is_volumedown_enough(volumedown_pressed_count)) { + stop_input_hook(); + } + } + } - return 0; + return 0; } bool ksu_is_safe_mode() { - static bool safe_mode = false; - if (safe_mode) { - // don't need to check again, userspace may call multiple times - return true; - } + static bool safe_mode = false; + if (safe_mode) { + // don't need to check again, userspace may call multiple times + return true; + } - // stop hook first! - stop_input_hook(); + // stop hook first! + stop_input_hook(); - pr_info("volumedown_pressed_count: %d\n", volumedown_pressed_count); - if (is_volumedown_enough(volumedown_pressed_count)) { - // pressed over 3 times - pr_info("KEY_VOLUMEDOWN pressed max times, safe mode detected!\n"); - safe_mode = true; - return true; - } + pr_info("volumedown_pressed_count: %d\n", volumedown_pressed_count); + if (is_volumedown_enough(volumedown_pressed_count)) { + // pressed over 3 times + pr_info("KEY_VOLUMEDOWN pressed max times, safe mode detected!\n"); + safe_mode = true; + return true; + } - return false; + return false; } #ifdef CONFIG_KSU_KPROBES_HOOK +// https://elixir.bootlin.com/linux/v5.10.158/source/fs/exec.c#L1864 static int execve_handler_pre(struct kprobe *p, struct pt_regs *regs) { - int *fd = (int *)&PT_REGS_PARM1(regs); - struct filename **filename_ptr = - (struct filename **)&PT_REGS_PARM2(regs); - struct user_arg_ptr argv; + int *fd = (int *)&PT_REGS_PARM1(regs); + struct filename **filename_ptr = + (struct filename **)&PT_REGS_PARM2(regs); + struct user_arg_ptr argv; #ifdef CONFIG_COMPAT - argv.is_compat = PT_REGS_PARM3(regs); - if (unlikely(argv.is_compat)) { - argv.ptr.compat = PT_REGS_CCALL_PARM4(regs); - } else { - argv.ptr.native = PT_REGS_CCALL_PARM4(regs); - } + argv.is_compat = PT_REGS_PARM3(regs); + if (unlikely(argv.is_compat)) { + argv.ptr.compat = PT_REGS_CCALL_PARM4(regs); + } else { + argv.ptr.native = PT_REGS_CCALL_PARM4(regs); + } #else - argv.ptr.native = PT_REGS_PARM3(regs); + argv.ptr.native = PT_REGS_PARM3(regs); #endif - return ksu_handle_execveat_ksud(fd, filename_ptr, &argv, NULL, NULL); + return ksu_handle_execveat_ksud(fd, filename_ptr, &argv, NULL, NULL); } static int sys_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); - const char __user *const __user *__argv = - (const char __user *const __user *)PT_REGS_PARM2(real_regs); - struct user_arg_ptr argv = { .ptr.native = __argv }; - struct filename filename_in, *filename_p; - char path[32]; + struct pt_regs *real_regs = PT_REAL_REGS(regs); + const char __user **filename_user = + (const char **)&PT_REGS_PARM1(real_regs); + const char __user *const __user *__argv = + (const char __user *const __user *)PT_REGS_PARM2(real_regs); + struct user_arg_ptr argv = { .ptr.native = __argv }; + struct filename filename_in, *filename_p; + char path[32]; - if (!filename_user) - return 0; + if (!filename_user) + return 0; - memset(path, 0, sizeof(path)); - ksu_strncpy_from_user_nofault(path, *filename_user, 32); - filename_in.name = path; + memset(path, 0, sizeof(path)); + ksu_strncpy_from_user_nofault(path, *filename_user, 32); + filename_in.name = path; - filename_p = &filename_in; - return ksu_handle_execveat_ksud(AT_FDCWD, &filename_p, &argv, NULL, - NULL); -} - -__maybe_unused static int vfs_read_handler_pre(struct kprobe *p, - struct pt_regs *regs) -{ - struct file **file_ptr = (struct file **)&PT_REGS_PARM1(regs); - char __user **buf_ptr = (char **)&PT_REGS_PARM2(regs); - size_t *count_ptr = (size_t *)&PT_REGS_PARM3(regs); - loff_t **pos_ptr = (loff_t **)&PT_REGS_CCALL_PARM4(regs); - - return ksu_handle_vfs_read(file_ptr, buf_ptr, count_ptr, pos_ptr); + filename_p = &filename_in; + return ksu_handle_execveat_ksud(AT_FDCWD, &filename_p, &argv, NULL, + NULL); } static int sys_read_handler_pre(struct kprobe *p, struct pt_regs *regs) { - struct pt_regs *real_regs = PT_REAL_REGS(regs); - unsigned int fd = PT_REGS_PARM1(real_regs); - char __user **buf_ptr = (char __user **)&PT_REGS_PARM2(real_regs); - size_t count_ptr = (size_t *)&PT_REGS_PARM3(real_regs); + struct pt_regs *real_regs = PT_REAL_REGS(regs); + unsigned int fd = PT_REGS_PARM1(real_regs); + char __user **buf_ptr = (char __user **)&PT_REGS_PARM2(real_regs); + size_t count_ptr = (size_t *)&PT_REGS_PARM3(real_regs); - return ksu_handle_sys_read(fd, buf_ptr, count_ptr); + return ksu_handle_sys_read(fd, buf_ptr, count_ptr); } static int input_handle_event_handler_pre(struct kprobe *p, - struct pt_regs *regs) + struct pt_regs *regs) { - unsigned int *type = (unsigned int *)&PT_REGS_PARM2(regs); - unsigned int *code = (unsigned int *)&PT_REGS_PARM3(regs); - int *value = (int *)&PT_REGS_CCALL_PARM4(regs); - return ksu_handle_input_handle_event(type, code, value); + unsigned int *type = (unsigned int *)&PT_REGS_PARM2(regs); + unsigned int *code = (unsigned int *)&PT_REGS_PARM3(regs); + int *value = (int *)&PT_REGS_CCALL_PARM4(regs); + return ksu_handle_input_handle_event(type, code, value); } static struct kprobe execve_kp = { - .symbol_name = SYS_EXECVE_SYMBOL, - .pre_handler = sys_execve_handler_pre, + .symbol_name = SYS_EXECVE_SYMBOL, + .pre_handler = sys_execve_handler_pre, }; static struct kprobe vfs_read_kp = { @@ -630,19 +625,19 @@ int __maybe_unused ksu_handle_compat_execve_ksud(const char __user *filename_use static void stop_vfs_read_hook(void) { #ifdef CONFIG_KSU_KPROBES_HOOK - bool ret = schedule_work(&stop_vfs_read_work); - pr_info("unregister vfs_read kprobe: %d!\n", ret); + bool ret = schedule_work(&stop_vfs_read_work); + pr_info("unregister vfs_read kprobe: %d!\n", ret); #else - ksu_vfs_read_hook = false; - pr_info("stop vfs_read_hook\n"); + ksu_vfs_read_hook = false; + pr_info("stop vfs_read_hook\n"); #endif } static void stop_execve_hook(void) { #ifdef CONFIG_KSU_KPROBES_HOOK - bool ret = schedule_work(&stop_execve_hook_work); - pr_info("unregister execve kprobe: %d!\n", ret); + bool ret = schedule_work(&stop_execve_hook_work); + pr_info("unregister execve kprobe: %d!\n", ret); #else ksu_execveat_hook = false; pr_info("stop execve_hook\n"); @@ -656,14 +651,13 @@ static void stop_execve_hook(void) static void stop_input_hook(void) { #ifdef CONFIG_KSU_KPROBES_HOOK - static bool input_hook_stopped = false; - if (input_hook_stopped) { - return; - } - input_hook_stopped = true; - - bool ret = schedule_work(&stop_input_hook_work); - pr_info("unregister input kprobe: %d!\n", ret); + static bool input_hook_stopped = false; + if (input_hook_stopped) { + return; + } + input_hook_stopped = true; + bool ret = schedule_work(&stop_input_hook_work); + pr_info("unregister input kprobe: %d!\n", ret); #else if (!ksu_input_hook) { return; } ksu_input_hook = false; @@ -675,31 +669,31 @@ static void stop_input_hook(void) void ksu_ksud_init(void) { #ifdef CONFIG_KSU_KPROBES_HOOK - int ret; + int ret; - ret = register_kprobe(&execve_kp); - pr_info("ksud: execve_kp: %d\n", ret); + ret = register_kprobe(&execve_kp); + pr_info("ksud: execve_kp: %d\n", ret); - ret = register_kprobe(&vfs_read_kp); - pr_info("ksud: vfs_read_kp: %d\n", ret); + ret = register_kprobe(&vfs_read_kp); + pr_info("ksud: vfs_read_kp: %d\n", ret); - ret = register_kprobe(&input_event_kp); - pr_info("ksud: input_event_kp: %d\n", ret); + ret = register_kprobe(&input_event_kp); + pr_info("ksud: input_event_kp: %d\n", ret); - INIT_WORK(&stop_vfs_read_work, do_stop_vfs_read_hook); - INIT_WORK(&stop_execve_hook_work, do_stop_execve_hook); - INIT_WORK(&stop_input_hook_work, do_stop_input_hook); + INIT_WORK(&stop_vfs_read_work, do_stop_vfs_read_hook); + INIT_WORK(&stop_execve_hook_work, do_stop_execve_hook); + INIT_WORK(&stop_input_hook_work, do_stop_input_hook); #endif } void ksu_ksud_exit(void) { #ifdef CONFIG_KSU_KPROBES_HOOK - unregister_kprobe(&execve_kp); + unregister_kprobe(&execve_kp); // this should be done before unregister vfs_read_kp // unregister_kprobe(&vfs_read_kp); - unregister_kprobe(&input_event_kp); + unregister_kprobe(&input_event_kp); #endif is_boot_phase = false; volumedown_pressed_count = 0; -} \ No newline at end of file +} diff --git a/kernel/selinux/rules.c b/kernel/selinux/rules.c index 832cb6d2..ca2c0d48 100644 --- a/kernel/selinux/rules.c +++ b/kernel/selinux/rules.c @@ -37,7 +37,6 @@ static struct policydb *get_policydb(void) } static DEFINE_MUTEX(ksu_rules); - void apply_kernelsu_rules(void) { struct policydb *db; diff --git a/kernel/sucompat.c b/kernel/sucompat.c index 095f1d3b..82c73009 100644 --- a/kernel/sucompat.c +++ b/kernel/sucompat.c @@ -264,7 +264,7 @@ int ksu_handle_devpts(struct inode *inode) return 0; if (ksu_devpts_sid) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 1, 0) || defined(KSU_OPTIONAL_SELINUX_INODE) struct inode_security_struct *sec = selinux_inode(inode); #else struct inode_security_struct *sec = diff --git a/kernel/throne_tracker.h b/kernel/throne_tracker.h index 969e4d63..7799a823 100644 --- a/kernel/throne_tracker.h +++ b/kernel/throne_tracker.h @@ -1,5 +1,5 @@ -#ifndef __KSU_H_UID_OBSERVER -#define __KSU_H_UID_OBSERVER +#ifndef __KSU_H_THRONE_TRACKER +#define __KSU_H_THRONE_TRACKER void ksu_throne_tracker_init(void);