From ff72f046884ac453d1b45e7b74b7a67b94328651 Mon Sep 17 00:00:00 2001 From: Yervant7 <71306082+Yervant7@users.noreply.github.com> Date: Sat, 5 Sep 2026 08:14:35 -0300 Subject: [PATCH 1/7] Add NO_ROOT and MANAGER build mode options Introduce a NO_ROOT build mode and OFFICIAL_MANAGER toggle: update CI to produce no-root artifacts and docs to document build flags. Extend kernel/Makefile to add ENABLE_ROOT/ENABLE_OFFICIAL_MANAGER, add CONFIG_KP_NO_ROOT and CONFIG_KP_NO_OFFICIAL_MANAGER compile-time guards, and exclude root/manager sources when disabled. Add stubs and conditional logic across patch sources (android/userd.c, supercall.c, supercmd.c, sucompat.h, accctl.h) to safely build/run without built-in root or official manager. Also add input validation fixes in kstorage.c, include/symbol adjustments, and a one-shot kernel init guard (xchg) to prevent double initialization. --- .github/workflows/build.yml | 6 +++ doc/en/build.md | 12 +++++ doc/zh-CN/build.md | 12 +++++ kernel/Makefile | 41 +++++++++++++++++ kernel/patch/android/userd.c | 47 +++++++++++++++++-- kernel/patch/common/kstorage.c | 9 +++- kernel/patch/common/supercall.c | 82 ++++++++++++++++++++------------- kernel/patch/common/supercmd.c | 2 + kernel/patch/include/accctl.h | 12 +++++ kernel/patch/include/sucompat.h | 21 +++++++++ kernel/patch/patch.c | 36 ++++++++++++++- 11 files changed, 241 insertions(+), 39 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 06a5d432..4bd5148a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,6 +39,11 @@ jobs: mv kpimg.elf kpimg.elf-android make clean + make kpimg NO_ROOT=1 + mv kpimg kpimg-android-no-root + mv kpimg.elf kpimg.elf-android-no-root + make clean + unset ANDROID make mv kpimg kpimg-linux @@ -116,6 +121,7 @@ jobs: artifacts: | kernel/kpimg-linux kernel/kpimg-android + kernel/kpimg-android-no-root kernel/kpimg-x86 kpms/demo-hello/demo-hello.kpm kpms/demo-inlinehook/demo-inlinehook.kpm diff --git a/doc/en/build.md b/doc/en/build.md index 5dc36200..22652f32 100644 --- a/doc/en/build.md +++ b/doc/en/build.md @@ -10,6 +10,18 @@ export TARGET_COMPILE=aarch64-none-elf- cd kernel export ANDROID=1 # Android version, including support for the 'su' command make + +# Or build without built-in root +make NO_ROOT=1 +# Or Android version without built-in root +make ANDROID=1 NO_ROOT=1 + +# Optional manager flags: +# When NO_ROOT=1, official manager rename hooks and APK scans are disabled by default. +# To enable official manager package monitoring in NO_ROOT builds: +make ANDROID=1 NO_ROOT=1 OFFICIAL_MANAGER=1 +# To disable official manager rename hooks in a standard root build: +make ANDROID=1 NO_MANAGER=1 ``` ## Build kptools diff --git a/doc/zh-CN/build.md b/doc/zh-CN/build.md index bf60111c..bdaf15ae 100644 --- a/doc/zh-CN/build.md +++ b/doc/zh-CN/build.md @@ -10,6 +10,18 @@ export TARGET_COMPILE=aarch64-none-elf- cd kernel export ANDROID=1 # Android 版本,包含 su 命令支持 make + +# 或者在不包含内置 root 的情况下编译 +make NO_ROOT=1 +# 或 Android 版本不包含内置 root +make ANDROID=1 NO_ROOT=1 + +# Manager 可选标志: +# 当 NO_ROOT=1 时,官方 Manager 的 rename hook 和 APK 扫描默认处于禁用状态。 +# 在 NO_ROOT 构建中启用官方 Manager 包监控: +make ANDROID=1 NO_ROOT=1 OFFICIAL_MANAGER=1 +# 在标准 root 构建中禁用官方 Manager rename hook: +make ANDROID=1 NO_MANAGER=1 ``` ## 编译 kptools diff --git a/kernel/Makefile b/kernel/Makefile index 44e7516e..b191610e 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -45,6 +45,35 @@ ifdef ANDROID X86_CFLAGS += -DANDROID endif +ENABLE_ROOT ?= 1 +ifneq ($(NO_ROOT),) + ENABLE_ROOT := 0 +endif +ifneq ($(DISABLE_ROOT),) + ENABLE_ROOT := 0 +endif + +ifeq ($(ENABLE_ROOT),0) + CFLAGS += -DCONFIG_KP_NO_ROOT + X86_CFLAGS += -DCONFIG_KP_NO_ROOT +endif + +ENABLE_OFFICIAL_MANAGER ?= $(ENABLE_ROOT) +ifneq ($(OFFICIAL_MANAGER),) + ENABLE_OFFICIAL_MANAGER := $(OFFICIAL_MANAGER) +endif +ifneq ($(NO_MANAGER),) + ENABLE_OFFICIAL_MANAGER := 0 +endif +ifneq ($(NO_OFFICIAL_MANAGER),) + ENABLE_OFFICIAL_MANAGER := 0 +endif + +ifeq ($(ENABLE_OFFICIAL_MANAGER),0) + CFLAGS += -DCONFIG_KP_NO_OFFICIAL_MANAGER + X86_CFLAGS += -DCONFIG_KP_NO_OFFICIAL_MANAGER +endif + INCLUDE := -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include X86_INCLUDE := -I. -Iinclude -Ipatch/include -Ilinux/include -Ilinux/include/uapi/asm-generic -Ilinux/tools/arch/arm64/include @@ -76,6 +105,18 @@ ifdef ANDROID BASE_SRCS += $(wildcard patch/android/*.c) endif +ifeq ($(ENABLE_ROOT),0) + ROOT_SRCS := \ + patch/common/accctl.c \ + patch/common/selinux_hide.c \ + patch/common/selinux_sepolicy.c \ + patch/common/sucompat.c \ + patch/common/supercmd.c \ + patch/common/user_event.c + + BASE_SRCS := $(filter-out $(ROOT_SRCS),$(BASE_SRCS)) +endif + SRCS += $(BASE_SRCS) SRCS += $(LINUX_SRCS) diff --git a/kernel/patch/android/userd.c b/kernel/patch/android/userd.c index d2b43b22..09784270 100644 --- a/kernel/patch/android/userd.c +++ b/kernel/patch/android/userd.c @@ -100,9 +100,10 @@ static const struct trusted_manager_entry trusted_managers[] = { }; static uid_t trusted_manager_uid = TRUSTED_MANAGER_UID_INVALID; -static int global_pkg_pos = 0; +static __maybe_unused int global_pkg_pos = 0; +#ifndef CONFIG_KP_NO_ROOT static const char ORIGIN_RC_FILES[][64] = { "/system/etc/init/hw/init.rc", "/init.rc", @@ -170,6 +171,7 @@ static int expand_rc_template(char *dst, size_t dst_size, const char *template, dst[out] = '\0'; return (int)out; } +#endif /* CONFIG_KP_NO_ROOT */ static const void *kernel_read_file(const char *path, loff_t *len) { @@ -209,7 +211,7 @@ static int path_has_suffix(const char *path, const char *suffix) return strcmp(path + path_len - suffix_len, suffix) == 0; } -static int is_packages_list_tmp_dentry_path(const char *path) +static __maybe_unused int is_packages_list_tmp_dentry_path(const char *path) { return path_has_suffix(path, "/system/packages.list.tmp"); } @@ -1125,7 +1127,7 @@ int refresh_trusted_manager_uid(void) return refresh_trusted_manager_state(); } -static int refresh_trusted_manager_state_from_packages_list(int use_tmp) +static __maybe_unused int refresh_trusted_manager_state_from_packages_list(int use_tmp) { uid_t uid = TRUSTED_MANAGER_UID_INVALID; int rc = refresh_trusted_manager_uid_from_packages_list(&uid, use_tmp); @@ -1144,29 +1146,41 @@ static int refresh_trusted_manager_state_from_packages_list(int use_tmp) int refresh_trusted_manager_state(void) { +#ifdef CONFIG_KP_NO_OFFICIAL_MANAGER + return 0; +#else return refresh_trusted_manager_state_from_packages_list(0); +#endif } KP_EXPORT_SYMBOL(refresh_trusted_manager_uid); int is_trusted_manager_uid_android(uid_t uid) { +#ifdef CONFIG_KP_NO_OFFICIAL_MANAGER + return 0; +#else uid_t trusted_uid = trusted_manager_uid; if (trusted_uid == TRUSTED_MANAGER_UID_INVALID) { return 0; } return uid == trusted_uid; +#endif } KP_EXPORT_SYMBOL(is_trusted_manager_uid_android); uid_t get_trusted_manager_uid(void) { +#ifdef CONFIG_KP_NO_OFFICIAL_MANAGER + return TRUSTED_MANAGER_UID_INVALID; +#else return trusted_manager_uid; +#endif } KP_EXPORT_SYMBOL(get_trusted_manager_uid); // Simple CSV field parser helper function -static char *parse_csv_field(char **line_ptr) +static __maybe_unused char *parse_csv_field(char **line_ptr) { char *start = *line_ptr; char *end = start; @@ -1213,6 +1227,9 @@ static char *parse_csv_field(char **line_ptr) // Returns: number of entries loaded, or negative error code int load_ap_package_config() { +#ifdef CONFIG_KP_NO_ROOT + return 0; +#else loff_t len = 0; const char *data = kernel_read_file(AP_PACKAGE_CONFIG_PATH, &len); @@ -1400,6 +1417,7 @@ int load_ap_package_config() kvfree(data); log_boot("package_config loaded: %d entries, skipped: %d\n", loaded_count, skipped_count); return loaded_count; +#endif /* CONFIG_KP_NO_ROOT */ } KP_EXPORT_SYMBOL(load_ap_package_config); @@ -1560,10 +1578,12 @@ static void post_init_second_stage() static void on_first_app_process() { +#ifndef CONFIG_KP_NO_OFFICIAL_MANAGER /* Refresh the trusted-manager state (APK scan) synchronously here. The scan * itself is two-phase so it cannot deadlock on the /data/app inode lock. */ int rc = refresh_trusted_manager_state(); log_boot("on_first_app_process: trusted manager refresh rc=%d\n", rc); +#endif } static void handle_before_execve(hook_local_t *hook_local, char **__user u_filename_p, char **__user uargv, @@ -1575,11 +1595,15 @@ static void handle_before_execve(hook_local_t *hook_local, char **__user u_filen hook_local->data2 = 0; // Check if current process is trusted manager, set auto-su flag +#ifndef CONFIG_KP_NO_ROOT if (is_trusted_manager_uid(current_uid())) { hook_local->data0 = 1; } else { hook_local->data0 = 0; } +#else + hook_local->data0 = 0; +#endif if (current_uid() != 0 && !hook_local->data0) return; @@ -1665,9 +1689,11 @@ static void after_execveat(hook_fargs5_t *args, void *udata); static void handle_after_execve(hook_local_t *hook_local, long ret) { // Auto-su for processes executed by trusted manager +#ifndef CONFIG_KP_NO_ROOT if (hook_local->data0 && ret >= 0) { commit_su(0, all_allow_sctx); } +#endif if (ret >= 0) { if (hook_local->data1) { @@ -1718,6 +1744,7 @@ static void after_execveat(hook_fargs5_t *args, void *udata) handle_after_execve(&args->local, args->ret); } +#ifndef CONFIG_KP_NO_ROOT // https://elixir.bootlin.com/linux/v6.1/source/fs/open.c#L1337 // SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags, umode_t, mode) static void before_openat(hook_fargs4_t *args, void *udata) @@ -1825,7 +1852,9 @@ static void after_openat(hook_fargs4_t *args, void *udata) unhook_syscalln(__NR_openat, before_openat, after_openat); } } +#endif /* CONFIG_KP_NO_ROOT */ +#ifndef CONFIG_KP_NO_OFFICIAL_MANAGER typedef char *(*kp_dentry_path_raw_t)(struct dentry *dentry, char *buf, int buflen); static kp_dentry_path_raw_t kp_dentry_path_raw; @@ -1869,6 +1898,11 @@ static void after_security_inode_rename(hook_fargs5_t *args, void *udata) static void hook_rename_lsm(void) { + if (trusted_managers[0].package[0] == '\0') { + log_boot("no official manager configured, skipping rename hook\n"); + return; + } + unsigned long addr; hook_err_t rc; @@ -1894,6 +1928,7 @@ static void hook_rename_lsm(void) log_boot("no symbol: security_path_rename/security_inode_rename\n"); } +#endif /* CONFIG_KP_NO_OFFICIAL_MANAGER */ #define EV_KEY 0x01 #define KEY_VOLUMEDOWN 114 @@ -1930,11 +1965,15 @@ int android_user_init() log_boot("hook __NR_execveat rc: %d\n", rc); ret |= rc; +#ifndef CONFIG_KP_NO_ROOT rc = hook_syscalln(__NR_openat, 4, before_openat, after_openat, 0); log_boot("hook __NR_openat rc: %d\n", rc); ret |= rc; +#endif +#ifndef CONFIG_KP_NO_OFFICIAL_MANAGER hook_rename_lsm(); +#endif unsigned long input_handle_event_addr = patch_config->input_handle_event; if (input_handle_event_addr) { diff --git a/kernel/patch/common/kstorage.c b/kernel/patch/common/kstorage.c index 93043d7d..ed5c285c 100644 --- a/kernel/patch/common/kstorage.c +++ b/kernel/patch/common/kstorage.c @@ -60,8 +60,8 @@ int kstorage_group_size(int gid) int write_kstorage(int gid, long did, void *data, int offset, int len, bool data_is_user) { - int rc = -ENOENT; - if (gid < 0 || gid >= KSTRORAGE_MAX_GROUP_NUM) return rc; + if (gid < 0 || gid >= KSTRORAGE_MAX_GROUP_NUM) return -ENOENT; + if (offset < 0 || len < 0) return -EINVAL; struct hlist_head *bucket = kstorage_bucket(gid, did); spinlock_t *lock = &kstorage_glocks[gid]; @@ -179,6 +179,11 @@ int read_kstorage(int gid, long did, void *data, int offset, int len, bool data_ return PTR_ERR(pos); } + if (offset < 0 || offset > pos->dlen || len < 0) { + rcu_read_unlock(); + return -EINVAL; + } + int min_len = pos->dlen - offset > len ? len : pos->dlen - offset; if (data_is_user) { diff --git a/kernel/patch/common/supercall.c b/kernel/patch/common/supercall.c index 5caa9892..5723d61f 100644 --- a/kernel/patch/common/supercall.c +++ b/kernel/patch/common/supercall.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,14 @@ #include +#ifdef CONFIG_KP_NO_ROOT +int is_su_allow_uid(uid_t uid) +{ + return uid == 0; +} +KP_EXPORT_SYMBOL(is_su_allow_uid); +#endif + static long call_test(long arg1, long arg2, long arg3) { return 0; @@ -132,6 +141,7 @@ static long call_kpm_info(const char *__user uname, char *__user out_info, int o return sz; } +#ifndef CONFIG_KP_NO_ROOT static long call_su(struct su_profile *__user uprofile) { struct su_profile *profile = memdup_user(uprofile, sizeof(struct su_profile)); @@ -151,6 +161,7 @@ static long call_su_task(pid_t pid, struct su_profile *__user uprofile) kvfree(profile); return rc; } +#endif /* CONFIG_KP_NO_ROOT */ static long call_skey_get(char *__user out_key, int out_len) { @@ -176,6 +187,25 @@ static long call_skey_root_enable(int enable) return 0; } +#ifdef ANDROID +extern int android_is_safe_mode; +static long call_su_get_safemode() +{ + int result = android_is_safe_mode; + logkfd("[call_su_get_safemode] %d\n", result); + return result; +} + +extern int load_ap_package_config(void); +static long call_ap_load_package_config() +{ + int result = load_ap_package_config(); + logkfd("[call_ap_load_package_config] loaded %d entries\n", result); + return result; +} +#endif /* ANDROID */ + +#ifndef CONFIG_KP_NO_ROOT static long call_grant_uid(struct su_profile *__user uprofile) { struct su_profile *profile = memdup_user(uprofile, sizeof(struct su_profile)); @@ -195,24 +225,6 @@ static long call_su_allow_uid_nums() return su_allow_uid_nums(); } -#ifdef ANDROID -extern int android_is_safe_mode; -static long call_su_get_safemode() -{ - int result = android_is_safe_mode; - logkfd("[call_su_get_safemode] %d\n", result); - return result; -} - -extern int load_ap_package_config(void); -static long call_ap_load_package_config() -{ - int result = load_ap_package_config(); - logkfd("[call_ap_load_package_config] loaded %d entries\n", result); - return result; -} -#endif - static long call_su_list_allow_uid(uid_t *__user uids, int num) { return su_allow_uids(1, uids, num); @@ -251,6 +263,7 @@ static long call_su_set_allow_sctx(char *__user usctx) if (len >= SUPERCALL_SCONTEXT_LEN && buf[SUPERCALL_SCONTEXT_LEN - 1]) return -E2BIG; return set_all_allow_sctx(buf); } +#endif /* CONFIG_KP_NO_ROOT */ static long call_kstorage_read(int gid, long did, void *out_data, int offset, int dlen) { @@ -286,12 +299,15 @@ static long supercall(int is_authed, long cmd, long arg1, long arg2, long arg3, return kver; case SUPERCALL_BUILD_TIME: return call_buildtime((char *__user)arg1, (int)arg2); - #ifdef ANDROID +#ifdef ANDROID case SUPERCALL_AP_LOAD_PACKAGE_CONFIG: return call_ap_load_package_config(); - #endif + case SUPERCALL_SU_GET_SAFEMODE: + return call_su_get_safemode(); +#endif } +#ifndef CONFIG_KP_NO_ROOT switch (cmd) { case SUPERCALL_SU: return call_su((struct su_profile * __user) arg1); @@ -317,6 +333,12 @@ static long supercall(int is_authed, long cmd, long arg1, long arg2, long arg3, case SUPERCALL_SU_SET_ALLOW_SCTX: return call_su_set_allow_sctx((char *__user)arg1); + default: + break; + } +#endif /* CONFIG_KP_NO_ROOT */ + + switch (cmd) { case SUPERCALL_KSTORAGE_READ: return call_kstorage_read((int)arg1, (long)arg2, (void *)arg3, (int)((long)arg4 >> 32), (long)arg4 << 32 >> 32); case SUPERCALL_KSTORAGE_WRITE: @@ -330,15 +352,6 @@ static long supercall(int is_authed, long cmd, long arg1, long arg2, long arg3, case SUPERCALL_CONTROL_FEATURE: return kp_control_feature_sc((const char __user *)arg1, (int)(long)arg2); -#ifdef ANDROID - case SUPERCALL_SU_GET_SAFEMODE: - return call_su_get_safemode(); -#endif - default: - break; - } - - switch (cmd) { case SUPERCALL_BOOTLOG: return call_bootlog(); case SUPERCALL_PANIC: @@ -358,7 +371,6 @@ static long supercall(int is_authed, long cmd, long arg1, long arg2, long arg3, return call_skey_set((char *__user)arg1); case SUPERCALL_SKEY_ROOT_ENABLE: return call_skey_root_enable((int)arg1); - break; } switch (cmd) { @@ -386,16 +398,21 @@ static long supercall(int is_authed, long cmd, long arg1, long arg2, long arg3, int is_trusted_manager_uid(uid_t uid) { - #ifdef ANDROID +#ifdef ANDROID return is_trusted_manager_uid_android(uid); - #endif +#else return 0; +#endif } static void before(hook_fargs6_t *args, void *udata) { int uid = current_uid(); +#ifndef CONFIG_KP_NO_ROOT if (get_ap_mod_exclude(uid)) return; +#else + if (uid != 0) return; +#endif int is_trusted_caller = 0; int is_authed = 0; @@ -408,6 +425,7 @@ static void before(hook_fargs6_t *args, void *udata) is_authed = !auth_superkey(key); is_trusted_caller = is_authed; } + if (is_trusted_manager_uid(uid)) { is_trusted_caller = 1; is_authed = 1; diff --git a/kernel/patch/common/supercmd.c b/kernel/patch/common/supercmd.c index 9cceaddf..4af0c20e 100644 --- a/kernel/patch/common/supercmd.c +++ b/kernel/patch/common/supercmd.c @@ -330,10 +330,12 @@ void handle_supercmd(char **__user u_filename_p, char **__user uargv) { int is_key_auth = 0; int is_trusted_manager = 0; +#if ANDROID is_trusted_manager = is_trusted_manager_uid(current_uid()); if (is_trusted_manager) { is_key_auth = 1; } +#endif /* ANDROID */ // key const char __user *p1 = get_user_arg_ptr(0, *uargv, 1); if (!p1 || IS_ERR(p1)) return; diff --git a/kernel/patch/include/accctl.h b/kernel/patch/include/accctl.h index 46235423..47d15330 100644 --- a/kernel/patch/include/accctl.h +++ b/kernel/patch/include/accctl.h @@ -15,6 +15,9 @@ #include #include +#include + +#ifndef CONFIG_KP_NO_ROOT extern char all_allow_sctx[SUPERCALL_SCONTEXT_LEN]; extern uint32_t all_allow_sid; @@ -37,5 +40,14 @@ static inline void set_priv_sel_allow(struct task_struct *task, bool val) ext->priv_sel_allow = val; dsb(ish); } +#else +static inline int set_all_allow_sctx(const char *sctx) { (void)sctx; return -ENOSYS; } +static inline int commit_kernel_su(void) { return -ENOSYS; } +static inline int commit_common_su(uid_t to_uid, const char *sctx) { (void)to_uid; (void)sctx; return -ENOSYS; } +static inline int commit_su(uid_t uid, const char *sctx) { (void)uid; (void)sctx; return -ENOSYS; } +static inline int task_su(pid_t pid, uid_t to_uid, const char *sctx) { (void)pid; (void)to_uid; (void)sctx; return -ENOSYS; } +static inline int bypass_selinux(void) { return 0; } +static inline void set_priv_sel_allow(struct task_struct *task, bool val) { (void)task; (void)val; } +#endif /* CONFIG_KP_NO_ROOT */ #endif \ No newline at end of file diff --git a/kernel/patch/include/sucompat.h b/kernel/patch/include/sucompat.h index f5ad9a5f..4c8c077b 100644 --- a/kernel/patch/include/sucompat.h +++ b/kernel/patch/include/sucompat.h @@ -10,6 +10,9 @@ #include #include +#include + +#ifndef CONFIG_KP_NO_ROOT extern const char sh_path[]; extern const char default_su_path[]; extern const char legacy_su_path[]; @@ -46,5 +49,23 @@ void sucompat_unregister_path_probe_hooks(void); /* Supercall handler: control a feature by name. * state: 1=enable, 0=disable, -1=query current state. */ long kp_control_feature_sc(const char __user *uname, int state); +#else +int is_su_allow_uid(uid_t uid); +static inline int su_compat_init(void) { return 0; } +static inline void sucompat_init(void) {} +static inline int su_add_allow_uid(uid_t uid, uid_t to_uid, const char *scontext) { (void)uid; (void)to_uid; (void)scontext; return -ENOSYS; } +static inline int su_remove_allow_uid(uid_t uid) { (void)uid; return -ENOSYS; } +static inline int su_allow_uid_nums(void) { return 0; } +static inline int su_allow_uids(int is_user, uid_t *out_uids, int out_num) { (void)is_user; (void)out_uids; (void)out_num; return 0; } +static inline int su_allow_uid_profile(int is_user, uid_t uid, struct su_profile *profile) { (void)is_user; (void)uid; (void)profile; return -ENOSYS; } +static inline int su_reset_path(const char *path) { (void)path; return -ENOSYS; } +static inline const char *su_get_path(void) { return ""; } +static inline int get_ap_mod_exclude(uid_t uid) { (void)uid; return 0; } +static inline int set_ap_mod_exclude(uid_t uid, int exclude) { (void)uid; (void)exclude; return -ENOSYS; } +static inline int list_ap_mod_exclude(uid_t *uids, int len) { (void)uids; (void)len; return 0; } +static inline void sucompat_register_path_probe_hooks(void) {} +static inline void sucompat_unregister_path_probe_hooks(void) {} +static inline long kp_control_feature_sc(const char __user *uname, int state) { (void)uname; (void)state; return -ENOSYS; } +#endif /* CONFIG_KP_NO_ROOT */ #endif diff --git a/kernel/patch/patch.c b/kernel/patch/patch.c index 6fe3b034..faa29fc4 100644 --- a/kernel/patch/patch.c +++ b/kernel/patch/patch.c @@ -12,6 +12,7 @@ #include #include #include +#include void print_bootlog() { @@ -47,14 +48,18 @@ int resolve_struct(); int task_observer(); int hotpatch_init(); int bypass_kcfi(); +#ifndef CONFIG_KP_NO_ROOT int bypass_selinux(); +#endif int resolve_pt_regs(); int supercall_install(); void module_init(); void syscall_init(); int kstorage_init(); +#ifndef CONFIG_KP_NO_ROOT int su_compat_init(); // int selinux_hide_init(); +#endif #ifdef ANDROID int android_user_init(); @@ -76,8 +81,10 @@ static void before_rest_init(hook_fargs4_t *args, void *udata) if ((rc = resolve_struct())) goto out; log_boot("resolve_struct done: %d\n", rc); +#ifndef CONFIG_KP_NO_ROOT if ((rc = bypass_selinux())) goto out; log_boot("bypass_selinux done: %d\n", rc); +#endif if ((rc = task_observer())) goto out; log_boot("task_observer done: %d\n", rc); @@ -88,11 +95,13 @@ static void before_rest_init(hook_fargs4_t *args, void *udata) rc = kstorage_init(); log_boot("kstorage_init done: %d\n", rc); +#ifndef CONFIG_KP_NO_ROOT rc = su_compat_init(); log_boot("su_compat_init done: %d\n", rc); // rc = selinux_hide_init(); // log_boot("selinux_hide_init done: %d\n", rc); +#endif rc = resolve_pt_regs(); log_boot("resolve_pt_regs done: %d\n", rc); @@ -139,14 +148,39 @@ void extra_event_init(const char *event) } KP_EXPORT_SYMBOL(extra_event_init); +// Guard against multiple executions: some vendor kernels (e.g. MediaTek with +// bootprof/mtprof instrumentation) or symbol collision cases may call into the +// resolved kernel_init address repeatedly. pre-kernel-init and post-kernel-init +// are strictly one-shot lifecycle events; this guard ensures they fire exactly +// once across all SMP cores without requiring unsafe in-trampoline hook unregistration. +// Note: We use the kernel's xchg() macro () instead of GCC's __atomic +// builtins because -moutline-atomics generates calls to libgcc's __aarch64_swp4_acq_rel, +// which does not exist in our nostdlib bare-metal environment. +static volatile int kernel_init_done = 0; + static void before_kernel_init(hook_fargs4_t *args, void *udata) { + args->local.data0 = 0; + if (kernel_init_done) { + args->skip_origin = 0; + return; + } + + if (xchg(&kernel_init_done, 1)) { + args->skip_origin = 0; + return; + } + + args->local.data0 = 1; extra_event_init(EXTRA_EVENT_PRE_KERNEL_INIT); } static void after_kernel_init(hook_fargs4_t *args, void *udata) { - extra_event_init(EXTRA_EVENT_POST_KERNEL_INIT); + if (args->local.data0) { + args->local.data0 = 0; + extra_event_init(EXTRA_EVENT_POST_KERNEL_INIT); + } } int patch() From 0aa7a3c87778365fb0d7cee37021b9d1eeed8aeb Mon Sep 17 00:00:00 2001 From: Yervant <71306082+Yervant7@users.noreply.github.com> Date: Sun, 6 Sep 2026 11:08:59 +0000 Subject: [PATCH 2/7] improve the last commit --- kernel/Makefile | 10 +++++----- kernel/patch/android/userd.c | 10 ++++++++-- kernel/patch/patch.c | 10 ++-------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/kernel/Makefile b/kernel/Makefile index b191610e..d82dde49 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -46,10 +46,10 @@ ifdef ANDROID endif ENABLE_ROOT ?= 1 -ifneq ($(NO_ROOT),) +ifeq ($(NO_ROOT),1) ENABLE_ROOT := 0 endif -ifneq ($(DISABLE_ROOT),) +ifeq ($(DISABLE_ROOT),1) ENABLE_ROOT := 0 endif @@ -59,13 +59,13 @@ ifeq ($(ENABLE_ROOT),0) endif ENABLE_OFFICIAL_MANAGER ?= $(ENABLE_ROOT) -ifneq ($(OFFICIAL_MANAGER),) +ifeq ($(OFFICIAL_MANAGER),1) ENABLE_OFFICIAL_MANAGER := $(OFFICIAL_MANAGER) endif -ifneq ($(NO_MANAGER),) +ifeq ($(NO_MANAGER),1) ENABLE_OFFICIAL_MANAGER := 0 endif -ifneq ($(NO_OFFICIAL_MANAGER),) +ifeq ($(NO_OFFICIAL_MANAGER),1) ENABLE_OFFICIAL_MANAGER := 0 endif diff --git a/kernel/patch/android/userd.c b/kernel/patch/android/userd.c index 09784270..3b7a181c 100644 --- a/kernel/patch/android/userd.c +++ b/kernel/patch/android/userd.c @@ -211,10 +211,12 @@ static int path_has_suffix(const char *path, const char *suffix) return strcmp(path + path_len - suffix_len, suffix) == 0; } -static __maybe_unused int is_packages_list_tmp_dentry_path(const char *path) +#ifndef CONFIG_KP_NO_OFFICIAL_MANAGER +static int is_packages_list_tmp_dentry_path(const char *path) { return path_has_suffix(path, "/system/packages.list.tmp"); } +#endif /* bounds-checked read: fails if [*pos, *pos+size) would fall outside [*pos, end) */ static int read_exact(struct file *fp, void *buf, size_t size, loff_t *pos, loff_t end) @@ -1127,7 +1129,8 @@ int refresh_trusted_manager_uid(void) return refresh_trusted_manager_state(); } -static __maybe_unused int refresh_trusted_manager_state_from_packages_list(int use_tmp) +#ifndef CONFIG_KP_NO_OFFICIAL_MANAGER +static int refresh_trusted_manager_state_from_packages_list(int use_tmp) { uid_t uid = TRUSTED_MANAGER_UID_INVALID; int rc = refresh_trusted_manager_uid_from_packages_list(&uid, use_tmp); @@ -1143,6 +1146,7 @@ static __maybe_unused int refresh_trusted_manager_state_from_packages_list(int u return 0; } +#endif int refresh_trusted_manager_state(void) { @@ -1179,6 +1183,7 @@ uid_t get_trusted_manager_uid(void) } KP_EXPORT_SYMBOL(get_trusted_manager_uid); +#ifndef CONFIG_KP_NO_ROOT // Simple CSV field parser helper function static __maybe_unused char *parse_csv_field(char **line_ptr) { @@ -1222,6 +1227,7 @@ static __maybe_unused char *parse_csv_field(char **line_ptr) return start; } +#endif // Load APatch package_config configuration file // Returns: number of entries loaded, or negative error code diff --git a/kernel/patch/patch.c b/kernel/patch/patch.c index faa29fc4..21da7771 100644 --- a/kernel/patch/patch.c +++ b/kernel/patch/patch.c @@ -161,15 +161,9 @@ static volatile int kernel_init_done = 0; static void before_kernel_init(hook_fargs4_t *args, void *udata) { args->local.data0 = 0; - if (kernel_init_done) { - args->skip_origin = 0; - return; - } + if (kernel_init_done) return; - if (xchg(&kernel_init_done, 1)) { - args->skip_origin = 0; - return; - } + if (xchg(&kernel_init_done, 1)) return; args->local.data0 = 1; extra_event_init(EXTRA_EVENT_PRE_KERNEL_INIT); From e6721330c87c90cf0ff6e17e0fec5808c3fc9273 Mon Sep 17 00:00:00 2001 From: Yervant7 <71306082+Yervant7@users.noreply.github.com> Date: Sun, 6 Sep 2026 11:49:23 -0300 Subject: [PATCH 3/7] Add build config AUTOLOAD_KPM autoloading for KPM Introduce AUTOLOAD_KPM support: docs updated (en/zh-CN) and Makefile flags (AUTOLOAD_KPM, CONFIG_KP_AUTOLOAD_KPM) to enable compile-time/runtime autoloading. Implement scanning/loading helpers in userd.c (file_exists_privileged, scan_and_load_kpm_dir), support both flat and directory KPM layouts, disable markers, and autoload_kpm_modules guarded by CONFIG_KP_AUTOLOAD_KPM. Adjust initialization to trigger autoload on first app/process exec paths. Refactor trusted-manager and root-only code with proper CONFIG_KP_NO_OFFICIAL_MANAGER / CONFIG_KP_NO_ROOT guards, add unhook_bypass_selinux, and update headers and prototypes accordingly. --- doc/en/build.md | 6 ++ doc/zh-CN/build.md | 6 ++ kernel/Makefile | 17 +++- kernel/patch/android/userd.c | 173 +++++++++++++++++++++++++++------- kernel/patch/common/accctl.c | 34 +++++++ kernel/patch/include/accctl.h | 21 +++-- kernel/patch/include/userd.h | 6 +- kernel/patch/patch.c | 4 - 8 files changed, 219 insertions(+), 48 deletions(-) diff --git a/doc/en/build.md b/doc/en/build.md index 22652f32..78c96505 100644 --- a/doc/en/build.md +++ b/doc/en/build.md @@ -22,6 +22,12 @@ make ANDROID=1 NO_ROOT=1 make ANDROID=1 NO_ROOT=1 OFFICIAL_MANAGER=1 # To disable official manager rename hooks in a standard root build: make ANDROID=1 NO_MANAGER=1 + +# Autoload KPM modules (for builds without official manager or with NO_ROOT): +# Automatically load KPM modules from /data/adb/kpm or /data/adb/ap/kpm once /data is mounted (post-fs-data, before Zygote): +make ANDROID=1 NO_ROOT=1 AUTOLOAD_KPM=1 +# Or in a standard root build without official manager: +make ANDROID=1 NO_MANAGER=1 AUTOLOAD_KPM=1 ``` ## Build kptools diff --git a/doc/zh-CN/build.md b/doc/zh-CN/build.md index bdaf15ae..4e514c39 100644 --- a/doc/zh-CN/build.md +++ b/doc/zh-CN/build.md @@ -22,6 +22,12 @@ make ANDROID=1 NO_ROOT=1 make ANDROID=1 NO_ROOT=1 OFFICIAL_MANAGER=1 # 在标准 root 构建中禁用官方 Manager rename hook: make ANDROID=1 NO_MANAGER=1 + +# 自动加载 KPM 模块(适用于无官方 Manager 或启用 NO_ROOT 的构建): +# 在 /data 分区挂载后(post-fs-data 阶段,Zygote 启动前)自动从 /data/adb/kpm 或 /data/adb/ap/kpm 加载 KPM 模块: +make ANDROID=1 NO_ROOT=1 AUTOLOAD_KPM=1 +# 或在无官方 Manager 的标准 root 构建中: +make ANDROID=1 NO_MANAGER=1 AUTOLOAD_KPM=1 ``` ## 编译 kptools diff --git a/kernel/Makefile b/kernel/Makefile index d82dde49..6dd2cada 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -74,6 +74,22 @@ ifeq ($(ENABLE_OFFICIAL_MANAGER),0) X86_CFLAGS += -DCONFIG_KP_NO_OFFICIAL_MANAGER endif +ENABLE_AUTOLOAD_KPM ?= 0 +ifeq ($(AUTOLOAD_KPM),1) + ENABLE_AUTOLOAD_KPM := 1 +endif +ifeq ($(CONFIG_KP_AUTOLOAD_KPM),1) + ENABLE_AUTOLOAD_KPM := 1 +endif +ifeq ($(NO_AUTOLOAD_KPM),1) + ENABLE_AUTOLOAD_KPM := 0 +endif + +ifeq ($(ENABLE_AUTOLOAD_KPM),1) + CFLAGS += -DCONFIG_KP_AUTOLOAD_KPM + X86_CFLAGS += -DCONFIG_KP_AUTOLOAD_KPM +endif + INCLUDE := -I. -Iinclude -Ipatch/include -Ilinux -Ilinux/include -Ilinux/arch/arm64/include -Ilinux/tools/arch/arm64/include X86_INCLUDE := -I. -Iinclude -Ipatch/include -Ilinux/include -Ilinux/include/uapi/asm-generic -Ilinux/tools/arch/arm64/include @@ -107,7 +123,6 @@ endif ifeq ($(ENABLE_ROOT),0) ROOT_SRCS := \ - patch/common/accctl.c \ patch/common/selinux_hide.c \ patch/common/selinux_sepolicy.c \ patch/common/sucompat.c \ diff --git a/kernel/patch/android/userd.c b/kernel/patch/android/userd.c index 3b7a181c..327092d5 100644 --- a/kernel/patch/android/userd.c +++ b/kernel/patch/android/userd.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #define REPLACE_RC_FILE "/dev/user_init.rc" @@ -55,6 +56,7 @@ #define AP_PACKAGE_CONFIG_PATH "/data/adb/ap/package_config" #define ANDROID_PACKAGES_LIST_PATH "/data/system/packages.list" #define ANDROID_PACKAGES_LIST_TMP_PATH "/data/system/packages.list.tmp" +#define ADB_KPM_DIR ADB_FLODER "kpm/" #define AP_KPM_DIR AP_DIR "kpm/" #define AP_KPM_NAME_LEN 128 #define AP_KPM_MAX_MODULES 256 @@ -72,6 +74,7 @@ extern int android_is_safe_mode; #define TRUSTED_MANAGER_DIGEST_LEN SHA256_BLOCK_SIZE #define TRUSTED_MANAGER_UID_INVALID ((uid_t)-1) +#ifndef CONFIG_KP_NO_OFFICIAL_MANAGER struct trusted_manager_entry { const char package[64]; const uint8_t digest[TRUSTED_MANAGER_DIGEST_LEN]; @@ -100,8 +103,7 @@ static const struct trusted_manager_entry trusted_managers[] = { }; static uid_t trusted_manager_uid = TRUSTED_MANAGER_UID_INVALID; -static __maybe_unused int global_pkg_pos = 0; - +#endif /* CONFIG_KP_NO_OFFICIAL_MANAGER */ #ifndef CONFIG_KP_NO_ROOT static const char ORIGIN_RC_FILES[][64] = { @@ -173,6 +175,7 @@ static int expand_rc_template(char *dst, size_t dst_size, const char *template, } #endif /* CONFIG_KP_NO_ROOT */ +#ifndef CONFIG_KP_NO_OFFICIAL_MANAGER static const void *kernel_read_file(const char *path, loff_t *len) { set_priv_sel_allow(current, true); @@ -199,6 +202,7 @@ static const void *kernel_read_file(const char *path, loff_t *len) set_priv_sel_allow(current, false); return data; } +#endif static int path_has_suffix(const char *path, const char *suffix) { @@ -216,7 +220,6 @@ static int is_packages_list_tmp_dentry_path(const char *path) { return path_has_suffix(path, "/system/packages.list.tmp"); } -#endif /* bounds-checked read: fails if [*pos, *pos+size) would fall outside [*pos, end) */ static int read_exact(struct file *fp, void *buf, size_t size, loff_t *pos, loff_t end) @@ -1057,8 +1060,6 @@ static int find_trusted_manager_apk_path(char *apk_path, return rc; } - - static int refresh_trusted_manager_uid_from_packages_list(uid_t *trusted_uid_out, int use_tmp) { uid_t last_uid = TRUSTED_MANAGER_UID_INVALID; @@ -1123,6 +1124,7 @@ static int refresh_trusted_manager_uid_from_packages_list(uid_t *trusted_uid_out *trusted_uid_out = last_uid; return 0; } +#endif /* CONFIG_KP_NO_OFFICIAL_MANAGER */ int refresh_trusted_manager_uid(void) { @@ -1185,7 +1187,7 @@ KP_EXPORT_SYMBOL(get_trusted_manager_uid); #ifndef CONFIG_KP_NO_ROOT // Simple CSV field parser helper function -static __maybe_unused char *parse_csv_field(char **line_ptr) +static char *parse_csv_field(char **line_ptr) { char *start = *line_ptr; char *end = start; @@ -1428,11 +1430,11 @@ int load_ap_package_config() KP_EXPORT_SYMBOL(load_ap_package_config); /* - * Scan /data/adb/ap/kpm without opening children from the readdir callback. + * Scan KPM directory without opening children from the readdir callback. * Some Android kernels hold the directory inode lock while iterate_dir() is * running, so child opens are deliberately deferred until after the scan. - * The expected layout is kpm//.kpm, with an optional - * kpm//disable marker. + * Supports both directory layout (kpm//.kpm) and flat layout + * (kpm/.kpm), along with their respective disable markers. */ struct ap_kpm_scan_ctx { struct dir_context dctx; @@ -1480,7 +1482,20 @@ static int ap_kpm_scan_actor_int(struct dir_context_int *dctx, const char *name, return 0; } -int load_ap_kpm_modules(void) +static bool file_exists_privileged(const char *path) +{ + struct file *f; + set_priv_sel_allow(current, true); + f = filp_open(path, O_RDONLY | O_NOFOLLOW, 0); + set_priv_sel_allow(current, false); + if (f && !IS_ERR(f)) { + filp_close(f, 0); + return true; + } + return false; +} + +static int scan_and_load_kpm_dir(const char *kpm_dir, const char *event) { struct file *dir; char *names; @@ -1488,19 +1503,33 @@ int load_ap_kpm_modules(void) int rc; if (android_is_safe_mode) return 0; - names = vmalloc((size_t)AP_KPM_MAX_MODULES * AP_KPM_NAME_LEN); - if (!names) return -ENOMEM; - memset(names, 0, (size_t)AP_KPM_MAX_MODULES * AP_KPM_NAME_LEN); + + // Check directory disable file: disable (e.g. /data/adb/kpm/disable) + char global_disable[AP_KPM_NAME_LEN + 128]; + int gd_len = snprintf(global_disable, sizeof(global_disable), "%sdisable", kpm_dir); + if (gd_len > 0 && gd_len < (int)sizeof(global_disable)) { + if (file_exists_privileged(global_disable)) { + log_boot("KPM directory disabled by %s\n", global_disable); + return 0; + } + } set_priv_sel_allow(current, true); - dir = filp_open(AP_KPM_DIR, O_RDONLY | O_NOFOLLOW, 0); + dir = filp_open(kpm_dir, O_RDONLY | O_NOFOLLOW, 0); if (!dir || IS_ERR(dir)) { rc = dir ? PTR_ERR(dir) : -ENOENT; set_priv_sel_allow(current, false); - kvfree(names); - if (rc != -ENOENT) log_boot("open AP KPM directory failed: %d\n", rc); - return rc == -ENOENT ? 0 : rc; + if (rc != -ENOENT) log_boot("open KPM directory failed: %s, rc: %d\n", kpm_dir, rc); + return rc; + } + + names = vmalloc((size_t)AP_KPM_MAX_MODULES * AP_KPM_NAME_LEN); + if (!names) { + filp_close(dir, 0); + set_priv_sel_allow(current, false); + return -ENOMEM; } + memset(names, 0, (size_t)AP_KPM_MAX_MODULES * AP_KPM_NAME_LEN); if (kver >= VERSION(6, 1, 0)) { struct ap_kpm_scan_ctx ctx = { .names = names }; @@ -1520,38 +1549,99 @@ int load_ap_kpm_modules(void) for (int i = 0; i < count; i++) { char *id = names + i * AP_KPM_NAME_LEN; - char path[AP_KPM_NAME_LEN * 2 + sizeof(AP_KPM_DIR) + 8]; - char disable[AP_KPM_NAME_LEN + sizeof(AP_KPM_DIR) + 16]; - int path_len = snprintf(path, sizeof(path), AP_KPM_DIR "%s/%s.kpm", id, id); - int disable_len = snprintf(disable, sizeof(disable), AP_KPM_DIR "%s/disable", id); - struct file *marker; + char path[AP_KPM_NAME_LEN * 2 + 128]; + char disable[AP_KPM_NAME_LEN + 128]; + int path_len, disable_len; - if (path_len <= 0 || path_len >= sizeof(path) || disable_len <= 0 || disable_len >= sizeof(disable)) { + if (path_has_suffix(id, ".disable")) { skipped++; continue; } - set_priv_sel_allow(current, true); - marker = filp_open(disable, O_RDONLY | O_NOFOLLOW, 0); - if (marker && !IS_ERR(marker)) { - filp_close(marker, 0); - set_priv_sel_allow(current, false); - log_boot("skip disabled AP KPM: %s\n", id); + + if (path_has_suffix(id, ".kpm")) { + path_len = snprintf(path, sizeof(path), "%s%s", kpm_dir, id); + disable_len = snprintf(disable, sizeof(disable), "%s%s.disable", kpm_dir, id); + } else { + path_len = snprintf(path, sizeof(path), "%s%s/%s.kpm", kpm_dir, id, id); + disable_len = snprintf(disable, sizeof(disable), "%s%s/disable", kpm_dir, id); + } + + if (path_len <= 0 || path_len >= (int)sizeof(path) || disable_len <= 0 || disable_len >= (int)sizeof(disable)) { skipped++; continue; } - set_priv_sel_allow(current, false); - rc = load_module_path_event(path, 0, EXTRA_EVENT_POST_FS_DATA, 0); - log_boot("load AP KPM: %s, event: %s, rc: %d\n", path, EXTRA_EVENT_POST_FS_DATA, rc); + if (file_exists_privileged(disable)) { + log_boot("skip disabled KPM: %s\n", id); + skipped++; + continue; + } + + if (path_has_suffix(id, ".kpm")) { + char disable2[AP_KPM_NAME_LEN + 128]; + int stem_len = (int)strlen(id) - 4; + if (stem_len > 0) { + int disable2_len = snprintf(disable2, sizeof(disable2), "%s%.*s.disable", kpm_dir, stem_len, id); + if (disable2_len > 0 && disable2_len < (int)sizeof(disable2)) { + if (file_exists_privileged(disable2)) { + log_boot("skip disabled KPM: %s\n", id); + skipped++; + continue; + } + } + } + } + + set_priv_sel_allow(current, true); + rc = load_module_path_event(path, 0, event, 0); + set_priv_sel_allow(current, false); + log_boot("load KPM: %s, event: %s, rc: %d\n", path, event, rc); if (!rc) loaded++; } kvfree(names); - log_boot("AP KPM loading done: loaded=%d skipped=%d total=%d\n", loaded, skipped, count); + log_boot("KPM loading from %s done: loaded=%d skipped=%d total=%d\n", kpm_dir, loaded, skipped, count); return loaded; } + +static volatile int adb_kpm_loaded = 0; +static volatile int ap_kpm_loaded = 0; + +int load_ap_kpm_modules(void) +{ + if (xchg(&ap_kpm_loaded, 1)) return 0; + return scan_and_load_kpm_dir(AP_KPM_DIR, EXTRA_EVENT_POST_FS_DATA); +} KP_EXPORT_SYMBOL(load_ap_kpm_modules); +#ifdef CONFIG_KP_AUTOLOAD_KPM +int autoload_kpm_modules(void) +{ + int loaded = 0; + + if (!adb_kpm_loaded) { + int rc = scan_and_load_kpm_dir(ADB_KPM_DIR, EXTRA_EVENT_POST_FS_DATA); + if (rc >= 0) { + adb_kpm_loaded = 1; + loaded += rc; + } + } + + if (!ap_kpm_loaded) { + int rc = scan_and_load_kpm_dir(AP_KPM_DIR, EXTRA_EVENT_POST_FS_DATA); + if (rc >= 0) { + ap_kpm_loaded = 1; + loaded += rc; + } + } + + if (loaded > 0) { + log_boot("autoload KPM done: %d loaded\n", loaded); + } + return loaded; +} +#endif + static void pre_user_exec_init() { extra_event_init(EXTRA_EVENT_PRE_EXEC_INIT); @@ -1584,12 +1674,23 @@ static void post_init_second_stage() static void on_first_app_process() { +#ifdef CONFIG_KP_AUTOLOAD_KPM + if (!adb_kpm_loaded || !ap_kpm_loaded) { + autoload_kpm_modules(); + adb_kpm_loaded = 1; + ap_kpm_loaded = 1; + } +#endif #ifndef CONFIG_KP_NO_OFFICIAL_MANAGER /* Refresh the trusted-manager state (APK scan) synchronously here. The scan * itself is two-phase so it cannot deadlock on the /data/app inode lock. */ int rc = refresh_trusted_manager_state(); log_boot("on_first_app_process: trusted manager refresh rc=%d\n", rc); #endif + +#if defined(CONFIG_KP_NO_ROOT) && defined(CONFIG_KP_NO_OFFICIAL_MANAGER) + unhook_bypass_selinux(); +#endif } static void handle_before_execve(hook_local_t *hook_local, char **__user u_filename_p, char **__user uargv, @@ -1685,6 +1786,12 @@ static void handle_before_execve(hook_local_t *hook_local, char **__user u_filen hook_local->data7 = 1; return; } + +#ifdef CONFIG_KP_AUTOLOAD_KPM + if (init_second_stage_executed && (!adb_kpm_loaded || !ap_kpm_loaded)) { + autoload_kpm_modules(); + } +#endif } static void before_execve(hook_fargs3_t *args, void *udata); diff --git a/kernel/patch/common/accctl.c b/kernel/patch/common/accctl.c index 62232e49..99fcd307 100644 --- a/kernel/patch/common/accctl.c +++ b/kernel/patch/common/accctl.c @@ -28,6 +28,7 @@ extern void kp_debug_write(const char *fmt, ...); +#ifndef CONFIG_KP_NO_ROOT char all_allow_sctx[SUPERCALL_SCONTEXT_LEN] = { '\0' }; uint32_t all_allow_sid = SECSID_NULL; @@ -174,6 +175,7 @@ int task_su(pid_t pid, uid_t to_uid, const char *sctx) out: return rc; } +#endif /* CONFIG_KP_NO_ROOT */ static int (*avc_denied_backup)(struct selinux_state *state, void *ssid, void *tsid, void *tclass, void *requested, void *driver, void *xperm, void *flags, struct av_decision *avd) = 0; @@ -181,6 +183,7 @@ static int (*avc_denied_backup)(struct selinux_state *state, void *ssid, void *t static int avc_denied_replace(struct selinux_state *_state, void *_ssid, void *_tsid, void *_tclass, void *_requested, void *_driver, void *_xperm, void *_flags, struct av_decision *_avd) { +#ifndef CONFIG_KP_NO_ROOT if (all_allow_sid != SECSID_NULL) { u32 ssid = (u32)(u64)_ssid; if ((uint64_t)_state <= 0xffffffffL) { @@ -195,6 +198,12 @@ static int avc_denied_replace(struct selinux_state *_state, void *_ssid, void *_ if (unlikely(task_ext_valid(ext) && (ext->sel_allow || ext->priv_sel_allow))) { goto allow; } +#else + struct task_ext *ext = get_current_task_ext(); + if (unlikely(task_ext_valid(ext) && ext->priv_sel_allow)) { + goto allow; + } +#endif int rc = avc_denied_backup(_state, _ssid, _tsid, _tclass, _requested, _driver, _xperm, _flags, _avd); return rc; @@ -218,6 +227,7 @@ static int slow_avc_audit_replace(struct selinux_state *_state, void *_ssid, voi void *_requested, void *_audited, void *_denied, void *_result, struct common_audit_data *_a) { +#ifndef CONFIG_KP_NO_ROOT if (all_allow_sid != SECSID_NULL) { u32 ssid = (u64)_ssid; if ((uint64_t)_state <= 0xffffffffL) { @@ -232,6 +242,12 @@ static int slow_avc_audit_replace(struct selinux_state *_state, void *_ssid, voi if (unlikely(task_ext_valid(ext) && (ext->sel_allow || ext->priv_sel_allow))) { return 0; } +#else + struct task_ext *ext = get_current_task_ext(); + if (unlikely(task_ext_valid(ext) && ext->priv_sel_allow)) { + return 0; + } +#endif int rc = slow_avc_audit_backup(_state, _ssid, _tsid, _tclass, _requested, _audited, _denied, _result, _a); return rc; @@ -258,3 +274,21 @@ int bypass_selinux() return 0; } + +#if defined(CONFIG_KP_NO_ROOT) && defined(CONFIG_KP_NO_OFFICIAL_MANAGER) +void unhook_bypass_selinux(void) +{ + unsigned long avc_denied_addr = patch_config->avc_denied; + if (avc_denied_addr && avc_denied_backup) { + unhook((void *)avc_denied_addr); + avc_denied_backup = 0; + } + + unsigned long slow_avc_audit_addr = patch_config->slow_avc_audit; + if (slow_avc_audit_addr && slow_avc_audit_backup) { + unhook((void *)slow_avc_audit_addr); + slow_avc_audit_backup = 0; + } + log_boot("unhooked bypass_selinux (SELinux restored to pristine state)\n"); +} +#endif diff --git a/kernel/patch/include/accctl.h b/kernel/patch/include/accctl.h index 47d15330..fd1d6b2a 100644 --- a/kernel/patch/include/accctl.h +++ b/kernel/patch/include/accctl.h @@ -26,6 +26,18 @@ int commit_kernel_su(); int commit_common_su(uid_t to_uid, const char *sctx); int commit_su(uid_t uid, const char *sctx); int task_su(pid_t pid, uid_t to_uid, const char *sctx); +#else +static inline int set_all_allow_sctx(const char *sctx) { (void)sctx; return -ENOSYS; } +static inline int commit_kernel_su(void) { return -ENOSYS; } +static inline int commit_common_su(uid_t to_uid, const char *sctx) { (void)to_uid; (void)sctx; return -ENOSYS; } +static inline int commit_su(uid_t uid, const char *sctx) { (void)uid; (void)sctx; return -ENOSYS; } +static inline int task_su(pid_t pid, uid_t to_uid, const char *sctx) { (void)pid; (void)to_uid; (void)sctx; return -ENOSYS; } +#endif /* CONFIG_KP_NO_ROOT */ + +int bypass_selinux(void); +#if defined(CONFIG_KP_NO_ROOT) && defined(CONFIG_KP_NO_OFFICIAL_MANAGER) +void unhook_bypass_selinux(void); +#endif /** * @brief Whether to make the current task bypass all selinux permission checks. @@ -40,14 +52,5 @@ static inline void set_priv_sel_allow(struct task_struct *task, bool val) ext->priv_sel_allow = val; dsb(ish); } -#else -static inline int set_all_allow_sctx(const char *sctx) { (void)sctx; return -ENOSYS; } -static inline int commit_kernel_su(void) { return -ENOSYS; } -static inline int commit_common_su(uid_t to_uid, const char *sctx) { (void)to_uid; (void)sctx; return -ENOSYS; } -static inline int commit_su(uid_t uid, const char *sctx) { (void)uid; (void)sctx; return -ENOSYS; } -static inline int task_su(pid_t pid, uid_t to_uid, const char *sctx) { (void)pid; (void)to_uid; (void)sctx; return -ENOSYS; } -static inline int bypass_selinux(void) { return 0; } -static inline void set_priv_sel_allow(struct task_struct *task, bool val) { (void)task; (void)val; } -#endif /* CONFIG_KP_NO_ROOT */ #endif \ No newline at end of file diff --git a/kernel/patch/include/userd.h b/kernel/patch/include/userd.h index af259898..e79492ac 100644 --- a/kernel/patch/include/userd.h +++ b/kernel/patch/include/userd.h @@ -10,12 +10,16 @@ int load_ap_package_config(void); int load_ap_kpm_modules(void); +#ifdef CONFIG_KP_AUTOLOAD_KPM +int autoload_kpm_modules(void); +#endif #ifdef ANDROID int refresh_trusted_manager_uid(void); int refresh_trusted_manager_state(void); - +#ifndef CONFIG_KP_NO_OFFICIAL_MANAGER uid_t get_trusted_manager_uid(void); +#endif int is_trusted_manager_uid_android(uid_t uid); #endif diff --git a/kernel/patch/patch.c b/kernel/patch/patch.c index 21da7771..b98282c7 100644 --- a/kernel/patch/patch.c +++ b/kernel/patch/patch.c @@ -48,9 +48,7 @@ int resolve_struct(); int task_observer(); int hotpatch_init(); int bypass_kcfi(); -#ifndef CONFIG_KP_NO_ROOT int bypass_selinux(); -#endif int resolve_pt_regs(); int supercall_install(); void module_init(); @@ -81,10 +79,8 @@ static void before_rest_init(hook_fargs4_t *args, void *udata) if ((rc = resolve_struct())) goto out; log_boot("resolve_struct done: %d\n", rc); -#ifndef CONFIG_KP_NO_ROOT if ((rc = bypass_selinux())) goto out; log_boot("bypass_selinux done: %d\n", rc); -#endif if ((rc = task_observer())) goto out; log_boot("task_observer done: %d\n", rc); From 9cb4127971c5ed6188b4b3ee2380319c18e5824a Mon Sep 17 00:00:00 2001 From: Yervant7 <71306082+Yervant7@users.noreply.github.com> Date: Sun, 6 Sep 2026 15:58:12 -0300 Subject: [PATCH 4/7] Fix KPM autoload and SELinux hook casting This change fixes the KPM autoload flow by guarding privileged directory access, ensuring post-fs-data detection runs only once, and avoiding the disabled-directory false-positive path. It also corrects the write_op symbol cast in selinux_hide so the hook resolves cleanly on modern builds. --- kernel/patch/android/userd.c | 67 +++++++++++++++++++++++------- kernel/patch/common/selinux_hide.c | 2 +- 2 files changed, 52 insertions(+), 17 deletions(-) diff --git a/kernel/patch/android/userd.c b/kernel/patch/android/userd.c index 327092d5..0c0546f7 100644 --- a/kernel/patch/android/userd.c +++ b/kernel/patch/android/userd.c @@ -46,7 +46,7 @@ #define REPLACE_RC_FILE "/dev/user_init.rc" -#define ADB_FLODER "/data/adb/" +#define ADB_FOLDER "/data/adb/" #define AP_DIR "/data/adb/ap/" #define DEV_LOG_DIR "/dev/user_init_log/" #define AP_BIN_DIR AP_DIR "bin/" @@ -56,7 +56,7 @@ #define AP_PACKAGE_CONFIG_PATH "/data/adb/ap/package_config" #define ANDROID_PACKAGES_LIST_PATH "/data/system/packages.list" #define ANDROID_PACKAGES_LIST_TMP_PATH "/data/system/packages.list.tmp" -#define ADB_KPM_DIR ADB_FLODER "kpm/" +#define ADB_KPM_DIR ADB_FOLDER "kpm/" #define AP_KPM_DIR AP_DIR "kpm/" #define AP_KPM_NAME_LEN 128 #define AP_KPM_MAX_MODULES 256 @@ -1504,25 +1504,33 @@ static int scan_and_load_kpm_dir(const char *kpm_dir, const char *event) if (android_is_safe_mode) return 0; + set_priv_sel_allow(current, true); + dir = filp_open(kpm_dir, O_RDONLY | O_NOFOLLOW | O_DIRECTORY, 0); + if (!dir || IS_ERR(dir)) { + rc = dir ? PTR_ERR(dir) : -ENOENT; + set_priv_sel_allow(current, false); + if (rc != -ENOENT && rc != -ENOTDIR) { + log_boot("open KPM directory failed: %s, rc: %d\n", kpm_dir, rc); + } + return (rc == -ENOENT || rc == -ENOTDIR) ? 0 : rc; + } + // Check directory disable file: disable (e.g. /data/adb/kpm/disable) + // Note: priv_sel_allow is already true here; use direct filp_open to avoid + // file_exists_privileged() clobbering it (that helper resets the flag to false). char global_disable[AP_KPM_NAME_LEN + 128]; int gd_len = snprintf(global_disable, sizeof(global_disable), "%sdisable", kpm_dir); if (gd_len > 0 && gd_len < (int)sizeof(global_disable)) { - if (file_exists_privileged(global_disable)) { + struct file *gf = filp_open(global_disable, O_RDONLY | O_NOFOLLOW, 0); + if (gf && !IS_ERR(gf)) { + filp_close(gf, 0); + filp_close(dir, 0); + set_priv_sel_allow(current, false); log_boot("KPM directory disabled by %s\n", global_disable); return 0; } } - set_priv_sel_allow(current, true); - dir = filp_open(kpm_dir, O_RDONLY | O_NOFOLLOW, 0); - if (!dir || IS_ERR(dir)) { - rc = dir ? PTR_ERR(dir) : -ENOENT; - set_priv_sel_allow(current, false); - if (rc != -ENOENT) log_boot("open KPM directory failed: %s, rc: %d\n", kpm_dir, rc); - return rc; - } - names = vmalloc((size_t)AP_KPM_MAX_MODULES * AP_KPM_NAME_LEN); if (!names) { filp_close(dir, 0); @@ -1615,6 +1623,21 @@ int load_ap_kpm_modules(void) KP_EXPORT_SYMBOL(load_ap_kpm_modules); #ifdef CONFIG_KP_AUTOLOAD_KPM +static volatile int kpm_autoload_done = 0; + +static bool dir_exists_privileged(const char *path) +{ + struct file *f; + set_priv_sel_allow(current, true); + f = filp_open(path, O_RDONLY | O_NOFOLLOW | O_DIRECTORY, 0); + set_priv_sel_allow(current, false); + if (f && !IS_ERR(f)) { + filp_close(f, 0); + return true; + } + return false; +} + int autoload_kpm_modules(void) { int loaded = 0; @@ -1640,6 +1663,20 @@ int autoload_kpm_modules(void) } return loaded; } + +static void try_autoload_post_fs_data(void) +{ + if (kpm_autoload_done) return; + + /* Check if /data is mounted: /data/system always exists on mounted /data + * Also check /data/adb/ in case it exists. */ + if (dir_exists_privileged("/data/system") || dir_exists_privileged(ADB_FOLDER)) { + if (!xchg(&kpm_autoload_done, 1)) { + log_boot("post-fs-data detected, autoloading KPM...\n"); + autoload_kpm_modules(); + } + } +} #endif static void pre_user_exec_init() @@ -1677,8 +1714,6 @@ static void on_first_app_process() #ifdef CONFIG_KP_AUTOLOAD_KPM if (!adb_kpm_loaded || !ap_kpm_loaded) { autoload_kpm_modules(); - adb_kpm_loaded = 1; - ap_kpm_loaded = 1; } #endif #ifndef CONFIG_KP_NO_OFFICIAL_MANAGER @@ -1788,8 +1823,8 @@ static void handle_before_execve(hook_local_t *hook_local, char **__user u_filen } #ifdef CONFIG_KP_AUTOLOAD_KPM - if (init_second_stage_executed && (!adb_kpm_loaded || !ap_kpm_loaded)) { - autoload_kpm_modules(); + if (init_second_stage_executed && !kpm_autoload_done) { + try_autoload_post_fs_data(); } #endif } diff --git a/kernel/patch/common/selinux_hide.c b/kernel/patch/common/selinux_hide.c index 4b1be5f3..9e12105a 100644 --- a/kernel/patch/common/selinux_hide.c +++ b/kernel/patch/common/selinux_hide.c @@ -385,7 +385,7 @@ static int selinux_hide_install_hooks(void) }else{ log_boot("selinux_hide: using fp_hook to hook write_op\n"); - write_op = lookup_name_with_suffix("write_op"); + write_op = (sel_write_op_fn *)lookup_name_with_suffix("write_op"); if (!write_op) { rc = -ENOENT; log_boot("selinux_hide: write_op not found\n"); From 91daf55f27d733c59ec8e58bc7610d4c2e2d3d82 Mon Sep 17 00:00:00 2001 From: Yervant7 <71306082+Yervant7@users.noreply.github.com> Date: Mon, 7 Sep 2026 06:43:44 -0300 Subject: [PATCH 5/7] Fix KPM autoload race and adb paths Normalize the /data/adb paths used by the Android user daemon and avoid duplicate KPM autoload attempts. The autoload logic now uses atomic xchg guards and only treats positive return codes as successful module loads, preventing redundant scans and incorrect loaded-state tracking. The mounted /data check was also clarified to account for the adb directory layout. --- kernel/patch/android/userd.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/kernel/patch/android/userd.c b/kernel/patch/android/userd.c index 0c0546f7..2fd6a176 100644 --- a/kernel/patch/android/userd.c +++ b/kernel/patch/android/userd.c @@ -46,7 +46,7 @@ #define REPLACE_RC_FILE "/dev/user_init.rc" -#define ADB_FOLDER "/data/adb/" +#define ADB_FOLDER "/data/adb" #define AP_DIR "/data/adb/ap/" #define DEV_LOG_DIR "/dev/user_init_log/" #define AP_BIN_DIR AP_DIR "bin/" @@ -56,7 +56,7 @@ #define AP_PACKAGE_CONFIG_PATH "/data/adb/ap/package_config" #define ANDROID_PACKAGES_LIST_PATH "/data/system/packages.list" #define ANDROID_PACKAGES_LIST_TMP_PATH "/data/system/packages.list.tmp" -#define ADB_KPM_DIR ADB_FOLDER "kpm/" +#define ADB_KPM_DIR ADB_FOLDER "/kpm" #define AP_KPM_DIR AP_DIR "kpm/" #define AP_KPM_NAME_LEN 128 #define AP_KPM_MAX_MODULES 256 @@ -1642,18 +1642,16 @@ int autoload_kpm_modules(void) { int loaded = 0; - if (!adb_kpm_loaded) { + if (!xchg(&adb_kpm_loaded, 1)) { int rc = scan_and_load_kpm_dir(ADB_KPM_DIR, EXTRA_EVENT_POST_FS_DATA); - if (rc >= 0) { - adb_kpm_loaded = 1; + if (rc > 0) { loaded += rc; } } - if (!ap_kpm_loaded) { + if (!xchg(&ap_kpm_loaded, 1)) { int rc = scan_and_load_kpm_dir(AP_KPM_DIR, EXTRA_EVENT_POST_FS_DATA); - if (rc >= 0) { - ap_kpm_loaded = 1; + if (rc > 0) { loaded += rc; } } @@ -1668,7 +1666,7 @@ static void try_autoload_post_fs_data(void) { if (kpm_autoload_done) return; - /* Check if /data is mounted: /data/system always exists on mounted /data + /* Check if /data is mounted: /data/system probably always exists on mounted /data * Also check /data/adb/ in case it exists. */ if (dir_exists_privileged("/data/system") || dir_exists_privileged(ADB_FOLDER)) { if (!xchg(&kpm_autoload_done, 1)) { From 8221a85ee37e26e5e5bbdcfa5f8f73dcdc194f17 Mon Sep 17 00:00:00 2001 From: Yervant7 <71306082+Yervant7@users.noreply.github.com> Date: Mon, 7 Sep 2026 06:53:16 -0300 Subject: [PATCH 6/7] Enable KPM autoload in kpimg-android-no-root build Update the Android no-root build job to compile kpimg with AUTOLOAD_KPM=1. This ensures the generated artifact includes KPM auto-loading support, matching the intended runtime behavior for Android no root builds. --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4bd5148a..f174f75e 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -39,7 +39,7 @@ jobs: mv kpimg.elf kpimg.elf-android make clean - make kpimg NO_ROOT=1 + make kpimg NO_ROOT=1 AUTOLOAD_KPM=1 mv kpimg kpimg-android-no-root mv kpimg.elf kpimg.elf-android-no-root make clean From 56ff5072ba5f22505cb71bf1d9792c1c6c8ee348 Mon Sep 17 00:00:00 2001 From: Yervant <71306082+Yervant7@users.noreply.github.com> Date: Mon, 7 Sep 2026 08:33:19 -0300 Subject: [PATCH 7/7] fix AP_KPM_DIR and ADB_KPM_DIR --- kernel/patch/android/userd.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/kernel/patch/android/userd.c b/kernel/patch/android/userd.c index 2fd6a176..ce290d37 100644 --- a/kernel/patch/android/userd.c +++ b/kernel/patch/android/userd.c @@ -47,7 +47,7 @@ #define REPLACE_RC_FILE "/dev/user_init.rc" #define ADB_FOLDER "/data/adb" -#define AP_DIR "/data/adb/ap/" +#define AP_DIR "/data/adb/ap" #define DEV_LOG_DIR "/dev/user_init_log/" #define AP_BIN_DIR AP_DIR "bin/" #define AP_LOG_DIR AP_DIR "log/" @@ -57,7 +57,7 @@ #define ANDROID_PACKAGES_LIST_PATH "/data/system/packages.list" #define ANDROID_PACKAGES_LIST_TMP_PATH "/data/system/packages.list.tmp" #define ADB_KPM_DIR ADB_FOLDER "/kpm" -#define AP_KPM_DIR AP_DIR "kpm/" +#define AP_KPM_DIR AP_DIR "/kpm" #define AP_KPM_NAME_LEN 128 #define AP_KPM_MAX_MODULES 256 @@ -1567,11 +1567,11 @@ static int scan_and_load_kpm_dir(const char *kpm_dir, const char *event) } if (path_has_suffix(id, ".kpm")) { - path_len = snprintf(path, sizeof(path), "%s%s", kpm_dir, id); - disable_len = snprintf(disable, sizeof(disable), "%s%s.disable", kpm_dir, id); + path_len = snprintf(path, sizeof(path), "%s/%s", kpm_dir, id); + disable_len = snprintf(disable, sizeof(disable), "%s/%s.disable", kpm_dir, id); } else { - path_len = snprintf(path, sizeof(path), "%s%s/%s.kpm", kpm_dir, id, id); - disable_len = snprintf(disable, sizeof(disable), "%s%s/disable", kpm_dir, id); + path_len = snprintf(path, sizeof(path), "%s/%s/%s.kpm", kpm_dir, id, id); + disable_len = snprintf(disable, sizeof(disable), "%s/%s/disable", kpm_dir, id); } if (path_len <= 0 || path_len >= (int)sizeof(path) || disable_len <= 0 || disable_len >= (int)sizeof(disable)) { @@ -1589,7 +1589,7 @@ static int scan_and_load_kpm_dir(const char *kpm_dir, const char *event) char disable2[AP_KPM_NAME_LEN + 128]; int stem_len = (int)strlen(id) - 4; if (stem_len > 0) { - int disable2_len = snprintf(disable2, sizeof(disable2), "%s%.*s.disable", kpm_dir, stem_len, id); + int disable2_len = snprintf(disable2, sizeof(disable2), "%s/%.*s.disable", kpm_dir, stem_len, id); if (disable2_len > 0 && disable2_len < (int)sizeof(disable2)) { if (file_exists_privileged(disable2)) { log_boot("skip disabled KPM: %s\n", id);