From bf06b92850f74ec9f8ffdd9a3b26c07103856ff4 Mon Sep 17 00:00:00 2001 From: backslashxx <118538522+backslashxx@users.noreply.github.com> Date: Mon, 19 May 2025 01:22:04 +0800 Subject: [PATCH] kernel: sucompat: increase reliability of execve_sucompat On plain ARMv8.0 devices (A53,A57,A73), strncpy_from_user_nofault() sometimes fails to copy `filename_user` string correctly. This breaks su ofc, breaking some apps like Termux (Play Store ver), ZArchiver and Root Explorer. This does NOT seem to affect newer ARMv8.2+ CPUs (A75/A76 and newer) My speculation? ARMv8.0 has weak speculation :) here we replace `strncpy_from_user_nofault()` with another routine: - access_ok() to validate the pointer - strncpy_from_user() to copy and validate string - manual null-termination just in case, as strncpy_from_user_nofault also does it - remove that memset, seems useless as it is an strncpy, not strncat Kind of mimicking _nofault, but yes with this one we allow pagefaults. Tested on: - ARMv8.0 A73.a53, A57.a53, A53.a53 - ARMv8.2 A76.a55 Tested-by: iDead XD Signed-off-by: backslashxx <118538522+backslashxx@users.noreply.github.com> --- kernel/sucompat.c | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/kernel/sucompat.c b/kernel/sucompat.c index bf0c34d3..854e1576 100644 --- a/kernel/sucompat.c +++ b/kernel/sucompat.c @@ -161,8 +161,25 @@ int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user, if (unlikely(!filename_user)) return 0; - memset(path, 0, sizeof(path)); - ksu_strncpy_from_user_nofault(path, *filename_user, sizeof(path)); + /* + * nofault variant fails silently due to pagefault_disable + * some cpus dont really have that good speculative execution + * access_ok to substitute set_fs, we check if pointer is accessible + */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,0,0) + if (!access_ok(VERIFY_READ, *filename_user, sizeof(path))) + return 0; +#else + if (!access_ok(*filename_user, sizeof(path))) + return 0; +#endif + // success = returns number of bytes and should be less than path + long len = strncpy_from_user(path, *filename_user, sizeof(path)); + if (len <= 0) + return 0; + + // strncpy_from_user_nofault does this too + path[sizeof(path) - 1] = '\0'; if (likely(memcmp(path, su, sizeof(su)))) return 0;