From 81f4f09d0c6f43ae0e2e460d87e914138f1d4dd9 Mon Sep 17 00:00:00 2001 From: Edrick Sinsuan <61485817+ederevx@users.noreply.github.com> Date: Tue, 15 Jul 2025 11:24:20 -0400 Subject: [PATCH] ksud: Address pagefault in ksu_handle_execveat_ksud (#662) * ksud: Address pagefault in ksu_handle_execveat_ksud As pointed out by @backslashxx, when strncpy pagefaults, it causes the first_arg to be completely NULL in some systems. This causes second_stage initialization to fail hence causing SU to be non-functional. This patch copies ksu_strncpy_from_user_retry from @backslashxx's commit: https://github.com/backslashxx/KernelSU/commit/e2fe25e485479e0b35d1a9eabc29b8bec1a4b7a7 This adds a fallback to perform a normal strncpy_from_user when nofault fails which allows us to get the first_arg in such cases. Co-authored-by: backslashxx <118538522+backslashxx@users.noreply.github.com> Signed-off-by: Edrick Sinsuan * Revert "ksud: Add second_stage init variant (#653)" This reverts commit c6b60a24e804939f814385d22d2857e23911b61c. --------- Signed-off-by: Edrick Sinsuan Co-authored-by: backslashxx <118538522+backslashxx@users.noreply.github.com> --- kernel/kernel_compat.c | 23 +++++++++++++++++++++++ kernel/kernel_compat.h | 3 +++ kernel/ksud.c | 9 ++++----- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/kernel/kernel_compat.c b/kernel/kernel_compat.c index 841a0e52..db47f8eb 100644 --- a/kernel/kernel_compat.c +++ b/kernel/kernel_compat.c @@ -173,3 +173,26 @@ long ksu_strncpy_from_user_nofault(char *dst, const void __user *unsafe_addr, return ret; } #endif + +static inline int ksu_access_ok(const void *addr, unsigned long size) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5,0,0) + return access_ok(addr, size); +#else + return access_ok(VERIFY_READ, addr, size); +#endif +} + +long ksu_strncpy_from_user_retry(char *dst, const void __user *unsafe_addr, + long count) +{ + long 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))) + return -EFAULT; + + return strncpy_from_user(dst, unsafe_addr, count); +} diff --git a/kernel/kernel_compat.h b/kernel/kernel_compat.h index 6d79f7ed..c85e9e2c 100644 --- a/kernel/kernel_compat.h +++ b/kernel/kernel_compat.h @@ -23,6 +23,9 @@ 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) extern struct key *init_session_keyring; diff --git a/kernel/ksud.c b/kernel/ksud.c index b30c69d0..2543239d 100644 --- a/kernel/ksud.c +++ b/kernel/ksud.c @@ -197,12 +197,11 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, const char __user *p = get_user_arg_ptr(*argv, 1); if (p && !IS_ERR(p)) { char first_arg[16]; - ksu_strncpy_from_user_nofault( + 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") || - (argc == 2 && !strcmp(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; @@ -223,7 +222,7 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, const char __user *p = get_user_arg_ptr(*argv, 1); if (p && !IS_ERR(p)) { char first_arg[16]; - ksu_strncpy_from_user_nofault( + 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")) { @@ -248,7 +247,7 @@ int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, } char env[256]; // Reading environment variable strings from user space - if (ksu_strncpy_from_user_nofault( + if (ksu_strncpy_from_user_retry( env, p, sizeof(env)) < 0) continue; // Parsing environment variable names and values