From 4311834fdc0ca0f2772aef7fa3c8e32d6fe91a9d Mon Sep 17 00:00:00 2001 From: Phapoom Saksri Date: Tue, 14 Jan 2025 00:49:01 +0700 Subject: [PATCH] susfs4ksu --- fs/Makefile | 2 + fs/dcache.c | 14 ++++ fs/inode.c | 21 +++++ fs/namei.c | 140 +++++++++++++++++++++++++++++++ fs/namespace.c | 169 ++++++++++++++++++++++++++++++++++++++ fs/overlayfs/inode.c | 10 +++ fs/overlayfs/overlayfs.h | 3 + fs/overlayfs/readdir.c | 12 +++ fs/overlayfs/super.c | 26 ++++++ fs/proc/cmdline.c | 9 ++ fs/proc/task_mmu.c | 16 ++++ fs/proc_namespace.c | 22 +++++ fs/readdir.c | 17 ++++ fs/stat.c | 18 +++- fs/statfs.c | 22 +++++ fs/sus_su.c | 140 +++++++++++++++++++++++++++++++ fs/susfs.c | 25 ++++-- include/linux/mount.h | 3 + include/linux/sched.h | 4 + include/linux/sus_su.h | 9 ++ include/linux/susfs.h | 47 +---------- include/linux/susfs_def.h | 55 +++++++++++++ kernel/kallsyms.c | 11 +++ kernel/sys.c | 7 ++ susfs4ksu | 1 - 25 files changed, 746 insertions(+), 57 deletions(-) create mode 100644 fs/sus_su.c create mode 100644 include/linux/sus_su.h create mode 100644 include/linux/susfs_def.h delete mode 160000 susfs4ksu diff --git a/fs/Makefile b/fs/Makefile index d643ec5d9e51..24aaf15f812c 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -13,6 +13,8 @@ obj-y := open.o read_write.o file_table.o super.o \ pnode.o splice.o sync.o utimes.o \ stack.o fs_struct.o statfs.o fs_pin.o nsfs.o +obj-$(CONFIG_KSU_SUSFS) += susfs.o + ifeq ($(CONFIG_BLOCK),y) obj-y += buffer.o block_dev.o direct-io.o mpage.o else diff --git a/fs/dcache.c b/fs/dcache.c index 9b5c495c69e8..10b1ddfa60d9 100644 --- a/fs/dcache.c +++ b/fs/dcache.c @@ -39,6 +39,9 @@ #include #include #include +#ifdef CONFIG_KSU_SUSFS_SUS_PATH +#include +#endif #include "internal.h" #include "mount.h" @@ -2220,6 +2223,11 @@ seqretry: continue; if (dentry_cmp(dentry, str, hashlen_len(hashlen)) != 0) continue; +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + if (dentry->d_inode && unlikely(dentry->d_inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) { + continue; + } +#endif } *seqp = seq; return dentry; @@ -2303,6 +2311,12 @@ struct dentry *__d_lookup(const struct dentry *parent, const struct qstr *name) if (dentry->d_name.hash != hash) continue; +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + if (dentry->d_inode && unlikely(dentry->d_inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) { + continue; + } +#endif + spin_lock(&dentry->d_lock); if (dentry->d_parent != parent) goto next; diff --git a/fs/inode.c b/fs/inode.c index dfb483ee41b2..a252f34a5b66 100644 --- a/fs/inode.c +++ b/fs/inode.c @@ -23,6 +23,10 @@ #include +#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT +extern bool susfs_is_current_ksu_domain(void); +#endif + /* * Inode locking rules: * @@ -1644,6 +1648,11 @@ int generic_update_time(struct inode *inode, struct timespec *time, int flags) { int iflags = I_DIRTY_TIME; +#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT + if (susfs_is_current_ksu_domain()) { + return 0; + } +#endif if (flags & S_ATIME) inode->i_atime = *time; if (flags & S_VERSION) @@ -1668,6 +1677,12 @@ static int update_time(struct inode *inode, struct timespec *time, int flags) { int (*update_time)(struct inode *, struct timespec *, int); +#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT + if (susfs_is_current_ksu_domain()) { + return 0; + } +#endif + update_time = inode->i_op->update_time ? inode->i_op->update_time : generic_update_time; @@ -1725,6 +1740,12 @@ void touch_atime(const struct path *path) struct inode *inode = d_inode(path->dentry); struct timespec now; +#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT + if (susfs_is_current_ksu_domain()) { + return; + } +#endif + if (!__atime_needs_update(path, inode, false)) return; diff --git a/fs/namei.c b/fs/namei.c index 811de0f87202..f9decfd344ed 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -41,6 +41,9 @@ #ifdef CONFIG_FSCRYPT_SDP #include #endif +#if defined(CONFIG_KSU_SUSFS_SUS_PATH) || defined(CONFIG_KSU_SUSFS_OPEN_REDIRECT) +#include +#endif #include "internal.h" #include "mount.h" @@ -932,6 +935,12 @@ static inline int may_follow_link(struct nameidata *nd) const struct inode *parent; kuid_t puid; +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + if (nd->inode && unlikely(nd->inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) { + return -ENOENT; + } +#endif + if (!sysctl_protected_symlinks) return 0; @@ -1008,6 +1017,12 @@ static int may_linkat(struct path *link) { struct inode *inode; +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + if (link->dentry->d_inode && unlikely(link->dentry->d_inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) { + return -ENOENT; + } +#endif + if (!sysctl_protected_hardlinks) return 0; @@ -1047,6 +1062,12 @@ static int may_linkat(struct path *link) static int may_create_in_sticky(umode_t dir_mode, kuid_t dir_uid, struct inode * const inode) { +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + if (unlikely(inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) { + return -ENOENT; + } +#endif + if ((!sysctl_protected_fifos && S_ISFIFO(inode->i_mode)) || (!sysctl_protected_regular && S_ISREG(inode->i_mode)) || likely(!(dir_mode & S_ISVTX)) || @@ -1581,6 +1602,9 @@ static struct dentry *lookup_real(struct inode *dir, struct dentry *dentry, unsigned int flags) { struct dentry *old; +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + int error; +#endif /* Don't create child dentry for a dead directory. */ if (unlikely(IS_DEADDIR(dir))) { @@ -1593,6 +1617,19 @@ static struct dentry *lookup_real(struct inode *dir, struct dentry *dentry, dput(dentry); dentry = old; } +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + if (!IS_ERR(dentry) && dentry->d_inode && unlikely(dentry->d_inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) { + if ((flags & (LOOKUP_CREATE | LOOKUP_EXCL))) { + error = inode_permission(dir, MAY_WRITE | MAY_EXEC); + if (error) { + dput(dentry); + return ERR_PTR(error); + } + } + dput(dentry); + return ERR_PTR(-ENOENT); + } +#endif return dentry; } @@ -1741,6 +1778,12 @@ again: dentry = old; } } +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + if (!IS_ERR(dentry) && dentry->d_inode && unlikely(dentry->d_inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) { + dput(dentry); + return ERR_PTR(-ENOENT); + } +#endif out: inode_unlock_shared(inode); return dentry; @@ -2213,6 +2256,12 @@ OK: } return -ENOTDIR; } +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + // we deal with sus sub path here + if (nd->inode && unlikely(nd->inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) { + return 0; + } +#endif } } @@ -2392,6 +2441,12 @@ static int filename_lookup(int dfd, struct filename *name, unsigned flags, if (likely(!retval)) audit_inode(name, path->dentry, flags & LOOKUP_PARENT); restore_nameidata(); +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + if (!retval && path->dentry->d_inode && unlikely(path->dentry->d_inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) { + putname(name); + return -ENOENT; + } +#endif putname(name); return retval; } @@ -2874,6 +2929,12 @@ static int may_delete(struct vfsmount *mnt, struct inode *dir, struct dentry *vi if (IS_APPEND(dir)) return -EPERM; +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + if (unlikely(inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) { + return -ENOENT; + } +#endif + if (check_sticky(dir, inode) || IS_APPEND(inode) || IS_IMMUTABLE(inode) || IS_SWAPFILE(inode) || HAS_UNMAPPED_ID(inode)) return -EPERM; @@ -2902,8 +2963,20 @@ static int may_delete(struct vfsmount *mnt, struct inode *dir, struct dentry *vi */ static inline int may_create(struct vfsmount *mnt, struct inode *dir, struct dentry *child) { +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + int error; +#endif struct user_namespace *s_user_ns; audit_inode_child(dir, child, AUDIT_TYPE_CHILD_CREATE); +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + if (child->d_inode && unlikely(child->d_inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) { + error = inode_permission2(mnt, dir, MAY_WRITE | MAY_EXEC); + if (error) { + return error; + } + return -ENOENT; + } +#endif if (child->d_inode) return -EEXIST; if (IS_DEADDIR(dir)) @@ -3003,6 +3076,12 @@ static int may_open(struct path *path, int acc_mode, int flag) if (!inode) return -ENOENT; +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + if (unlikely(inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) { + return -ENOENT; + } +#endif + switch (inode->i_mode & S_IFMT) { case S_IFLNK: return -ELOOP; @@ -3074,7 +3153,20 @@ static inline int open_to_namei_flags(int flag) static int may_o_create(const struct path *dir, struct dentry *dentry, umode_t mode) { struct user_namespace *s_user_ns; +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + int error; + + if (dentry->d_inode && unlikely(dentry->d_inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) { + error = inode_permission2(dir->mnt, dir->dentry->d_inode, MAY_WRITE | MAY_EXEC); + if (error) { + return error; + } + return -ENOENT; + } + error = security_path_mknod(dir, dentry, mode, 0); +#else int error = security_path_mknod(dir, dentry, mode, 0); +#endif if (error) return error; @@ -3221,6 +3313,12 @@ static int lookup_open(struct nameidata *nd, struct path *path, } if (dentry->d_inode) { /* Cached positive dentry: will open in f_op->open */ +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + if (unlikely(dentry->d_inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) { + dput(dentry); + return -ENOENT; + } +#endif goto out_no_open; } @@ -3264,6 +3362,16 @@ static int lookup_open(struct nameidata *nd, struct path *path, mode, opened); if (unlikely(error == -ENOENT) && create_error) error = create_error; +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + if (!IS_ERR(dentry) && dentry->d_inode && unlikely(dentry->d_inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) { + if (create_error) { + dput(dentry); + return create_error; + } + dput(dentry); + return -ENOENT; + } +#endif return error; } @@ -3279,6 +3387,12 @@ no_open: } dput(dentry); dentry = res; +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + if (dentry->d_inode && unlikely(dentry->d_inode->i_state & INODE_STATE_SUS_PATH) && likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC)) { + dput(dentry); + return -ENOENT; + } +#endif } } @@ -3634,12 +3748,19 @@ out2: return file; } +#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT +extern struct filename* susfs_get_redirected_path(unsigned long ino); +#endif + struct file *do_filp_open(int dfd, struct filename *pathname, const struct open_flags *op) { struct nameidata nd; int flags = op->lookup_flags; struct file *filp; +#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT + struct filename *fake_pathname; +#endif set_nameidata(&nd, dfd, pathname); filp = path_openat(&nd, op, flags | LOOKUP_RCU); @@ -3647,6 +3768,25 @@ struct file *do_filp_open(int dfd, struct filename *pathname, filp = path_openat(&nd, op, flags); if (unlikely(filp == ERR_PTR(-ESTALE))) filp = path_openat(&nd, op, flags | LOOKUP_REVAL); +#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT + if (!IS_ERR(filp) && unlikely(filp->f_inode->i_state & INODE_STATE_OPEN_REDIRECT) && current_uid().val < 2000) { + fake_pathname = susfs_get_redirected_path(filp->f_inode->i_ino); + if (!IS_ERR(fake_pathname)) { + restore_nameidata(); + filp_close(filp, NULL); + // no need to do `putname(pathname);` here as it will be done by calling process + set_nameidata(&nd, dfd, fake_pathname); + filp = path_openat(&nd, op, flags | LOOKUP_RCU); + if (unlikely(filp == ERR_PTR(-ECHILD))) + filp = path_openat(&nd, op, flags); + if (unlikely(filp == ERR_PTR(-ESTALE))) + filp = path_openat(&nd, op, flags | LOOKUP_REVAL); + restore_nameidata(); + putname(fake_pathname); + return filp; + } + } +#endif restore_nameidata(); return filp; } diff --git a/fs/namespace.c b/fs/namespace.c index 6763280fd54b..14115742ec55 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -24,11 +24,31 @@ #include #include #include +#if defined(CONFIG_KSU_SUSFS_SUS_MOUNT) || defined(CONFIG_KSU_SUSFS_TRY_UMOUNT) +#include +#endif #include #include #include "pnode.h" #include "internal.h" +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +extern bool susfs_is_current_ksu_domain(void); +extern bool susfs_is_current_zygote_domain(void); +#define CL_SUSFS_COPY_MNT_NS 0x1000000 +#define DEFAULT_SUS_MNT_GROUP_ID 1000 +#endif + +#ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT +extern int susfs_auto_add_sus_bind_mount(const char *pathname, struct path *path_target); +#endif +#ifdef CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT +extern void susfs_auto_add_try_umount_for_bind_mount(struct path *path); +#endif +#ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT +extern void susfs_auto_add_sus_ksu_default_mount(const char __user *to_pathname); +#endif + #ifdef CONFIG_RKP_NS_PROT #define KDP_MOUNT_SYSTEM "/system" #define KDP_MOUNT_SYSTEM_LEN strlen(KDP_MOUNT_SYSTEM) @@ -290,6 +310,20 @@ static int mnt_alloc_vfsmount(struct mount *mnt) static void mnt_free_id(struct mount *mnt) { int id = mnt->mnt_id; +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + int orig_id; + // If mnt->mnt.susfs_orig_mnt_id is not zero, it means mnt->mnt_id is spoofed, + // so here we return the original mnt_id for being freed. + if (unlikely(mnt->mnt.susfs_orig_mnt_id)) { + orig_id = mnt->mnt.susfs_orig_mnt_id; + spin_lock(&mnt_id_lock); + ida_remove(&mnt_id_ida, orig_id); + if (mnt_id_start > orig_id) + mnt_id_start = orig_id; + spin_unlock(&mnt_id_lock); + return; + } +#endif spin_lock(&mnt_id_lock); ida_remove(&mnt_id_ida, id); if (mnt_id_start > id) @@ -297,6 +331,14 @@ static void mnt_free_id(struct mount *mnt) spin_unlock(&mnt_id_lock); } +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +static void susfs_mnt_alloc_group_id(struct mount *mnt) +{ + // Just assign the same default sus mount_group_id to mnt->mnt_group_id + mnt->mnt_group_id = DEFAULT_SUS_MNT_GROUP_ID; +} +#endif + /* * Allocate a new peer group ID * @@ -324,6 +366,14 @@ static int mnt_alloc_group_id(struct mount *mnt) void mnt_release_group_id(struct mount *mnt) { int id = mnt->mnt_group_id; +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + // If mnt->mnt_group_id >= DEFAULT_SUS_MNT_GROUP_ID, it means 'mnt' is sus mount, + // here we don't need to free the mnt_group_id and just simply return and do nothing. + if (unlikely(mnt->mnt_group_id >= DEFAULT_SUS_MNT_GROUP_ID)) { + mnt->mnt_group_id = 0; + return; + } +#endif ida_remove(&mnt_group_ida, id); if (mnt_group_start > id) mnt_group_start = id; @@ -1271,6 +1321,14 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void mnt->mnt_mountpoint = mnt->mnt.mnt_root; #endif mnt->mnt_parent = mnt; + +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + if (susfs_is_current_zygote_domain()) { + mnt->mnt.susfs_orig_mnt_id = mnt->mnt_id; + mnt->mnt_id = current->susfs_last_fake_mnt_id++; + } +#endif + lock_mount_hash(); list_add_tail(&mnt->mnt_instance, &root->d_sb->s_mounts); unlock_mount_hash(); @@ -1396,6 +1454,14 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root, mnt->mnt_mountpoint = mnt->mnt.mnt_root; #endif mnt->mnt_parent = mnt; + +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + if (susfs_is_current_zygote_domain() && !(flag & CL_SUSFS_COPY_MNT_NS)) { + mnt->mnt.susfs_orig_mnt_id = mnt->mnt_id; + mnt->mnt_id = current->susfs_last_fake_mnt_id++; + } +#endif + lock_mount_hash(); list_add_tail(&mnt->mnt_instance, &sb->s_mounts); unlock_mount_hash(); @@ -2370,6 +2436,17 @@ static int invent_group_ids(struct mount *mnt, bool recurse) { struct mount *p; +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + if (susfs_is_current_ksu_domain()) { + for (p = mnt; p; p = recurse ? next_mnt(p, mnt) : NULL) { + if (!p->mnt_group_id && !IS_MNT_SHARED(p)) { + susfs_mnt_alloc_group_id(p); + } + } + return 0; + } +#endif + for (p = mnt; p; p = recurse ? next_mnt(p, mnt) : NULL) { if (!p->mnt_group_id && !IS_MNT_SHARED(p)) { int err = mnt_alloc_group_id(p); @@ -2748,6 +2825,24 @@ static int do_loopback(struct path *path, const char *old_name, umount_tree(mnt, UMOUNT_SYNC); unlock_mount_hash(); } +#if defined(CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT) || defined(CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT) + // Check if bind mounted path should be hidden and umounted automatically. + // 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)) { + 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); +#endif + } +#if defined(CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT) +orig_flow: +#endif +#endif // #if defined(CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT) || defined(CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT) + out2: unlock_mount(mp); out: @@ -3433,6 +3528,13 @@ long do_mount(const char *dev_name, const char __user *dir_name, #else retval = do_new_mount(&path, type_page, sb_flags, mnt_flags, dev_name, data_page); +#ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT +if (!retval && (!(flags & (MS_REMOUNT | MS_BIND | MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)))) { + if (susfs_is_current_ksu_domain()) { + susfs_auto_add_sus_ksu_default_mount(dir_name); + } + } +#endif #endif dput_out: path_put(&path); @@ -3511,6 +3613,10 @@ struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns, struct mount *old; struct mount *new; int copy_flags; +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + bool is_zygote_pid = susfs_is_current_zygote_domain(); + int last_entry_mnt_id = 0; +#endif BUG_ON(!ns); @@ -3530,6 +3636,12 @@ struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns, copy_flags = CL_COPY_UNBINDABLE | CL_EXPIRE; if (user_ns != ns->user_ns) copy_flags |= CL_SHARED_TO_SLAVE | CL_UNPRIVILEGED; +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + if (is_zygote_pid) { + // Let clone_mnt() in copy_tree() know we only interested in function called by copy_mnt_ns() + copy_flags |= CL_SUSFS_COPY_MNT_NS; + } +#endif #ifdef CONFIG_RKP_NS_PROT new = copy_tree(old, old->mnt->mnt_root, copy_flags); #else @@ -3586,6 +3698,29 @@ struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns, #endif p = next_mnt(p, old); } +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + // current->susfs_last_fake_mnt_id -> to record last valid fake mnt_id to zygote pid + // q->mnt.susfs_orig_mnt_id -> original mnt_id + // q->mnt_id -> will be modified to the fake mnt_id + + // Here We are only interested in processes of which original mnt namespace belongs to zygote + // Also we just make use of existing 'q' mount pointer, no need to delcare extra mount pointer + if (is_zygote_pid) { + last_entry_mnt_id = list_first_entry(&new_ns->list, struct mount, mnt_list)->mnt_id; + list_for_each_entry(q, &new_ns->list, mnt_list) { + if (unlikely(q->mnt.mnt_root->d_inode->i_state & INODE_STATE_SUS_MOUNT)) { + continue; + } + q->mnt.susfs_orig_mnt_id = q->mnt_id; + q->mnt_id = last_entry_mnt_id++; + } + } + // Assign the 'last_entry_mnt_id' to 'current->susfs_last_fake_mnt_id' for later use. + // should be fine here assuming zygote is forking/unsharing app in one single thread. + // Or should we put a lock here? + current->susfs_last_fake_mnt_id = last_entry_mnt_id; +#endif + namespace_unlock(); if (rootmnt) @@ -4211,3 +4346,37 @@ const struct proc_ns_operations mntns_operations = { .install = mntns_install, .owner = mntns_owner, }; + +#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT +extern void susfs_try_umount_all(uid_t uid); +void susfs_run_try_umount_for_current_mnt_ns(void) { + struct mount *mnt; + struct mnt_namespace *mnt_ns; + + mnt_ns = current->nsproxy->mnt_ns; + // Lock the namespace + namespace_lock(); + list_for_each_entry(mnt, &mnt_ns->list, mnt_list) { + // Change the sus mount to be private + if (mnt->mnt.mnt_root->d_inode->i_state & INODE_STATE_SUS_MOUNT) { + change_mnt_propagation(mnt, MS_PRIVATE); + } + } + // Unlock the namespace + namespace_unlock(); + susfs_try_umount_all(current_uid().val); +} +#endif +#ifdef CONFIG_KSU_SUSFS +bool susfs_is_mnt_devname_ksu(struct path *path) { + struct mount *mnt; + + if (path && path->mnt) { + mnt = real_mount(path->mnt); + if (mnt && mnt->mnt_devname && !strcmp(mnt->mnt_devname, "KSU")) { + return true; + } + } + return false; +} +#endif \ No newline at end of file diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 146105a3042c..72ae5f9aa70d 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -115,6 +115,16 @@ static int ovl_getattr(const struct path *path, struct kstat *stat, const struct cred *old_cred; int err; +#ifdef CONFIG_KSU_SUSFS_SUS_OVERLAYFS + ovl_path_lowerdata(dentry, &realpath); + if (likely(realpath.mnt && realpath.dentry)) { + old_cred = ovl_override_creds(dentry->d_sb); + err = vfs_getattr(&realpath, stat); + ovl_revert_creds(old_cred); + return err; + } +#endif + ovl_path_real(dentry, &realpath); old_cred = ovl_override_creds(dentry->d_sb); err = vfs_getattr(&realpath, stat, request_mask, flags); diff --git a/fs/overlayfs/overlayfs.h b/fs/overlayfs/overlayfs.h index 83f22daeeeb3..01031d0159a0 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -148,6 +148,9 @@ u64 ovl_dentry_version_get(struct dentry *dentry); void ovl_dentry_version_inc(struct dentry *dentry); void ovl_path_upper(struct dentry *dentry, struct path *path); void ovl_path_lower(struct dentry *dentry, struct path *path); +#ifdef CONFIG_KSU_SUSFS_SUS_OVERLAYFS +void ovl_path_lowerdata(struct dentry *dentry, struct path *path); +#endif enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path); int ovl_path_next(int idx, struct dentry *dentry, struct path *path); struct dentry *ovl_dentry_upper(struct dentry *dentry); diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index 4fcbfd0953c0..03f0f58dcdff 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c @@ -499,7 +499,19 @@ static int ovl_dir_open(struct inode *inode, struct file *file) if (!od) return -ENOMEM; +#ifdef CONFIG_KSU_SUSFS_SUS_OVERLAYFS + ovl_path_lowerdata(file->f_path.dentry, &realpath); + if (likely(realpath.mnt && realpath.dentry)) { + // We still use '__OVL_PATH_UPPER' here which should be fine. + type = __OVL_PATH_UPPER; + goto bypass_orig_flow; + } +#endif + type = ovl_path_real(file->f_path.dentry, &realpath); +#ifdef CONFIG_KSU_SUSFS_SUS_OVERLAYFS +bypass_orig_flow: +#endif realfile = ovl_path_open(&realpath, file->f_flags); if (IS_ERR(realfile)) { kfree(od); diff --git a/fs/overlayfs/super.c b/fs/overlayfs/super.c index db6dde7f1ac8..8559bc019854 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -186,6 +186,20 @@ void ovl_path_lower(struct dentry *dentry, struct path *path) *path = oe->numlower ? oe->lowerstack[0] : (struct path) { NULL, NULL }; } +#ifdef CONFIG_KSU_SUSFS_SUS_OVERLAYFS +void ovl_path_lowerdata(struct dentry *dentry, struct path *path) +{ + struct ovl_entry *oe = dentry->d_fsdata; + + if (oe->numlower) { + path->mnt = oe->lowerstack[oe->numlower - 1].mnt; + path->dentry = oe->lowerstack[oe->numlower - 1].dentry; + } else { + *path = (struct path) { }; + } +} +#endif + int ovl_want_write(struct dentry *dentry) { struct ovl_fs *ofs = dentry->d_sb->s_fs_info; @@ -654,6 +668,18 @@ static int ovl_statfs(struct dentry *dentry, struct kstatfs *buf) struct path path; int err; +#ifdef CONFIG_KSU_SUSFS_SUS_OVERLAYFS + ovl_path_lowerdata(root_dentry, &path); + if (likely(path.mnt && path.dentry)) { + err = vfs_statfs(&path, buf); + if (!err) { + buf->f_namelen = 255; // 255 for erofs, ext2/4, f2fs + buf->f_type = path.dentry->d_sb->s_magic; + } + return err; + } +#endif + ovl_path_real(root_dentry, &path); err = vfs_statfs(&path, buf); diff --git a/fs/proc/cmdline.c b/fs/proc/cmdline.c index a54dc033962c..bfe8be3bf56d 100644 --- a/fs/proc/cmdline.c +++ b/fs/proc/cmdline.c @@ -3,6 +3,9 @@ #include #include #include +#ifdef CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG +extern int susfs_spoof_cmdline_or_bootconfig(struct seq_file *m); +#endif enum { FLAG_DELETE = 0, @@ -13,6 +16,12 @@ static char new_command_line[COMMAND_LINE_SIZE]; static int cmdline_proc_show(struct seq_file *m, void *v) { + +#ifdef CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG + if (!susfs_spoof_cmdline_or_bootconfig(m)) { + seq_putc(m, '\n'); + return 0; + } +#endif seq_puts(m, new_command_line); seq_putc(m, '\n'); return 0; diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index 2cb1097d8df5..2cd201f8f1ca 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -16,6 +16,9 @@ #include #include #include +#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT +#include +#endif #include #include @@ -375,6 +378,10 @@ static void show_vma_header_prefix(struct seq_file *m, MAJOR(dev), MINOR(dev), ino); } +#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT +extern void susfs_sus_ino_for_show_map_vma(unsigned long ino, dev_t *out_dev, unsigned long *out_ino); +#endif + static void show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid) { @@ -390,8 +397,17 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid) if (file) { struct inode *inode = file_inode(vma->vm_file); +#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT + if (unlikely(inode->i_state & INODE_STATE_SUS_KSTAT)) { + susfs_sus_ino_for_show_map_vma(inode->i_ino, &dev, &ino); + goto bypass_orig_flow; + } +#endif dev = inode->i_sb->s_dev; ino = inode->i_ino; +#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT +bypass_orig_flow: +#endif pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT; } diff --git a/fs/proc_namespace.c b/fs/proc_namespace.c index 681a12c91d33..5ec01c2a3175 100644 --- a/fs/proc_namespace.c +++ b/fs/proc_namespace.c @@ -11,10 +11,17 @@ #include #include #include "proc/internal.h" /* only for get_proc_task() in ->open() */ +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +#include +#endif #include "pnode.h" #include "internal.h" +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +extern bool susfs_is_current_ksu_domain(void); +#endif + static unsigned mounts_poll(struct file *file, poll_table *wait) { struct seq_file *m = file->private_data; @@ -99,6 +106,11 @@ static int show_vfsmnt(struct seq_file *m, struct vfsmount *mnt) struct super_block *sb = mnt_path.dentry->d_sb; int err; +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + if (unlikely((r->mnt.mnt_root->d_inode->i_state & INODE_STATE_SUS_MOUNT) && !susfs_is_current_ksu_domain())) + return 0; +#endif + if (sb->s_op->show_devname) { err = sb->s_op->show_devname(m, mnt_path.dentry); if (err) @@ -135,6 +147,11 @@ static int show_mountinfo(struct seq_file *m, struct vfsmount *mnt) struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt }; int err; +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + if (unlikely((r->mnt.mnt_root->d_inode->i_state & INODE_STATE_SUS_MOUNT) && !susfs_is_current_ksu_domain())) + return 0; +#endif + seq_printf(m, "%i %i %u:%u ", r->mnt_id, r->mnt_parent->mnt_id, MAJOR(sb->s_dev), MINOR(sb->s_dev)); if (sb->s_op->show_path) { @@ -199,6 +216,11 @@ static int show_vfsstat(struct seq_file *m, struct vfsmount *mnt) struct super_block *sb = mnt_path.dentry->d_sb; int err; +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + if (unlikely((r->mnt.mnt_root->d_inode->i_state & INODE_STATE_SUS_MOUNT) && !susfs_is_current_ksu_domain())) + return 0; +#endif + /* device */ if (sb->s_op->show_devname) { seq_puts(m, "device "); diff --git a/fs/readdir.c b/fs/readdir.c index 1059f2a9be0b..17f50d9426dc 100644 --- a/fs/readdir.c +++ b/fs/readdir.c @@ -18,9 +18,16 @@ #include #include #include +#ifdef CONFIG_KSU_SUSFS_SUS_PATH +#include +#endif #include +#ifdef CONFIG_KSU_SUSFS_SUS_PATH +extern int susfs_sus_ino_for_filldir64(unsigned long ino); +#endif + int iterate_dir(struct file *file, struct dir_context *ctx) { struct inode *inode = file_inode(file); @@ -206,6 +213,11 @@ static int filldir(struct dir_context *ctx, const char *name, int namlen, int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2, sizeof(long)); +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + if (likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC) && susfs_sus_ino_for_filldir64(ino)) { + return 0; + } +#endif buf->error = verify_dirent_name(name, namlen); if (unlikely(buf->error)) return buf->error; @@ -295,6 +307,11 @@ static int filldir64(struct dir_context *ctx, const char *name, int namlen, int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1, sizeof(u64)); +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + if (likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC) && susfs_sus_ino_for_filldir64(ino)) { + return 0; + } +#endif buf->error = verify_dirent_name(name, namlen); if (unlikely(buf->error)) return buf->error; diff --git a/fs/stat.c b/fs/stat.c index f14c68164c46..81b21789efca 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -14,10 +14,16 @@ #include #include #include - +#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT +#include +#endif #include #include +#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT +extern void susfs_sus_ino_for_generic_fillattr(unsigned long ino, struct kstat *stat); +#endif + /** * generic_fillattr - Fill in the basic attributes from the inode struct * @inode: Inode to use as the source @@ -29,6 +35,16 @@ */ void generic_fillattr(struct inode *inode, struct kstat *stat) { +#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT + if (unlikely(inode->i_state & INODE_STATE_SUS_KSTAT)) { + susfs_sus_ino_for_generic_fillattr(inode->i_ino, stat); + stat->mode = inode->i_mode; + stat->rdev = inode->i_rdev; + stat->uid = inode->i_uid; + stat->gid = inode->i_gid; + return; + } +#endif stat->dev = inode->i_sb->s_dev; stat->ino = inode->i_ino; stat->mode = inode->i_mode; diff --git a/fs/statfs.c b/fs/statfs.c index 083dc0ac9140..1365a3261efa 100644 --- a/fs/statfs.c +++ b/fs/statfs.c @@ -89,6 +89,22 @@ retry: goto retry; } } +#ifdef CONFIG_KSU_SUSFS_SUS_OVERLAYFS + /* - When mounting overlay, the f_flags are set with 'ro' and 'relatime', + * but this is an abnormal status, as when we inspect the output from mountinfo, + * we will find that all partitions set with 'ro' will have 'noatime' set as well. + * - But what is strange here is that the vfsmnt f_flags of the lowest layer has corrent f_flags set, + * and still it is always changed to 'relatime' instead of 'noatime' for the final result, + * I can't think of any other reason to explain about this, maybe the f_flags is set by its own + * filesystem implementation but not the one from overlayfs. + * - Anyway we just cannot use the retrieved f_flags from ovl_getattr() of overlayfs, + * we need to run one more check for user_statfs() and fd_statfs() by ourselves. + */ + if (unlikely((st->f_flags & ST_RDONLY) && (st->f_flags & ST_RELATIME))) { + st->f_flags &= ~ST_RELATIME; + st->f_flags |= ST_NOATIME; + } +#endif return error; } @@ -100,6 +116,12 @@ int fd_statfs(int fd, struct kstatfs *st) error = vfs_statfs(&f.file->f_path, st); fdput(f); } +#ifdef CONFIG_KSU_SUSFS_SUS_OVERLAYFS + if (unlikely((st->f_flags & ST_RDONLY) && (st->f_flags & ST_RELATIME))) { + st->f_flags &= ~ST_RELATIME; + st->f_flags |= ST_NOATIME; + } +#endif return error; } diff --git a/fs/sus_su.c b/fs/sus_su.c new file mode 100644 index 000000000000..d140468d0714 --- /dev/null +++ b/fs/sus_su.c @@ -0,0 +1,140 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_KSU_SUSFS_ENABLE_LOG +extern bool susfs_is_log_enabled __read_mostly; +#define SUSFS_LOGI(fmt, ...) if (susfs_is_log_enabled) pr_info("susfs_sus_su:[%u][%u][%s] " fmt, current_uid().val, current->pid, __func__, ##__VA_ARGS__) +#define SUSFS_LOGE(fmt, ...) if (susfs_is_log_enabled) pr_err("susfs_sus_su:[%u][%u][%s]" fmt, current_uid().val, current->pid, __func__, ##__VA_ARGS__) +#else +#define SUSFS_LOGI(fmt, ...) +#define SUSFS_LOGE(fmt, ...) +#endif + +#define FIFO_SIZE 1024 +#define MAX_DRV_NAME 255 + +static int cur_maj_dev_num = -1; +static char fifo_buffer[FIFO_SIZE]; +static struct cdev sus_su_cdev; +static const char *sus_su_token = "!@#$SU_IS_SUS$#@!-pRE6W9BKXrJr1hEKyvDq0CvWziVKbatT8yzq06fhtrEGky2tVS7Q2QTjhtMfVMGV"; +static char rand_drv_path[MAX_DRV_NAME+1] = "/dev/"; +static bool is_sus_su_enabled_before = false; + +extern bool susfs_is_allow_su(void); +extern void ksu_escape_to_root(void); + +static void gen_rand_drv_name(char *buffer, size_t min_length, size_t max_length) { + const char *symbols = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-+@#:="; + size_t symbols_length = strlen(symbols); + size_t length, i; + unsigned int rand_value; + + // Determine the random length of the string + get_random_bytes(&rand_value, sizeof(rand_value)); + length = min_length + (rand_value % (max_length - min_length + 1)); + + for (i = 0; i < length; ++i) { + get_random_bytes(&rand_value, sizeof(rand_value)); + buffer[i] = symbols[rand_value % symbols_length]; + } + buffer[length] = '\0'; // Null-terminate the string +} + +static int fifo_open(struct inode *inode, struct file *file) { + return 0; +} + +static int fifo_release(struct inode *inode, struct file *file) { + return 0; +} + +static ssize_t fifo_read(struct file *file, char __user *buf, size_t len, loff_t *offset) { + return 0; +} + +static ssize_t fifo_write(struct file *file, const char __user *buf, size_t len, loff_t *offset) { + int sus_su_token_len = strlen(sus_su_token); + + if (!susfs_is_allow_su()) { + SUSFS_LOGE("root is not allowed for uid: '%d', pid: '%d'\n", current_uid().val, current->pid); + return 0; + } + + if (copy_from_user(fifo_buffer, buf, sus_su_token_len+1)) { + SUSFS_LOGE("copy_from_user() failed, uid: '%d', pid: '%d'\n", current_uid().val, current->pid); + return 0; + } + + if (!memcmp(fifo_buffer, sus_su_token, sus_su_token_len+1)) { + SUSFS_LOGI("granting root access for uid: '%d', pid: '%d'\n", current_uid().val, current->pid); + ksu_escape_to_root(); + } else { + SUSFS_LOGI("wrong token! deny root access for uid: '%d', pid: '%d'\n", current_uid().val, current->pid); + } + memset(fifo_buffer, 0, FIFO_SIZE); + return 0; +} + +static struct file_operations fops = { + .owner = THIS_MODULE, + .open = fifo_open, + .release = fifo_release, + .read = fifo_read, + .write = fifo_write, +}; + +int sus_su_fifo_init(int *maj_dev_num, char *drv_path) { + if (cur_maj_dev_num > 0) { + SUSFS_LOGE("'%s' is already registered\n", rand_drv_path); + return -1; + } + + // generate a random driver name if it is executed for the first time + if (!is_sus_su_enabled_before) { + // min length 192, max length 248, just make sure max length doesn't exceeds 255 + gen_rand_drv_name(rand_drv_path+5, 192, 248); + } + + cur_maj_dev_num = register_chrdev(0, rand_drv_path+5, &fops); + if (cur_maj_dev_num < 0) { + SUSFS_LOGE("Failed to register character device\n"); + return -1; + } + + cdev_init(&sus_su_cdev, &fops); + if (cdev_add(&sus_su_cdev, MKDEV(cur_maj_dev_num, 0), 1) < 0) { + unregister_chrdev(cur_maj_dev_num, rand_drv_path+5); + SUSFS_LOGE("Failed to add cdev\n"); + return -1; + } + + strncpy(drv_path, rand_drv_path, strlen(rand_drv_path)); + *maj_dev_num = cur_maj_dev_num; + SUSFS_LOGI("'%s' registered with major device number %d\n", rand_drv_path, cur_maj_dev_num); + + if (!is_sus_su_enabled_before) + is_sus_su_enabled_before = true; + + return 0; +} + +int sus_su_fifo_exit(int *maj_dev_num, char *drv_path) { + if (cur_maj_dev_num < 0) { + SUSFS_LOGE("'%s' was already unregistered before\n", rand_drv_path); + return 0; + } + + cdev_del(&sus_su_cdev); + unregister_chrdev(cur_maj_dev_num, rand_drv_path+5); + cur_maj_dev_num = -1; + *maj_dev_num = cur_maj_dev_num; + strncpy(drv_path, rand_drv_path, strlen(rand_drv_path)); + SUSFS_LOGI("'%s' unregistered\n", rand_drv_path); + return 0; +} diff --git a/fs/susfs.c b/fs/susfs.c index 96947f4e033e..e82962665bd5 100644 --- a/fs/susfs.c +++ b/fs/susfs.c @@ -19,9 +19,6 @@ #endif static spinlock_t susfs_spin_lock; -#ifdef CONFIG_KSU_SUSFS_HAS_MAGIC_MOUNT -static const char *magic_mount_workdir = "/debug_ramdisk/workdir"; -#endif extern bool susfs_is_current_ksu_domain(void); #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT @@ -43,16 +40,26 @@ static DEFINE_HASHTABLE(SUS_PATH_HLIST, 10); static int susfs_update_sus_path_inode(char *target_pathname) { struct path p; struct inode *inode = NULL; + const char *dev_type; if (kern_path(target_pathname, LOOKUP_FOLLOW, &p)) { SUSFS_LOGE("Failed opening file '%s'\n", target_pathname); return 1; } - // We don't allow path of which filesystem type is "tmpfs", because its inode->i_ino is starting from 1 again, - // which will cause wrong comparison in function susfs_sus_ino_for_filldir64() - if (strcmp(p.mnt->mnt_sb->s_type->name, "tmpfs") == 0) { - SUSFS_LOGE("target_pathname: '%s' cannot be added since its filesystem is 'tmpfs'\n", target_pathname); + // - We don't allow paths of which filesystem type is "tmpfs" or "fuse". + // For tmpfs, because its starting inode->i_ino will begin with 1 again, + // so it will cause wrong comparison in function susfs_sus_ino_for_filldir64() + // For fuse, which is almost storage related, sus_path should not handle any paths of + // which filesystem is "fuse" as well, since app can write to "fuse" and lookup files via + // like binder / system API (you can see the uid is changed to 1000)/ + // - so sus_path should be applied only on read-only filesystem like "erofs" or "f2fs", but not "tmpfs" or "fuse", + // people may rely on HMA for /data isolation instead. + dev_type = p.mnt->mnt_sb->s_type->name; + if (!strcmp(dev_type, "tmpfs") || + !strcmp(dev_type, "fuse")) { + SUSFS_LOGE("target_pathname: '%s' cannot be added since its filesystem type is '%s'\n", + target_pathname, dev_type); path_put(&p); return 1; } @@ -576,7 +583,7 @@ void susfs_auto_add_try_umount_for_bind_mount(struct path *path) { } #ifdef CONFIG_KSU_SUSFS_HAS_MAGIC_MOUNT - if (strstr(dpath, magic_mount_workdir)) { + if (strstr(dpath, MAGIC_MOUNT_WORKDIR)) { is_magic_mount_path = true; } #endif @@ -602,7 +609,7 @@ void susfs_auto_add_try_umount_for_bind_mount(struct path *path) { #ifdef CONFIG_KSU_SUSFS_HAS_MAGIC_MOUNT if (is_magic_mount_path) { - strncpy(new_list->info.target_pathname, dpath + strlen(magic_mount_workdir), SUSFS_MAX_LEN_PATHNAME-1); + strncpy(new_list->info.target_pathname, dpath + strlen(MAGIC_MOUNT_WORKDIR), SUSFS_MAX_LEN_PATHNAME-1); goto out_add_to_list; } #endif diff --git a/include/linux/mount.h b/include/linux/mount.h index af2bb68be4c7..01834fe49d19 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -71,6 +71,9 @@ struct vfsmount { #endif int mnt_flags; void *data; +#ifdef CONFIG_KSU_SUSFS + u64 susfs_orig_mnt_id; +#endif }; struct file; /* forward dec */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 9e8d26c1f9a6..946d8e8a95fe 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -2185,6 +2185,10 @@ struct task_struct { atomic_t stack_refcount; #endif /* CPU-specific state of this task */ +#ifdef CONFIG_KSU_SUSFS + u64 susfs_task_state; + u64 susfs_last_fake_mnt_id; +#endif struct thread_struct thread; /* * WARNING: on x86, 'thread_struct' contains a variable-sized diff --git a/include/linux/sus_su.h b/include/linux/sus_su.h new file mode 100644 index 000000000000..98e8f3b357ac --- /dev/null +++ b/include/linux/sus_su.h @@ -0,0 +1,9 @@ +#ifndef __KSU_H_SUS_SU +#define __KSU_H_SUS_SU + +#include "../../drivers/kernelsu/core_hook.h" + +int sus_su_fifo_init(int *maj_dev_num, char *drv_path); +int sus_su_fifo_exit(int *maj_dev_num, char *drv_path); + +#endif diff --git a/include/linux/susfs.h b/include/linux/susfs.h index 5083274a9093..c3d4501ff1aa 100644 --- a/include/linux/susfs.h +++ b/include/linux/susfs.h @@ -6,6 +6,7 @@ #include #include #include +#include #define SUSFS_VERSION "v1.5.3" #if LINUX_VERSION_CODE < KERNEL_VERSION(5,0,0) @@ -14,52 +15,6 @@ #define SUSFS_VARIANT "GKI" #endif -/********/ -/* ENUM */ -/********/ -/* shared with userspace ksu_susfs tool */ -#define CMD_SUSFS_ADD_SUS_PATH 0x55550 -#define CMD_SUSFS_ADD_SUS_MOUNT 0x55560 -#define CMD_SUSFS_ADD_SUS_KSTAT 0x55570 -#define CMD_SUSFS_UPDATE_SUS_KSTAT 0x55571 -#define CMD_SUSFS_ADD_SUS_KSTAT_STATICALLY 0x55572 -#define CMD_SUSFS_ADD_TRY_UMOUNT 0x55580 -#define CMD_SUSFS_SET_UNAME 0x55590 -#define CMD_SUSFS_ENABLE_LOG 0x555a0 -#define CMD_SUSFS_SET_CMDLINE_OR_BOOTCONFIG 0x555b0 -#define CMD_SUSFS_ADD_OPEN_REDIRECT 0x555c0 -#define CMD_SUSFS_RUN_UMOUNT_FOR_CURRENT_MNT_NS 0x555d0 -#define CMD_SUSFS_SHOW_VERSION 0x555e1 -#define CMD_SUSFS_SHOW_ENABLED_FEATURES 0x555e2 -#define CMD_SUSFS_SHOW_VARIANT 0x555e3 -#define CMD_SUSFS_SHOW_SUS_SU_WORKING_MODE 0x555e4 -#define CMD_SUSFS_IS_SUS_SU_READY 0x555f0 -#define CMD_SUSFS_SUS_SU 0x60000 - -#define SUSFS_MAX_LEN_PATHNAME 256 // 256 should address many paths already unless you are doing some strange experimental stuff, then set your own desired length -#define SUSFS_FAKE_CMDLINE_OR_BOOTCONFIG_SIZE 4096 - -#define TRY_UMOUNT_DEFAULT 0 -#define TRY_UMOUNT_DETACH 1 - -#define SUS_SU_DISABLED 0 -#define SUS_SU_WITH_OVERLAY 1 /* deprecated */ -#define SUS_SU_WITH_HOOKS 2 - -/* - * inode->i_state => storing flag 'INODE_STATE_' - * mount->mnt.android_kabi_reserved4 => storing original mnt_id - * task_struct->android_kabi_reserved8 => storing last valid fake mnt_id - * user_struct->android_kabi_reserved2 => storing flag 'USER_STRUCT_KABI2_' - */ - -#define INODE_STATE_SUS_PATH 16777216 // 1 << 24 -#define INODE_STATE_SUS_MOUNT 33554432 // 1 << 25 -#define INODE_STATE_SUS_KSTAT 67108864 // 1 << 26 -#define INODE_STATE_OPEN_REDIRECT 134217728 // 1 << 27 - -#define USER_STRUCT_KABI2_NON_ROOT_USER_APP_PROFILE 16777216 // 1 << 24, for distinguishing root/no-root granted user app process - /*********/ /* MACRO */ /*********/ diff --git a/include/linux/susfs_def.h b/include/linux/susfs_def.h new file mode 100644 index 000000000000..72e285bd7b07 --- /dev/null +++ b/include/linux/susfs_def.h @@ -0,0 +1,55 @@ +#ifndef KSU_SUSFS_DEF_H +#define KSU_SUSFS_DEF_H + +#include + +/********/ +/* ENUM */ +/********/ +/* shared with userspace ksu_susfs tool */ +#define CMD_SUSFS_ADD_SUS_PATH 0x55550 +#define CMD_SUSFS_ADD_SUS_MOUNT 0x55560 +#define CMD_SUSFS_ADD_SUS_KSTAT 0x55570 +#define CMD_SUSFS_UPDATE_SUS_KSTAT 0x55571 +#define CMD_SUSFS_ADD_SUS_KSTAT_STATICALLY 0x55572 +#define CMD_SUSFS_ADD_TRY_UMOUNT 0x55580 +#define CMD_SUSFS_SET_UNAME 0x55590 +#define CMD_SUSFS_ENABLE_LOG 0x555a0 +#define CMD_SUSFS_SET_CMDLINE_OR_BOOTCONFIG 0x555b0 +#define CMD_SUSFS_ADD_OPEN_REDIRECT 0x555c0 +#define CMD_SUSFS_RUN_UMOUNT_FOR_CURRENT_MNT_NS 0x555d0 +#define CMD_SUSFS_SHOW_VERSION 0x555e1 +#define CMD_SUSFS_SHOW_ENABLED_FEATURES 0x555e2 +#define CMD_SUSFS_SHOW_VARIANT 0x555e3 +#define CMD_SUSFS_SHOW_SUS_SU_WORKING_MODE 0x555e4 +#define CMD_SUSFS_IS_SUS_SU_READY 0x555f0 +#define CMD_SUSFS_SUS_SU 0x60000 + +#define SUSFS_MAX_LEN_PATHNAME 256 // 256 should address many paths already unless you are doing some strange experimental stuff, then set your own desired length +#define SUSFS_FAKE_CMDLINE_OR_BOOTCONFIG_SIZE 4096 + +#define TRY_UMOUNT_DEFAULT 0 +#define TRY_UMOUNT_DETACH 1 + +#define SUS_SU_DISABLED 0 +#define SUS_SU_WITH_OVERLAY 1 /* deprecated */ +#define SUS_SU_WITH_HOOKS 2 + +/* + * inode->i_state => storing flag 'INODE_STATE_' + * mount->mnt.susfs_orig_mnt_id => storing original mnt_id + * task_struct->susfs_last_fake_mnt_id => storing last valid fake mnt_id + * task_struct->susfs_task_state => storing flag 'TASK_STRUCT_KABI' + */ + +#define INODE_STATE_SUS_PATH BIT(24) +#define INODE_STATE_SUS_MOUNT BIT(25) +#define INODE_STATE_SUS_KSTAT BIT(26) +#define INODE_STATE_OPEN_REDIRECT BIT(27) + +#define TASK_STRUCT_NON_ROOT_USER_APP_PROC BIT(24) + +#define MAGIC_MOUNT_WORKDIR "/debug_ramdisk/workdir" + + +#endif diff --git a/kernel/kallsyms.c b/kernel/kallsyms.c index 7e3674b17e1f..6f2bf9935810 100644 --- a/kernel/kallsyms.c +++ b/kernel/kallsyms.c @@ -678,8 +678,19 @@ static int s_show(struct seq_file *m, void *p) seq_printf(m, "%pK %c %s\t[%s]\n", (void *)iter->value, type, iter->name, iter->module_name); } else + +#ifndef CONFIG_KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS seq_printf(m, "%pK %c %s\n", (void *)iter->value, iter->type, iter->name); +#else + { + if (strstr(iter->name, "ksu_") || !strncmp(iter->name, "susfs_", 6) || !strncmp(iter->name, "ksud", 4)) { + return 0; + } + seq_printf(m, "%pK %c %s\n", (void *)iter->value, + iter->type, iter->name); + } +#endif return 0; } diff --git a/kernel/sys.c b/kernel/sys.c index 1394136442e9..0c9274fba7b0 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -1340,6 +1340,10 @@ static int override_release(char __user *release, size_t len) return ret; } +#ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME +extern void susfs_spoof_uname(struct new_utsname* tmp); +#endif + SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name) { struct new_utsname tmp; @@ -1353,6 +1357,9 @@ SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name) pr_debug("fake uname: %s/%d release=%s\n", current->comm, current->pid, tmp.release); } +#ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME + susfs_spoof_uname(&tmp); +#endif up_read(&uts_sem); if (copy_to_user(name, &tmp, sizeof(tmp))) return -EFAULT; diff --git a/susfs4ksu b/susfs4ksu deleted file mode 160000 index df4332728f0c..000000000000 --- a/susfs4ksu +++ /dev/null @@ -1 +0,0 @@ -Subproject commit df4332728f0c56842c8c0a1bc1d357f3f3c96a7c