From e0a4064163886dd27e8c71902ed26abb140e6c62 Mon Sep 17 00:00:00 2001 From: Ksawlii Date: Wed, 29 Jan 2025 18:01:59 +0100 Subject: [PATCH] Update and fix susfs4ksu --- fs/namei.c | 3 ++ fs/namespace.c | 102 +++++++++++++++++++++++++++++++++++++++++---- fs/notify/fdinfo.c | 52 ++++++++++++++++++++++- fs/proc/fd.c | 44 +++++++++++++++++++ fs/stat.c | 22 +++++++++- fs/statfs.c | 23 +++++++++- 6 files changed, 234 insertions(+), 12 deletions(-) diff --git a/fs/namei.c b/fs/namei.c index 41b53d3df..e938bcc46 100755 --- a/fs/namei.c +++ b/fs/namei.c @@ -39,6 +39,9 @@ #include #include #include +#if defined(CONFIG_KSU_SUSFS_SUS_PATH) || defined(CONFIG_KSU_SUSFS_OPEN_REDIRECT) +#include +#endif #ifdef CONFIG_FSCRYPT_SDP #include diff --git a/fs/namespace.c b/fs/namespace.c index 2cd49eb26..619038c22 100755 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -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); @@ -238,7 +250,7 @@ static void mnt_free_id(struct mount *mnt) static int mnt_alloc_group_id(struct mount *mnt) { #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT - int res; + int res; // Check if mnt has sus mnt_id if (mnt->mnt_id >= DEFAULT_SUS_MNT_ID) { @@ -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,8 +1176,18 @@ 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"); - if (!mnt) +#endif + if (!mnt) return ERR_PTR(-ENOMEM); if (fc->sb_flags & SB_KERNMOUNT) @@ -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,9 +1297,53 @@ 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); - if (!mnt) +#endif + if (!mnt) return ERR_PTR(-ENOMEM); if (flag & (CL_SLAVE | CL_PRIVATE | CL_SHARED_TO_SLAVE)) @@ -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) - susfs_auto_add_try_umount_for_bind_mount(path); + 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()) { diff --git a/fs/notify/fdinfo.c b/fs/notify/fdinfo.c index 55081ae3a..aedfd6e0c 100755 --- a/fs/notify/fdinfo.c +++ b/fs/notify/fdinfo.c @@ -12,6 +12,9 @@ #include #include #include +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +#include +#endif #include "inotify/inotify.h" #include "fanotify/fanotify.h" @@ -22,17 +25,28 @@ #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) { - show(m, mark); - if (seq_has_overflowed(m)) +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + show(m, mark, f); +#else + show(m, mark); +#endif + if (seq_has_overflowed(m)) break; } fsnotify_group_unlock(group); @@ -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)); diff --git a/fs/proc/fd.c b/fs/proc/fd.c index 35b92009c..90c27120f 100755 --- a/fs/proc/fd.c +++ b/fs/proc/fd.c @@ -13,6 +13,9 @@ #include #include +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +#include +#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); diff --git a/fs/stat.c b/fs/stat.c index 8a5481b54..4a4cc1cec 100755 --- a/fs/stat.c +++ b/fs/stat.c @@ -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,8 +239,16 @@ retry: goto out; error = vfs_getattr(&path, stat, request_mask, flags); - stat->mnt_id = real_mount(path.mnt)->mnt_id; - stat->result_mask |= STATX_MNT_ID; +#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; stat->attributes_mask |= STATX_ATTR_MOUNT_ROOT; diff --git a/fs/statfs.c b/fs/statfs.c index 553f995a0..4d41a9063 100755 --- a/fs/statfs.c +++ b/fs/statfs.c @@ -9,6 +9,10 @@ #include #include #include +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +#include +#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;