Update and fix susfs4ksu

This commit is contained in:
Ksawlii 2025-01-29 18:01:59 +01:00
parent c8b3e5b969
commit e0a4064163
6 changed files with 234 additions and 12 deletions

View file

@ -39,6 +39,9 @@
#include <linux/bitops.h>
#include <linux/init_task.h>
#include <linux/uaccess.h>
#if defined(CONFIG_KSU_SUSFS_SUS_PATH) || defined(CONFIG_KSU_SUSFS_OPEN_REDIRECT)
#include <linux/susfs_def.h>
#endif
#ifdef CONFIG_FSCRYPT_SDP
#include <linux/fscrypto_sdp_name.h>

View file

@ -197,6 +197,18 @@ static inline struct hlist_head *mp_hash(struct dentry *dentry)
return &mountpoint_hashtable[tmp & mp_hash_mask];
}
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
// Our own mnt_alloc_id() that assigns mnt_id starting from DEFAULT_SUS_MNT_ID
static int susfs_mnt_alloc_id(struct mount *mnt)
{
int res = ida_alloc_min(&susfs_mnt_id_ida, DEFAULT_SUS_MNT_ID, GFP_KERNEL);
if (res < 0)
return res;
mnt->mnt_id = res;
return 0;
}
#endif
static int mnt_alloc_id(struct mount *mnt)
{
int res = ida_alloc(&mnt_id_ida, GFP_KERNEL);
@ -309,13 +321,31 @@ int mnt_get_count(struct mount *mnt)
#endif
}
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
static struct mount *alloc_vfsmnt(const char *name, bool should_spoof, int custom_mnt_id)
#else
static struct mount *alloc_vfsmnt(const char *name)
#endif
{
struct mount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL);
if (mnt) {
int err;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
if (should_spoof) {
if (!custom_mnt_id) {
err = susfs_mnt_alloc_id(mnt);
} else {
mnt->mnt_id = custom_mnt_id;
err = 0;
}
goto bypass_orig_flow;
}
#endif
err = mnt_alloc_id(mnt);
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
bypass_orig_flow:
#endif
if (err)
goto out_free_cache;
#ifdef CONFIG_KDP_NS
@ -1146,7 +1176,17 @@ struct vfsmount *vfs_create_mount(struct fs_context *fc)
if (!fc->root)
return ERR_PTR(-EINVAL);
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
// For newly created mounts, the only caller process we care is KSU
if (unlikely(susfs_is_current_ksu_domain())) {
mnt = alloc_vfsmnt(fc->source ?: "none", true, 0);
goto bypass_orig_flow;
}
mnt = alloc_vfsmnt(fc->source ?: "none", false, 0);
bypass_orig_flow:
#else
mnt = alloc_vfsmnt(fc->source ?: "none");
#endif
if (!mnt)
return ERR_PTR(-ENOMEM);
@ -1169,12 +1209,12 @@ struct vfsmount *vfs_create_mount(struct fs_context *fc)
mnt->mnt_parent = mnt;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
// If caller process is zygote, then it is a normal mount, so we just reorder the mnt_id
if (susfs_is_current_zygote_domain()) {
mnt->mnt.susfs_orig_mnt_id = mnt->mnt_id;
mnt->mnt.susfs_mnt_id_backup = mnt->mnt_id;
mnt->mnt_id = current->susfs_last_fake_mnt_id++;
}
#endif
lock_mount_hash();
#ifdef CONFIG_KDP_NS
list_add_tail(&mnt->mnt_instance, &((struct kdp_mount *)mnt)->mnt->mnt_sb->s_mounts);
@ -1257,8 +1297,52 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,
#endif
struct mount *mnt;
int err;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
bool is_current_ksu_domain = susfs_is_current_ksu_domain();
bool is_current_zygote_domain = susfs_is_current_zygote_domain();
/* - It is very important that we need to use CL_COPY_MNT_NS to identify whether
* the clone is a copy_tree() or single mount like called by __do_loopback()
* - if caller process is KSU, consider the following situation:
* 1. it is NOT doing unshare => call alloc_vfsmnt() to assign a new sus mnt_id
* 2. it is doing unshare => spoof the new mnt_id with the old mnt_id
* - If caller process is zygote and old mnt_id is sus => call alloc_vfsmnt() to assign a new sus mnt_id
* - For the rest of caller process that doing unshare => call alloc_vfsmnt() to assign a new sus mnt_id only for old sus mount
*/
// Firstly, check if it is KSU process
if (unlikely(is_current_ksu_domain)) {
// if it is doing single clone
if (!(flag & CL_COPY_MNT_NS)) {
mnt = alloc_vfsmnt(old->mnt_devname, true, 0);
goto bypass_orig_flow;
}
// if it is doing unshare
mnt = alloc_vfsmnt(old->mnt_devname, true, old->mnt_id);
if (mnt) {
mnt->mnt.susfs_mnt_id_backup = DEFAULT_SUS_MNT_ID_FOR_KSU_PROC_UNSHARE;
}
goto bypass_orig_flow;
}
// Secondly, check if it is zygote process and no matter it is doing unshare or not
if (likely(is_current_zygote_domain) && (old->mnt_id >= DEFAULT_SUS_MNT_ID)) {
/* Important Note:
* - Here we can't determine whether the unshare is called zygisk or not,
* so we can only patch out the unshare code in zygisk source code for now
* - But at least we can deal with old sus mounts using alloc_vfsmnt()
*/
mnt = alloc_vfsmnt(old->mnt_devname, true, 0);
goto bypass_orig_flow;
}
// Lastly, for other process that is doing unshare operation, but only deal with old sus mount
if ((flag & CL_COPY_MNT_NS) && (old->mnt_id >= DEFAULT_SUS_MNT_ID)) {
mnt = alloc_vfsmnt(old->mnt_devname, true, 0);
goto bypass_orig_flow;
}
mnt = alloc_vfsmnt(old->mnt_devname, false, 0);
bypass_orig_flow:
#else
mnt = alloc_vfsmnt(old->mnt_devname);
#endif
if (!mnt)
return ERR_PTR(-ENOMEM);
@ -2798,12 +2882,15 @@ static int do_loopback(struct path *path, const char *old_name,
// And we target only process with ksu domain.
if (susfs_is_current_ksu_domain()) {
#if defined(CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT)
if (susfs_auto_add_sus_bind_mount(old_name, &old_path)) {
if (susfs_is_auto_add_sus_bind_mount_enabled &&
susfs_auto_add_sus_bind_mount(old_name, &old_path)) {
goto orig_flow;
}
#endif
#if defined(CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT)
if (susfs_is_auto_add_try_umount_for_bind_mount_enabled) {
susfs_auto_add_try_umount_for_bind_mount(path);
}
#endif
}
#if defined(CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT)
@ -3996,6 +4083,7 @@ SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
goto out_data;
ret = do_mount(kernel_dev, dir_name, kernel_type, flags, options);
#if defined(CONFIG_KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT) && defined(CONFIG_KSU_SUSFS_HAS_MAGIC_MOUNT)
// Just for the compatibility of Magic Mount KernelSU
if (!ret && susfs_is_auto_add_sus_ksu_default_mount_enabled && susfs_is_current_ksu_domain()) {

View file

@ -12,6 +12,9 @@
#include <linux/types.h>
#include <linux/seq_file.h>
#include <linux/exportfs.h>
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
#include <linux/susfs_def.h>
#endif
#include "inotify/inotify.h"
#include "fanotify/fanotify.h"
@ -22,16 +25,27 @@
#if defined(CONFIG_INOTIFY_USER) || defined(CONFIG_FANOTIFY)
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
static void show_fdinfo(struct seq_file *m, struct file *f,
void (*show)(struct seq_file *m,
struct fsnotify_mark *mark,
struct file *file))
#else
static void show_fdinfo(struct seq_file *m, struct file *f,
void (*show)(struct seq_file *m,
struct fsnotify_mark *mark))
#endif
{
struct fsnotify_group *group = f->private_data;
struct fsnotify_mark *mark;
fsnotify_group_lock(group);
list_for_each_entry(mark, &group->marks_list, g_list) {
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
show(m, mark, f);
#else
show(m, mark);
#endif
if (seq_has_overflowed(m))
break;
}
@ -73,7 +87,11 @@ static void show_mark_fhandle(struct seq_file *m, struct inode *inode)
#ifdef CONFIG_INOTIFY_USER
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
static void inotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark, struct file *file)
#else
static void inotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark)
#endif
{
struct inotify_inode_mark *inode_mark;
struct inode *inode;
@ -84,6 +102,36 @@ static void inotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark)
inode_mark = container_of(mark, struct inotify_inode_mark, fsn_mark);
inode = igrab(fsnotify_conn_inode(mark->connector));
if (inode) {
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
if (likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC) &&
unlikely(inode->i_state & INODE_STATE_SUS_KSTAT)) {
struct path path;
char *pathname = kmalloc(PAGE_SIZE, GFP_KERNEL);
char *dpath;
if (!pathname) {
goto out_seq_printf;
}
dpath = d_path(&file->f_path, pathname, PAGE_SIZE);
if (!dpath) {
goto out_free_pathname;
}
if (kern_path(dpath, 0, &path)) {
goto out_free_pathname;
}
seq_printf(m, "inotify wd:%x ino:%lx sdev:%x mask:%x ignored_mask:0 ",
inode_mark->wd, path.dentry->d_inode->i_ino, path.dentry->d_inode->i_sb->s_dev,
inotify_mark_user_mask(mark));
show_mark_fhandle(m, path.dentry->d_inode);
seq_putc(m, '\n');
iput(inode);
path_put(&path);
kfree(pathname);
return;
out_free_pathname:
kfree(pathname);
}
out_seq_printf:
#endif
seq_printf(m, "inotify wd:%x ino:%lx sdev:%x mask:%x ignored_mask:0 ",
inode_mark->wd, inode->i_ino, inode->i_sb->s_dev,
inotify_mark_user_mask(mark));

View file

@ -13,6 +13,9 @@
#include <linux/fs.h>
#include <linux/proc_fs.h>
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
#include <linux/susfs_def.h>
#endif
#include "../mount.h"
#include "internal.h"
@ -24,6 +27,9 @@ static int seq_show(struct seq_file *m, void *v)
int f_flags = 0, ret = -ENOENT;
struct file *file = NULL;
struct task_struct *task;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
struct mount *mnt = NULL;
#endif
task = get_proc_task(m->private);
if (!task)
@ -54,10 +60,48 @@ static int seq_show(struct seq_file *m, void *v)
if (ret)
return ret;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
mnt = real_mount(file->f_path.mnt);
if (likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC) &&
mnt->mnt_id >= DEFAULT_SUS_MNT_ID) {
struct path path;
char *pathname = kmalloc(PAGE_SIZE, GFP_KERNEL);
char *dpath;
for (; mnt->mnt_id >= DEFAULT_SUS_MNT_ID; mnt = mnt->mnt_parent) { }
if (!pathname) {
goto out_seq_printf;
}
dpath = d_path(&file->f_path, pathname, PAGE_SIZE);
if (!dpath) {
goto out_free_pathname;
}
if (kern_path(dpath, 0, &path)) {
goto out_free_pathname;
}
seq_printf(m, "pos:\t%lli\nflags:\t0%o\nmnt_id:\t%i\nino:\t%lu\n",
(long long)file->f_pos, f_flags,
mnt->mnt_id,
path.dentry->d_inode->i_ino);
path_put(&path);
kfree(pathname);
goto bypass_orig_flow;
out_free_pathname:
kfree(pathname);
}
out_seq_printf:
seq_printf(m, "pos:\t%lli\nflags:\t0%o\nmnt_id:\t%i\nino:\t%lu\n",
(long long)file->f_pos, f_flags,
mnt->mnt_id,
file_inode(file)->i_ino);
bypass_orig_flow:
#else
seq_printf(m, "pos:\t%lli\nflags:\t0%o\nmnt_id:\t%i\nino:\t%lu\n",
(long long)file->f_pos, f_flags,
real_mount(file->f_path.mnt)->mnt_id,
file_inode(file)->i_ino);
#endif
/* show_fd_locks() never deferences files so a stale value is safe */
show_fd_locks(m, file, files);

View file

@ -139,6 +139,7 @@ EXPORT_SYMBOL(vfs_getattr_nosec);
*
* 0 will be returned on success, and a -ve error code if unsuccessful.
*/
int vfs_getattr(const struct path *path, struct kstat *stat,
u32 request_mask, unsigned int query_flags)
{
@ -201,6 +202,15 @@ static int vfs_statx(int dfd, const char __user *filename, int flags,
struct path path;
unsigned lookup_flags = 0;
int error;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
struct mount *mnt;
#endif
#ifdef CONFIG_KSU_SUSFS_SUS_SU
if (susfs_is_sus_su_hooks_enabled) {
ksu_handle_stat(&dfd, &filename, &flags);
}
#endif
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
struct mount *mnt;
@ -229,7 +239,15 @@ retry:
goto out;
error = vfs_getattr(&path, stat, request_mask, flags);
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
mnt = real_mount(path.mnt);
if (likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) {
for (; mnt->mnt_id >= DEFAULT_SUS_MNT_ID; mnt = mnt->mnt_parent) {}
}
stat->mnt_id = mnt->mnt_id;
#else
stat->mnt_id = real_mount(path.mnt)->mnt_id;
#endif
stat->result_mask |= STATX_MNT_ID;
if (path.mnt->mnt_root == path.dentry)
stat->attributes |= STATX_ATTR_MOUNT_ROOT;

View file

@ -9,6 +9,10 @@
#include <linux/security.h>
#include <linux/uaccess.h>
#include <linux/compat.h>
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
#include <linux/susfs_def.h>
#include "mount.h"
#endif
#include "internal.h"
static int flags_by_mnt(int mnt_flags)
@ -86,11 +90,23 @@ EXPORT_SYMBOL(vfs_get_fsid);
int vfs_statfs(const struct path *path, struct kstatfs *buf)
{
int error;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
struct mount *mnt;
mnt = real_mount(path->mnt);
if (likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) {
for (; mnt->mnt_id >= DEFAULT_SUS_MNT_ID; mnt = mnt->mnt_parent) {}
}
error = statfs_by_dentry(mnt->mnt.mnt_root, buf);
if (!error)
buf->f_flags = calculate_f_flags(&mnt->mnt);
return error;
#else
error = statfs_by_dentry(path->dentry, buf);
if (!error)
buf->f_flags = calculate_f_flags(path->mnt);
return error;
#endif
}
EXPORT_SYMBOL(vfs_statfs);
@ -262,6 +278,11 @@ static int vfs_ustat(dev_t dev, struct kstatfs *sbuf)
if (!s)
return -EINVAL;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
if (unlikely(s->s_root->d_inode->i_state & INODE_STATE_SUS_MOUNT)) {
return -EINVAL;
}
#endif
err = statfs_by_dentry(s->s_root, sbuf);
drop_super(s);
return err;