forked from tiann/KernelSU
-
Notifications
You must be signed in to change notification settings - Fork 43
Open
Labels
documentationImprovements or additions to documentationImprovements or additions to documentation
Description
This refactors original KSU hooks to replace deep kernel function hooks with targeted hooks.
This backports KernelSU pr#1657 and having pr#2084 elements (32-bit sucompat).
It reduces the scope of kernel function interception and still maintains full fucntionality.
notes:
- devpts hook? Yes! theres no need for devpts hook, just use:
su -c pm - per task sucompat works fine.
- I added emojis so YOU can see important notices.
- These hooks are made for the driver on THIS REPO. These hooks working on others are not assured.
🟢 sys_execve hook
- for sucompat
- choose one which suits your kernel version
- these are ordered via performance and preference.
show patch/diff (3.18+ via do_execve)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1886,12 +1886,26 @@ static int do_execveat_common(int fd, struct filename *filename,
return retval;
}
+#ifdef CONFIG_KSU
+__attribute__((hot))
+extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr,
+ void *argv, void *envp, int *flags);
+#endif
+
int do_execve(struct filename *filename,
const char __user *const __user *__argv,
const char __user *const __user *__envp)
{
struct user_arg_ptr argv = { .ptr.native = __argv };
struct user_arg_ptr envp = { .ptr.native = __envp };
+#ifdef CONFIG_KSU
+ ksu_handle_execveat((int *)AT_FDCWD, &filename, &argv, &envp, 0);
+#endif
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
}
@@ -1919,6 +1933,10 @@ static int compat_do_execve(struct filename *filename,
.is_compat = true,
.ptr.compat = __envp,
};
+#ifdef CONFIG_KSU // 32-bit ksud and 32-on-64 support
+ ksu_handle_execveat((int *)AT_FDCWD, &filename, &argv, &envp, 0);
+#endif
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
}
show patch/diff (3.18, via do_execve_common)
- for 3.18, we can repurpose upstream's do_execveat_common hook for do_execve_common
- no sys_execveat on <= 3.18, so this makes sense instead of hooking sys_execve + compat_sys_execve
- take note: struct filename *filename
--- a/fs/exec.c
+++ b/fs/exec.c
/*
* sys_execve() executes a new program.
*/
+#ifdef CONFIG_KSU
+__attribute__((hot))
+extern int ksu_handle_execveat(int *fd,
+ struct filename **filename_ptr,
+ void *argv, void *envp, int *flags);
+#endif
+
static int do_execve_common(struct filename *filename,
struct user_arg_ptr argv,
struct user_arg_ptr envp)
{
struct linux_binprm *bprm;
struct file *file;
struct files_struct *displaced;
int retval;
if (IS_ERR(filename))
return PTR_ERR(filename);
+#ifdef CONFIG_KSU
+ ksu_handle_execveat((int *)AT_FDCWD, &filename, &argv, &envp, 0);
+#endif
/*
* We move the actual failure in case of RLIMIT_NPROC excess from
* set*uid() to execve() because too many poorly written programsshow patch/diff (3.0 - 3.10, via do_execve_common)
- for <= 3.10, this repo provides a handler for do_execve_common
- no sys_execveat on <= 3.18, so this makes sense instead of hooking sys_execve + compat_sys_execve
- take note: const char *filename
/*
* sys_execve() executes a new program.
*/
+
+#ifdef CONFIG_KSU
+__attribute__((hot))
+extern int ksu_legacy_execve_sucompat(const char **filename_ptr,
+ void *__never_use_argv,
+ void *__never_use_envp);
+#endif
+
static int do_execve_common(const char *filename,
struct user_arg_ptr argv,
struct user_arg_ptr envp)
{
struct linux_binprm *bprm;
struct file *file;
struct files_struct *displaced;
bool clear_in_exec;
int retval;
const struct cred *cred = current_cred();
+
+#ifdef CONFIG_KSU
+ ksu_legacy_execve_sucompat(&filename, &argv, &envp);
+#endif
+
/*
* We move the actual failure in case of RLIMIT_NPROC excess from
* set*uid() to execve() because too many poorly written programsshow patch/diff (3.18+ via sys_execve)
- just put the hook right at syscall entry
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1968,11 +1968,22 @@ void set_dumpable(struct mm_struct *mm, int value)
} while (cmpxchg(&mm->flags, old, new) != old);
}
+#ifdef CONFIG_KSU
+__attribute__((hot))
+extern int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user,
+ void *__never_use_argv, void *__never_use_envp,
+ int *__never_use_flags);
+#endif
+
SYSCALL_DEFINE3(execve,
const char __user *, filename,
const char __user *const __user *, argv,
const char __user *const __user *, envp)
{
+#ifdef CONFIG_KSU
+ ksu_handle_execve_sucompat((int *)AT_FDCWD, &filename, NULL, NULL, NULL);
+#endif
return do_execve(getname(filename), argv, envp);
}
@@ -1994,6 +2005,9 @@ COMPAT_SYSCALL_DEFINE3(execve, const char __user *, filename,
const compat_uptr_t __user *, argv,
const compat_uptr_t __user *, envp)
{
+#ifdef CONFIG_KSU // 32-bit ksud and 32-on-64 support
+ ksu_handle_execve_sucompat((int *)AT_FDCWD, &filename, NULL, NULL, NULL);
+#endif
return compat_do_execve(getname(filename), argv, envp);
}show patch/diff (3.10, via sys_execve)
- for 3.10, just hook right at the entrance of sys_execve / compat_sys_execve
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1736,11 +1736,22 @@ int get_dumpable(struct mm_struct *mm)
return __get_dumpable(mm->flags);
}
+#ifdef CONFIG_KSU
+__attribute__((hot))
+extern int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user,
+ void *__never_use_argv, void *__never_use_envp,
+ int *__never_use_flags);
+#endif
+
SYSCALL_DEFINE3(execve,
const char __user *, filename,
const char __user *const __user *, argv,
const char __user *const __user *, envp)
{
+#ifdef CONFIG_KSU
+ ksu_handle_execve_sucompat((int *)AT_FDCWD, &filename, NULL, NULL, NULL);
+#endif
struct filename *path = getname(filename);
int error = PTR_ERR(path);
if (!IS_ERR(path)) {
@@ -1754,6 +1765,9 @@ asmlinkage long compat_sys_execve(const char __user * filename,
const compat_uptr_t __user * argv,
const compat_uptr_t __user * envp)
{
+#ifdef CONFIG_KSU // 32-bit sucompat and 32-on-64 support
+ ksu_handle_execve_sucompat((int *)AT_FDCWD, &filename, NULL, NULL, NULL);
+#endif
struct filename *path = getname(filename);
int error = PTR_ERR(path);
if (!IS_ERR(path)) {show patch/diff (3.0 / 3.4, via sys_execve)
- for 3.0 / 3.4, handling this is a bit different.
- The location of the syscall is on arch/$ARCH/kernel/sys_$ARCH.c
- you must pass filenamei to the hook which is what the syscall received.
--- a/arch/arm/kernel/sys_arm.c
+++ b/arch/arm/kernel/sys_arm.c
@@ -62,13 +62,26 @@ asmlinkage int sys_vfork(struct pt_regs *regs)
/* sys_execve() executes a new program.
* This is called indirectly via a small wrapper
*/
+#ifdef CONFIG_KSU
+__attribute__((hot))
+extern int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user,
+ void *__never_use_argv, void *__never_use_envp,
+ int *__never_use_flags);
+#endif
+
asmlinkage int sys_execve(const char __user *filenamei,
const char __user *const __user *argv,
const char __user *const __user *envp, struct pt_regs *regs)
{
int error;
struct filename *filename;
-
+#ifdef CONFIG_KSU
+ ksu_handle_execve_sucompat((int *)AT_FDCWD, &filenamei, NULL, NULL, NULL);
+#endif
filename = getname(filenamei);
error = PTR_ERR(filename);
if (IS_ERR(filename))🟢 sys_faccessat hook
- for sucompat
- from original guide
- hook sys_faccessat even if you have do_faccessat, this is for scope minimization.
show patch/diff (4.19 and newer)
--- a/fs/open.c
+++ b/fs/open.c
@@ -450,8 +450,16 @@ long do_faccessat(int dfd, const char __user *filename, int mode)
return res;
}
+#ifdef CONFIG_KSU
+__attribute__((hot))
+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user,
+ int *mode, int *flags);
+#endif
+
SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
{
+#ifdef CONFIG_KSU
+ ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+#endif
return do_faccessat(dfd, filename, mode);
}
show patch/diff (4.14 and older)
--- a/fs/open.c
+++ b/fs/open.c
@@ -354,6 +354,11 @@ SYSCALL_DEFINE4(fallocate, int, fd, int, mode, loff_t, offset, loff_t, len)
return error;
}
+#ifdef CONFIG_KSU
+__attribute__((hot))
+extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user,
+ int *mode, int *flags);
+#endif
+
/*
* access() needs to use the real uid/gid, not the effective uid/gid.
* We do this by temporarily clearing all FS-related capabilities and
@@ -369,6 +374,10 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
int res;
unsigned int lookup_flags = LOOKUP_FOLLOW;
+#ifdef CONFIG_KSU
+ ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
+#endif
+
if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */
return -EINVAL;
🟢 sys_newfstatat hook
- for sucompat
- scope minimized
- you now have to hook sys_newfstatat, instead of vfs_statx()
- optionally hook sys_fstatat64 if 32-bit su is needed.
show patch/diff
--- a/fs/stat.c
+++ b/fs/stat.c
@@ -353,6 +353,10 @@ SYSCALL_DEFINE2(newlstat, const char __user *, filename,
return cp_new_stat(&stat, statbuf);
}
+#ifdef CONFIG_KSU
+__attribute__((hot))
+extern int ksu_handle_stat(int *dfd, const char __user **filename_user,
+ int *flags);
+#endif
+
#if !defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_SYS_NEWFSTATAT)
SYSCALL_DEFINE4(newfstatat, int, dfd, const char __user *, filename,
struct stat __user *, statbuf, int, flag)
@@ -360,6 +364,9 @@ SYSCALL_DEFINE4(newfstatat, int, dfd, const char __user *, filename,
struct kstat stat;
int error;
+#ifdef CONFIG_KSU
+ ksu_handle_stat(&dfd, &filename, &flag);
+#endif
error = vfs_fstatat(dfd, filename, &stat, flag);
if (error)
return error;
@@ -504,6 +511,9 @@ SYSCALL_DEFINE4(fstatat64, int, dfd, const char __user *, filename,
struct kstat stat;
int error;
+#ifdef CONFIG_KSU // 32-bit su
+ ksu_handle_stat(&dfd, &filename, &flag);
+#endif
error = vfs_fstatat(dfd, filename, &stat, flag);
if (error)
return error;🟢 sys_reboot hook
- this is needed by new KernelSU supercall introduced at 12143
- just go to where sys_reboot is and hook right on its entry.
⚠️ if KPROBES is enabled and working on your kernel, this manual hook is not needed.
show patch/diff (3.18+)
--- a/kernel/reboot.c
+++ b/kernel/reboot.c
@@ -277,6 +277,11 @@ static DEFINE_MUTEX(reboot_mutex);
*
* reboot doesn't sync: do that yourself before calling this.
*/
+
+#ifdef CONFIG_KSU
+extern int ksu_handle_sys_reboot(int magic1, int magic2, unsigned int cmd, void __user **arg);
+#endif
+
SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
void __user *, arg)
{
@@ -284,6 +289,9 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
char buffer[256];
int ret = 0;
+#ifdef CONFIG_KSU
+ ksu_handle_sys_reboot(magic1, magic2, cmd, &arg);
+#endif
/* We only trust the superuser with rebooting the system. */
if (!ns_capable(pid_ns->user_ns, CAP_SYS_BOOT))
return -EPERM;show patch/diff (3.0~3.10)
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -463,6 +463,11 @@ static DEFINE_MUTEX(reboot_mutex);
*
* reboot doesn't sync: do that yourself before calling this.
*/
+
+#ifdef CONFIG_KSU
+extern int ksu_handle_sys_reboot(int magic1, int magic2, unsigned int cmd, void __user **arg);
+#endif
+
SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
void __user *, arg)
{
@@ -470,6 +475,10 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
char buffer[256];
int ret = 0;
+#ifdef CONFIG_KSU
+ ksu_handle_sys_reboot(magic1, magic2, cmd, &arg);
+#endif
+
/* We only trust the superuser with rebooting the system. */
if (!ns_capable(pid_ns->user_ns, CAP_SYS_BOOT))
return -EPERM;🟢 sys_read hook
- for ksud
- scope minimized
- you now have to hook sys_read instead of vfs_read().
⚠️ if KPROBES is enabled and working on your kernel, this manual hook is not needed.
show patch/diff (4.19 and newer)
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -586,8 +586,18 @@ ssize_t ksys_read(unsigned int fd, char __user *buf, size_t count)
return ret;
}
+#ifdef CONFIG_KSU
+extern bool ksu_vfs_read_hook __read_mostly;
+extern __attribute__((cold)) int ksu_handle_sys_read(unsigned int fd,
+ char __user **buf_ptr, size_t *count_ptr);
+#endif
+
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
{
+#ifdef CONFIG_KSU
+ if (unlikely(ksu_vfs_read_hook))
+ ksu_handle_sys_read(fd, &buf, &count);
+#endif
return ksys_read(fd, buf, count);
}show patch/diff (4.14 and older)
--- a/fs/read_write.c
+++ b/fs/read_write.c
@@ -568,11 +568,21 @@ static inline void file_pos_write(struct file *file, loff_t pos)
file->f_pos = pos;
}
+#ifdef CONFIG_KSU
+extern bool ksu_vfs_read_hook __read_mostly;
+extern __attribute__((cold)) int ksu_handle_sys_read(unsigned int fd,
+ char __user **buf_ptr, size_t *count_ptr);
+#endif
+
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
{
struct fd f = fdget_pos(fd);
ssize_t ret = -EBADF;
+#ifdef CONFIG_KSU
+ if (unlikely(ksu_vfs_read_hook))
+ ksu_handle_sys_read(fd, &buf, &count);
+#endif
if (f.file) {
loff_t pos = file_pos_read(f.file);
ret = vfs_read(f.file, buf, count, &pos);🟢 input hook
- for safemode feature
- you now have to hook input_event() instead of input_handle_event()
- this is just to be in line with upstream hooks
⚠️ if KPROBES is enabled and working on your kernel, this manual hook is not needed.
show patch/diff
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -436,11 +436,22 @@ static void input_handle_event(struct input_dev *dev,
* to 'seed' initial state of a switch or initial position of absolute
* axis, etc.
*/
+#ifdef CONFIG_KSU
+extern bool ksu_input_hook __read_mostly;
+extern __attribute__((cold)) int ksu_handle_input_handle_event(
+ unsigned int *type, unsigned int *code, int *value);
+#endif
+
void input_event(struct input_dev *dev,
unsigned int type, unsigned int code, int value)
{
unsigned long flags;
+#ifdef CONFIG_KSU
+ if (unlikely(ksu_input_hook))
+ ksu_handle_input_handle_event(&type, &code, &value);
+#endif
+
if (is_event_supported(type, dev->evbit, EV_MAX)) {
spin_lock_irqsave(&dev->event_lock, flags);🟢 selinux hook
‼️ ONLY FOR 4.9 AND OLDER- this is needed so we can force allow init -> su transitions for ksud
- from
allow init exec ksud under nosuidby @F-19-F ⚠️ if KPROBES / KRETPROBES is enabled and working on your 3.18/4.4/4.9 kernel, this manual hook is not needed.
show patch/diff (3.18 to 4.9)
- you will be adding it to check_nnp_nosuid
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
+#ifdef CONFIG_KSU
+extern bool is_ksu_transition(const struct task_security_struct *old_tsec,
+ const struct task_security_struct *new_tsec);
+#endif
static int check_nnp_nosuid(const struct linux_binprm *bprm,
const struct task_security_struct *old_tsec,
const struct task_security_struct *new_tsec)
{
int nnp = (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS);
int nosuid = !mnt_may_suid(bprm->file->f_path.mnt);
int rc;
if (!nnp && !nosuid)
return 0; /* neither NNP nor nosuid */
if (new_tsec->sid == old_tsec->sid)
return 0; /* No change in credentials */
+
+#ifdef CONFIG_KSU
+ if (is_ksu_transition(old_tsec, new_tsec))
+ return 0;
+#endif
/*
* The only transitions we permit under NNP or nosuid
* are transitions to bounded SIDs, i.e. SIDs that areshow patch/diff (3.10 and older)
- you will be adding it to selinux_bprm_set_creds
- make sure to put it after it after execve sid reset
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -2100,6 +2100,12 @@ static int selinux_vm_enough_memory(struct mm_struct *mm, long pages)
/* binprm security operations */
+#ifdef CONFIG_KSU
+extern bool is_ksu_transition(const struct task_security_struct *old_tsec,
+ const struct task_security_struct *new_tsec);
+#endif
+
static int selinux_bprm_set_creds(struct linux_binprm *bprm)
{
const struct task_security_struct *old_tsec;
@@ -2136,6 +2142,11 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
/* Reset exec SID on execve. */
new_tsec->exec_sid = 0;
+#ifdef CONFIG_KSU
+ if (is_ksu_transition(old_tsec, new_tsec))
+ return 0;
+#endif
/*
* Minimize confusion: if no_new_privs and a transition is
* explicitly requested, then fail the exec.🟢 walk_component
‼️ ONLY FOR 3.10 and 3.18, possibly 3.4- a kthreaded throne_tracker is required so we have
current->commto strstr - from
kernelsu, fs: do not do lookup_slow if caller is throne_tracker kthreadby @acroreiser
show patch/diff (3.10 / 3.18)
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1609,6 +1609,13 @@ static inline int walk_component(struct nameidata *nd, struct path *path,
return handle_dots(nd, nd->last_type);
err = lookup_fast(nd, path, &inode);
if (unlikely(err)) {
+#ifdef CONFIG_KSU
+ if (unlikely(current->flags & PF_KTHREAD)
+ && !strcmp(current->comm, "throne_tracker")) {
+ err = -ENOENT;
+ goto out_err;
+ }
+#endif
if (err < 0)
goto out_err;
show patch/diff (3.4 - backported from 3.5)
--- a/fs/namei.c
+++ b/fs/namei.c
@@ -1543,7 +1543,11 @@ static inline int walk_component(struct nameidata *nd, struct path *path,
if (err < 0)
goto out_err;
- err = lookup_slow(nd, name, path);
+ if (strstr(current->comm, "throne_tracker") == NULL)
+ err = lookup_slow(nd, name, path);
+ else
+ err = -ENOENT;
+
if (err < 0)
goto out_err;
Revisions
- v1.1, add ksu_handle_compat_execve_ksud for 32-on-64 usecase, deprecate do_execve hooking.
- v1.2, deprecate devpts hooking
- v1.3, add is_ksu_transition handler (selinux "hook")
- 250611, edit: remove "ksu_execveat_hook" check for selinux hook
- reported by @edenadversary
- v1.4, multiple changes
- add walk_component for UL
- mark sucompat hooks as
__attribute__((hot, always_inline))- ksu_handle_execve_sucompat, ksu_handle_faccessat, ksu_handle_stat
- 250612, edit: remove always_inline for old compiler compatibility.
- v1.5, multiple changes
- deprecate execve_ksud handlers in favor of LSM hooking
- edit walk_component for 3.10 for clarity.
- add
ksu_legacy_execve_sucompatfor do_execve_common as another option for 3.0~3.10 - add
getname_flagshandlers and hooks - added hybridization notes
- 250922, kprobe support added for sys_read and input_event
- 250925, kprobe replacement for selinux_hook
- 250925, added cold attribute to sys_read and input_event
- added vfs_statx >= 5.18 handler documentation
- remove old commit links, people are likely smart enough to look for them anyway
- v1.6, sys_reboot hook for new supercall
- 251123, added a kthread flag for walk_component's check
Metadata
Metadata
Assignees
Labels
documentationImprovements or additions to documentationImprovements or additions to documentation