Open
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:
Ultra Legacy - if you're on 3.X check second post for extras!
devpts hook? Yes! theres no need for devpts hook, this has been moved to LSM.
🟢 sys_execve hook
- if you are building this repo's KernelSU, you can hook execve this way
- if not, add the following to your kernelsu driver:
- execve_ksud handlers are depreciated now in favor of LSM hooking (250619)
- if you want old if-else'd sys_execve and do_execve hooks, check depreciated
show patch/diff (3.18+)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1986,11 +1968,26 @@ void set_dumpable(struct mm_struct *mm, int value)
} while (cmpxchg(&mm->flags, old, new) != old);
}
+#ifdef CONFIG_KSU
+extern __attribute__((hot)) 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);
}
@@ -2012,6 +2009,10 @@ 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 su 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);
}
🟢 sys_faccessat hook
- 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
+extern __attribute__((hot)) 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
+extern __attribute__((hot)) 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_read hook
- scope minimized
- you now have to hook sys_read instead of vfs_read().
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 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,6 +568,12 @@ 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 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);
@@ -575,6 +581,10 @@ SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
if (f.file) {
loff_t pos = file_pos_read(f.file);
+#ifdef CONFIG_KSU
+ if (unlikely(ksu_vfs_read_hook))
+ ksu_handle_sys_read(fd, &buf, &count);
+#endif
ret = vfs_read(f.file, buf, count, &pos);
if (ret >= 0)
file_pos_write(f.file, pos);
🟢 stat hook
- 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
+extern __attribute__((hot)) 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;
🟢 selinux hook
‼️ ONLY FOR 4.9 AND OLDER- if you are building this repo's KernelSU, you can use this "hook"
- if not, add this commit to your kernelsu driver:
- from
allow init exec ksud under nosuid
by @F-19-F
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
@@ -2314,6 +2314,12 @@ static u32 ptrace_parent_sid(struct task_struct *task)
return sid;
}
+#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)
@@ -2327,6 +2333,10 @@ static int check_nnp_nosuid(const struct linux_binprm *bprm,
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
show 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.
🟢 input hook for safemode
- you now have to hook input_event() instead of input_handle_event()
- this is just to be in line with upstream hooks
show patch/diff
--- a/drivers/input/input.c
+++ b/drivers/input/input.c
@@ -436,11 +436,21 @@ 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 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);
Revisions
- v1.1, add ksu_handle_compat_execve_ksud for 32-on-64 usecase, depreciate do_execve hooking.
- v1.2, depreciate 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, depreciate execve_ksud handlers in favor of LSM hooking
show depreciated
sys_execve hook (old, if-else'd with _ksud)
- if you are building this repo's KernelSU, you can hook execve syscall directly.
- if not, add the following to your kernelsu driver:
- this has been depreciated, but this will still work as I provided dummies for execve_ksud
show patch/diff (3.18+)
- thanks to selfmusing and 0ctobot for testing (4.14, 6.1)
- v1.1 edit, added ksu_handle_compat_execve_ksud on compat_sys_execve for 32-on-64 support
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -1986,11 +1968,26 @@ void set_dumpable(struct mm_struct *mm, int value)
} while (cmpxchg(&mm->flags, old, new) != old);
}
+#ifdef CONFIG_KSU
+extern bool ksu_execveat_hook __read_mostly;
+extern __attribute__((hot)) int ksu_handle_execve_sucompat(int *fd,
+ const char __user **filename_user,
+ void *__never_use_argv, void *__never_use_envp,
+ int *__never_use_flags);
+extern int ksu_handle_execve_ksud(const char __user *filename_user,
+ const char __user *const __user *__argv);
+#ifdef CONFIG_COMPAT // 32-on-64 support
+extern int ksu_handle_compat_execve_ksud(const char __user *filename_user,
+ const compat_uptr_t __user *__argv);
+#endif
+#endif
+
SYSCALL_DEFINE3(execve,
const char __user *, filename,
const char __user *const __user *, argv,
const char __user *const __user *, envp)
{
+#ifdef CONFIG_KSU
+ if (unlikely(ksu_execveat_hook))
+ ksu_handle_execve_ksud(filename, argv);
+ else
+ ksu_handle_execve_sucompat((int *)AT_FDCWD, &filename, NULL, NULL, NULL);
+#endif
return do_execve(getname(filename), argv, envp);
}
@@ -2012,6 +2009,10 @@ 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 su and 32-on-64 support
+ if (unlikely(ksu_execveat_hook))
+ ksu_handle_compat_execve_ksud(filename, argv);
+ else
+ ksu_handle_execve_sucompat((int *)AT_FDCWD, &filename, NULL, NULL, NULL);
+#endif
return compat_do_execve(getname(filename), argv, envp);
}
devpts hook
- this is being DEPRECIATED in favor of
kernel: core_hook: intercept devpts via security_inode_permission LSM
- If you are building this repo's KernelSU, you do NOT need this hook anymore.
- this is kept for reference purposes
show patch/diff (4.9 and newer)
- this is just to be in line with upstream hooks
- take note: struct file *file,
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -711,11 +711,18 @@ static struct tty_struct *ptm_unix98_lookup(struct tty_driver *driver,
* This provides our locking for the tty pointer.
*/
+#ifdef CONFIG_KSU
+extern int ksu_handle_devpts(struct inode*);
+#endif
+
static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver,
struct file *file, int idx)
{
struct tty_struct *tty;
+#ifdef CONFIG_KSU
+ ksu_handle_devpts((struct inode *)file->f_path.dentry->d_inode);
+#endif
mutex_lock(&devpts_mutex);
tty = devpts_get_priv(file->f_path.dentry);
mutex_unlock(&devpts_mutex);
show patch/diff (4.4 and older)
- take note of struct inode *pts_inode
- thanks to sandatjepil for testing
--- a/drivers/tty/pty.c
+++ b/drivers/tty/pty.c
@@ -648,11 +648,19 @@ static struct tty_struct *ptm_unix98_lookup(struct tty_driver *driver,
* This provides our locking for the tty pointer.
*/
+#ifdef CONFIG_KSU
+extern int ksu_handle_devpts(struct inode*);
+#endif
+
static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver,
struct inode *pts_inode, int idx)
{
struct tty_struct *tty;
+#ifdef CONFIG_KSU
+ ksu_handle_devpts(pts_inode);
+#endif
+
mutex_lock(&devpts_mutex);
tty = devpts_get_priv(pts_inode);
mutex_unlock(&devpts_mutex);
do_execve hook
- WARNING: this is being depreciated. If you can use sys_execve hooks, use it.
- this is kept for reference purposes
show patch/diff
- scope minimized
- you now have to hook do_execve(), instead of do_execveat_common()
- optionally hook compat_do_execve() if 32-bit su is needed, or you're on 32-on-64
- make sure to hook do_execve not do_execveat, it can be confusing!!
- if you want this oversimplified, check second one
- edit, added ksu_handle_execveat on compat_do_execve for 32-on-64 support
--- 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
+extern bool ksu_execveat_hook __read_mostly;
+extern int ksu_handle_execveat(int *fd, struct filename **filename_ptr, void *argv,
+ void *envp, int *flags);
+extern int ksu_handle_execveat_sucompat(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
+ if (unlikely(ksu_execveat_hook))
+ ksu_handle_execveat((int *)AT_FDCWD, &filename, &argv, &envp, 0);
+ else
+ ksu_handle_execveat_sucompat((int *)AT_FDCWD, &filename, NULL, NULL, NULL);
+#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 su, 32-on-64 ksud support
+ if (unlikely(ksu_execveat_hook))
+ ksu_handle_execveat((int *)AT_FDCWD, &filename, &argv, &envp, 0);
+ else
+ ksu_handle_execveat_sucompat((int *)AT_FDCWD, &filename, NULL, NULL, NULL);
+#endif
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
}
show oversimplified
- This is an oversimplified version.
- The one above is still better for optimization.
--- 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
+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
+ ksu_handle_execveat((int *)AT_FDCWD, &filename, &argv, &envp, 0);
+#endif
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
}