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.
🟢 sys_execve hook
- if you are building this repo's KernelSU, you can hook execve syscall directly.
- if not, add the following to your kernelsu driver:
- if you want old do_execve hooks instead, check depreciated
- if you're on Ultra-Legacy (3.0 / 3.4 / 3.10) check second post.
show patch/diff
- 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 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);
}
🟢 sys_faccessat hook
- from original guide
- hook sys_faccessat even if you have do_faccessat, this is for scope minimization.
- if your sys_faccessat does NOT look like the first, check second variant
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 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 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().
- if your sys_read does NOT look like the first, check second variant
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 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;
🟢 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
show depreciated
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
- you now have to hook pts_unix98_lookup() on drivers/tty/pty.c instead of fs/devpts/inode.c
- if your pts_unix98_lookup does NOT look like this check second patch
- this is just to be in line with upstream hooks
show patch/diff (4.9 and newer)
- 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);
}