kernel: add wrapper for creds, refine disable_seccomp, revert some changes (#131)

1. Wrapper for creds:
* Some older kernel does not have {.val}, so, for nicer compatibility support and clean code,
make some wrapper for credential use.
* After this change, do not use current_uid().val, instead, use ksu_current_uid(). For more
info, check kernel/include/ksu_creds.h.

2. Refine disable_seccomp (need to add k6.11+ support)
https://github.com/tiann/KernelSU/pull/2708
https://github.com/tiann/KernelSU/issues/2706

3. Revert "Handle unmount for isolated process correctly"
Reason: https://github.com/tiann/KernelSU/pull/2696#issuecomment-3181866301

Co-authored-by: Wang Han <416810799@qq.com>
Co-authored-by: rsuntk <rsuntk@yukiprjkt.my.id>
This commit is contained in:
ShirkNeko
2025-08-16 14:25:17 +08:00
parent 69a13ff999
commit 8a0d52a2ee
5 changed files with 151 additions and 140 deletions

View File

@@ -19,6 +19,7 @@ kernelsu-objs += selinux/rules.o
ccflags-y += -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include ccflags-y += -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include
ccflags-y += -I$(objtree)/security/selinux -include $(srctree)/include/uapi/asm-generic/errno.h ccflags-y += -I$(objtree)/security/selinux -include $(srctree)/include/uapi/asm-generic/errno.h
ccflags-y += -include $(srctree)/$(src)/include/ksu_creds.h
obj-$(CONFIG_KSU) += kernelsu.o obj-$(CONFIG_KSU) += kernelsu.o
obj-$(CONFIG_KSU_TRACEPOINT_HOOK) += ksu_trace_export.o obj-$(CONFIG_KSU_TRACEPOINT_HOOK) += ksu_trace_export.o

View File

@@ -24,7 +24,7 @@
#include <linux/fs.h> #include <linux/fs.h>
#include <linux/namei.h> #include <linux/namei.h>
#ifndef KSU_HAS_PATH_UMOUNT #ifndef KSU_HAS_PATH_UMOUNT
#include <linux/syscalls.h> // sys_umount #include <linux/syscalls.h> // sys_umount (<4.17) & ksys_umount (4.17+)
#endif #endif
#ifdef MODULE #ifdef MODULE
@@ -49,6 +49,7 @@
#include "selinux/selinux.h" #include "selinux/selinux.h"
#include "throne_tracker.h" #include "throne_tracker.h"
#include "kernel_compat.h" #include "kernel_compat.h"
#include "include/ksu_creds.h"
#ifdef CONFIG_KPM #ifdef CONFIG_KPM
#include "kpm/kpm.h" #include "kpm/kpm.h"
@@ -61,7 +62,7 @@ bool susfs_is_allow_su(void)
// we are manager, allow! // we are manager, allow!
return true; return true;
} }
return ksu_is_allow_uid(current_uid().val); return ksu_is_allow_uid(ksu_current_uid());
} }
extern u32 susfs_zygote_sid; extern u32 susfs_zygote_sid;
@@ -143,7 +144,7 @@ static inline bool is_allow_su()
// we are manager, allow! // we are manager, allow!
return true; return true;
} }
return ksu_is_allow_uid(current_uid().val); return ksu_is_allow_uid(ksu_current_uid());
} }
static inline bool is_unsupported_uid(uid_t uid) static inline bool is_unsupported_uid(uid_t uid)
@@ -196,11 +197,17 @@ static void setup_groups(struct root_profile *profile, struct cred *cred)
groups_sort(group_info); groups_sort(group_info);
set_groups(cred, group_info); set_groups(cred, group_info);
put_group_info(group_info);
} }
static void disable_seccomp() static void disable_seccomp(void)
{ {
assert_spin_locked(&current->sighand->siglock); struct task_struct *tsk = get_current();
pr_info("%s++\n", __func__);
spin_lock_irq(&tsk->sighand->siglock);
assert_spin_locked(&tsk->sighand->siglock);
// disable seccomp // disable seccomp
#if defined(CONFIG_GENERIC_ENTRY) && \ #if defined(CONFIG_GENERIC_ENTRY) && \
LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0) LINUX_VERSION_CODE >= KERNEL_VERSION(5, 11, 0)
@@ -210,46 +217,56 @@ static void disable_seccomp()
#endif #endif
#ifdef CONFIG_SECCOMP #ifdef CONFIG_SECCOMP
current->seccomp.mode = 0; tsk->seccomp.mode = 0;
current->seccomp.filter = NULL; if (tsk->seccomp.filter == NULL) {
pr_warn("tsk->seccomp.filter is NULL already!\n");
goto out;
}
// 5.9+ have filter_count and use seccomp_filter_release
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0)
seccomp_filter_release(tsk);
atomic_set(&tsk->seccomp.filter_count, 0);
#else #else
put_seccomp_filter(tsk);
tsk->seccomp.filter = NULL;
#endif #endif
#endif
out:
spin_unlock_irq(&tsk->sighand->siglock);
pr_info("%s--\n", __func__);
} }
void ksu_escape_to_root(void) void ksu_escape_to_root(void)
{ {
struct cred *cred; struct cred *newcreds = prepare_creds();
if (newcreds == NULL) {
if (current_euid().val == 0) { pr_err("%s: failed to allocate new cred.\n", __func__);
pr_warn("Already root, don't escape!\n");
return; return;
} }
rcu_read_lock(); if (ksu_cred_euid(newcreds) == 0) {
pr_warn("Already root, don't escape!\n");
abort_creds(newcreds);
return;
}
do { struct root_profile *profile =
cred = (struct cred *)__task_cred((current)); ksu_get_root_profile(ksu_cred_uid(newcreds));
if (!cred) {
pr_err("%s: cred is NULL! bailing out..\n", __func__);
rcu_read_unlock();
return;
}
} while (!get_cred_rcu(cred));
rcu_read_unlock(); ksu_cred_uid(newcreds) = profile->uid;
ksu_cred_suid(newcreds) = profile->uid;
ksu_cred_euid(newcreds) = profile->uid;
ksu_cred_fsuid(newcreds) = profile->uid;
struct root_profile *profile = ksu_get_root_profile(cred->uid.val); ksu_cred_gid(newcreds) = profile->gid;
ksu_cred_fsgid(newcreds) = profile->gid;
ksu_cred_sgid(newcreds) = profile->gid;
ksu_cred_egid(newcreds) = profile->gid;
cred->uid.val = profile->uid; // no wrapper, ignore it.
cred->suid.val = profile->uid; newcreds->securebits = 0;
cred->euid.val = profile->uid;
cred->fsuid.val = profile->uid;
cred->gid.val = profile->gid;
cred->fsgid.val = profile->gid;
cred->sgid.val = profile->gid;
cred->egid.val = profile->gid;
cred->securebits = 0;
BUILD_BUG_ON(sizeof(profile->capabilities.effective) != BUILD_BUG_ON(sizeof(profile->capabilities.effective) !=
sizeof(kernel_cap_t)); sizeof(kernel_cap_t));
@@ -259,23 +276,17 @@ void ksu_escape_to_root(void)
// we add it here but don't add it to cap_inhertiable, it would be dropped automaticly after exec! // we add it here but don't add it to cap_inhertiable, it would be dropped automaticly after exec!
u64 cap_for_ksud = u64 cap_for_ksud =
profile->capabilities.effective | CAP_DAC_READ_SEARCH; profile->capabilities.effective | CAP_DAC_READ_SEARCH;
memcpy(&cred->cap_effective, &cap_for_ksud, memcpy(&newcreds->cap_effective, &cap_for_ksud,
sizeof(cred->cap_effective)); sizeof(newcreds->cap_effective));
memcpy(&cred->cap_permitted, &profile->capabilities.effective, memcpy(&newcreds->cap_permitted, &profile->capabilities.effective,
sizeof(cred->cap_permitted)); sizeof(newcreds->cap_permitted));
memcpy(&cred->cap_bset, &profile->capabilities.effective, memcpy(&newcreds->cap_bset, &profile->capabilities.effective,
sizeof(cred->cap_bset)); sizeof(newcreds->cap_bset));
setup_groups(profile, cred); setup_groups(profile, newcreds);
commit_creds(newcreds);
put_cred(cred); // - release here - include/linux/cred.h
// Refer to kernel/seccomp.c: seccomp_set_mode_strict
// When disabling Seccomp, ensure that current->sighand->siglock is held during the operation.
spin_lock_irq(&current->sighand->siglock);
disable_seccomp(); disable_seccomp();
spin_unlock_irq(&current->sighand->siglock);
ksu_setup_selinux(profile->selinux_domain); ksu_setup_selinux(profile->selinux_domain);
} }
@@ -286,7 +297,7 @@ int ksu_handle_rename(struct dentry *old_dentry, struct dentry *new_dentry)
return 0; return 0;
} }
if (current_uid().val != 1000) { if (ksu_current_uid() != 1000) {
// skip non system uid // skip non system uid
return 0; return 0;
} }
@@ -354,14 +365,14 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
} }
// TODO: find it in throne tracker! // TODO: find it in throne tracker!
uid_t current_uid_val = current_uid().val; uid_t current_uid_val = ksu_current_uid();
uid_t manager_uid = ksu_get_manager_uid(); uid_t manager_uid = ksu_get_manager_uid();
if (current_uid_val != manager_uid && if (current_uid_val != manager_uid &&
current_uid_val % 100000 == manager_uid) { current_uid_val % 100000 == manager_uid) {
ksu_set_manager_uid(current_uid_val); ksu_set_manager_uid(current_uid_val);
} }
bool from_root = 0 == current_uid().val; bool from_root = 0 == ksu_current_uid();
bool from_manager = ksu_is_manager(); bool from_manager = ksu_is_manager();
if (!from_root && !from_manager) { if (!from_root && !from_manager) {
@@ -385,7 +396,7 @@ int ksu_handle_prctl(int option, unsigned long arg2, unsigned long arg3,
if (arg2 == CMD_GRANT_ROOT) { if (arg2 == CMD_GRANT_ROOT) {
if (is_allow_su()) { if (is_allow_su()) {
pr_info("allow root for: %d\n", current_uid().val); pr_info("allow root for: %d\n", ksu_current_uid());
ksu_escape_to_root(); ksu_escape_to_root();
if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) { if (copy_to_user(result, &reply_ok, sizeof(reply_ok))) {
pr_err("grant_root: prctl reply error\n"); pr_err("grant_root: prctl reply error\n");
@@ -1131,7 +1142,7 @@ static bool should_umount(struct path *path)
if (current->nsproxy->mnt_ns == init_nsproxy.mnt_ns) { if (current->nsproxy->mnt_ns == init_nsproxy.mnt_ns) {
pr_info("ignore global mnt namespace process: %d\n", pr_info("ignore global mnt namespace process: %d\n",
current_uid().val); ksu_current_uid());
return false; return false;
} }
@@ -1146,12 +1157,13 @@ static bool should_umount(struct path *path)
#endif #endif
} }
#ifdef KSU_HAS_PATH_UMOUNT #if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) || defined(KSU_HAS_PATH_UMOUNT)
static void ksu_path_umount(const char *mnt, struct path *path, int flags) static void ksu_path_umount(const char *mnt, struct path *path, int flags)
{ {
int err = path_umount(path, flags); int ret = path_umount(path, flags);
pr_info("%s: path: %s ret: %d\n", __func__, mnt, err); pr_info("%s: path: %s ret: %d\n", __func__, mnt, ret);
} }
#define ksu_umount_mnt(mnt, path, flags) (ksu_path_umount(mnt, path, flags))
#else #else
// TODO: Search a way to make this works without set_fs functions // TODO: Search a way to make this works without set_fs functions
static void ksu_sys_umount(const char *mnt, int flags) static void ksu_sys_umount(const char *mnt, int flags)
@@ -1168,9 +1180,16 @@ static void ksu_sys_umount(const char *mnt, int flags)
ret = sys_umount(usermnt, flags); // cuz asmlinkage long sys##name ret = sys_umount(usermnt, flags); // cuz asmlinkage long sys##name
#endif #endif
set_fs(old_fs); set_fs(old_fs);
pr_info("%s: path: %s ret: %d \n", __func__, usermnt, ret); pr_info("%s: path: %s ret: %d\n", __func__, usermnt, ret);
} }
#endif
#define ksu_umount_mnt(mnt, __unused, flags) \
({ \
path_put(__unused); \
ksu_sys_umount(mnt, flags); \
})
#endif
#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT #ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT
void ksu_try_umount(const char *mnt, bool check_mnt, int flags, uid_t uid) void ksu_try_umount(const char *mnt, bool check_mnt, int flags, uid_t uid)
@@ -1202,11 +1221,7 @@ static void try_umount(const char *mnt, bool check_mnt, int flags)
} }
#endif #endif
#ifdef KSU_HAS_PATH_UMOUNT ksu_umount_mnt(mnt, &path, flags);
ksu_path_umount(mnt, &path, flags);
#else
ksu_sys_umount(mnt, flags);
#endif
} }
#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT #ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT
@@ -1333,7 +1348,7 @@ out_ksu_try_umount:
return 0; return 0;
} else { } else {
#ifdef CONFIG_KSU_DEBUG #ifdef CONFIG_KSU_DEBUG
pr_info("uid: %d should not umount!\n", current_uid().val); pr_info("uid: %d should not umount!\n", ksu_current_uid());
#endif #endif
} }
#ifndef CONFIG_KSU_SUSFS #ifndef CONFIG_KSU_SUSFS
@@ -1383,75 +1398,6 @@ out_susfs_try_umount_all:
return 0; return 0;
} }
// Init functons
static int handler_pre(struct kprobe *p, struct pt_regs *regs)
{
struct pt_regs *real_regs = PT_REAL_REGS(regs);
int option = (int)PT_REGS_PARM1(real_regs);
unsigned long arg2 = (unsigned long)PT_REGS_PARM2(real_regs);
unsigned long arg3 = (unsigned long)PT_REGS_PARM3(real_regs);
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 16, 0)
// PRCTL_SYMBOL is the arch-specificed one, which receive raw pt_regs from syscall
unsigned long arg4 = (unsigned long)PT_REGS_SYSCALL_PARM4(real_regs);
#else
// PRCTL_SYMBOL is the common one, called by C convention in do_syscall_64
// https://elixir.bootlin.com/linux/v4.15.18/source/arch/x86/entry/common.c#L287
unsigned long arg4 = (unsigned long)PT_REGS_CCALL_PARM4(real_regs);
#endif
unsigned long arg5 = (unsigned long)PT_REGS_PARM5(real_regs);
return ksu_handle_prctl(option, arg2, arg3, arg4, arg5);
}
static struct kprobe prctl_kp = {
.symbol_name = PRCTL_SYMBOL,
.pre_handler = handler_pre,
};
static int renameat_handler_pre(struct kprobe *p, struct pt_regs *regs)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
// https://elixir.bootlin.com/linux/v5.12-rc1/source/include/linux/fs.h
struct renamedata *rd = PT_REGS_PARM1(regs);
struct dentry *old_entry = rd->old_dentry;
struct dentry *new_entry = rd->new_dentry;
#else
struct dentry *old_entry = (struct dentry *)PT_REGS_PARM2(regs);
struct dentry *new_entry = (struct dentry *)PT_REGS_CCALL_PARM4(regs);
#endif
return ksu_handle_rename(old_entry, new_entry);
}
static struct kprobe renameat_kp = {
.symbol_name = "vfs_rename",
.pre_handler = renameat_handler_pre,
};
__maybe_unused int ksu_kprobe_init(void)
{
int rc = 0;
rc = register_kprobe(&prctl_kp);
if (rc) {
pr_info("prctl kprobe failed: %d.\n", rc);
return rc;
}
rc = register_kprobe(&renameat_kp);
pr_info("renameat kp: %d\n", rc);
return rc;
}
__maybe_unused int ksu_kprobe_exit(void)
{
unregister_kprobe(&prctl_kp);
unregister_kprobe(&renameat_kp);
return 0;
}
static int ksu_task_prctl(int option, unsigned long arg2, unsigned long arg3, static int ksu_task_prctl(int option, unsigned long arg2, unsigned long arg3,
unsigned long arg4, unsigned long arg5) unsigned long arg4, unsigned long arg5)
{ {
@@ -1459,7 +1405,9 @@ static int ksu_task_prctl(int option, unsigned long arg2, unsigned long arg3,
return -ENOSYS; return -ENOSYS;
} }
// kernel 4.4 and 4.9 // kernel 4.4 and 4.9
#if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || defined(CONFIG_IS_HW_HISI) || defined(CONFIG_KSU_ALLOWLIST_WORKAROUND) #if LINUX_VERSION_CODE < KERNEL_VERSION(4, 10, 0) || \
defined(CONFIG_IS_HW_HISI) || \
defined(CONFIG_KSU_ALLOWLIST_WORKAROUND)
static int ksu_key_permission(key_ref_t key_ref, const struct cred *cred, static int ksu_key_permission(key_ref_t key_ref, const struct cred *cred,
unsigned perm) unsigned perm)
{ {
@@ -1523,6 +1471,27 @@ void __init ksu_lsm_hook_init(void)
} }
#else #else
// keep renameat_handler for LKM support
static int renameat_handler_pre(struct kprobe *p, struct pt_regs *regs)
{
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 12, 0)
// https://elixir.bootlin.com/linux/v5.12-rc1/source/include/linux/fs.h
struct renamedata *rd = PT_REGS_PARM1(regs);
struct dentry *old_entry = rd->old_dentry;
struct dentry *new_entry = rd->new_dentry;
#else
struct dentry *old_entry = (struct dentry *)PT_REGS_PARM2(regs);
struct dentry *new_entry = (struct dentry *)PT_REGS_CCALL_PARM4(regs);
#endif
return ksu_handle_rename(old_entry, new_entry);
}
static struct kprobe renameat_kp = {
.symbol_name = "vfs_rename",
.pre_handler = renameat_handler_pre,
};
static int override_security_head(void *head, const void *new_head, size_t len) static int override_security_head(void *head, const void *new_head, size_t len)
{ {
unsigned long base = (unsigned long)head & PAGE_MASK; unsigned long base = (unsigned long)head & PAGE_MASK;
@@ -1702,4 +1671,4 @@ void ksu_core_exit(void)
// we dont use this now // we dont use this now
// ksu_kprobe_exit(); // ksu_kprobe_exit();
#endif #endif
} }

View File

@@ -0,0 +1,39 @@
/*
* KernelSU creds wrapper
*
* Provide a wrapper for a few credentials use (e.g current_uid().val),
* so it would be easier to maintain
* some older linux versions.
*/
#ifndef __KSU_H_CREDS
#define __KSU_H_CREDS
#include <linux/cred.h>
#include <linux/version.h>
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(3, 5, 0) && \
defined(CONFIG_UIDGID_STRICT_TYPE_CHECKS)) || \
LINUX_VERSION_CODE >= KERNEL_VERSION(3, 14, 0)
#define ksu_cred_uid(x) ((x)->uid.val)
#define ksu_cred_suid(x) ((x)->suid.val)
#define ksu_cred_euid(x) ((x)->euid.val)
#define ksu_cred_fsuid(x) ((x)->fsuid.val)
#define ksu_cred_gid(x) ((x)->gid.val)
#define ksu_cred_fsgid(x) ((x)->fsgid.val)
#define ksu_cred_sgid(x) ((x)->sgid.val)
#define ksu_cred_egid(x) ((x)->egid.val)
#define ksu_current_uid() (current_uid().val)
#else
#define ksu_cred_uid(x) ((x)->uid)
#define ksu_cred_suid(x) ((x)->suid)
#define ksu_cred_euid(x) ((x)->euid)
#define ksu_cred_fsuid(x) ((x)->fsuid)
#define ksu_cred_gid(x) ((x)->gid)
#define ksu_cred_fsgid(x) ((x)->fsgid)
#define ksu_cred_sgid(x) ((x)->sgid)
#define ksu_cred_egid(x) ((x)->egid)
#define ksu_current_uid() (current_uid())
#endif
#endif

View File

@@ -3,6 +3,7 @@
#include <linux/cred.h> #include <linux/cred.h>
#include <linux/types.h> #include <linux/types.h>
#include "include/ksu_creds.h"
#define KSU_INVALID_UID -1 #define KSU_INVALID_UID -1
@@ -20,7 +21,7 @@ static inline bool ksu_is_manager_uid_valid()
static inline bool ksu_is_manager() static inline bool ksu_is_manager()
{ {
return unlikely(ksu_is_any_manager(current_uid().val) || ksu_manager_uid == current_uid().val); return unlikely(ksu_is_any_manager(ksu_current_uid()) || ksu_manager_uid == ksu_current_uid());
} }
static inline uid_t ksu_get_manager_uid() static inline uid_t ksu_get_manager_uid()

View File

@@ -24,6 +24,7 @@
#include "klog.h" // IWYU pragma: keep #include "klog.h" // IWYU pragma: keep
#include "ksud.h" #include "ksud.h"
#include "kernel_compat.h" #include "kernel_compat.h"
#include "include/ksu_creds.h"
#define SU_PATH "/system/bin/su" #define SU_PATH "/system/bin/su"
#define SH_PATH "/system/bin/sh" #define SH_PATH "/system/bin/sh"
@@ -66,7 +67,7 @@ int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
#endif #endif
#ifndef CONFIG_KSU_SUSFS_SUS_SU #ifndef CONFIG_KSU_SUSFS_SUS_SU
if (!ksu_is_allow_uid(current_uid().val)) { if (!ksu_is_allow_uid(ksu_current_uid())) {
return 0; return 0;
} }
#endif #endif
@@ -116,7 +117,7 @@ int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags)
#endif #endif
#ifndef CONFIG_KSU_SUSFS_SUS_SU #ifndef CONFIG_KSU_SUSFS_SUS_SU
if (!ksu_is_allow_uid(current_uid().val)) { if (!ksu_is_allow_uid(ksu_current_uid())) {
return 0; return 0;
} }
#endif #endif
@@ -181,7 +182,7 @@ int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr,
return 0; return 0;
#ifndef CONFIG_KSU_SUSFS_SUS_SU #ifndef CONFIG_KSU_SUSFS_SUS_SU
if (!ksu_is_allow_uid(current_uid().val)) if (!ksu_is_allow_uid(ksu_current_uid()))
return 0; return 0;
#endif #endif
@@ -231,7 +232,7 @@ int ksu_handle_execve_sucompat(int *fd, const char __user **filename_user,
if (likely(memcmp(path, su, sizeof(su)))) if (likely(memcmp(path, su, sizeof(su))))
return 0; return 0;
if (!ksu_is_allow_uid(current_uid().val)) if (!ksu_is_allow_uid(ksu_current_uid()))
return 0; return 0;
pr_info("sys_execve su found\n"); pr_info("sys_execve su found\n");
@@ -248,7 +249,7 @@ static int ksu_inline_handle_devpts(struct inode *inode)
return 0; return 0;
} }
uid_t uid = current_uid().val; uid_t uid = ksu_current_uid();
if (uid % 100000 < 10000) { if (uid % 100000 < 10000) {
// not untrusted_app, ignore it // not untrusted_app, ignore it
return 0; return 0;