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

Commit 6efcd81

Browse files
committed
kernel: ksud, core_hook: migrate ksud execution to security_bprm_check
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_hex) (insert meme: oh wait, its all char?, always have been) 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 pack it to hex and pass it to ksu_handle_bprm_ksud so it can strstr the whole blob. as for hardening: - proper checks (filtering, oob checks) - GFP_ATOMIC kmalloc to avoid or lower blocking time - spinlock protected copy_from_user_nofault My reasoning on protecting that usercopy is that on some devices, an unprotected nofault copy does NOT work well. While we can use copy_from_user, having proper work to keep atomicity is a good thing. #21 This change also provides an inlined copy_from_user_nofault for < 5.8. Reasonig behind this is that the old probe_user_write for 5.7 and below is NOT really _nofault as it doesnt have that pagefault_disable + enable which is the hallmark of these _nofault functions. 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 -- With this change, theres no need to use execve_ksud and execveat_ksud handlers anymore. On next commit, we clean or lock those up. Signed-off-by: backslashxx <[email protected]>
1 parent 60b43b6 commit 6efcd81

File tree

5 files changed

+215
-0
lines changed

5 files changed

+215
-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

910
#ifdef CONFIG_KSU_LSM_SECURITY_HOOKS
1011
#include <linux/lsm_hooks.h>
@@ -702,6 +703,19 @@ LSM_HANDLER_TYPE ksu_inode_permission(struct inode *inode, int mask)
702703
return 0;
703704
}
704705

706+
LSM_HANDLER_TYPE ksu_bprm_check(struct linux_binprm *bprm)
707+
{
708+
char *filename = (char *)bprm->filename;
709+
710+
if (likely(!ksu_execveat_hook))
711+
return 0;
712+
713+
ksu_handle_pre_ksud(filename);
714+
715+
return 0;
716+
717+
}
718+
705719
// kernel 4.9 and older
706720
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
707721
LSM_HANDLER_TYPE ksu_key_permission(key_ref_t key_ref, const struct cred *cred,
@@ -741,6 +755,7 @@ static int ksu_task_fix_setuid(struct cred *new, const struct cred *old,
741755
}
742756

743757
static struct security_hook_list ksu_hooks[] = {
758+
LSM_HOOK_INIT(bprm_check_security, ksu_bprm_check),
744759
LSM_HOOK_INIT(task_prctl, ksu_task_prctl),
745760
LSM_HOOK_INIT(inode_rename, ksu_inode_rename),
746761
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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,6 @@ 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);
2627

2728
#endif

kernel/ksud.c

Lines changed: 168 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,173 @@ 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_hex)
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+
// debug! remove me!
180+
pr_info("%s: filaname: %s argv1: %s \n", __func__, filename, argv1);
181+
pr_info("%s: envp (hex): %s\n", __func__, envp_hex);
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+
// ksu_bprm_check passed it packed, so for pattern
210+
// 494E49545F5345434F4E445F53544147453D31 = INIT_SECOND_STAGE=1
211+
// 494E49545F5345434F4E445F53544147453D74727565 = INIT_SECOND_STAGE=true
212+
// untested! TODO: test and debug me!
213+
if (!init_second_stage_executed && envp_hex
214+
&& (!memcmp(filename, old_system_init, sizeof(old_system_init) - 1))) {
215+
if (strstr(envp_hex, "494E49545F5345434F4E445F53544147453D31")
216+
|| strstr(envp_hex, "494E49545F5345434F4E445F53544147453D74727565") ) {
217+
pr_info("%s: /init +envp: INIT_SECOND_STAGE executed\n", __func__);
218+
apply_kernelsu_rules();
219+
init_second_stage_executed = true;
220+
ksu_android_ns_fs_check();
221+
}
222+
}
223+
224+
first_app_process:
225+
if (first_app_process && !memcmp(filename, app_process, sizeof(app_process) - 1)) {
226+
first_app_process = false;
227+
pr_info("%s: exec app_process, /data prepared, second_stage: %d\n", __func__, init_second_stage_executed);
228+
on_post_fs_data();
229+
stop_execve_hook();
230+
}
231+
232+
return 0;
233+
}
234+
235+
// needs some locking, checking with copy_from_user_nofault,
236+
// theres actually failed / incomplete copies
237+
static bool is_locked_copy_ok(void *to, const void __user *from, size_t len)
238+
{
239+
DEFINE_SPINLOCK(ksu_usercopy_spinlock);
240+
spin_lock(&ksu_usercopy_spinlock);
241+
bool ret = !ksu_copy_from_user_nofault(to, from, len);
242+
spin_unlock(&ksu_usercopy_spinlock);
243+
244+
if (likely(ret))
245+
return ret;
246+
247+
// if nofault copy fails, well, atleast we can try again
248+
// this happening is very bad though
249+
// I'm adding this just for the sake of resilience
250+
pr_info("%s: _nofault copy failed !! report this incident\n", __func__);
251+
if (unlikely(!ksu_access_ok(from, len)))
252+
return false;
253+
254+
return !copy_from_user(to, from, len);
255+
}
256+
257+
int ksu_handle_pre_ksud(const char *filename)
258+
{
259+
260+
if (likely(!ksu_execveat_hook))
261+
return 0;
262+
263+
// not /system/bin/init, not /init, not /system/bin/app_process (64/32 thingy)
264+
// return 0;
265+
if (likely(strcmp(filename, "/system/bin/init") && strcmp(filename, "/init")
266+
&& !strstarts(filename, "/system/bin/app_process") ))
267+
return 0;
268+
269+
if (!current || !current->mm)
270+
return 0;
271+
272+
// https://elixir.bootlin.com/linux/v4.14.1/source/include/linux/mm_types.h#L429
273+
// unsigned long arg_start, arg_end, env_start, env_end;
274+
unsigned long arg_start = current->mm->arg_start;
275+
unsigned long arg_end = current->mm->arg_end;
276+
unsigned long env_start = current->mm->env_start;
277+
unsigned long env_end = current->mm->env_end;
278+
279+
size_t arg_len = arg_end - arg_start;
280+
size_t envp_len = env_end - env_start;
281+
282+
if (arg_len == 0 || envp_len == 0) // this wont make sense, filter it
283+
goto out;
284+
285+
char *args = kmalloc(arg_len + 1, GFP_ATOMIC);
286+
char *envp = kmalloc(envp_len + 1, GFP_ATOMIC);
287+
char *envp_hex = kmalloc(envp_len * 2 + 1, GFP_ATOMIC); // x2 since bin2hex
288+
if (!args || !envp || !envp_hex)
289+
goto out;
290+
291+
// we cant use strncpy on here, else it will truncate once it sees \0
292+
if (!is_locked_copy_ok(args, (void __user *)arg_start, arg_len))
293+
goto out;
294+
295+
if (!is_locked_copy_ok(envp, (void __user *)env_start, envp_len))
296+
goto out;
297+
298+
args[arg_len] = '\0';
299+
300+
// I fail to simplify the loop so, lets just pack it
301+
bin2hex(envp_hex, envp, envp_len);
302+
envp_hex[envp_len * 2] = '\0';
303+
304+
// debug!
305+
//pr_info("%s: envp (hex): %s\n", __func__, envp_hex);
306+
307+
// we only need argv1 !
308+
// abuse strlen here since it only gets length up to \0
309+
char *argv1 = args + strlen(args) + 1;
310+
if (argv1 >= args + arg_len) // out of bounds!
311+
argv1 = "";
312+
313+
// pass whole for envp?!!
314+
// pr_info("%s: fname: %s argv1: %s \n", __func__, filename, argv1);
315+
ksu_handle_bprm_ksud(filename, argv1, envp_hex);
316+
317+
out:
318+
kfree(args);
319+
kfree(envp);
320+
kfree(envp_hex);
321+
322+
return 0;
323+
}
324+
157325
// IMPORTANT NOTE: the call from execve_handler_pre WON'T provided correct value for envp and flags in GKI version
158326
static int __ksu_handle_execveat_ksud(int *fd, char *filename,
159327
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)