diff --git a/KernelSU-Next b/KernelSU-Next index 81dff86d0298..e8e683642547 160000 --- a/KernelSU-Next +++ b/KernelSU-Next @@ -1 +1 @@ -Subproject commit 81dff86d0298da936024c9702f4db01fde1f7200 +Subproject commit e8e6836425478ee44d026576f8127936d383ded5 diff --git a/drivers/Makefile b/drivers/Makefile index 0a2c925ae30e..23d3a5d29846 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -193,4 +193,4 @@ obj-$(CONFIG_TRUSTY) += trusty/ obj-$(CONFIG_GNSS_SIRF) += gnsssirf/ obj-$(CONFIG_GNSS) += gnss/ -obj-y += kernelsu/ +obj-$(CONFIG_KSU) += kernelsu/ diff --git a/fs/overlayfs/inode.c b/fs/overlayfs/inode.c index 5db4237d75d8..a0cef4b6a4e0 100644 --- a/fs/overlayfs/inode.c +++ b/fs/overlayfs/inode.c @@ -69,14 +69,13 @@ int ovl_getattr(const struct path *path, struct kstat *stat, bool is_dir = S_ISDIR(dentry->d_inode->i_mode); 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, request_mask, flags); goto out; } -#endif type = 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 7ff95b4fdcf6..5a994030bc50 100644 --- a/fs/overlayfs/overlayfs.h +++ b/fs/overlayfs/overlayfs.h @@ -199,9 +199,8 @@ bool ovl_dentry_weird(struct dentry *dentry); enum ovl_path_type ovl_path_type(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); struct dentry *ovl_dentry_upper(struct dentry *dentry); struct dentry *ovl_dentry_lower(struct dentry *dentry); diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index aac4c49f21e8..35ab4236db95 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c @@ -838,19 +838,8 @@ 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 59c03f853708..9502dc0c482a 100644 --- a/fs/overlayfs/super.c +++ b/fs/overlayfs/super.c @@ -282,7 +282,7 @@ 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); @@ -292,7 +292,6 @@ static int ovl_statfs(struct dentry *dentry, struct kstatfs *buf) } return err; } -#endif ovl_path_real(root_dentry, &path); diff --git a/fs/overlayfs/util.c b/fs/overlayfs/util.c index 8746024a17ea..61d3e44be3ce 100644 --- a/fs/overlayfs/util.c +++ b/fs/overlayfs/util.c @@ -142,7 +142,7 @@ void ovl_path_lower(struct dentry *dentry, struct path *path) *path = oe->numlower ? oe->lowerstack[0] : (struct path) { }; } -#ifdef CONFIG_KSU_SUSFS_SUS_OVERLAYFS + void ovl_path_lowerdata(struct dentry *dentry, struct path *path) { struct ovl_entry *oe = dentry->d_fsdata; @@ -154,7 +154,6 @@ void ovl_path_lowerdata(struct dentry *dentry, struct path *path) *path = (struct path) { }; } } -#endif enum ovl_path_type ovl_path_real(struct dentry *dentry, struct path *path) { diff --git a/fs/stat.c b/fs/stat.c index 7f3953b9722f..220992b55b97 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -17,9 +17,7 @@ #include #include #include -#if defined(CONFIG_KSU_SUSFS_SUS_KSTAT) || defined(CONFIG_KSU_SUSFS_SUS_MOUNT) #include -#endif #include #include @@ -34,22 +32,11 @@ * operation is supplied. */ #ifdef CONFIG_KSU_SUSFS_SUS_KSTAT -extern void susfs_sus_ino_for_generic_fillattr(unsigned long ino, struct kstat *stat); +extern void susfs_generic_fillattr_spoofer(struct inode *inode, struct kstat *stat); #endif void generic_fillattr(struct inode *inode, struct kstat *stat) { -#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT - if (likely(current->susfs_task_state & TASK_STRUCT_NON_ROOT_USER_APP_PROC) && - 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; @@ -64,6 +51,10 @@ void generic_fillattr(struct inode *inode, struct kstat *stat) stat->blksize = i_blocksize(inode); stat->blocks = inode->i_blocks; +#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT + susfs_generic_fillattr_spoofer(inode, stat); +#endif + if (IS_NOATIME(inode)) stat->result_mask &= ~STATX_ATIME; if (IS_AUTOMOUNT(inode)) @@ -94,8 +85,18 @@ int vfs_getattr_nosec(const struct path *path, struct kstat *stat, request_mask &= STATX_ALL; query_flags &= KSTAT_QUERY_FLAGS; if (inode->i_op->getattr) +#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT + { + int err = inode->i_op->getattr(path, stat, request_mask, + query_flags); + if (!err) + susfs_generic_fillattr_spoofer(inode, stat); + return err; + } +#else return inode->i_op->getattr(path, stat, request_mask, query_flags); +#endif generic_fillattr(inode, stat); return 0; diff --git a/fs/sus_su.c b/fs/sus_su.c deleted file mode 100644 index d140468d0714..000000000000 --- a/fs/sus_su.c +++ /dev/null @@ -1,140 +0,0 @@ -#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 2d9576899da5..dcdce9bc5a50 100644 --- a/fs/susfs.c +++ b/fs/susfs.c @@ -8,371 +8,340 @@ #include #include #include +#include #include #include #include #include #include +#include +#include +#include +#include +#include #include +#include "fuse/fuse_i.h" #include "mount.h" -static spinlock_t susfs_spin_lock; - extern bool susfs_is_current_ksu_domain(void); -#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT -extern void ksu_try_umount(const char *mnt, bool check_mnt, int flags, uid_t uid); -#endif #ifdef CONFIG_KSU_SUSFS_ENABLE_LOG bool susfs_is_log_enabled __read_mostly = true; -#define SUSFS_LOGI(fmt, ...) if (susfs_is_log_enabled) pr_info("susfs:[%u][%d][%s] " fmt, current_uid().val, current->pid, __func__, ##__VA_ARGS__) -#define SUSFS_LOGE(fmt, ...) if (susfs_is_log_enabled) pr_err("susfs:[%u][%d][%s]" fmt, current_uid().val, current->pid, __func__, ##__VA_ARGS__) +#define SUSFS_LOGI(fmt, ...) if (READ_ONCE(susfs_is_log_enabled)) pr_info("susfs:[%u][%d][%s] " fmt, current_uid().val, current->pid, __func__, ##__VA_ARGS__) +#define SUSFS_LOGE(fmt, ...) if (READ_ONCE(susfs_is_log_enabled)) pr_err("susfs:[%u][%d][%s]" fmt, current_uid().val, current->pid, __func__, ##__VA_ARGS__) #else #define SUSFS_LOGI(fmt, ...) #define SUSFS_LOGE(fmt, ...) #endif -/* sus_path */ -#ifdef CONFIG_KSU_SUSFS_SUS_PATH -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 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; - } - - inode = d_inode(p.dentry); - if (!inode) { - SUSFS_LOGE("inode is NULL\n"); - path_put(&p); - return 1; - } - - if (!(inode->i_state & INODE_STATE_SUS_PATH)) { - spin_lock(&inode->i_lock); - inode->i_state |= INODE_STATE_SUS_PATH; - spin_unlock(&inode->i_lock); - } - path_put(&p); - return 0; +bool susfs_starts_with(const char *str, const char *prefix) { + while (*prefix) { + if (*str++ != *prefix++) + return false; + } + return true; } -int susfs_add_sus_path(struct st_susfs_sus_path* __user user_info) { - struct st_susfs_sus_path info; - struct st_susfs_sus_path_hlist *new_entry, *tmp_entry; - struct hlist_node *tmp_node; - int bkt; - bool update_hlist = false; - - if (copy_from_user(&info, user_info, sizeof(info))) { - SUSFS_LOGE("failed copying from userspace\n"); - return 1; - } - - spin_lock(&susfs_spin_lock); - hash_for_each_safe(SUS_PATH_HLIST, bkt, tmp_node, tmp_entry, node) { - if (!strcmp(tmp_entry->target_pathname, info.target_pathname)) { - hash_del(&tmp_entry->node); - kfree(tmp_entry); - update_hlist = true; - break; - } - } - spin_unlock(&susfs_spin_lock); - - new_entry = kmalloc(sizeof(struct st_susfs_sus_path_hlist), GFP_KERNEL); - if (!new_entry) { - SUSFS_LOGE("no enough memory\n"); - return 1; - } - - new_entry->target_ino = info.target_ino; - strncpy(new_entry->target_pathname, info.target_pathname, SUSFS_MAX_LEN_PATHNAME-1); - if (susfs_update_sus_path_inode(new_entry->target_pathname)) { - kfree(new_entry); - return 1; - } - spin_lock(&susfs_spin_lock); - hash_add(SUS_PATH_HLIST, &new_entry->node, info.target_ino); - if (update_hlist) { - SUSFS_LOGI("target_ino: '%lu', target_pathname: '%s' is successfully updated to SUS_PATH_HLIST\n", - new_entry->target_ino, new_entry->target_pathname); - } else { - SUSFS_LOGI("target_ino: '%lu', target_pathname: '%s' is successfully added to SUS_PATH_HLIST\n", - new_entry->target_ino, new_entry->target_pathname); - } - spin_unlock(&susfs_spin_lock); - return 0; -} - -int susfs_sus_ino_for_filldir64(unsigned long ino) { - struct st_susfs_sus_path_hlist *entry; +#ifndef FUSE_SUPER_MAGIC +#define FUSE_SUPER_MAGIC 0x65735546 +#endif - hash_for_each_possible(SUS_PATH_HLIST, entry, node, ino) { - if (entry->target_ino == ino) - return 1; - } - return 0; -} -#endif // #ifdef CONFIG_KSU_SUSFS_SUS_PATH +/* sus_path */ +#ifdef CONFIG_KSU_SUSFS_SUS_PATH +DEFINE_STATIC_SRCU(susfs_srcu_sus_path_loop); +static DEFINE_SPINLOCK(susfs_spin_lock_sus_path); +static LIST_HEAD(LH_SUS_PATH_LOOP); +const struct qstr susfs_fake_qstr_name = QSTR_INIT("..5.u.S", 7); // used to re-test the dcache lookup, make sure you don't have file named like this!! -/* sus_mount */ -#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT -static LIST_HEAD(LH_SUS_MOUNT); -static void susfs_update_sus_mount_inode(char *target_pathname) { - struct mount *mnt = NULL; - struct path p; +void susfs_add_sus_path(void __user **user_info) { + struct st_susfs_sus_path info = {0}; + struct path path; struct inode *inode = NULL; - int err = 0; + struct fuse_inode *fi = NULL; - err = kern_path(target_pathname, LOOKUP_FOLLOW, &p); - if (err) { - SUSFS_LOGE("Failed opening file '%s'\n", target_pathname); - return; + if (copy_from_user(&info, (struct st_susfs_sus_path __user*)*user_info, sizeof(info))) { + info.err = -EFAULT; + goto out_copy_to_user; } - /* It is important to check if the mount has a legit peer group id, if so we cannot add them to sus_mount, - * since there are chances that the mount is a legit mountpoint, and it can be misued by other susfs functions in future. - * And by doing this it won't affect the sus_mount check as other susfs functions check by mnt->mnt_id - * instead of INODE_STATE_SUS_MOUNT. - */ - mnt = real_mount(p.mnt); - if (mnt->mnt_group_id > 0 && // 0 means no peer group - mnt->mnt_group_id < DEFAULT_SUS_MNT_GROUP_ID) { - SUSFS_LOGE("skip setting SUS_MOUNT inode state for path '%s' since its source mount has a legit peer group id\n", target_pathname); - return; + info.err = kern_path(info.target_pathname, LOOKUP_FOLLOW, &path); + if (info.err) { + SUSFS_LOGE("failed opening file '%s'\n", info.target_pathname); + goto out_copy_to_user; } - inode = d_inode(p.dentry); + inode = d_backing_inode(path.dentry); if (!inode) { - path_put(&p); SUSFS_LOGE("inode is NULL\n"); - return; + info.err = -ENOENT; + goto out_path_put_path; } - if (!(inode->i_state & INODE_STATE_SUS_MOUNT)) { - spin_lock(&inode->i_lock); - inode->i_state |= INODE_STATE_SUS_MOUNT; - spin_unlock(&inode->i_lock); + if (inode->i_sb->s_magic == FUSE_SUPER_MAGIC) { + fi = get_fuse_inode(inode); + if (!fi) { + SUSFS_LOGE("fi is NULL\n"); + info.err = -ENOENT; + goto out_path_put_path; + } + set_bit(AS_FLAGS_SUS_PATH, &fi->inode.i_state); + SUSFS_LOGI("flagged AS_FLAGS_SUS_PATH on pathname: '%s', fi->nodeid: %llu, fi->inode.i_ino: %lu, fi->inode.i_state: 0x%lx\n", + info.target_pathname, fi->nodeid, fi->inode.i_ino, fi->inode.i_state); + info.err = 0; + goto out_path_put_path; + } + + set_bit(AS_FLAGS_SUS_PATH, &inode->i_state); + SUSFS_LOGI("flagged AS_FLAGS_SUS_PATH on pathname: '%s', ino: '%lu', inode->i_state: 0x%lx\n", + info.target_pathname, inode->i_ino, inode->i_state); + info.err = 0; +out_path_put_path: + path_put(&path); +out_copy_to_user: + if (copy_to_user(&((struct st_susfs_sus_path __user*)*user_info)->err, &info.err, sizeof(info.err))) { + info.err = -EFAULT; } - path_put(&p); + SUSFS_LOGI("CMD_SUSFS_ADD_SUS_PATH -> ret: %d\n", info.err); } -int susfs_add_sus_mount(struct st_susfs_sus_mount* __user user_info) { - struct st_susfs_sus_mount_list *cursor = NULL, *temp = NULL; - struct st_susfs_sus_mount_list *new_list = NULL; - struct st_susfs_sus_mount info; +void susfs_add_sus_path_loop(void __user **user_info) { + struct st_susfs_sus_path_list *new_list = NULL; + struct st_susfs_sus_path info = {0}; - if (copy_from_user(&info, user_info, sizeof(info))) { - SUSFS_LOGE("failed copying from userspace\n"); - return 1; + if (copy_from_user(&info, (struct st_susfs_sus_path __user*)*user_info, sizeof(info))) { + info.err = -EFAULT; + goto out_copy_to_user; } -#if defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_COMPAT_STAT64) -#ifdef CONFIG_MIPS - info.target_dev = new_decode_dev(info.target_dev); -#else - info.target_dev = huge_decode_dev(info.target_dev); -#endif /* CONFIG_MIPS */ -#else - info.target_dev = old_decode_dev(info.target_dev); -#endif /* defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_COMPAT_STAT64) */ - - list_for_each_entry_safe(cursor, temp, &LH_SUS_MOUNT, list) { - if (unlikely(!strcmp(cursor->info.target_pathname, info.target_pathname))) { - spin_lock(&susfs_spin_lock); - memcpy(&cursor->info, &info, sizeof(info)); - susfs_update_sus_mount_inode(cursor->info.target_pathname); - SUSFS_LOGI("target_pathname: '%s', target_dev: '%lu', is successfully updated to LH_SUS_MOUNT\n", - cursor->info.target_pathname, cursor->info.target_dev); - spin_unlock(&susfs_spin_lock); - return 0; - } + if (*info.target_pathname == '\0') { + SUSFS_LOGE("target_pathname cannot be empty\n"); + info.err = -EINVAL; + goto out_copy_to_user; } - new_list = kmalloc(sizeof(struct st_susfs_sus_mount_list), GFP_KERNEL); + new_list = kzalloc(sizeof(struct st_susfs_sus_path_list), GFP_KERNEL); if (!new_list) { - SUSFS_LOGE("no enough memory\n"); - return 1; + info.err = -ENOMEM; + goto out_copy_to_user; } - - memcpy(&new_list->info, &info, sizeof(info)); - susfs_update_sus_mount_inode(new_list->info.target_pathname); - + strncpy(new_list->info.target_pathname, info.target_pathname, SUSFS_MAX_LEN_PATHNAME - 1); + strncpy(new_list->target_pathname, info.target_pathname, SUSFS_MAX_LEN_PATHNAME - 1); INIT_LIST_HEAD(&new_list->list); - spin_lock(&susfs_spin_lock); - list_add_tail(&new_list->list, &LH_SUS_MOUNT); - SUSFS_LOGI("target_pathname: '%s', target_dev: '%lu', is successfully added to LH_SUS_MOUNT\n", - new_list->info.target_pathname, new_list->info.target_dev); - spin_unlock(&susfs_spin_lock); - return 0; + spin_lock(&susfs_spin_lock_sus_path); + list_add_tail_rcu(&new_list->list, &LH_SUS_PATH_LOOP); + spin_unlock(&susfs_spin_lock_sus_path); + SUSFS_LOGI("target_pathname: '%s', is successfully added to LH_SUS_PATH_LOOP\n", new_list->target_pathname); + info.err = 0; +out_copy_to_user: + if (copy_to_user(&((struct st_susfs_sus_path __user*)*user_info)->err, &info.err, sizeof(info.err))) { + info.err = -EFAULT; + } + SUSFS_LOGI("CMD_SUSFS_ADD_SUS_PATH_LOOP -> ret: %d\n", info.err); } -#ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT -int susfs_auto_add_sus_bind_mount(const char *pathname, struct path *path_target) { - struct mount *mnt; +void susfs_run_sus_path_loop(void) { + struct st_susfs_sus_path_list *cursor = NULL; + struct path path; struct inode *inode; - - mnt = real_mount(path_target->mnt); - if (mnt->mnt_group_id > 0 && // 0 means no peer group - mnt->mnt_group_id < DEFAULT_SUS_MNT_GROUP_ID) { - SUSFS_LOGE("skip setting SUS_MOUNT inode state for path '%s' since its source mount has a legit peer group id\n", pathname); - // return 0 here as we still want it to be added to try_umount list - return 0; - } - inode = path_target->dentry->d_inode; - if (!inode) return 1; - if (!(inode->i_state & INODE_STATE_SUS_MOUNT)) { - spin_lock(&inode->i_lock); - inode->i_state |= INODE_STATE_SUS_MOUNT; - spin_unlock(&inode->i_lock); - SUSFS_LOGI("set SUS_MOUNT inode state for source bind mount path '%s'\n", pathname); + struct fuse_inode *fi = NULL; + int srcu_idx = srcu_read_lock(&susfs_srcu_sus_path_loop); + + list_for_each_entry_rcu(cursor, &LH_SUS_PATH_LOOP, list) { + if (!kern_path(cursor->target_pathname, 0, &path)) + { + inode = d_backing_inode(path.dentry); + if (!inode) { + SUSFS_LOGE("inode is NULL\n"); + path_put(&path); + continue; + } + if (inode->i_sb->s_magic == FUSE_SUPER_MAGIC) { + fi = get_fuse_inode(inode); + if (!fi) { + SUSFS_LOGE("fi is NULL\n"); + path_put(&path); + continue; + } + set_bit(AS_FLAGS_SUS_PATH, &fi->inode.i_state); + SUSFS_LOGI("re-flag AS_FLAGS_SUS_PATH on path '%s', fi->inode.i_ino: '%lu', fi->inode.i_state: 0x%lx\n", + cursor->target_pathname, fi->inode.i_ino, fi->inode.i_state); + } else { + set_bit(AS_FLAGS_SUS_PATH, &inode->i_state); + SUSFS_LOGI("re-flag AS_FLAGS_SUS_PATH on path '%s', inode->i_ino: '%lu', inode->i_state: 0x%lx\n", + cursor->target_pathname, inode->i_ino, inode->i_state); + } + path_put(&path); + } } - return 0; + srcu_read_unlock(&susfs_srcu_sus_path_loop, srcu_idx); } -#endif // #ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT -#ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT -void susfs_auto_add_sus_ksu_default_mount(const char __user *to_pathname) { - char *pathname = NULL; - struct path path; - struct inode *inode; +static inline bool is_i_uid_not_allowed(uid_t i_uid) { + return likely(current_uid().val != i_uid); +} - pathname = kmalloc(SUSFS_MAX_LEN_PATHNAME, GFP_KERNEL); - if (!pathname) { - SUSFS_LOGE("no enough memory\n"); - return; - } - // Here we need to re-retrieve the struct path as we want the new struct path, not the old one - if (strncpy_from_user(pathname, to_pathname, SUSFS_MAX_LEN_PATHNAME-1) < 0) { - SUSFS_LOGE("strncpy_from_user()\n"); - goto out_free_pathname; - return; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0) +bool susfs_is_inode_sus_path(struct mnt_idmap* idmap, struct inode *inode) +#else +bool susfs_is_inode_sus_path(struct inode *inode) +#endif +{ + struct fuse_inode *fi = NULL; + if (!susfs_is_current_proc_umounted_app()) { + return false; + } + if (inode->i_sb->s_magic == FUSE_SUPER_MAGIC) { + fi = get_fuse_inode(inode); + if (!fi) { + SUSFS_LOGE("fi is NULL\n"); + return false; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0) + if (unlikely(test_bit(AS_FLAGS_SUS_PATH, &fi->inode.i_state) && + is_i_uid_not_allowed(i_uid_into_vfsuid(idmap, &fi->inode).val))) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) + if (unlikely(test_bit(AS_FLAGS_SUS_PATH, &fi->inode.i_state) && + is_i_uid_not_allowed(i_uid_into_mnt(i_user_ns(&fi->inode), &fi->inode).val))) +#else + if (unlikely(test_bit(AS_FLAGS_SUS_PATH, &fi->inode.i_state) && + is_i_uid_not_allowed(fi->inode.i_uid.val))) +#endif + { + SUSFS_LOGI("hiding path with ino '%lu'\n", inode->i_ino); + return true; + } + return false; + } +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0) + if (unlikely(test_bit(AS_FLAGS_SUS_PATH, &inode->i_state) && + is_i_uid_not_allowed(i_uid_into_vfsuid(idmap, inode).val))) +#elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) + if (unlikely(test_bit(AS_FLAGS_SUS_PATH, &inode->i_state) && + is_i_uid_not_allowed(i_uid_into_mnt(i_user_ns(inode), inode).val))) +#else + if (unlikely(test_bit(AS_FLAGS_SUS_PATH, &inode->i_state) && + is_i_uid_not_allowed(inode->i_uid.val))) +#endif + { + SUSFS_LOGI("hiding path with ino '%lu'\n", inode->i_ino); + return true; } - if ((!strncmp(pathname, "/data/adb/modules", 17) || - !strncmp(pathname, "/debug_ramdisk", 14) || - !strncmp(pathname, "/system", 7) || - !strncmp(pathname, "/system_ext", 11) || - !strncmp(pathname, "/vendor", 7) || - !strncmp(pathname, "/product", 8) || - !strncmp(pathname, "/odm", 4)) && - !kern_path(pathname, LOOKUP_FOLLOW, &path)) { - goto set_inode_sus_mount; - } - goto out_free_pathname; -set_inode_sus_mount: - inode = path.dentry->d_inode; - if (!inode) { - goto out_path_put; - return; + return false; +} +#endif // #ifdef CONFIG_KSU_SUSFS_SUS_PATH + +/* sus_mount */ +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT +bool susfs_hide_sus_mnts_for_non_su_procs = true; // hide sus mounts for all processes by default + +void susfs_set_hide_sus_mnts_for_non_su_procs(void __user **user_info) { + struct st_susfs_hide_sus_mnts_for_non_su_procs info = {0}; + + if (copy_from_user(&info, (struct st_susfs_hide_sus_mnts_for_non_su_procs __user*)*user_info, sizeof(info))) { + info.err = -EFAULT; + goto out_copy_to_user; } - if (!(inode->i_state & INODE_STATE_SUS_MOUNT)) { - spin_lock(&inode->i_lock); - inode->i_state |= INODE_STATE_SUS_MOUNT; - spin_unlock(&inode->i_lock); - SUSFS_LOGI("set SUS_MOUNT inode state for default KSU mount path '%s'\n", pathname); + + WRITE_ONCE(susfs_hide_sus_mnts_for_non_su_procs, info.enabled); + SUSFS_LOGI("susfs_hide_sus_mnts_for_non_su_procs: %d\n", info.enabled); + info.err = 0; +out_copy_to_user: + if (copy_to_user(&((struct st_susfs_hide_sus_mnts_for_non_su_procs __user*)*user_info)->err, &info.err, sizeof(info.err))) { + info.err = -EFAULT; } -out_path_put: - path_put(&path); -out_free_pathname: - kfree(pathname); + SUSFS_LOGI("CMD_SUSFS_hide_sus_mnts_for_non_su_procs -> ret: %d\n", info.err); } -#endif // #ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT #endif // #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT /* sus_kstat */ #ifdef CONFIG_KSU_SUSFS_SUS_KSTAT +static DEFINE_SPINLOCK(susfs_spin_lock_sus_kstat); static DEFINE_HASHTABLE(SUS_KSTAT_HLIST, 10); -static int susfs_update_sus_kstat_inode(char *target_pathname) { - struct path p; +static int susfs_mark_inode_sus_kstat(char *target_pathname, struct st_susfs_sus_kstat_hlist *new_entry) { + struct path path; struct inode *inode = NULL; + struct fuse_inode *fi = NULL; int err = 0; - err = kern_path(target_pathname, LOOKUP_FOLLOW, &p); + err = kern_path(target_pathname, 0, &path); if (err) { - 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); - path_put(&p); - return 1; + SUSFS_LOGE("failed opening file '%s'\n", target_pathname); + return err; } - inode = d_inode(p.dentry); + inode = d_backing_inode(path.dentry); if (!inode) { - path_put(&p); SUSFS_LOGE("inode is NULL\n"); - return 1; + err = -ENOENT; + goto out_path_put_path; } - if (!(inode->i_state & INODE_STATE_SUS_KSTAT)) { - spin_lock(&inode->i_lock); - inode->i_state |= INODE_STATE_SUS_KSTAT; - spin_unlock(&inode->i_lock); - } - path_put(&p); + if (inode->i_sb->s_magic == FUSE_SUPER_MAGIC) { + fi = get_fuse_inode(inode); + if (!fi) { + SUSFS_LOGE("fi is NULL\n"); + err = -ENOENT; + goto out_path_put_path; + } + set_bit(AS_FLAGS_SUS_KSTAT, &fi->inode.i_state); + new_entry->is_fuse = true; + new_entry->target_dev = fi->inode.i_sb->s_dev; + SUSFS_LOGI("flagged AS_FLAGS_SUS_KSTAT on pathname: '%s', is_fuse: %d, fi->inode.i_sb->s_dev: %u, fi->nodeid: %llu, fi->inode.i_ino: %lu, fi->inode.i_state: 0x%lx\n", + target_pathname, new_entry->is_fuse, fi->inode.i_sb->s_dev, fi->nodeid, fi->inode.i_ino, fi->inode.i_state); + err = 0; + goto out_path_put_path; + } + + set_bit(AS_FLAGS_SUS_KSTAT, &inode->i_state); + new_entry->is_fuse = false; + new_entry->target_dev = inode->i_sb->s_dev; + SUSFS_LOGI("flagged AS_FLAGS_SUS_KSTAT on pathname: '%s', is_fuse: %d, inode->i_sb->s_dev: %u, inode->i_ino: %lu, inode->i_state: 0x%lx\n", + target_pathname, new_entry->is_fuse, inode->i_sb->s_dev, inode->i_ino, inode->i_state); + +out_path_put_path: + path_put(&path); return 0; } -int susfs_add_sus_kstat(struct st_susfs_sus_kstat* __user user_info) { - struct st_susfs_sus_kstat info; +void susfs_add_sus_kstat(void __user **user_info) { + struct st_susfs_sus_kstat info = {0}; struct st_susfs_sus_kstat_hlist *new_entry, *tmp_entry; - struct hlist_node *tmp_node; - int bkt; - bool update_hlist = false; - - if (copy_from_user(&info, user_info, sizeof(info))) { - SUSFS_LOGE("failed copying from userspace\n"); - return 1; - } - if (strlen(info.target_pathname) == 0) { - SUSFS_LOGE("target_pathname is an empty string\n"); - return 1; + if (copy_from_user(&info, (struct st_susfs_sus_kstat __user*)*user_info, sizeof(info))) { + info.err = -EFAULT; + goto out_copy_to_user; } - spin_lock(&susfs_spin_lock); - hash_for_each_safe(SUS_KSTAT_HLIST, bkt, tmp_node, tmp_entry, node) { - if (!strcmp(tmp_entry->info.target_pathname, info.target_pathname)) { - hash_del(&tmp_entry->node); - kfree(tmp_entry); - update_hlist = true; - break; - } + if (*info.target_pathname == '\0') { + info.err = -EINVAL; + goto out_copy_to_user; } - spin_unlock(&susfs_spin_lock); - new_entry = kmalloc(sizeof(struct st_susfs_sus_kstat_hlist), GFP_KERNEL); + new_entry = kzalloc(sizeof(struct st_susfs_sus_kstat_hlist), GFP_KERNEL); if (!new_entry) { - SUSFS_LOGE("no enough memory\n"); - return 1; + info.err = -ENOMEM; + goto out_copy_to_user; + } + + // If it is added statically, check for duplicated entry, and remove it first if so + if (info.is_statically) { + spin_lock(&susfs_spin_lock_sus_kstat); + hash_for_each_possible(SUS_KSTAT_HLIST, tmp_entry, node, info.target_ino) { + if (!strcmp(tmp_entry->info.target_pathname, info.target_pathname)) { + memcpy(&new_entry->info, &tmp_entry->info, sizeof(tmp_entry->info)); + new_entry->target_ino = info.target_ino; + new_entry->info.target_ino = info.target_ino; + hash_del_rcu(&tmp_entry->node); + spin_unlock(&susfs_spin_lock_sus_kstat); + synchronize_rcu(); + kfree(tmp_entry); + goto out_add_new_entry; + } + } + spin_unlock(&susfs_spin_lock_sus_kstat); } +out_add_new_entry: #if defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_COMPAT_STAT64) #ifdef CONFIG_MIPS info.spoofed_dev = new_decode_dev(info.spoofed_dev); @@ -386,288 +355,289 @@ int susfs_add_sus_kstat(struct st_susfs_sus_kstat* __user user_info) { new_entry->target_ino = info.target_ino; memcpy(&new_entry->info, &info, sizeof(info)); - if (susfs_update_sus_kstat_inode(new_entry->info.target_pathname)) { + info.err = susfs_mark_inode_sus_kstat(new_entry->info.target_pathname, new_entry); + if (info.err) { kfree(new_entry); - return 1; + goto out_copy_to_user; } - spin_lock(&susfs_spin_lock); - hash_add(SUS_KSTAT_HLIST, &new_entry->node, info.target_ino); + spin_lock(&susfs_spin_lock_sus_kstat); #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0) - if (update_hlist) { - SUSFS_LOGI("is_statically: '%d', target_ino: '%lu', target_pathname: '%s', spoofed_ino: '%lu', spoofed_dev: '%lu', spoofed_nlink: '%u', spoofed_size: '%llu', spoofed_atime_tv_sec: '%ld', spoofed_mtime_tv_sec: '%ld', spoofed_ctime_tv_sec: '%ld', spoofed_atime_tv_nsec: '%ld', spoofed_mtime_tv_nsec: '%ld', spoofed_ctime_tv_nsec: '%ld', spoofed_blksize: '%lu', spoofed_blocks: '%llu', is successfully added to SUS_KSTAT_HLIST\n", - new_entry->info.is_statically, new_entry->info.target_ino, new_entry->info.target_pathname, - new_entry->info.spoofed_ino, new_entry->info.spoofed_dev, - new_entry->info.spoofed_nlink, new_entry->info.spoofed_size, - new_entry->info.spoofed_atime_tv_sec, new_entry->info.spoofed_mtime_tv_sec, new_entry->info.spoofed_ctime_tv_sec, - new_entry->info.spoofed_atime_tv_nsec, new_entry->info.spoofed_mtime_tv_nsec, new_entry->info.spoofed_ctime_tv_nsec, - new_entry->info.spoofed_blksize, new_entry->info.spoofed_blocks); - } else { - SUSFS_LOGI("is_statically: '%d', target_ino: '%lu', target_pathname: '%s', spoofed_ino: '%lu', spoofed_dev: '%lu', spoofed_nlink: '%u', spoofed_size: '%llu', spoofed_atime_tv_sec: '%ld', spoofed_mtime_tv_sec: '%ld', spoofed_ctime_tv_sec: '%ld', spoofed_atime_tv_nsec: '%ld', spoofed_mtime_tv_nsec: '%ld', spoofed_ctime_tv_nsec: '%ld', spoofed_blksize: '%lu', spoofed_blocks: '%llu', is successfully updated to SUS_KSTAT_HLIST\n", - new_entry->info.is_statically, new_entry->info.target_ino, new_entry->info.target_pathname, - new_entry->info.spoofed_ino, new_entry->info.spoofed_dev, - new_entry->info.spoofed_nlink, new_entry->info.spoofed_size, - new_entry->info.spoofed_atime_tv_sec, new_entry->info.spoofed_mtime_tv_sec, new_entry->info.spoofed_ctime_tv_sec, - new_entry->info.spoofed_atime_tv_nsec, new_entry->info.spoofed_mtime_tv_nsec, new_entry->info.spoofed_ctime_tv_nsec, - new_entry->info.spoofed_blksize, new_entry->info.spoofed_blocks); - } + SUSFS_LOGI("is_fuse: %d, is_statically: '%d', target_ino: '%lu', target_pathname: '%s', spoofed_ino: '%lu', spoofed_dev: '%lu', spoofed_nlink: '%u', spoofed_size: '%llu', spoofed_atime_tv_sec: '%ld', spoofed_mtime_tv_sec: '%ld', spoofed_ctime_tv_sec: '%ld', spoofed_atime_tv_nsec: '%ld', spoofed_mtime_tv_nsec: '%ld', spoofed_ctime_tv_nsec: '%ld', spoofed_blksize: '%lu', spoofed_blocks: '%llu', is successfully added to SUS_KSTAT_HLIST\n", + new_entry->is_fuse, + new_entry->info.is_statically, new_entry->info.target_ino, new_entry->info.target_pathname, + new_entry->info.spoofed_ino, new_entry->info.spoofed_dev, + new_entry->info.spoofed_nlink, new_entry->info.spoofed_size, + new_entry->info.spoofed_atime_tv_sec, new_entry->info.spoofed_mtime_tv_sec, new_entry->info.spoofed_ctime_tv_sec, + new_entry->info.spoofed_atime_tv_nsec, new_entry->info.spoofed_mtime_tv_nsec, new_entry->info.spoofed_ctime_tv_nsec, + new_entry->info.spoofed_blksize, new_entry->info.spoofed_blocks); #else - if (update_hlist) { - SUSFS_LOGI("is_statically: '%d', target_ino: '%lu', target_pathname: '%s', spoofed_ino: '%lu', spoofed_dev: '%lu', spoofed_nlink: '%u', spoofed_size: '%u', spoofed_atime_tv_sec: '%ld', spoofed_mtime_tv_sec: '%ld', spoofed_ctime_tv_sec: '%ld', spoofed_atime_tv_nsec: '%ld', spoofed_mtime_tv_nsec: '%ld', spoofed_ctime_tv_nsec: '%ld', spoofed_blksize: '%lu', spoofed_blocks: '%llu', is successfully added to SUS_KSTAT_HLIST\n", - new_entry->info.is_statically, new_entry->info.target_ino, new_entry->info.target_pathname, - new_entry->info.spoofed_ino, new_entry->info.spoofed_dev, - new_entry->info.spoofed_nlink, new_entry->info.spoofed_size, - new_entry->info.spoofed_atime_tv_sec, new_entry->info.spoofed_mtime_tv_sec, new_entry->info.spoofed_ctime_tv_sec, - new_entry->info.spoofed_atime_tv_nsec, new_entry->info.spoofed_mtime_tv_nsec, new_entry->info.spoofed_ctime_tv_nsec, - new_entry->info.spoofed_blksize, new_entry->info.spoofed_blocks); + SUSFS_LOGI("is_fuse: %d, is_statically: '%d', target_ino: '%lu', target_pathname: '%s', spoofed_ino: '%lu', spoofed_dev: '%lu', spoofed_nlink: '%u', spoofed_size: '%u', spoofed_atime_tv_sec: '%ld', spoofed_mtime_tv_sec: '%ld', spoofed_ctime_tv_sec: '%ld', spoofed_atime_tv_nsec: '%ld', spoofed_mtime_tv_nsec: '%ld', spoofed_ctime_tv_nsec: '%ld', spoofed_blksize: '%lu', spoofed_blocks: '%llu', is successfully added to SUS_KSTAT_HLIST\n", + new_entry->is_fuse, + new_entry->info.is_statically, new_entry->info.target_ino, new_entry->info.target_pathname, + new_entry->info.spoofed_ino, new_entry->info.spoofed_dev, + new_entry->info.spoofed_nlink, new_entry->info.spoofed_size, + new_entry->info.spoofed_atime_tv_sec, new_entry->info.spoofed_mtime_tv_sec, new_entry->info.spoofed_ctime_tv_sec, + new_entry->info.spoofed_atime_tv_nsec, new_entry->info.spoofed_mtime_tv_nsec, new_entry->info.spoofed_ctime_tv_nsec, + new_entry->info.spoofed_blksize, new_entry->info.spoofed_blocks); +#endif + hash_add_rcu(SUS_KSTAT_HLIST, &new_entry->node, info.target_ino); + spin_unlock(&susfs_spin_lock_sus_kstat); + info.err = 0; +out_copy_to_user: + if (copy_to_user(&((struct st_susfs_sus_kstat __user*)*user_info)->err, &info.err, sizeof(info.err))) { + info.err = -EFAULT; + } + if (!info.is_statically) { + SUSFS_LOGI("CMD_SUSFS_ADD_SUS_KSTAT -> ret: %d\n", info.err); } else { - SUSFS_LOGI("is_statically: '%d', target_ino: '%lu', target_pathname: '%s', spoofed_ino: '%lu', spoofed_dev: '%lu', spoofed_nlink: '%u', spoofed_size: '%u', spoofed_atime_tv_sec: '%ld', spoofed_mtime_tv_sec: '%ld', spoofed_ctime_tv_sec: '%ld', spoofed_atime_tv_nsec: '%ld', spoofed_mtime_tv_nsec: '%ld', spoofed_ctime_tv_nsec: '%ld', spoofed_blksize: '%lu', spoofed_blocks: '%llu', is successfully updated to SUS_KSTAT_HLIST\n", - new_entry->info.is_statically, new_entry->info.target_ino, new_entry->info.target_pathname, - new_entry->info.spoofed_ino, new_entry->info.spoofed_dev, - new_entry->info.spoofed_nlink, new_entry->info.spoofed_size, - new_entry->info.spoofed_atime_tv_sec, new_entry->info.spoofed_mtime_tv_sec, new_entry->info.spoofed_ctime_tv_sec, - new_entry->info.spoofed_atime_tv_nsec, new_entry->info.spoofed_mtime_tv_nsec, new_entry->info.spoofed_ctime_tv_nsec, - new_entry->info.spoofed_blksize, new_entry->info.spoofed_blocks); + SUSFS_LOGI("CMD_SUSFS_ADD_SUS_KSTAT_STATICALLY -> ret: %d\n", info.err); } -#endif - spin_unlock(&susfs_spin_lock); - return 0; } -int susfs_update_sus_kstat(struct st_susfs_sus_kstat* __user user_info) { - struct st_susfs_sus_kstat info; +void susfs_update_sus_kstat(void __user **user_info) { + struct st_susfs_sus_kstat info = {0}; struct st_susfs_sus_kstat_hlist *new_entry, *tmp_entry; - struct hlist_node *tmp_node; - int bkt; - int err = 0; - if (copy_from_user(&info, user_info, sizeof(info))) { - SUSFS_LOGE("failed copying from userspace\n"); - return 1; + if (copy_from_user(&info, (struct st_susfs_sus_kstat __user*)*user_info, sizeof(info))) { + info.err = -EFAULT; + goto out_copy_to_user; } - spin_lock(&susfs_spin_lock); - hash_for_each_safe(SUS_KSTAT_HLIST, bkt, tmp_node, tmp_entry, node) { + new_entry = kzalloc(sizeof(struct st_susfs_sus_kstat_hlist), GFP_KERNEL); + if (!new_entry) { + info.err = -ENOMEM; + goto out_copy_to_user; + } + + + spin_lock(&susfs_spin_lock_sus_kstat); + hash_for_each_possible(SUS_KSTAT_HLIST, tmp_entry, node, info.target_ino) { if (!strcmp(tmp_entry->info.target_pathname, info.target_pathname)) { - if (susfs_update_sus_kstat_inode(tmp_entry->info.target_pathname)) { - err = 1; - goto out_spin_unlock; - } - new_entry = kmalloc(sizeof(struct st_susfs_sus_kstat_hlist), GFP_KERNEL); - if (!new_entry) { - SUSFS_LOGE("no enough memory\n"); - err = 1; - goto out_spin_unlock; - } memcpy(&new_entry->info, &tmp_entry->info, sizeof(tmp_entry->info)); - SUSFS_LOGI("updating target_ino from '%lu' to '%lu' for pathname: '%s' in SUS_KSTAT_HLIST\n", - new_entry->info.target_ino, info.target_ino, info.target_pathname); new_entry->target_ino = info.target_ino; new_entry->info.target_ino = info.target_ino; - if (info.spoofed_size > 0) { - SUSFS_LOGI("updating spoofed_size from '%lld' to '%lld' for pathname: '%s' in SUS_KSTAT_HLIST\n", - new_entry->info.spoofed_size, info.spoofed_size, info.target_pathname); - new_entry->info.spoofed_size = info.spoofed_size; - } - if (info.spoofed_blocks > 0) { - SUSFS_LOGI("updating spoofed_blocks from '%llu' to '%llu' for pathname: '%s' in SUS_KSTAT_HLIST\n", - new_entry->info.spoofed_blocks, info.spoofed_blocks, info.target_pathname); - new_entry->info.spoofed_blocks = info.spoofed_blocks; - } - hash_del(&tmp_entry->node); + hash_del_rcu(&tmp_entry->node); + spin_unlock(&susfs_spin_lock_sus_kstat); + synchronize_rcu(); kfree(tmp_entry); - hash_add(SUS_KSTAT_HLIST, &new_entry->node, info.target_ino); - goto out_spin_unlock; + goto out_add_new_entry; } } -out_spin_unlock: - spin_unlock(&susfs_spin_lock); - return err; + spin_unlock(&susfs_spin_lock_sus_kstat); + info.err = -ENOENT; + goto out_copy_to_user; + +out_add_new_entry: + info.err = susfs_mark_inode_sus_kstat(new_entry->info.target_pathname, new_entry); + if (info.err) { + kfree(new_entry); + goto out_copy_to_user; + } + SUSFS_LOGI("updating target_ino from '%lu' to '%lu' for pathname: '%s' in SUS_KSTAT_HLIST\n", + new_entry->info.target_ino, info.target_ino, info.target_pathname); + spin_lock(&susfs_spin_lock_sus_kstat); + hash_add_rcu(SUS_KSTAT_HLIST, &new_entry->node, info.target_ino); + spin_unlock(&susfs_spin_lock_sus_kstat); + info.err = 0; + +out_copy_to_user: + if (copy_to_user(&((struct st_susfs_sus_kstat __user*)*user_info)->err, &info.err, sizeof(info.err))) { + info.err = -EFAULT; + } + SUSFS_LOGI("CMD_SUSFS_UPDATE_SUS_KSTAT -> ret: %d\n", info.err); } -void susfs_sus_ino_for_generic_fillattr(unsigned long ino, struct kstat *stat) { - struct st_susfs_sus_kstat_hlist *entry; - - hash_for_each_possible(SUS_KSTAT_HLIST, entry, node, ino) { - if (entry->target_ino == ino) { - stat->dev = entry->info.spoofed_dev; - stat->ino = entry->info.spoofed_ino; - stat->nlink = entry->info.spoofed_nlink; - stat->size = entry->info.spoofed_size; - stat->atime.tv_sec = entry->info.spoofed_atime_tv_sec; - stat->atime.tv_nsec = entry->info.spoofed_atime_tv_nsec; - stat->mtime.tv_sec = entry->info.spoofed_mtime_tv_sec; - stat->mtime.tv_nsec = entry->info.spoofed_mtime_tv_nsec; - stat->ctime.tv_sec = entry->info.spoofed_ctime_tv_sec; - stat->ctime.tv_nsec = entry->info.spoofed_ctime_tv_nsec; - stat->blocks = entry->info.spoofed_blocks; - stat->blksize = entry->info.spoofed_blksize; +void susfs_generic_fillattr_spoofer(struct inode *inode, struct kstat *stat) +{ + struct st_susfs_sus_kstat_hlist *entry = NULL; + struct fuse_inode *fi = NULL; + unsigned long target_ino = 0; + dev_t target_dev = 0; + bool is_fuse = false; + + if (inode->i_sb->s_magic == FUSE_SUPER_MAGIC) { + fi = get_fuse_inode(inode); + if (!fi) { + SUSFS_LOGE("fi is NULL\n"); + return; + } + if (!test_bit(AS_FLAGS_SUS_KSTAT, &fi->inode.i_state) || + !susfs_is_current_proc_umounted_app()) + return; + target_ino = fi->inode.i_ino; + target_dev = fi->inode.i_sb->s_dev; + is_fuse = true; + goto out_spoof_kstat; + } + + if (!test_bit(AS_FLAGS_SUS_KSTAT, &inode->i_state) || + !susfs_is_current_proc_umounted_app()) + return; + + target_ino = inode->i_ino; + target_dev = inode->i_sb->s_dev; + +out_spoof_kstat: + rcu_read_lock(); + hash_for_each_possible_rcu(SUS_KSTAT_HLIST, entry, node, target_ino) { + if (entry->target_ino == target_ino && + entry->target_dev == target_dev && + entry->is_fuse == is_fuse) + { + SUSFS_LOGI("spoofing kstat for path: %s, target_ino: %lu, target_dev: %u\n", + entry->info.target_pathname, target_ino, target_dev); + if (entry->info.flags & KSTAT_SPOOF_INO) + stat->ino = entry->info.spoofed_ino; + if (entry->info.flags & KSTAT_SPOOF_DEV) + stat->dev = entry->info.spoofed_dev; + if (entry->info.flags & KSTAT_SPOOF_NLINK) + stat->nlink = entry->info.spoofed_nlink; + if (entry->info.flags & KSTAT_SPOOF_SIZE) + stat->size = entry->info.spoofed_size; + if (entry->info.flags & KSTAT_SPOOF_ATIME_TV_SEC) + stat->atime.tv_sec = entry->info.spoofed_atime_tv_sec; + if (entry->info.flags & KSTAT_SPOOF_ATIME_TV_NSEC) + stat->atime.tv_nsec = entry->info.spoofed_atime_tv_nsec; + if (entry->info.flags & KSTAT_SPOOF_MTIME_TV_SEC) + stat->mtime.tv_sec = entry->info.spoofed_mtime_tv_sec; + if (entry->info.flags & KSTAT_SPOOF_MTIME_TV_NSEC) + stat->mtime.tv_nsec = entry->info.spoofed_mtime_tv_nsec; + if (entry->info.flags & KSTAT_SPOOF_CTIME_TV_SEC) + stat->ctime.tv_sec = entry->info.spoofed_ctime_tv_sec; + if (entry->info.flags & KSTAT_SPOOF_CTIME_TV_NSEC) + stat->ctime.tv_nsec = entry->info.spoofed_ctime_tv_nsec; + if (entry->info.flags & KSTAT_SPOOF_BLKSIZE) + stat->blksize = entry->info.spoofed_blksize; + if (entry->info.flags & KSTAT_SPOOF_BLOCKS) + stat->blocks = entry->info.spoofed_blocks; + rcu_read_unlock(); return; } } + rcu_read_unlock(); } -void susfs_sus_ino_for_show_map_vma(unsigned long ino, dev_t *out_dev, unsigned long *out_ino) { - struct st_susfs_sus_kstat_hlist *entry; - hash_for_each_possible(SUS_KSTAT_HLIST, entry, node, ino) { - if (entry->target_ino == ino) { +void susfs_show_map_vma_spoofer(struct inode *inode, dev_t *out_dev, unsigned long *out_ino) { + struct st_susfs_sus_kstat_hlist *entry = NULL; + struct fuse_inode *fi = NULL; + unsigned long target_ino = 0; + dev_t target_dev = 0; + bool is_fuse = false; + + if (inode->i_sb->s_magic == FUSE_SUPER_MAGIC) { + fi = get_fuse_inode(inode); + if (!fi) { + SUSFS_LOGE("fi is NULL\n"); + return; + } + if (!test_bit(AS_FLAGS_SUS_KSTAT, &fi->inode.i_state) || + !susfs_is_current_proc_umounted_app()) + return; + target_ino = fi->inode.i_ino; + target_dev = fi->inode.i_sb->s_dev; + is_fuse = true; + goto out_spoof_kstat; + } + + if (!test_bit(AS_FLAGS_SUS_KSTAT, &inode->i_state) || + !susfs_is_current_proc_umounted_app()) + return; + + target_ino = inode->i_ino; + target_dev = inode->i_sb->s_dev; + +out_spoof_kstat: + rcu_read_lock(); + hash_for_each_possible_rcu(SUS_KSTAT_HLIST, entry, node, target_ino) { + if (entry->target_ino == target_ino && + entry->target_dev == target_dev && + entry->is_fuse == is_fuse) + { + SUSFS_LOGI("spoofing kstat for target_ino: %lu, target_dev: %u\n", target_ino, target_dev); *out_dev = entry->info.spoofed_dev; *out_ino = entry->info.spoofed_ino; + rcu_read_unlock(); return; } } + rcu_read_unlock(); } #endif // #ifdef CONFIG_KSU_SUSFS_SUS_KSTAT - /* try_umount */ #ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT +static DEFINE_SPINLOCK(susfs_spin_lock_try_umount); +extern void try_umount(const char *mnt, int flags); static LIST_HEAD(LH_TRY_UMOUNT_PATH); -int susfs_add_try_umount(struct st_susfs_try_umount* __user user_info) { - struct st_susfs_try_umount_list *cursor = NULL, *temp = NULL; +void susfs_add_try_umount(void __user **user_info) { + struct st_susfs_try_umount info = {0}; struct st_susfs_try_umount_list *new_list = NULL; - struct st_susfs_try_umount info; - if (copy_from_user(&info, user_info, sizeof(info))) { - SUSFS_LOGE("failed copying from userspace\n"); - return 1; + if (copy_from_user(&info, (struct st_susfs_try_umount __user*)*user_info, sizeof(info))) { + info.err = -EFAULT; + goto out_copy_to_user; } - list_for_each_entry_safe(cursor, temp, &LH_TRY_UMOUNT_PATH, list) { - if (unlikely(!strcmp(info.target_pathname, cursor->info.target_pathname))) { - SUSFS_LOGE("target_pathname: '%s' is already created in LH_TRY_UMOUNT_PATH\n", info.target_pathname); - return 1; - } + if (info.mnt_mode == TRY_UMOUNT_DEFAULT) { + info.mnt_mode = 0; + } else if (info.mnt_mode == TRY_UMOUNT_DETACH) { + info.mnt_mode = MNT_DETACH; + } else { + SUSFS_LOGE("Unsupported mnt_mode: %d\n", info.mnt_mode); + info.err = -EINVAL; + goto out_copy_to_user; } - new_list = kmalloc(sizeof(struct st_susfs_try_umount_list), GFP_KERNEL); + new_list = kzalloc(sizeof(struct st_susfs_try_umount_list), GFP_KERNEL); if (!new_list) { - SUSFS_LOGE("no enough memory\n"); - return 1; + info.err = -ENOMEM; + goto out_copy_to_user; } memcpy(&new_list->info, &info, sizeof(info)); INIT_LIST_HEAD(&new_list->list); - spin_lock(&susfs_spin_lock); + spin_lock(&susfs_spin_lock_try_umount); list_add_tail(&new_list->list, &LH_TRY_UMOUNT_PATH); - spin_unlock(&susfs_spin_lock); - SUSFS_LOGI("target_pathname: '%s', mnt_mode: %d, is successfully added to LH_TRY_UMOUNT_PATH\n", new_list->info.target_pathname, new_list->info.mnt_mode); - return 0; + spin_unlock(&susfs_spin_lock_try_umount); + SUSFS_LOGI("target_pathname: '%s', umount options: %d, is successfully added to LH_TRY_UMOUNT_PATH\n", new_list->info.target_pathname, new_list->info.mnt_mode); + info.err = 0; +out_copy_to_user: + if (copy_to_user(&((struct st_susfs_try_umount __user*)*user_info)->err, &info.err, sizeof(info.err))) { + info.err = -EFAULT; + } + SUSFS_LOGI("CMD_SUSFS_ADD_TRY_UMOUNT -> ret: %d\n", info.err); } -void susfs_try_umount(uid_t target_uid) { +void susfs_try_umount(uid_t uid) { struct st_susfs_try_umount_list *cursor = NULL; // We should umount in reversed order list_for_each_entry_reverse(cursor, &LH_TRY_UMOUNT_PATH, list) { - if (cursor->info.mnt_mode == TRY_UMOUNT_DEFAULT) { - ksu_try_umount(cursor->info.target_pathname, false, 0, target_uid); - } else if (cursor->info.mnt_mode == TRY_UMOUNT_DETACH) { - ksu_try_umount(cursor->info.target_pathname, false, MNT_DETACH, target_uid); - } else { - SUSFS_LOGE("failed umounting '%s' for uid: %d, mnt_mode '%d' not supported\n", - cursor->info.target_pathname, target_uid, cursor->info.mnt_mode); - } - } -} - -#ifdef CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT -void susfs_auto_add_try_umount_for_bind_mount(struct path *path) { - struct st_susfs_try_umount_list *cursor = NULL, *temp = NULL; - struct st_susfs_try_umount_list *new_list = NULL; - char *pathname = NULL, *dpath = NULL; -#ifdef CONFIG_KSU_SUSFS_HAS_MAGIC_MOUNT - bool is_magic_mount_path = false; -#endif - -#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT - if (path->dentry->d_inode->i_state & INODE_STATE_SUS_KSTAT) { - SUSFS_LOGI("skip adding path to try_umount list as its inode is flagged INODE_STATE_SUS_KSTAT already\n"); - return; - } -#endif - - pathname = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!pathname) { - SUSFS_LOGE("no enough memory\n"); - return; - } - - dpath = d_path(path, pathname, PAGE_SIZE); - if (!dpath) { - SUSFS_LOGE("dpath is NULL\n"); - goto out_free_pathname; - } - -#ifdef CONFIG_KSU_SUSFS_HAS_MAGIC_MOUNT - if (strstr(dpath, MAGIC_MOUNT_WORKDIR)) { - is_magic_mount_path = true; - } -#endif - - list_for_each_entry_safe(cursor, temp, &LH_TRY_UMOUNT_PATH, list) { -#ifdef CONFIG_KSU_SUSFS_HAS_MAGIC_MOUNT - if (is_magic_mount_path && strstr(dpath, cursor->info.target_pathname)) { - goto out_free_pathname; - } -#endif - if (unlikely(!strcmp(dpath, cursor->info.target_pathname))) { - SUSFS_LOGE("target_pathname: '%s', ino: %lu, is already created in LH_TRY_UMOUNT_PATH\n", - dpath, path->dentry->d_inode->i_ino); - goto out_free_pathname; - } - } - - new_list = kmalloc(sizeof(struct st_susfs_try_umount_list), GFP_KERNEL); - if (!new_list) { - SUSFS_LOGE("no enough memory\n"); - goto out_free_pathname; - } - -#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); - goto out_add_to_list; + SUSFS_LOGI("umounting '%s' for uid: %u\n", cursor->info.target_pathname, uid); + try_umount(cursor->info.target_pathname, cursor->info.mnt_mode); } -#endif - strncpy(new_list->info.target_pathname, dpath, SUSFS_MAX_LEN_PATHNAME-1); - -#ifdef CONFIG_KSU_SUSFS_HAS_MAGIC_MOUNT -out_add_to_list: -#endif - - new_list->info.mnt_mode = TRY_UMOUNT_DETACH; - - INIT_LIST_HEAD(&new_list->list); - spin_lock(&susfs_spin_lock); - list_add_tail(&new_list->list, &LH_TRY_UMOUNT_PATH); - spin_unlock(&susfs_spin_lock); - SUSFS_LOGI("target_pathname: '%s', ino: %lu, mnt_mode: %d, is successfully added to LH_TRY_UMOUNT_PATH\n", - new_list->info.target_pathname, path->dentry->d_inode->i_ino, new_list->info.mnt_mode); -out_free_pathname: - kfree(pathname); } -#endif // #ifdef CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT #endif // #ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT /* spoof_uname */ #ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME -static spinlock_t susfs_uname_spin_lock; +static DEFINE_SPINLOCK(susfs_spin_lock_set_uname); static struct st_susfs_uname my_uname; static void susfs_my_uname_init(void) { memset(&my_uname, 0, sizeof(my_uname)); } -int susfs_set_uname(struct st_susfs_uname* __user user_info) { - struct st_susfs_uname info; +void susfs_set_uname(void __user **user_info) { + struct st_susfs_uname info = {0}; - if (copy_from_user(&info, user_info, sizeof(struct st_susfs_uname))) { - SUSFS_LOGE("failed copying from userspace.\n"); - return 1; + if (copy_from_user(&info, (struct st_susfs_uname __user*)*user_info, sizeof(info))) { + info.err = -EFAULT; + goto out_copy_to_user; } - spin_lock(&susfs_uname_spin_lock); + spin_lock(&susfs_spin_lock_set_uname); if (!strcmp(info.release, "default")) { strncpy(my_uname.release, utsname()->release, __NEW_UTS_LEN); } else { @@ -678,239 +648,841 @@ int susfs_set_uname(struct st_susfs_uname* __user user_info) { } else { strncpy(my_uname.version, info.version, __NEW_UTS_LEN); } - spin_unlock(&susfs_uname_spin_lock); + spin_unlock(&susfs_spin_lock_set_uname); SUSFS_LOGI("setting spoofed release: '%s', version: '%s'\n", my_uname.release, my_uname.version); - return 0; + info.err = 0; +out_copy_to_user: + if (copy_to_user(&((struct st_susfs_uname __user*)*user_info)->err, &info.err, sizeof(info.err))) { + info.err = -EFAULT; + } + SUSFS_LOGI("CMD_SUSFS_SET_UNAME -> ret: %d\n", info.err); } void susfs_spoof_uname(struct new_utsname* tmp) { - if (unlikely(my_uname.release[0] == '\0' || spin_is_locked(&susfs_uname_spin_lock))) + if (unlikely(my_uname.release[0] == '\0' || spin_is_locked(&susfs_spin_lock_set_uname))) return; strncpy(tmp->release, my_uname.release, __NEW_UTS_LEN); strncpy(tmp->version, my_uname.version, __NEW_UTS_LEN); } #endif // #ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME -/* set_log */ +/* enable_log */ #ifdef CONFIG_KSU_SUSFS_ENABLE_LOG -void susfs_set_log(bool enabled) { - spin_lock(&susfs_spin_lock); - susfs_is_log_enabled = enabled; - spin_unlock(&susfs_spin_lock); - if (susfs_is_log_enabled) { + +void susfs_enable_log(void __user **user_info) { + struct st_susfs_log info = {0}; + + if (copy_from_user(&info, (struct st_susfs_log __user*)*user_info, sizeof(info))) { + info.err = -EFAULT; + goto out_copy_to_user; + } + + WRITE_ONCE(susfs_is_log_enabled, info.enabled); + + if (info.enabled) { pr_info("susfs: enable logging to kernel"); } else { pr_info("susfs: disable logging to kernel"); } + info.err = 0; +out_copy_to_user: + if (copy_to_user(&((struct st_susfs_log __user*)*user_info)->err, &info.err, sizeof(info.err))) { + info.err = -EFAULT; + } + SUSFS_LOGI("CMD_SUSFS_ENABLE_LOG -> ret: %d\n", info.err); } #endif // #ifdef CONFIG_KSU_SUSFS_ENABLE_LOG /* spoof_cmdline_or_bootconfig */ #ifdef CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG static char *fake_cmdline_or_bootconfig = NULL; -int susfs_set_cmdline_or_bootconfig(char* __user user_fake_cmdline_or_bootconfig) { - int res; +static bool susfs_is_fake_cmdline_or_bootconfig_set = false; +static DEFINE_SEQLOCK(susfs_fake_cmdline_or_bootconfig_seqlock); + +void susfs_set_cmdline_or_bootconfig(void __user **user_info) { + struct st_susfs_spoof_cmdline_or_bootconfig *info = (struct st_susfs_spoof_cmdline_or_bootconfig *)kzalloc(sizeof(struct st_susfs_spoof_cmdline_or_bootconfig), GFP_KERNEL); + + if (!info) { + info->err = -ENOMEM; + goto out_copy_to_user; + } + + if (copy_from_user(info, (struct st_susfs_spoof_cmdline_or_bootconfig __user*)*user_info, sizeof(struct st_susfs_spoof_cmdline_or_bootconfig))) { + info->err = -EFAULT; + goto out_copy_to_user; + } + + if (*info->fake_cmdline_or_bootconfig == '\0') { + info->err = -EINVAL; + goto out_copy_to_user; + } if (!fake_cmdline_or_bootconfig) { - // 4096 is enough I guess - fake_cmdline_or_bootconfig = kmalloc(SUSFS_FAKE_CMDLINE_OR_BOOTCONFIG_SIZE, GFP_KERNEL); + fake_cmdline_or_bootconfig = (char *)kzalloc(SUSFS_FAKE_CMDLINE_OR_BOOTCONFIG_SIZE, GFP_KERNEL); if (!fake_cmdline_or_bootconfig) { - SUSFS_LOGE("no enough memory\n"); - return -ENOMEM; + info->err = -ENOMEM; + goto out_copy_to_user; } } - spin_lock(&susfs_spin_lock); - memset(fake_cmdline_or_bootconfig, 0, SUSFS_FAKE_CMDLINE_OR_BOOTCONFIG_SIZE); - res = strncpy_from_user(fake_cmdline_or_bootconfig, user_fake_cmdline_or_bootconfig, SUSFS_FAKE_CMDLINE_OR_BOOTCONFIG_SIZE-1); - spin_unlock(&susfs_spin_lock); - - if (res > 0) { -#if LINUX_VERSION_CODE >= KERNEL_VERSION(6,1,0) - SUSFS_LOGI("fake_cmdline_or_bootconfig is set, length of string: %lu\n", strlen(fake_cmdline_or_bootconfig)); -#else - SUSFS_LOGI("fake_cmdline_or_bootconfig is set, length of string: %u\n", strlen(fake_cmdline_or_bootconfig)); -#endif - return 0; + write_seqlock(&susfs_fake_cmdline_or_bootconfig_seqlock); + strncpy(fake_cmdline_or_bootconfig, + info->fake_cmdline_or_bootconfig, + SUSFS_FAKE_CMDLINE_OR_BOOTCONFIG_SIZE-1); + susfs_is_fake_cmdline_or_bootconfig_set = true; + write_sequnlock(&susfs_fake_cmdline_or_bootconfig_seqlock); + SUSFS_LOGI("fake_cmdline_or_bootconfig is set\n"); + info->err = 0; +out_copy_to_user: + if (copy_to_user(&((struct st_susfs_spoof_cmdline_or_bootconfig __user*)*user_info)->err, &info->err, sizeof(info->err))) { + info->err = -EFAULT; + } + SUSFS_LOGI("CMD_SUSFS_SET_CMDLINE_OR_BOOTCONFIG -> ret: %d\n", info->err); + if (info) { + kfree(info); } - SUSFS_LOGI("failed setting fake_cmdline_or_bootconfig\n"); - return res; } int susfs_spoof_cmdline_or_bootconfig(struct seq_file *m) { - if (fake_cmdline_or_bootconfig != NULL) { - seq_puts(m, fake_cmdline_or_bootconfig); - return 0; - } - return 1; + unsigned seq; + int err = -EINVAL; + + do { + seq = read_seqbegin(&susfs_fake_cmdline_or_bootconfig_seqlock); + if (susfs_is_fake_cmdline_or_bootconfig_set) { + seq_puts(m, fake_cmdline_or_bootconfig); + err = 0; + } + } while (read_seqretry(&susfs_fake_cmdline_or_bootconfig_seqlock, seq)); + + return err; } #endif /* open_redirect */ #ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT +static DEFINE_SPINLOCK(susfs_spin_lock_open_redirect); static DEFINE_HASHTABLE(OPEN_REDIRECT_HLIST, 10); -static int susfs_update_open_redirect_inode(struct st_susfs_open_redirect_hlist *new_entry) { - struct path path_target; - struct inode *inode_target; - int err = 0; +DEFINE_STATIC_SRCU(susfs_srcu_open_redirect); - err = kern_path(new_entry->target_pathname, LOOKUP_FOLLOW, &path_target); - if (err) { - SUSFS_LOGE("Failed opening file '%s'\n", new_entry->target_pathname); - return err; +void susfs_add_open_redirect(void __user **user_info) { + struct st_susfs_open_redirect info = {0}; + struct st_susfs_open_redirect_hlist *new_entry_target, *new_entry_redirected, *tmp_entry_target, *tmp_entry_redirected; + struct path target_path, redirected_path; + struct inode *target_inode, *redirected_inode; + bool is_first_dup_found = false; + bool is_second_dup_found = false; + + if (copy_from_user(&info, (struct st_susfs_open_redirect __user*)*user_info, sizeof(info))) { + info.err = -EFAULT; + goto out_copy_to_user; } - inode_target = d_inode(path_target.dentry); - if (!inode_target) { - SUSFS_LOGE("inode_target is NULL\n"); - err = 1; - goto out_path_put_target; + if (*info.target_pathname == '\0') { + info.err = -EINVAL; + SUSFS_LOGE("empty target_pathname\n"); + goto out_copy_to_user; + } + + if (info.uid_scheme < UID_NON_APP_PROC || info.uid_scheme > UID_UMOUNTED_PROC) { + info.err = -EINVAL; + SUSFS_LOGE("invalid uid scheme: %d\n", info.uid_scheme); + goto out_copy_to_user; } - spin_lock(&inode_target->i_lock); - inode_target->i_state |= INODE_STATE_OPEN_REDIRECT; - spin_unlock(&inode_target->i_lock); + info.err = kern_path(info.redirected_pathname, 0, &redirected_path); + if (info.err) { + SUSFS_LOGE("failed opening redirected file '%s'\n", info.redirected_pathname); + goto out_copy_to_user; + } -out_path_put_target: - path_put(&path_target); - return err; -} + info.err = kern_path(info.target_pathname, 0, &target_path); + if (info.err) { + SUSFS_LOGE("failed opening target file '%s'\n", info.target_pathname); + goto out_path_put_redirected_path; + } + + redirected_inode = d_backing_inode(redirected_path.dentry); + if (!redirected_inode) { + SUSFS_LOGE("redirected_inode is NULL\n"); + info.err = -ENOENT; + goto out_path_put_target_path; + } -int susfs_add_open_redirect(struct st_susfs_open_redirect* __user user_info) { - struct st_susfs_open_redirect info; - struct st_susfs_open_redirect_hlist *new_entry, *tmp_entry; - struct hlist_node *tmp_node; - int bkt; - bool update_hlist = false; + target_inode = d_backing_inode(target_path.dentry); + if (!target_inode) { + SUSFS_LOGE("target_inode is NULL\n"); + info.err = -ENOENT; + goto out_path_put_target_path; + } - if (copy_from_user(&info, user_info, sizeof(info))) { - SUSFS_LOGE("failed copying from userspace\n"); - return 1; + if (redirected_inode->i_sb->s_magic == FUSE_SUPER_MAGIC || + target_inode->i_sb->s_magic == FUSE_SUPER_MAGIC) { + SUSFS_LOGE("FUSE fs is not supported for open_redirect feature\n"); + info.err = -EINVAL; + goto out_path_put_target_path; } - spin_lock(&susfs_spin_lock); - hash_for_each_safe(OPEN_REDIRECT_HLIST, bkt, tmp_node, tmp_entry, node) { - if (!strcmp(tmp_entry->target_pathname, info.target_pathname)) { - hash_del(&tmp_entry->node); - kfree(tmp_entry); - update_hlist = true; + new_entry_target = kzalloc(sizeof(struct st_susfs_open_redirect_hlist), GFP_KERNEL); + if (!new_entry_target) { + info.err = -ENOMEM; + goto out_path_put_target_path; + } + + new_entry_redirected = kzalloc(sizeof(struct st_susfs_open_redirect_hlist), GFP_KERNEL); + if (!new_entry_redirected) { + info.err = -ENOMEM; + kfree(new_entry_target); + goto out_path_put_target_path; + } + + // check for existing entries, delete it first if so + spin_lock(&susfs_spin_lock_open_redirect); + hash_for_each_possible(OPEN_REDIRECT_HLIST, tmp_entry_target, node, target_inode->i_ino) { + if (!strcmp(tmp_entry_target->info.target_pathname, info.target_pathname)) { + if (tmp_entry_target->reversed_lookup_only) { + SUSFS_LOGE("duplicated '%s' cannot be removed/added because it is used for reversed lookup only\n", info.target_pathname); + spin_unlock(&susfs_spin_lock_open_redirect); + info.err = -EINVAL; + kfree(new_entry_redirected); + kfree(new_entry_target); + goto out_path_put_target_path; + } + is_first_dup_found = true; + hash_del_rcu(&tmp_entry_target->node); break; } } - spin_unlock(&susfs_spin_lock); - new_entry = kmalloc(sizeof(struct st_susfs_open_redirect_hlist), GFP_KERNEL); - if (!new_entry) { - SUSFS_LOGE("no enough memory\n"); - return 1; + if (is_first_dup_found) { + hash_for_each_possible(OPEN_REDIRECT_HLIST, tmp_entry_redirected, node, redirected_inode->i_ino) { + if (!strcmp(tmp_entry_redirected->info.target_pathname, info.redirected_pathname)) { + is_second_dup_found = true; + hash_del_rcu(&tmp_entry_redirected->node); + break; + } + } + spin_unlock(&susfs_spin_lock_open_redirect); + synchronize_rcu(); + if (is_second_dup_found) + kfree(tmp_entry_redirected); + kfree(tmp_entry_target); + goto out_add_new_entry; + } + spin_unlock(&susfs_spin_lock_open_redirect); + +out_add_new_entry: + new_entry_target->target_ino = target_inode->i_ino; + new_entry_target->target_dev = target_inode->i_sb->s_dev; + new_entry_target->redirected_ino = redirected_inode->i_ino; + new_entry_target->redirected_dev = redirected_inode->i_sb->s_dev; + new_entry_target->info.uid_scheme = info.uid_scheme; + new_entry_target->reversed_lookup_only = false; + new_entry_target->spoofed_mnt_id = real_mount(target_path.mnt)->mnt_id; + (void)vfs_statfs(&target_path, &new_entry_target->spoofed_kstatfs); + memcpy(&new_entry_target->info, &info, sizeof(info)); + + new_entry_redirected->target_ino = redirected_inode->i_ino; + new_entry_redirected->target_dev = redirected_inode->i_sb->s_dev; + new_entry_redirected->redirected_ino = target_inode->i_ino; + new_entry_redirected->redirected_dev = target_inode->i_sb->s_dev; + new_entry_redirected->info.uid_scheme = info.uid_scheme; + new_entry_redirected->reversed_lookup_only = true; + new_entry_redirected->spoofed_mnt_id = real_mount(target_path.mnt)->mnt_id; + memcpy(&new_entry_redirected->spoofed_kstatfs, &new_entry_target->spoofed_kstatfs, sizeof(struct kstatfs)); + strncpy(new_entry_redirected->info.target_pathname, info.redirected_pathname, SUSFS_MAX_LEN_PATHNAME - 1); + strncpy(new_entry_redirected->info.redirected_pathname, info.target_pathname, SUSFS_MAX_LEN_PATHNAME - 1); + + spin_lock(&susfs_spin_lock_open_redirect); + SUSFS_LOGI("target_pathname: '%s', redirected_pathname: '%s', target_i_ino: '%lu', redirected_i_ino: '%lu', target_s_dev: '%lu', redirected_s_dev: '%lu', uid_scheme: '%d', reversed_lookup_only: %d, spoofed_mnt_id: %d, is successfully added to OPEN_REDIRECT_HLIST\n", + new_entry_target->info.target_pathname, new_entry_target->info.redirected_pathname, new_entry_target->target_ino, new_entry_target->redirected_ino, new_entry_target->target_dev, new_entry_target->redirected_dev, new_entry_target->info.uid_scheme, new_entry_target->reversed_lookup_only, new_entry_target->spoofed_mnt_id); + SUSFS_LOGI("target_pathname: '%s', redirected_pathname: '%s', target_i_ino: '%lu', redirected_i_ino: '%lu', target_s_dev: '%lu', redirected_s_dev: '%lu', uid_scheme: '%d', reversed_lookup_only: %d, spoofed_mnt_id: %d, is successfully added to OPEN_REDIRECT_HLIST\n", + new_entry_redirected->info.target_pathname, new_entry_redirected->info.redirected_pathname, new_entry_redirected->target_ino, new_entry_redirected->redirected_ino, new_entry_redirected->target_dev, new_entry_redirected->redirected_dev, new_entry_redirected->info.uid_scheme, new_entry_redirected->reversed_lookup_only, new_entry_redirected->spoofed_mnt_id); + hash_add_rcu(OPEN_REDIRECT_HLIST, &new_entry_target->node, new_entry_target->target_ino); + hash_add_rcu(OPEN_REDIRECT_HLIST, &new_entry_redirected->node, new_entry_redirected->target_ino); + // we need to mark both target and redirected path inode just for spoofing readlink as well + set_bit(AS_FLAGS_OPEN_REDIRECT, &redirected_inode->i_state); + set_bit(AS_FLAGS_OPEN_REDIRECT, &target_inode->i_state); + spin_unlock(&susfs_spin_lock_open_redirect); + + info.err = 0; +out_path_put_target_path: + path_put(&target_path); +out_path_put_redirected_path: + path_put(&redirected_path); +out_copy_to_user: + if (copy_to_user(&((struct st_susfs_open_redirect __user*)*user_info)->err, &info.err, sizeof(info.err))) { + info.err = -EFAULT; + } + SUSFS_LOGI("CMD_SUSFS_ADD_OPEN_REDIRECT -> ret: %d\n", info.err); +} + +struct filename *susfs_open_redirect_spoof_do_sys_openat(struct inode *inode) { + struct st_susfs_open_redirect_hlist *entry = NULL; + struct filename *new_filename = NULL; + int srcu_idx = srcu_read_lock(&susfs_srcu_open_redirect); + + hash_for_each_possible_rcu(OPEN_REDIRECT_HLIST, entry, node, inode->i_ino) { + if (!entry->reversed_lookup_only && + entry->target_ino == inode->i_ino && + entry->target_dev == inode->i_sb->s_dev) + { + switch(entry->info.uid_scheme) { + case UID_NON_APP_PROC: + if (current_uid().val % 100000 < 10000) + break; + goto out_srcu_read_unlock; + case UID_ROOT_PROC_EXCEPT_SU_PROC: + if (current_uid().val == 0 && !susfs_is_current_ksu_domain()) + break; + goto out_srcu_read_unlock; + case UID_NON_SU_PROC: + if (!susfs_is_current_ksu_domain()) + break; + goto out_srcu_read_unlock; + case UID_UMOUNTED_APP_PROC: + if (susfs_is_current_proc_umounted_app()) + break; + goto out_srcu_read_unlock; + case UID_UMOUNTED_PROC: + if (susfs_is_current_proc_umounted()) + break; + goto out_srcu_read_unlock; + default: + goto out_srcu_read_unlock; + } + SUSFS_LOGI("redirect path '%s' to '%s', uid_scheme: %d\n", + entry->info.target_pathname, entry->info.redirected_pathname, entry->info.uid_scheme); + new_filename = getname_kernel(entry->info.redirected_pathname); + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return new_filename; + } } +out_srcu_read_unlock: + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return new_filename; +} - new_entry->target_ino = info.target_ino; - strncpy(new_entry->target_pathname, info.target_pathname, SUSFS_MAX_LEN_PATHNAME-1); - strncpy(new_entry->redirected_pathname, info.redirected_pathname, SUSFS_MAX_LEN_PATHNAME-1); - if (susfs_update_open_redirect_inode(new_entry)) { - SUSFS_LOGE("failed adding path '%s' to OPEN_REDIRECT_HLIST\n", new_entry->target_pathname); - kfree(new_entry); - return 1; +int susfs_open_redirect_spoof_vfs_readlink(struct inode *inode, char __user *buffer, int buflen) { + struct st_susfs_open_redirect_hlist *entry = NULL; + int srcu_idx = srcu_read_lock(&susfs_srcu_open_redirect); + + hash_for_each_possible_rcu(OPEN_REDIRECT_HLIST, entry, node, inode->i_ino) { + if (entry->reversed_lookup_only && + entry->target_ino == inode->i_ino && + entry->target_dev == inode->i_sb->s_dev) + { + SUSFS_LOGI("spoof path '%s' to '%s'\n", + entry->info.target_pathname, entry->info.redirected_pathname); + if (strlen(entry->info.redirected_pathname) >= buflen) { + SUSFS_LOGE("buflen not big enough\n"); + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return -ENAMETOOLONG; + } + if (copy_to_user(buffer, entry->info.redirected_pathname, strlen(entry->info.redirected_pathname))) { + SUSFS_LOGE("copy_to_user() failed\n"); + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return -EFAULT; + } + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return 0; + } } + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return -ENOENT; +} - spin_lock(&susfs_spin_lock); - hash_add(OPEN_REDIRECT_HLIST, &new_entry->node, info.target_ino); - if (update_hlist) { - SUSFS_LOGI("target_ino: '%lu', target_pathname: '%s', redirected_pathname: '%s', is successfully updated to OPEN_REDIRECT_HLIST\n", - new_entry->target_ino, new_entry->target_pathname, new_entry->redirected_pathname); - } else { - SUSFS_LOGI("target_ino: '%lu', target_pathname: '%s' redirected_pathname: '%s', is successfully added to OPEN_REDIRECT_HLIST\n", - new_entry->target_ino, new_entry->target_pathname, new_entry->redirected_pathname); +int susfs_open_redirect_spoof_do_proc_readlink(struct inode *inode, char *tmp_buf, int buflen) { + struct st_susfs_open_redirect_hlist *entry = NULL; + int srcu_idx = srcu_read_lock(&susfs_srcu_open_redirect); + + hash_for_each_possible_rcu(OPEN_REDIRECT_HLIST, entry, node, inode->i_ino) { + if (entry->reversed_lookup_only && + entry->target_ino == inode->i_ino && + entry->target_dev == inode->i_sb->s_dev) + { + SUSFS_LOGI("spoof path '%s' to '%s'\n", + entry->info.target_pathname, entry->info.redirected_pathname); + if (strlen(entry->info.redirected_pathname) >= buflen) { + SUSFS_LOGE("buflen not big enough\n"); + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return -ENAMETOOLONG; + } + strncpy(tmp_buf, entry->info.redirected_pathname, SUSFS_MAX_LEN_PATHNAME - 1); + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return 0; + } } - spin_unlock(&susfs_spin_lock); - return 0; + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return -ENOENT; +} + +int susfs_open_redirect_spoof_vfs_statfs(struct inode *inode, struct kstatfs *buf) { + struct st_susfs_open_redirect_hlist *entry = NULL; + int srcu_idx = srcu_read_lock(&susfs_srcu_open_redirect); + + hash_for_each_possible_rcu(OPEN_REDIRECT_HLIST, entry, node, inode->i_ino) { + if (entry->reversed_lookup_only && + entry->target_ino == inode->i_ino && + entry->target_dev == inode->i_sb->s_dev) + { + SUSFS_LOGI("spoof kstatfs for redirected path: '%s'\n", + entry->info.target_pathname); + memcpy(buf, &entry->spoofed_kstatfs, sizeof(struct kstatfs)); + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return 0; + } + } + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return -EINVAL; } -struct filename* susfs_get_redirected_path(unsigned long ino) { - struct st_susfs_open_redirect_hlist *entry; +int susfs_open_redirect_spoof_seq_show(struct inode *inode, int *out_mnt_id, unsigned long *out_ino) { + struct st_susfs_open_redirect_hlist *entry = NULL; + int srcu_idx = srcu_read_lock(&susfs_srcu_open_redirect); + + hash_for_each_possible_rcu(OPEN_REDIRECT_HLIST, entry, node, inode->i_ino) { + if (entry->reversed_lookup_only && + entry->target_ino == inode->i_ino && + entry->target_dev == inode->i_sb->s_dev) + { + *out_mnt_id = entry->spoofed_mnt_id; + *out_ino = entry->redirected_ino; + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return 0; + } + } + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return -EINVAL; +} - hash_for_each_possible(OPEN_REDIRECT_HLIST, entry, node, ino) { - if (entry->target_ino == ino) { - SUSFS_LOGI("Redirect for ino: %lu\n", ino); - return getname_kernel(entry->redirected_pathname); +int susfs_open_redirect_spoof_show_map_vma(struct inode *inode, unsigned long *out_ino, dev_t *out_dev, char *spoofed_name) { + struct st_susfs_open_redirect_hlist *entry = NULL; + int srcu_idx = srcu_read_lock(&susfs_srcu_open_redirect); + + if (spoofed_name) { + SUSFS_LOGE("spoofed_name must be NULL first!\n"); + return -EINVAL; + } + + hash_for_each_possible_rcu(OPEN_REDIRECT_HLIST, entry, node, inode->i_ino) { + if (entry->reversed_lookup_only && + entry->target_ino == inode->i_ino && + entry->target_dev == inode->i_sb->s_dev) + { + spoofed_name = kzalloc(SUSFS_MAX_LEN_PATHNAME, GFP_KERNEL); + if (!spoofed_name) { + SUSFS_LOGE("no enough memeory\n"); + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return -ENOMEM; + } + SUSFS_LOGI("spoof maps ino/dev/name for redirected path: '%s'\n", + entry->info.target_pathname); + *out_ino = entry->redirected_ino; + *out_dev = entry->redirected_dev; + strncpy(spoofed_name, entry->info.redirected_pathname, SUSFS_MAX_LEN_PATHNAME - 1); + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return 0; } } - return ERR_PTR(-ENOENT); + srcu_read_unlock(&susfs_srcu_open_redirect, srcu_idx); + return -EINVAL; } #endif // #ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT -/* sus_su */ -#ifdef CONFIG_KSU_SUSFS_SUS_SU -bool susfs_is_sus_su_hooks_enabled __read_mostly = false; -static int susfs_sus_su_working_mode = 0; -extern void ksu_susfs_enable_sus_su(void); -extern void ksu_susfs_disable_sus_su(void); +/* sus_map */ +#ifdef CONFIG_KSU_SUSFS_SUS_MAP +void susfs_add_sus_map(void __user **user_info) { + struct st_susfs_sus_map info = {0}; + struct path path; + struct inode *inode = NULL; + + if (copy_from_user(&info, (struct st_susfs_sus_map __user*)*user_info, sizeof(info))) { + info.err = -EFAULT; + goto out_copy_to_user; + } + + info.err = kern_path(info.target_pathname, LOOKUP_FOLLOW, &path); + if (info.err) { + SUSFS_LOGE("Failed opening file '%s'\n", info.target_pathname); + goto out_copy_to_user; + } + + inode = d_backing_inode(path.dentry); + if (!inode) { + SUSFS_LOGE("inode is NULL\n"); + info.err = -ENOENT; + goto out_path_put_path; + } + set_bit(AS_FLAGS_SUS_MAP, &inode->i_state); + SUSFS_LOGI("pathname: '%s', is flagged as AS_FLAGS_SUS_MAP\n", info.target_pathname); + info.err = 0; +out_path_put_path: + path_put(&path); +out_copy_to_user: + if (copy_to_user(&((struct st_susfs_sus_map __user*)*user_info)->err, &info.err, sizeof(info.err))) { + info.err = -EFAULT; + } + SUSFS_LOGI("CMD_SUSFS_ADD_SUS_MAP -> ret: %d\n", info.err); +} +#endif // #ifdef CONFIG_KSU_SUSFS_SUS_MAP + +/* susfs avc log spoofing */ +extern bool susfs_is_avc_log_spoofing_enabled; + +void susfs_set_avc_log_spoofing(void __user **user_info) { + struct st_susfs_avc_log_spoofing info = {0}; -int susfs_get_sus_su_working_mode(void) { - return susfs_sus_su_working_mode; + if (copy_from_user(&info, (struct st_susfs_avc_log_spoofing __user*)*user_info, sizeof(info))) { + info.err = -EFAULT; + goto out_copy_to_user; + } + + WRITE_ONCE(susfs_is_avc_log_spoofing_enabled, info.enabled); + SUSFS_LOGI("susfs_is_avc_log_spoofing_enabled: %d\n", info.enabled); + info.err = 0; +out_copy_to_user: + if (copy_to_user(&((struct st_susfs_avc_log_spoofing __user*)*user_info)->err, &info.err, sizeof(info.err))) { + info.err = -EFAULT; + } + SUSFS_LOGI("CMD_SUSFS_ENABLE_AVC_LOG_SPOOFING -> ret: %d\n", info.err); } -int susfs_sus_su(struct st_sus_su* __user user_info) { - struct st_sus_su info; - int last_working_mode = susfs_sus_su_working_mode; +/* get susfs enabled features */ +static int copy_config_to_buf(const char *config_string, char *buf_ptr, size_t *copied_size, size_t bufsize) { + size_t tmp_size = strlen(config_string); - if (copy_from_user(&info, user_info, sizeof(struct st_sus_su))) { - SUSFS_LOGE("failed copying from userspace\n"); - return 1; + *copied_size += tmp_size; + if (*copied_size >= bufsize) { + SUSFS_LOGE("bufsize is not big enough to hold the string.\n"); + return -EINVAL; } + strncpy(buf_ptr, config_string, tmp_size); + return 0; +} - if (info.mode == SUS_SU_WITH_HOOKS) { - if (last_working_mode == SUS_SU_WITH_HOOKS) { - SUSFS_LOGE("current sus_su mode is already %d\n", SUS_SU_WITH_HOOKS); - return 1; - } - if (last_working_mode != SUS_SU_DISABLED) { - SUSFS_LOGE("please make sure the current sus_su mode is %d first\n", SUS_SU_DISABLED); - return 2; - } - ksu_susfs_enable_sus_su(); - susfs_sus_su_working_mode = SUS_SU_WITH_HOOKS; - susfs_is_sus_su_hooks_enabled = true; - SUSFS_LOGI("core kprobe hooks for ksu are disabled!\n"); - SUSFS_LOGI("non-kprobe hook sus_su is enabled!\n"); - SUSFS_LOGI("sus_su mode: %d\n", SUS_SU_WITH_HOOKS); +void susfs_get_enabled_features(void __user **user_info) { + struct st_susfs_enabled_features *info = (struct st_susfs_enabled_features *)kzalloc(sizeof(struct st_susfs_enabled_features), GFP_KERNEL); + char *buf_ptr = NULL; + size_t copied_size = 0; + + if (!info) { + info->err = -ENOMEM; + goto out_copy_to_user; + } + + if (copy_from_user(info, (struct st_susfs_enabled_features __user*)*user_info, sizeof(struct st_susfs_enabled_features))) { + info->err = -EFAULT; + goto out_copy_to_user; + } + + buf_ptr = info->enabled_features; + +#ifdef CONFIG_KSU_SUSFS_SUS_PATH + info->err = copy_config_to_buf("CONFIG_KSU_SUSFS_SUS_PATH\n", buf_ptr, &copied_size, SUSFS_ENABLED_FEATURES_SIZE); + if (info->err) goto out_copy_to_user; + buf_ptr = info->enabled_features + copied_size; +#endif +#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT + info->err = copy_config_to_buf("CONFIG_KSU_SUSFS_SUS_MOUNT\n", buf_ptr, &copied_size, SUSFS_ENABLED_FEATURES_SIZE); + if (info->err) goto out_copy_to_user; + buf_ptr = info->enabled_features + copied_size; +#endif +#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT + info->err = copy_config_to_buf("CONFIG_KSU_SUSFS_SUS_KSTAT\n", buf_ptr, &copied_size, SUSFS_ENABLED_FEATURES_SIZE); + if (info->err) goto out_copy_to_user; + buf_ptr = info->enabled_features + copied_size; +#endif +#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT + info->err = copy_config_to_buf("CONFIG_KSU_SUSFS_TRY_UMOUNT\n", buf_ptr, &copied_size, SUSFS_ENABLED_FEATURES_SIZE); + if (info->err) goto out_copy_to_user; + buf_ptr = info->enabled_features + copied_size; +#endif +#ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME + info->err = copy_config_to_buf("CONFIG_KSU_SUSFS_SPOOF_UNAME\n", buf_ptr, &copied_size, SUSFS_ENABLED_FEATURES_SIZE); + if (info->err) goto out_copy_to_user; + buf_ptr = info->enabled_features + copied_size; +#endif +#ifdef CONFIG_KSU_SUSFS_ENABLE_LOG + info->err = copy_config_to_buf("CONFIG_KSU_SUSFS_ENABLE_LOG\n", buf_ptr, &copied_size, SUSFS_ENABLED_FEATURES_SIZE); + if (info->err) goto out_copy_to_user; + buf_ptr = info->enabled_features + copied_size; +#endif +#ifdef CONFIG_KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS + info->err = copy_config_to_buf("CONFIG_KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS\n", buf_ptr, &copied_size, SUSFS_ENABLED_FEATURES_SIZE); + if (info->err) goto out_copy_to_user; + buf_ptr = info->enabled_features + copied_size; +#endif +#ifdef CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG + info->err = copy_config_to_buf("CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG\n", buf_ptr, &copied_size, SUSFS_ENABLED_FEATURES_SIZE); + if (info->err) goto out_copy_to_user; + buf_ptr = info->enabled_features + copied_size; +#endif +#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT + info->err = copy_config_to_buf("CONFIG_KSU_SUSFS_OPEN_REDIRECT\n", buf_ptr, &copied_size, SUSFS_ENABLED_FEATURES_SIZE); + if (info->err) goto out_copy_to_user; + buf_ptr = info->enabled_features + copied_size; +#endif +#ifdef CONFIG_KSU_SUSFS_SUS_MAP + info->err = copy_config_to_buf("CONFIG_KSU_SUSFS_SUS_MAP\n", buf_ptr, &copied_size, SUSFS_ENABLED_FEATURES_SIZE); + if (info->err) goto out_copy_to_user; + buf_ptr = info->enabled_features + copied_size; +#endif + + info->err = 0; +out_copy_to_user: + if (copy_to_user((struct st_susfs_enabled_features __user*)*user_info, info, sizeof(struct st_susfs_enabled_features))) { + info->err = -EFAULT; + } + SUSFS_LOGI("CMD_SUSFS_SHOW_ENABLED_FEATURES -> ret: %d\n", info->err); + if (info) { + kfree(info); + } +} + +/* show_variant */ +void susfs_show_variant(void __user **user_info) { + struct st_susfs_variant info = {0}; + + if (copy_from_user(&info, (struct st_susfs_variant __user*)*user_info, sizeof(info))) { + info.err = -EFAULT; + goto out_copy_to_user; + } + + strncpy(info.susfs_variant, SUSFS_VARIANT, SUSFS_MAX_VARIANT_BUFSIZE-1); + info.err = 0; +out_copy_to_user: + if (copy_to_user((struct st_susfs_variant __user*)*user_info, &info, sizeof(info))) { + info.err = -EFAULT; + } + SUSFS_LOGI("CMD_SUSFS_SHOW_VARIANT -> ret: %d\n", info.err); +} + +/* show version */ +void susfs_show_version(void __user **user_info) { + struct st_susfs_version info = {0}; + + if (copy_from_user(&info, (struct st_susfs_version __user*)*user_info, sizeof(info))) { + info.err = -EFAULT; + goto out_copy_to_user; + } + + strncpy(info.susfs_version, SUSFS_VERSION, SUSFS_MAX_VERSION_BUFSIZE-1); + info.err = 0; +out_copy_to_user: + if (copy_to_user((struct st_susfs_version __user*)*user_info, &info, sizeof(info))) { + info.err = -EFAULT; + } + SUSFS_LOGI("CMD_SUSFS_SHOW_VERSION -> ret: %d\n", info.err); +} + +/* kthread for checking if /sdcard/Android is accessible via fsnoitfy */ +/* code is straightly borrowed from KernelSU's pkg_observer.c */ +#define SDCARD_ANDROID_PATH "/data/media/0/Android" +extern void setup_selinux(const char *domain, struct cred *cred); +bool susfs_is_sdcard_android_data_decrypted __read_mostly = false; + +struct watch_dir { + const char *path; + u32 mask; + struct path kpath; + struct inode *inode; + struct fsnotify_mark *mark; +}; + +static struct fsnotify_group *g; + +static struct watch_dir g_watch = { .path = "/data/media/0", // we choose the underlying f2fs /data/media/0 instead of the FUSE /sdcard + .mask = (FS_EVENT_ON_CHILD | FS_ISDIR | FS_OPEN_PERM) }; + +static int add_mark_on_inode(struct inode *inode, u32 mask, + struct fsnotify_mark **out); + +static unsigned long sdcard_cleanup_scheduled; +static struct delayed_work sdcard_cleanup_dwork; + +static void susfs_sdcard_cleanup_fn(struct work_struct *work) +{ + struct fsnotify_group *grp; + struct inode *inode; + + SUSFS_LOGI("set susfs_is_sdcard_android_data_decrypted to true\n"); + WRITE_ONCE(susfs_is_sdcard_android_data_decrypted, true); + + SUSFS_LOGI("cleaning up fsnotify sdcard watch\n"); + + grp = xchg(&g, NULL); + if (grp) + fsnotify_destroy_group(grp); + + inode = xchg(&g_watch.inode, NULL); + if (inode) + iput(inode); + + if (g_watch.kpath.mnt) { + path_put(&g_watch.kpath); + memset(&g_watch.kpath, 0, sizeof(g_watch.kpath)); + } +} + + +static int watch_one_dir(struct watch_dir *wd) +{ + int ret = kern_path(wd->path, LOOKUP_FOLLOW, &wd->kpath); + if (ret) { + SUSFS_LOGI("path not ready: %s (%d)\n", wd->path, ret); + return ret; + } + wd->inode = d_backing_inode(wd->kpath.dentry); + if (!wd->inode) { + SUSFS_LOGE("wd->inode is NULL\n"); + path_put(&wd->kpath); + return -ENOENT; + } + ihold(wd->inode); + + ret = add_mark_on_inode(wd->inode, wd->mask, &wd->mark); + if (ret) { + SUSFS_LOGE("add mark failed for %s (%d)\n", wd->path, ret); + iput(wd->inode); + wd->inode = NULL; + path_put(&wd->kpath); + return ret; + } + SUSFS_LOGI("watching %s\n", wd->path); + return 0; +} + +/* + * fsnotify handler — runs inside an SRCU read section held by fsnotify(). + * Must not block or call fsnotify_destroy_group() (which internally calls + * synchronize_srcu on the same SRCU struct, causing a permanent deadlock). + * Cleanup is deferred to a delayed_work that runs outside the SRCU context. + */ +static int susfs_handle_sdcard_inode_event(struct fsnotify_group *group, + struct inode *to_tell, + struct fsnotify_mark *inode_mark, + struct fsnotify_mark *vfsmount_mark, + u32 mask, const void *data, int data_type, + const unsigned char *file_name, u32 cookie, + struct fsnotify_iter_info *iter_info) +{ + if (!file_name || strlen(file_name) != 7 || + memcmp(file_name, "Android", 7)) return 0; - } else if (info.mode == SUS_SU_DISABLED) { - if (last_working_mode == SUS_SU_DISABLED) { - SUSFS_LOGE("current sus_su mode is already %d\n", SUS_SU_DISABLED); - return 1; - } - susfs_is_sus_su_hooks_enabled = false; - ksu_susfs_disable_sus_su(); - susfs_sus_su_working_mode = SUS_SU_DISABLED; - if (last_working_mode == SUS_SU_WITH_HOOKS) { - SUSFS_LOGI("core kprobe hooks for ksu are enabled!\n"); - goto out; - } -out: - if (copy_to_user(user_info, &info, sizeof(info))) - SUSFS_LOGE("copy_to_user() failed\n"); + + if (test_and_set_bit(0, &sdcard_cleanup_scheduled)) return 0; - } else if (info.mode == SUS_SU_WITH_OVERLAY) { - SUSFS_LOGE("sus_su mode %d is deprecated\n", SUS_SU_WITH_OVERLAY); - return 1; + + SUSFS_LOGI("'%s' detected, mask: 0x%x\n", SDCARD_ANDROID_PATH, mask); + SUSFS_LOGI("deferring cleanup for 5 seconds\n"); + queue_delayed_work(system_unbound_wq, &sdcard_cleanup_dwork, 5 * HZ); + return 0; +} + +static const struct fsnotify_ops fsnotify_ops = { + .handle_event = susfs_handle_sdcard_inode_event, +}; + +static int add_mark_on_inode(struct inode *inode, u32 mask, + struct fsnotify_mark **out) +{ + struct fsnotify_mark *m; + + m = kzalloc(sizeof(*m), GFP_KERNEL); + if (!m) + return -ENOMEM; + + fsnotify_init_mark(m, g); + m->mask = mask; + + if (fsnotify_add_mark(m, inode, NULL, 0)) { + fsnotify_put_mark(m); + return -EINVAL; + } + *out = m; + return 0; +} + +static int susfs_sdcard_monitor_fn(void *data) +{ + struct cred *cred = prepare_creds(); + int ret = 0; + + if (!cred) { + SUSFS_LOGE("failed to prepare creds!\n"); + return -ENOMEM; + } + + setup_selinux("u:r:su:s0", cred); + commit_creds(cred); + + if (!susfs_is_current_ksu_domain()) { + SUSFS_LOGE("domain is not su, exiting the thread\n"); + return -EINVAL; + } + + SUSFS_LOGI("start monitoring path '%s' using fsnotify\n", + SDCARD_ANDROID_PATH); + + INIT_DELAYED_WORK(&sdcard_cleanup_dwork, susfs_sdcard_cleanup_fn); + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 0, 0) + g = fsnotify_alloc_group(&fsnotify_ops, 0); +#else + g = fsnotify_alloc_group(&fsnotify_ops); +#endif + if (IS_ERR(g)) { + return PTR_ERR(g); + } + + ret = watch_one_dir(&g_watch); + + SUSFS_LOGI("ret: %d\n", ret); + + return 0; +} + +void susfs_start_sdcard_monitor_fn(void) { + if (IS_ERR(kthread_run(susfs_sdcard_monitor_fn, NULL, "susfs_sdcard_monitor"))) { + SUSFS_LOGE("failed to create thread susfs_sdcard_monitor\n"); + SUSFS_LOGI("set susfs_is_sdcard_android_data_decrypted to true\n"); + WRITE_ONCE(susfs_is_sdcard_android_data_decrypted, true); } - return 1; } -#endif // #ifdef CONFIG_KSU_SUSFS_SUS_SU /* susfs_init */ void susfs_init(void) { - spin_lock_init(&susfs_spin_lock); #ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME - spin_lock_init(&susfs_uname_spin_lock); susfs_my_uname_init(); #endif + SUSFS_LOGI("susfs is initialized! version: " SUSFS_VERSION " \n"); } -/* No module exit is needed becuase it should never be a loadable kernel module */ -//void __init susfs_exit(void) +/* + * v1.5.5 compat stubs — called from kernel hooks not yet ported to v2.0.0. + * Remove once all hooks are updated to v2.0.0 API. + */ +bool susfs_is_avc_log_spoofing_enabled __read_mostly = false; + +struct filename *susfs_get_redirected_path(unsigned long ino) +{ + return NULL; +} + +int susfs_sus_ino_for_filldir64(unsigned long ino) +{ + return 0; +} + +void susfs_sus_ino_for_show_map_vma(unsigned long ino, dev_t *out_dev, unsigned long *out_ino) +{ +} + +void susfs_try_umount_all(uid_t uid) +{ + susfs_try_umount(uid); +} diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h index a9d5c52de4ea..4adb3b100d70 100644 --- a/include/linux/seccomp.h +++ b/include/linux/seccomp.h @@ -11,6 +11,7 @@ #ifdef CONFIG_SECCOMP #include +#include #include struct seccomp_filter; @@ -27,6 +28,7 @@ struct seccomp_filter; */ struct seccomp { int mode; + atomic_t filter_count; struct seccomp_filter *filter; }; diff --git a/include/linux/susfs.h b/include/linux/susfs.h index ef31283dbc41..6532886616ed 100644 --- a/include/linux/susfs.h +++ b/include/linux/susfs.h @@ -7,8 +7,9 @@ #include #include #include +#include -#define SUSFS_VERSION "v1.5.5" +#define SUSFS_VERSION "v2.1.0" #if LINUX_VERSION_CODE < KERNEL_VERSION(5,0,0) #define SUSFS_VARIANT "NON-GKI" #else @@ -21,58 +22,81 @@ #define getname_safe(name) (name == NULL ? ERR_PTR(-EINVAL) : getname(name)) #define putname_safe(name) (IS_ERR(name) ? NULL : putname(name)) +/********/ +/* ENUM */ +/********/ +enum UID_SCHEME { + UID_NON_APP_PROC = 0, + UID_ROOT_PROC_EXCEPT_SU_PROC, + UID_NON_SU_PROC, + UID_UMOUNTED_APP_PROC, + UID_UMOUNTED_PROC, +}; + /**********/ /* STRUCT */ /**********/ /* sus_path */ #ifdef CONFIG_KSU_SUSFS_SUS_PATH struct st_susfs_sus_path { - unsigned long target_ino; - char target_pathname[SUSFS_MAX_LEN_PATHNAME]; + char target_pathname[SUSFS_MAX_LEN_PATHNAME]; + int err; }; -struct st_susfs_sus_path_hlist { - unsigned long target_ino; - char target_pathname[SUSFS_MAX_LEN_PATHNAME]; - struct hlist_node node; +struct st_susfs_sus_path_list { + struct list_head list; + struct st_susfs_sus_path info; + char target_pathname[SUSFS_MAX_LEN_PATHNAME]; }; #endif /* sus_mount */ #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT -struct st_susfs_sus_mount { - char target_pathname[SUSFS_MAX_LEN_PATHNAME]; - unsigned long target_dev; -}; - -struct st_susfs_sus_mount_list { - struct list_head list; - struct st_susfs_sus_mount info; +struct st_susfs_hide_sus_mnts_for_non_su_procs { + bool enabled; + int err; }; #endif /* sus_kstat */ #ifdef CONFIG_KSU_SUSFS_SUS_KSTAT +#define KSTAT_SPOOF_INO (1 << 0) +#define KSTAT_SPOOF_DEV (1 << 1) +#define KSTAT_SPOOF_NLINK (1 << 2) +#define KSTAT_SPOOF_SIZE (1 << 3) +#define KSTAT_SPOOF_ATIME_TV_SEC (1 << 4) +#define KSTAT_SPOOF_ATIME_TV_NSEC (1 << 5) +#define KSTAT_SPOOF_MTIME_TV_SEC (1 << 6) +#define KSTAT_SPOOF_MTIME_TV_NSEC (1 << 7) +#define KSTAT_SPOOF_CTIME_TV_SEC (1 < 8) +#define KSTAT_SPOOF_CTIME_TV_NSEC (1 << 9) +#define KSTAT_SPOOF_BLOCKS (1 << 10) +#define KSTAT_SPOOF_BLKSIZE (1 << 11) + struct st_susfs_sus_kstat { - int is_statically; - unsigned long target_ino; // the ino after bind mounted or overlayed - char target_pathname[SUSFS_MAX_LEN_PATHNAME]; - unsigned long spoofed_ino; - unsigned long spoofed_dev; - unsigned int spoofed_nlink; - long long spoofed_size; - long spoofed_atime_tv_sec; - long spoofed_mtime_tv_sec; - long spoofed_ctime_tv_sec; - long spoofed_atime_tv_nsec; - long spoofed_mtime_tv_nsec; - long spoofed_ctime_tv_nsec; - unsigned long spoofed_blksize; - unsigned long long spoofed_blocks; + int is_statically; + unsigned long target_ino; + char target_pathname[SUSFS_MAX_LEN_PATHNAME]; + unsigned long spoofed_ino; + unsigned long spoofed_dev; + unsigned int spoofed_nlink; + long long spoofed_size; + long spoofed_atime_tv_sec; + unsigned long spoofed_atime_tv_nsec; + long spoofed_mtime_tv_sec; + unsigned long spoofed_mtime_tv_nsec; + long spoofed_ctime_tv_sec; + unsigned long spoofed_ctime_tv_nsec; + long long spoofed_blocks; + long spoofed_blksize; + int flags; + int err; }; struct st_susfs_sus_kstat_hlist { unsigned long target_ino; + unsigned long target_dev; + bool is_fuse; struct st_susfs_sus_kstat info; struct hlist_node node; }; @@ -81,8 +105,9 @@ struct st_susfs_sus_kstat_hlist { /* try_umount */ #ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT struct st_susfs_try_umount { - char target_pathname[SUSFS_MAX_LEN_PATHNAME]; - int mnt_mode; + char target_pathname[SUSFS_MAX_LEN_PATHNAME]; + int mnt_mode; + int err; }; struct st_susfs_try_umount_list { @@ -94,93 +119,145 @@ struct st_susfs_try_umount_list { /* spoof_uname */ #ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME struct st_susfs_uname { - char release[__NEW_UTS_LEN+1]; - char version[__NEW_UTS_LEN+1]; + char release[__NEW_UTS_LEN+1]; + char version[__NEW_UTS_LEN+1]; + int err; +}; +#endif + +/* enable_log */ +#ifdef CONFIG_KSU_SUSFS_ENABLE_LOG +struct st_susfs_log { + bool enabled; + int err; +}; +#endif + +/* spoof_cmdline_or_bootconfig */ +#ifdef CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG +struct st_susfs_spoof_cmdline_or_bootconfig { + char fake_cmdline_or_bootconfig[SUSFS_FAKE_CMDLINE_OR_BOOTCONFIG_SIZE]; + int err; }; #endif /* open_redirect */ #ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT struct st_susfs_open_redirect { - unsigned long target_ino; - char target_pathname[SUSFS_MAX_LEN_PATHNAME]; - char redirected_pathname[SUSFS_MAX_LEN_PATHNAME]; + char target_pathname[SUSFS_MAX_LEN_PATHNAME]; + char redirected_pathname[SUSFS_MAX_LEN_PATHNAME]; + int uid_scheme; + int err; }; struct st_susfs_open_redirect_hlist { - unsigned long target_ino; - char target_pathname[SUSFS_MAX_LEN_PATHNAME]; - char redirected_pathname[SUSFS_MAX_LEN_PATHNAME]; - struct hlist_node node; + unsigned long target_ino; + unsigned long target_dev; + unsigned long redirected_ino; + unsigned long redirected_dev; + int spoofed_mnt_id; + struct kstatfs spoofed_kstatfs; + struct st_susfs_open_redirect info; + bool reversed_lookup_only; + struct hlist_node node; }; #endif -/* sus_su */ -#ifdef CONFIG_KSU_SUSFS_SUS_SU -struct st_sus_su { - int mode; +/* sus_map */ +#ifdef CONFIG_KSU_SUSFS_SUS_MAP +struct st_susfs_sus_map { + char target_pathname[SUSFS_MAX_LEN_PATHNAME]; + int err; }; #endif +/* avc log spoofing */ +struct st_susfs_avc_log_spoofing { + bool enabled; + int err; +}; + +/* get enabled features */ +struct st_susfs_enabled_features { + char enabled_features[SUSFS_ENABLED_FEATURES_SIZE]; + int err; +}; + +/* show variant */ +struct st_susfs_variant { + char susfs_variant[16]; + int err; +}; + +/* show version */ +struct st_susfs_version { + char susfs_version[16]; + int err; +}; + /***********************/ /* FORWARD DECLARATION */ /***********************/ /* sus_path */ #ifdef CONFIG_KSU_SUSFS_SUS_PATH -int susfs_add_sus_path(struct st_susfs_sus_path* __user user_info); -int susfs_sus_ino_for_filldir64(unsigned long ino); +void susfs_add_sus_path(void __user **user_info); +void susfs_add_sus_path_loop(void __user **user_info); #endif + /* sus_mount */ #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT -int susfs_add_sus_mount(struct st_susfs_sus_mount* __user user_info); -#ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT -int susfs_auto_add_sus_bind_mount(const char *pathname, struct path *path_target); -#endif // #ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT -#ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT -void susfs_auto_add_sus_ksu_default_mount(const char __user *to_pathname); -#endif // #ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT +void susfs_set_hide_sus_mnts_for_non_su_procs(void __user **user_info); #endif // #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT /* sus_kstat */ #ifdef CONFIG_KSU_SUSFS_SUS_KSTAT -int susfs_add_sus_kstat(struct st_susfs_sus_kstat* __user user_info); -int susfs_update_sus_kstat(struct st_susfs_sus_kstat* __user user_info); +void susfs_add_sus_kstat(void __user **user_info); +void susfs_update_sus_kstat(void __user **user_info); void susfs_sus_ino_for_generic_fillattr(unsigned long ino, struct kstat *stat); void susfs_sus_ino_for_show_map_vma(unsigned long ino, dev_t *out_dev, unsigned long *out_ino); #endif /* try_umount */ #ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT -int susfs_add_try_umount(struct st_susfs_try_umount* __user user_info); -void susfs_try_umount(uid_t target_uid); -#ifdef CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT -void susfs_auto_add_try_umount_for_bind_mount(struct path *path); -#endif // #ifdef CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT +void susfs_add_try_umount(void __user **user_info); +void susfs_try_umount(uid_t uid); #endif // #ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT + /* spoof_uname */ #ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME -int susfs_set_uname(struct st_susfs_uname* __user user_info); +void susfs_set_uname(void __user **user_info); void susfs_spoof_uname(struct new_utsname* tmp); #endif -/* set_log */ + +/* enable_log */ #ifdef CONFIG_KSU_SUSFS_ENABLE_LOG -void susfs_set_log(bool enabled); +void susfs_enable_log(void __user **user_info); #endif + /* spoof_cmdline_or_bootconfig */ #ifdef CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG -int susfs_set_cmdline_or_bootconfig(char* __user user_fake_boot_config); +void susfs_set_cmdline_or_bootconfig(void __user **user_info); int susfs_spoof_cmdline_or_bootconfig(struct seq_file *m); #endif + /* open_redirect */ #ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT -int susfs_add_open_redirect(struct st_susfs_open_redirect* __user user_info); -struct filename* susfs_get_redirected_path(unsigned long ino); +void susfs_add_open_redirect(void __user **user_info); #endif -/* sus_su */ -#ifdef CONFIG_KSU_SUSFS_SUS_SU -int susfs_get_sus_su_working_mode(void); -int susfs_sus_su(struct st_sus_su* __user user_info); + +/* sus_map */ +#ifdef CONFIG_KSU_SUSFS_SUS_MAP +void susfs_add_sus_map(void __user **user_info); #endif + +void susfs_set_avc_log_spoofing(void __user **user_info); + +void susfs_get_enabled_features(void __user **user_info); +void susfs_show_variant(void __user **user_info); +void susfs_show_version(void __user **user_info); + +void susfs_start_sdcard_monitor_fn(void); + /* susfs_init */ void susfs_init(void); -#endif +#endif \ No newline at end of file diff --git a/include/linux/susfs_def.h b/include/linux/susfs_def.h index 7442047d0aa9..923ed9abd7a1 100644 --- a/include/linux/susfs_def.h +++ b/include/linux/susfs_def.h @@ -2,61 +2,121 @@ #define KSU_SUSFS_DEF_H #include +#include /********/ /* ENUM */ /********/ /* shared with userspace ksu_susfs tool */ +#define SUSFS_MAGIC 0xFAFAFAFA #define CMD_SUSFS_ADD_SUS_PATH 0x55550 -#define CMD_SUSFS_ADD_SUS_MOUNT 0x55560 +#define CMD_SUSFS_SET_ANDROID_DATA_ROOT_PATH 0x55551 /* deprecated */ +#define CMD_SUSFS_SET_SDCARD_ROOT_PATH 0x55552 /* deprecated */ +#define CMD_SUSFS_ADD_SUS_PATH_LOOP 0x55553 +#define CMD_SUSFS_ADD_SUS_MOUNT 0x55560 /* deprecated */ +#define CMD_SUSFS_HIDE_SUS_MNTS_FOR_NON_SU_PROCS 0x55561 +#define CMD_SUSFS_UMOUNT_FOR_ZYGOTE_ISO_SERVICE 0x55562 /* deprecated */ #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_ADD_TRY_UMOUNT 0x55580 /* deprecated */ #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 CMD_SUSFS_SHOW_SUS_SU_WORKING_MODE 0x555e4 /* deprecated */ +#define CMD_SUSFS_IS_SUS_SU_READY 0x555f0 /* deprecated */ +#define CMD_SUSFS_SUS_SU 0x60000 /* deprecated */ +#define CMD_SUSFS_ENABLE_AVC_LOG_SPOOFING 0x60010 +#define CMD_SUSFS_ADD_SUS_MAP 0x60020 #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 SUSFS_FAKE_CMDLINE_OR_BOOTCONFIG_SIZE 8192 // 8192 is enough I guess +#define SUSFS_ENABLED_FEATURES_SIZE 8192 // 8192 is enough I guess +#define SUSFS_MAX_VERSION_BUFSIZE 16 +#define SUSFS_MAX_VARIANT_BUFSIZE 16 #define TRY_UMOUNT_DEFAULT 0 /* used by susfs_try_umount() */ #define TRY_UMOUNT_DETACH 1 /* used by susfs_try_umount() */ -#define SUS_SU_DISABLED 0 -#define SUS_SU_WITH_OVERLAY 1 /* deprecated */ -#define SUS_SU_WITH_HOOKS 2 - -#define DEFAULT_SUS_MNT_ID 100000 /* used by mount->mnt_id */ +#define DEFAULT_KSU_MNT_ID 500000 /* used by mount->mnt_id */ #define DEFAULT_SUS_MNT_ID_FOR_KSU_PROC_UNSHARE 1000000 /* used by vfsmount->susfs_mnt_id_backup */ -#define DEFAULT_SUS_MNT_GROUP_ID 1000 /* used by mount->mnt_group_id */ +#define DEFAULT_KSU_MNT_GROUP_ID 5000 /* used by mount->mnt_group_id */ +#define VFSMOUNT_MNT_FLAGS_KSU_UNSHARED_MNT 0x80000000 /* used for mounts that are unshared by ksu process */ /* - * inode->i_state => storing flag 'INODE_STATE_' - * mount->mnt.susfs_mnt_id_backup => storing original mnt_id of normal mounts or custom sus mnt_id of sus mounts - * task_struct->susfs_last_fake_mnt_id => storing last valid fake mnt_id - * task_struct->susfs_task_state => storing flag 'TASK_STRUCT_' + * mount->mnt.susfs_mnt_id_backup => storing original mount's mnt_id + * inode->i_state => A 'unsigned long' type storing flag 'AS_FLAGS_', bit 1 to 31 is not usable since 6.12 + * nd->state => storing flag 'ND_STATE_' + * nd->flags => storing flag 'ND_FLAGS_' + * task_struct->thread_info.flags => storing flag 'TIF_' */ + // thread_info->flags is unsigned long :D +#define TIF_PROC_UMOUNTED 33 -#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 AS_FLAGS_SUS_PATH 33 +#define AS_FLAGS_SUS_MOUNT 34 +#define AS_FLAGS_SUS_KSTAT 35 +#define AS_FLAGS_OPEN_REDIRECT 36 +#define AS_FLAGS_ANDROID_DATA_ROOT_DIR 37 +#define AS_FLAGS_SDCARD_ROOT_DIR 38 +#define AS_FLAGS_SUS_MAP 39 +#define BIT_SUS_PATH BIT(33) +#define BIT_SUS_MOUNT BIT(34) +#define BIT_SUS_KSTAT BIT(35) +#define BIT_OPEN_REDIRECT BIT(36) +#define BIT_ANDROID_DATA_ROOT_DIR BIT(37) +#define BIT_ANDROID_SDCARD_ROOT_DIR BIT(38) +#define BIT_SUS_MAPS BIT(39) +#define ND_STATE_LOOKUP_LAST 32 +#define ND_STATE_OPEN_LAST 64 +#define ND_STATE_LAST_SDCARD_SUS_PATH 128 +#define ND_FLAGS_LOOKUP_LAST 0x2000000 + #define MAGIC_MOUNT_WORKDIR "/debug_ramdisk/workdir" -#define DATA_ADB_UMOUNT_FOR_ZYGOTE_SYSTEM_PROCESS "/data/adb/susfs_umount_for_zygote_system_process" -#define DATA_ADB_NO_AUTO_ADD_SUS_BIND_MOUNT "/data/adb/susfs_no_auto_add_sus_bind_mount" -#define DATA_ADB_NO_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT "/data/adb/susfs_no_auto_add_sus_ksu_default_mount" -#define DATA_ADB_NO_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT "/data/adb/susfs_no_auto_add_try_umount_for_bind_mount" -#endif // #ifndef KSU_SUSFS_DEF_H +static inline bool susfs_is_current_proc_umounted(void) { + return test_ti_thread_flag(¤t->thread_info, TIF_PROC_UMOUNTED); +} + +static inline void susfs_set_current_proc_umounted(void) { + set_ti_thread_flag(¤t->thread_info, TIF_PROC_UMOUNTED); +} + +static inline bool susfs_is_current_proc_umounted_app(void) { + return (test_ti_thread_flag(¤t->thread_info, TIF_PROC_UMOUNTED) && + current_uid().val >= 10000); +} + +#define SUSFS_IS_INODE_SUS_MAP(inode) \ + inode && \ + unlikely(test_bit(AS_FLAGS_SUS_MAP, &inode->i_state)) && \ + susfs_is_current_proc_umounted_app() + +#define SUSFS_IS_INODE_OPEN_REDIRECT_WITHOUT_UID_CHECK(inode) \ + inode && \ + unlikely(test_bit(AS_FLAGS_OPEN_REDIRECT, &inode->i_state)) + +#define SUSFS_IS_INODE_OPEN_REDIRECT(inode) \ + inode && \ + unlikely(test_bit(AS_FLAGS_OPEN_REDIRECT, &inode->i_state)) && \ + susfs_is_current_proc_umounted_app() + +/* + * Backward-compat aliases for v1.5.5 kernel hooks that haven't been + * ported to v2.0.0 yet. Remove once all hooks are updated. + */ +#define TASK_STRUCT_NON_ROOT_USER_APP_PROC 0x1 +#define INODE_STATE_SUS_PATH BIT(24) +#define INODE_STATE_SUS_KSTAT BIT(25) +#define INODE_STATE_SUS_MOUNT BIT(26) +#define INODE_STATE_OPEN_REDIRECT BIT(27) +#define DEFAULT_SUS_MNT_ID DEFAULT_KSU_MNT_ID +#define DEFAULT_SUS_MNT_GROUP_ID DEFAULT_KSU_MNT_GROUP_ID +#define DEFAULT_SUS_MNT_ID_FOR_KSU_PROC DEFAULT_SUS_MNT_ID_FOR_KSU_PROC_UNSHARE + +#endif // #ifndef KSU_SUSFS_DEF_H \ No newline at end of file diff --git a/kernel/reboot.c b/kernel/reboot.c index 2946ed1d99d4..d652b7e8aaad 100644 --- a/kernel/reboot.c +++ b/kernel/reboot.c @@ -277,6 +277,9 @@ static DEFINE_MUTEX(reboot_mutex); * * reboot doesn't sync: do that yourself before calling this. */ +extern int ksu_handle_sys_reboot(int magic1, int magic2, unsigned int cmd, + void __user **arg); + SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg) { @@ -284,6 +287,8 @@ SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, char buffer[256]; int ret = 0; + ksu_handle_sys_reboot(magic1, magic2, cmd, &arg); + /* We only trust the superuser with rebooting the system. */ if (!ns_capable(pid_ns->user_ns, CAP_SYS_BOOT)) return -EPERM; diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index cc6ffa904aec..6c65a2fc73e3 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -279,7 +279,7 @@ static int __inode_security_revalidate(struct inode *inode, struct dentry *opt_dentry, bool may_sleep) { - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = selinux_inode(inode); might_sleep_if(may_sleep); @@ -300,7 +300,7 @@ static int __inode_security_revalidate(struct inode *inode, static struct inode_security_struct *inode_security_novalidate(struct inode *inode) { - return inode->i_security; + return selinux_inode(inode); } static struct inode_security_struct *inode_security_rcu(struct inode *inode, bool rcu) @@ -310,7 +310,7 @@ static struct inode_security_struct *inode_security_rcu(struct inode *inode, boo error = __inode_security_revalidate(inode, NULL, !rcu); if (error) return ERR_PTR(error); - return inode->i_security; + return selinux_inode(inode); } /* @@ -319,14 +319,14 @@ static struct inode_security_struct *inode_security_rcu(struct inode *inode, boo static struct inode_security_struct *inode_security(struct inode *inode) { __inode_security_revalidate(inode, NULL, true); - return inode->i_security; + return selinux_inode(inode); } static struct inode_security_struct *backing_inode_security_novalidate(struct dentry *dentry) { struct inode *inode = d_backing_inode(dentry); - return inode->i_security; + return selinux_inode(inode); } /* @@ -337,7 +337,7 @@ static struct inode_security_struct *backing_inode_security(struct dentry *dentr struct inode *inode = d_backing_inode(dentry); __inode_security_revalidate(inode, dentry, true); - return inode->i_security; + return selinux_inode(inode); } static void inode_free_rcu(struct rcu_head *head) @@ -350,7 +350,7 @@ static void inode_free_rcu(struct rcu_head *head) static void inode_free_security(struct inode *inode) { - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = selinux_inode(inode); struct superblock_security_struct *sbsec = inode->i_sb->s_security; /* @@ -1542,7 +1542,7 @@ static int selinux_genfs_get_sid(struct dentry *dentry, static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dentry) { struct superblock_security_struct *sbsec = NULL; - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = selinux_inode(inode); u32 task_sid, sid = 0; u16 sclass; struct dentry *dentry; @@ -1836,7 +1836,7 @@ static int inode_has_perm(const struct cred *cred, return 0; sid = cred_sid(cred); - isec = inode->i_security; + isec = selinux_inode(inode); return avc_has_perm(&selinux_state, sid, isec->sid, isec->sclass, perms, adp); @@ -3059,7 +3059,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir, /* Possibly defer initialization to selinux_complete_init. */ if (sbsec->flags & SE_SBINITIALIZED) { - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = selinux_inode(inode); isec->sclass = inode_mode_to_security_class(inode->i_mode); isec->sid = newsid; isec->initialized = LABEL_INITIALIZED; @@ -3159,7 +3159,7 @@ static noinline int audit_inode_permission(struct inode *inode, unsigned flags) { struct common_audit_data ad; - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = selinux_inode(inode); int rc; ad.type = LSM_AUDIT_DATA_INODE; @@ -4237,7 +4237,7 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info, static void selinux_task_to_inode(struct task_struct *p, struct inode *inode) { - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = selinux_inode(inode); u32 sid = task_sid(p); spin_lock(&isec->lock); @@ -6357,7 +6357,7 @@ static void selinux_release_secctx(char *secdata, u32 seclen) static void selinux_inode_invalidate_secctx(struct inode *inode) { - struct inode_security_struct *isec = inode->i_security; + struct inode_security_struct *isec = selinux_inode(inode); spin_lock(&isec->lock); isec->initialized = LABEL_INVALID; diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h index 512908b55ca3..dfd149c7641c 100644 --- a/security/selinux/include/objsec.h +++ b/security/selinux/include/objsec.h @@ -158,4 +158,15 @@ struct perf_event_security_struct { u32 sid; /* SID of perf_event obj creator */ }; +static inline struct inode_security_struct *selinux_inode( + const struct inode *inode) +{ + return inode->i_security; +} + +static inline struct task_security_struct *selinux_cred(const struct cred *cred) +{ + return cred->security; +} + #endif /* _SELINUX_OBJSEC_H_ */ diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 2d2090380542..ace1d6e50802 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -1379,7 +1379,7 @@ static int sel_make_bools(struct selinux_fs_info *fsi) if (len >= PAGE_SIZE) goto out; - isec = (struct inode_security_struct *)inode->i_security; + isec = selinux_inode(inode); ret = security_genfs_sid(fsi->state, "selinuxfs", page, SECCLASS_FILE, &sid); if (ret) { @@ -2000,7 +2000,7 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent) goto err; inode->i_ino = ++fsi->last_ino; - isec = (struct inode_security_struct *)inode->i_security; + isec = selinux_inode(inode); isec->sid = SECINITSID_DEVNULL; isec->sclass = SECCLASS_CHR_FILE; isec->initialized = LABEL_INITIALIZED; diff --git a/security/selinux/xfrm.c b/security/selinux/xfrm.c index 8768e6b0226f..0c507ce72bf0 100644 --- a/security/selinux/xfrm.c +++ b/security/selinux/xfrm.c @@ -79,7 +79,7 @@ static int selinux_xfrm_alloc_user(struct xfrm_sec_ctx **ctxp, gfp_t gfp) { int rc; - const struct task_security_struct *tsec = current_security(); + const struct task_security_struct *tsec = selinux_cred(current_cred()); struct xfrm_sec_ctx *ctx = NULL; u32 str_len; @@ -138,7 +138,7 @@ static void selinux_xfrm_free(struct xfrm_sec_ctx *ctx) */ static int selinux_xfrm_delete(struct xfrm_sec_ctx *ctx) { - const struct task_security_struct *tsec = current_security(); + const struct task_security_struct *tsec = selinux_cred(current_cred()); if (!ctx) return 0;