Skip to content
This repository was archived by the owner on Oct 31, 2025. It is now read-only.

Commit 9affd49

Browse files
committed
kernel: ksud: migrate ksud execution to security_bprm_check (tiann#2653)
This migrates ksud execution decision-making to bprm_check_security. This requires passing proper argv and envp to a modified _ksud handler aptly named 'ksu_handle_bprm_ksud'. Introduces: int ksu_handle_bprm_ksud(const char *filename, const char *argv1, const char *envp, size_t envp_len) which is adapted from: int ksu_handle_execveat_ksud(int *fd, struct filename **filename_ptr, struct user_arg_ptr *argv, struct user_arg_ptr *envp, int *flags) ksu_handle_bprm_ksud handles all the decision making, it decides when it is time to apply_kernelsu_rules depending if it sees "second_stage". For LSM hook, turns out we can pull out argv and envp from mm_struct. The code in here explains itself on how to do it. whole blob exists on arg_start to arg_end, so we just pull it out and grab next array after the first null terminator. as for envp, we pass the pointer then hunt for it when needed My reasoning on adding a fallback on usercopy is that on some devices a fault happens, and it copies garbled data. On my creation of this, I actually had to lock that _nofault copy on a spinlock as a way to mimic preempt_disable/enable without actually doing it. As per user reports, no failed _nofault copies anyway but we have-to-have a fallback for resilience. References: - old version1 6efcd81 - old version2 37d5938 - bad usercopy #21 This now provides a small helper function, ksu_copy_from_user_retry, which explains itself. First we attempt a _nofault copy, if that fails, we try plain. While using strncpy_from_user_nofault was considered, this wont do, this will only copy up to the first \0. devlog: ximi-libra-test/android_kernel_xiaomi_libra@16e5dce...16c1f5f ximi-mojito-test/mojito_krenol@28642e6...728de0c References: https://elixir.bootlin.com/linux/v4.14.1/source/include/linux/mm_types.h#L429 https://elixir.bootlin.com/linux/v4.14.1/source/include/linux/lsm_hooks.h Stale: tiann#2653 Signed-off-by: backslashxx <[email protected]>
1 parent c2cdd37 commit 9affd49

File tree

5 files changed

+213
-0
lines changed

5 files changed

+213
-0
lines changed

kernel/core_hook.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <linux/init.h>
66
#include <linux/init_task.h>
77
#include <linux/kernel.h>
8+
#include <linux/binfmts.h>
89
#include <linux/lsm_hooks.h>
910
#include <linux/nsproxy.h>
1011
#include <linux/path.h>
@@ -691,6 +692,19 @@ int ksu_inode_permission(struct inode *inode, int mask)
691692
return 0;
692693
}
693694

695+
int ksu_bprm_check(struct linux_binprm *bprm)
696+
{
697+
char *filename = (char *)bprm->filename;
698+
699+
if (likely(!ksu_execveat_hook))
700+
return 0;
701+
702+
ksu_handle_pre_ksud(filename);
703+
704+
return 0;
705+
706+
}
707+
694708
static int ksu_task_prctl(int option, unsigned long arg2, unsigned long arg3,
695709
unsigned long arg4, unsigned long arg5)
696710
{
@@ -728,6 +742,7 @@ static int ksu_task_fix_setuid(struct cred *new, const struct cred *old,
728742
}
729743

730744
static struct security_hook_list ksu_hooks[] = {
745+
LSM_HOOK_INIT(bprm_check_security, ksu_bprm_check),
731746
LSM_HOOK_INIT(task_prctl, ksu_task_prctl),
732747
LSM_HOOK_INIT(inode_rename, ksu_inode_rename),
733748
LSM_HOOK_INIT(task_fix_setuid, ksu_task_fix_setuid),

kernel/kernel_compat.c

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,9 @@ struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode)
8888
// switch mnt_ns even if current is not wq_worker, to ensure what we open is the correct file in android mnt_ns, rather than user created mnt_ns
8989
struct ksu_ns_fs_saved saved;
9090
if (android_context_saved_enabled) {
91+
#ifdef CONFIG_KSU_DEBUG
9192
pr_info("start switch current nsproxy and fs to android context\n");
93+
#endif
9294
task_lock(current);
9395
ksu_save_ns_fs(&saved);
9496
ksu_load_ns_fs(&android_context_saved);
@@ -99,7 +101,9 @@ struct file *ksu_filp_open_compat(const char *filename, int flags, umode_t mode)
99101
task_lock(current);
100102
ksu_load_ns_fs(&saved);
101103
task_unlock(current);
104+
#ifdef CONFIG_KSU_DEBUG
102105
pr_info("switch current nsproxy and fs back to saved successfully\n");
106+
#endif
103107
}
104108
return fp;
105109
}
@@ -181,3 +185,27 @@ int ksu_access_ok(const void *addr, unsigned long size)
181185
return access_ok(VERIFY_READ, addr, size);
182186
#endif
183187
}
188+
189+
long ksu_copy_from_user_nofault(void *dst, const void __user *src, size_t size)
190+
{
191+
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 8, 0)
192+
return copy_from_user_nofault(dst, src, size);
193+
#else
194+
// https://elixir.bootlin.com/linux/v5.8/source/mm/maccess.c#L205
195+
long ret = -EFAULT;
196+
mm_segment_t old_fs = get_fs();
197+
198+
set_fs(USER_DS);
199+
// tweaked to use ksu_access_ok
200+
if (ksu_access_ok(src, size)) {
201+
pagefault_disable();
202+
ret = __copy_from_user_inatomic(dst, src, size);
203+
pagefault_enable();
204+
}
205+
set_fs(old_fs);
206+
207+
if (ret)
208+
return -EFAULT;
209+
return 0;
210+
#endif
211+
}

kernel/kernel_compat.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,23 @@ extern ssize_t ksu_kernel_write_compat(struct file *p, const void *buf,
2323
size_t count, loff_t *pos);
2424

2525
extern int ksu_access_ok(const void *addr, unsigned long size);
26+
extern long ksu_copy_from_user_nofault(void *dst, const void __user *src, size_t size);
27+
28+
/*
29+
* ksu_copy_from_user_retry
30+
* try nofault copy first, if it fails, try with plain
31+
* paramters are the same as copy_from_user
32+
* 0 = success
33+
*/
34+
static long ksu_copy_from_user_retry(void *to,
35+
const void __user *from, unsigned long count)
36+
{
37+
long ret = ksu_copy_from_user_nofault(to, from, count);
38+
if (likely(!ret))
39+
return ret;
40+
41+
// we faulted! fallback to slow path
42+
return copy_from_user(to, from, count);
43+
}
2644

2745
#endif

kernel/ksud.c

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#else
2424
#include <linux/sched.h> /* fatal_signal_pending */
2525
#endif
26+
#include <linux/slab.h>
2627

2728
#include "allowlist.h"
2829
#include "klog.h" // IWYU pragma: keep
@@ -154,6 +155,154 @@ static int __maybe_unused count(struct user_arg_ptr argv, int max)
154155
return i;
155156
}
156157

158+
// since _ksud handler only uses argv and envp for comparisons
159+
// this can probably work
160+
// adapted from ksu_handle_execveat_ksud
161+
static int ksu_handle_bprm_ksud(const char *filename, const char *argv1, const char *envp, size_t envp_len)
162+
{
163+
static const char app_process[] = "/system/bin/app_process";
164+
static bool first_app_process = true;
165+
166+
/* This applies to versions Android 10+ */
167+
static const char system_bin_init[] = "/system/bin/init";
168+
/* This applies to versions between Android 6 ~ 9 */
169+
static const char old_system_init[] = "/init";
170+
static bool init_second_stage_executed = false;
171+
172+
// return early when disabled
173+
if (!ksu_execveat_hook)
174+
return 0;
175+
176+
if (!filename)
177+
return 0;
178+
179+
180+
// debug! remove me!
181+
pr_info("%s: filename: %s argv1: %s envp_len: %zu\n", __func__, filename, argv1, envp_len);
182+
183+
if (init_second_stage_executed)
184+
goto first_app_process;
185+
186+
// /system/bin/init with argv1
187+
if (!init_second_stage_executed
188+
&& (!memcmp(filename, system_bin_init, sizeof(system_bin_init) - 1))) {
189+
if (argv1 && !strcmp(argv1, "second_stage")) {
190+
pr_info("%s: /system/bin/init second_stage executed\n", __func__);
191+
apply_kernelsu_rules();
192+
init_second_stage_executed = true;
193+
ksu_android_ns_fs_check();
194+
}
195+
}
196+
197+
// /init with argv1
198+
if (!init_second_stage_executed
199+
&& (!memcmp(filename, old_system_init, sizeof(old_system_init) - 1))) {
200+
if (argv1 && !strcmp(argv1, "--second-stage")) {
201+
pr_info("%s: /init --second-stage executed\n", __func__);
202+
apply_kernelsu_rules();
203+
init_second_stage_executed = true;
204+
ksu_android_ns_fs_check();
205+
}
206+
}
207+
208+
// /init without argv1/useless-argv1 but usable envp
209+
// untested! TODO: test and debug me!
210+
if (!init_second_stage_executed && (!memcmp(filename, old_system_init, sizeof(old_system_init) - 1))) {
211+
212+
// we hunt for "INIT_SECOND_STAGE"
213+
const char *envp_n = envp;
214+
unsigned int envc = 1;
215+
do {
216+
if (strstarts(envp_n, "INIT_SECOND_STAGE"))
217+
break;
218+
envp_n += strlen(envp_n) + 1;
219+
envc++;
220+
} while (envp_n < envp + envp_len);
221+
pr_info("%s: envp[%d]: %s\n", __func__, envc, envp_n);
222+
223+
if (!strcmp(envp_n, "INIT_SECOND_STAGE=1")
224+
|| !strcmp(envp_n, "INIT_SECOND_STAGE=true") ) {
225+
pr_info("%s: /init +envp: INIT_SECOND_STAGE executed\n", __func__);
226+
apply_kernelsu_rules();
227+
init_second_stage_executed = true;
228+
ksu_android_ns_fs_check();
229+
}
230+
}
231+
232+
first_app_process:
233+
if (first_app_process && !memcmp(filename, app_process, sizeof(app_process) - 1)) {
234+
first_app_process = false;
235+
pr_info("%s: exec app_process, /data prepared, second_stage: %d\n", __func__, init_second_stage_executed);
236+
on_post_fs_data();
237+
stop_execve_hook();
238+
}
239+
240+
return 0;
241+
}
242+
243+
int ksu_handle_pre_ksud(const char *filename)
244+
{
245+
if (likely(!ksu_execveat_hook))
246+
return 0;
247+
248+
// not /system/bin/init, not /init, not /system/bin/app_process (64/32 thingy)
249+
// return 0;
250+
if (likely(strcmp(filename, "/system/bin/init") && strcmp(filename, "/init")
251+
&& !strstarts(filename, "/system/bin/app_process") ))
252+
return 0;
253+
254+
if (!current || !current->mm)
255+
return 0;
256+
257+
// https://elixir.bootlin.com/linux/v4.14.1/source/include/linux/mm_types.h#L429
258+
// unsigned long arg_start, arg_end, env_start, env_end;
259+
unsigned long arg_start = current->mm->arg_start;
260+
unsigned long arg_end = current->mm->arg_end;
261+
unsigned long env_start = current->mm->env_start;
262+
unsigned long env_end = current->mm->env_end;
263+
264+
size_t arg_len = arg_end - arg_start;
265+
size_t envp_len = env_end - env_start;
266+
267+
if (arg_len <= 0 || envp_len <= 0) // this wont make sense, filter it
268+
return 0;
269+
270+
#define ARGV_MAX 32 // this is enough for argv1
271+
#define ENVP_MAX 256 // this is enough for INIT_SECOND_STAGE
272+
char args[ARGV_MAX];
273+
size_t argv_copy_len = (arg_len > ARGV_MAX) ? ARGV_MAX : arg_len;
274+
char envp[ENVP_MAX];
275+
size_t envp_copy_len = (envp_len > ENVP_MAX) ? ENVP_MAX : envp_len;
276+
277+
// we cant use strncpy on here, else it will truncate once it sees \0
278+
if (ksu_copy_from_user_retry(args, (void __user *)arg_start, argv_copy_len))
279+
return 0;
280+
281+
if (ksu_copy_from_user_retry(envp, (void __user *)env_start, envp_copy_len))
282+
return 0;
283+
284+
args[ARGV_MAX - 1] = '\0';
285+
envp[ENVP_MAX - 1] = '\0';
286+
287+
#ifdef CONFIG_KSU_DEBUG
288+
char *envp_n = envp;
289+
unsigned int envc = 1;
290+
do {
291+
pr_info("%s: envp[%d]: %s\n", __func__, envc, envp_n);
292+
envp_n += strlen(envp_n) + 1;
293+
envc++;
294+
} while (envp_n < envp + envp_copy_len);
295+
#endif
296+
297+
// we only need argv1 !
298+
// abuse strlen here since it only gets length up to \0
299+
char *argv1 = args + strlen(args) + 1;
300+
if (argv1 >= args + argv_copy_len) // out of bounds!
301+
argv1 = "";
302+
303+
return ksu_handle_bprm_ksud(filename, argv1, envp, envp_copy_len);
304+
}
305+
157306
// IMPORTANT NOTE: the call from execve_handler_pre WON'T provided correct value for envp and flags in GKI version
158307
static int __ksu_handle_execveat_ksud(int *fd, char *filename,
159308
struct user_arg_ptr *argv,

kernel/ksud.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,7 @@ bool ksu_is_safe_mode(void);
1111

1212
extern u32 ksu_devpts_sid;
1313

14+
extern bool ksu_execveat_hook __read_mostly;
15+
extern int ksu_handle_pre_ksud(const char *filename);
16+
1417
#endif

0 commit comments

Comments
 (0)