diff --git a/fs/namei.c b/fs/namei.c index ea5e42e16a8b..4be9b0f019e5 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -1618,6 +1618,7 @@ retry: #ifdef CONFIG_KSU_SUSFS_SUS_PATH { if (!found_sus_path && !IS_ERR(dentry) && dentry->d_inode && susfs_is_inode_sus_path(dentry->d_inode)) { + dput(dentry); dentry = lookup_dcache(&susfs_fake_qstr_name, base, flags); found_sus_path = true; goto retry; @@ -1686,6 +1687,7 @@ static int lookup_fast(struct nameidata *nd, #ifdef CONFIG_KSU_SUSFS_SUS_PATH if (is_nd_state_lookup_last_and_open_last && dentry && !IS_ERR(dentry) && dentry->d_inode && parent->d_inode) { if (susfs_is_inode_sus_path(dentry->d_inode)) { + dput(dentry); dentry = __d_lookup_rcu(parent, &susfs_fake_qstr_name, &backup_next_seq); } } @@ -1758,6 +1760,7 @@ skip_orig_flow1: #ifdef CONFIG_KSU_SUSFS_SUS_PATH if (is_nd_state_lookup_last_and_open_last && dentry && !IS_ERR(dentry) && dentry->d_inode && parent->d_inode) { if (susfs_is_inode_sus_path(dentry->d_inode)) { + dput(dentry); dentry = __d_lookup(parent, &susfs_fake_qstr_name); } } @@ -1859,6 +1862,7 @@ retry: if (is_nd_flags_lookup_last && !found_sus_path) { if (dentry && !IS_ERR(dentry) && dentry->d_inode) { if (susfs_is_inode_sus_path(dentry->d_inode)) { + dput(dentry); dentry = d_alloc_parallel(dir, &susfs_fake_qstr_name, &sus_wq); found_sus_path = true; goto retry; @@ -3386,6 +3390,7 @@ static int lookup_open(struct nameidata *nd, struct path *path, #ifdef CONFIG_KSU_SUSFS_SUS_PATH if (is_nd_state_open_last && dentry && !IS_ERR(dentry) && dentry->d_inode) { if (susfs_is_inode_sus_path(dentry->d_inode)) { + dput(dentry); dentry = d_lookup(dir, &susfs_fake_qstr_name); found_sus_path = true; } @@ -3861,7 +3866,7 @@ struct file *do_filp_open(int dfd, struct filename *pathname, if (unlikely(filp == ERR_PTR(-ESTALE))) filp = path_openat(&nd, op, flags | LOOKUP_REVAL); #ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT - if (!IS_ERR(filp) && unlikely(filp->f_inode->i_state & INODE_STATE_OPEN_REDIRECT) && current_uid().val < 11000) { + if (!IS_ERR(filp) && unlikely(filp->f_inode->i_mapping->flags & BIT_OPEN_REDIRECT) && current_uid().val < 11000) { fake_pathname = susfs_get_redirected_path(filp->f_inode->i_ino); if (!IS_ERR(fake_pathname)) { restore_nameidata(); diff --git a/fs/notify/fdinfo.c b/fs/notify/fdinfo.c index 1ccf953775d6..e6cc28a4bc50 100644 --- a/fs/notify/fdinfo.c +++ b/fs/notify/fdinfo.c @@ -103,7 +103,7 @@ static void inotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark) if (inode) { #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT if (likely(susfs_is_current_non_root_user_app_proc()) && - unlikely(inode->i_state & INODE_STATE_SUS_KSTAT)) { + unlikely(inode->i_mapping->flags & BIT_SUS_KSTAT)) { struct path path; char *pathname = kmalloc(PAGE_SIZE, GFP_KERNEL); char *dpath; diff --git a/fs/proc/task_mmu.c b/fs/proc/task_mmu.c index e68ca4666262..864833d31621 100644 --- a/fs/proc/task_mmu.c +++ b/fs/proc/task_mmu.c @@ -370,7 +370,7 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma, int is_pid) if (file) { struct inode *inode = file_inode(vma->vm_file); #ifdef CONFIG_KSU_SUSFS_SUS_KSTAT - if (unlikely(inode->i_state & INODE_STATE_SUS_KSTAT)) { + if (unlikely(inode->i_mapping->flags & BIT_SUS_KSTAT)) { susfs_sus_ino_for_show_map_vma(inode->i_ino, &dev, &ino); goto bypass_orig_flow; } diff --git a/fs/stat.c b/fs/stat.c index 0cfa6629b44b..50063917bd2a 100644 --- a/fs/stat.c +++ b/fs/stat.c @@ -38,7 +38,7 @@ void generic_fillattr(struct inode *inode, struct kstat *stat) { #ifdef CONFIG_KSU_SUSFS_SUS_KSTAT if (likely(susfs_is_current_non_root_user_app_proc()) && - unlikely(inode->i_state & INODE_STATE_SUS_KSTAT)) { + unlikely(inode->i_mapping->flags & BIT_SUS_KSTAT)) { susfs_sus_ino_for_generic_fillattr(inode->i_ino, stat); stat->mode = inode->i_mode; stat->rdev = inode->i_rdev; diff --git a/fs/statfs.c b/fs/statfs.c index 8752ced3b3f9..dc433176f25a 100644 --- a/fs/statfs.c +++ b/fs/statfs.c @@ -237,7 +237,7 @@ int vfs_ustat(dev_t dev, struct kstatfs *sbuf) return -EINVAL; #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT - if (unlikely(s->s_root->d_inode->i_state & INODE_STATE_SUS_MOUNT)) { + if (unlikely(s->s_root->d_inode->i_mapping->flags & BIT_SUS_MOUNT)) { return -EINVAL; } #endif diff --git a/fs/susfs.c b/fs/susfs.c index c0a7a9043467..603aba889790 100644 --- a/fs/susfs.c +++ b/fs/susfs.c @@ -35,6 +35,7 @@ bool susfs_is_log_enabled __read_mostly = true; /* sus_path */ #ifdef CONFIG_KSU_SUSFS_SUS_PATH +static LIST_HEAD(LH_SUS_PATH_LOOP); static LIST_HEAD(LH_SUS_PATH_ANDROID_DATA); static LIST_HEAD(LH_SUS_PATH_SDCARD); static struct st_android_data_path android_data_path = {0}; @@ -127,7 +128,7 @@ int susfs_add_sus_path(struct st_susfs_sus_path* __user user_info) { return err; } - err = kern_path(info.target_pathname, LOOKUP_FOLLOW, &path); + err = kern_path(info.target_pathname, 0, &path); if (err) { SUSFS_LOGE("Failed opening file '%s'\n", info.target_pathname); return err; @@ -228,8 +229,8 @@ int susfs_add_sus_path(struct st_susfs_sus_path* __user user_info) { } spin_lock(&inode->i_lock); - inode->i_state |= INODE_STATE_SUS_PATH; - SUSFS_LOGI("pathname: '%s', ino: '%lu', is flagged as INODE_STATE_SUS_PATH\n", resolved_pathname, info.target_ino); + set_bit(AS_FLAGS_SUS_PATH, &inode->i_mapping->flags); + SUSFS_LOGI("pathname: '%s', ino: '%lu', is flagged as AS_FLAGS_SUS_PATH\n", resolved_pathname, info.target_ino); spin_unlock(&inode->i_lock); out_kfree_tmp_buf: kfree(tmp_buf); @@ -238,6 +239,111 @@ out_path_put_path: return err; } +int susfs_add_sus_path_loop(struct st_susfs_sus_path* __user user_info) { + struct st_susfs_sus_path_list *cursor = NULL, *temp = NULL; + struct st_susfs_sus_path_list *new_list = NULL; + struct st_susfs_sus_path info; + struct path path; + struct inode *inode = NULL; + char *resolved_pathname = NULL, *tmp_buf = NULL; + int err = 0; + + err = copy_from_user(&info, user_info, sizeof(info)); + if (err) { + SUSFS_LOGE("failed copying from userspace\n"); + return err; + } + + err = kern_path(info.target_pathname, 0, &path); + if (err) { + SUSFS_LOGE("Failed opening file '%s'\n", info.target_pathname); + return err; + } + + if (!path.dentry->d_inode) { + err = -EINVAL; + goto out_path_put_path; + } + inode = d_inode(path.dentry); + + tmp_buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!tmp_buf) { + err = -ENOMEM; + goto out_path_put_path; + } + + resolved_pathname = d_path(&path, tmp_buf, PAGE_SIZE); + SUSFS_LOGI("resolved_pathname: %s\n", resolved_pathname); + if (!resolved_pathname) { + err = -ENOMEM; + goto out_kfree_tmp_buf; + } + + if (susfs_starts_with(resolved_pathname, "/storage/")) { + err = -EINVAL; + SUSFS_LOGE("path starts with /storage and /sdcard cannot be added by add_sus_path_loop\n"); + goto out_kfree_tmp_buf; + } + + list_for_each_entry_safe(cursor, temp, &LH_SUS_PATH_LOOP, list) { + if (unlikely(!strcmp(cursor->info.target_pathname, resolved_pathname))) { + spin_lock(&susfs_spin_lock); + cursor->info.target_ino = info.target_ino; + strncpy(cursor->info.target_pathname, resolved_pathname, SUSFS_MAX_LEN_PATHNAME - 1); + strncpy(cursor->target_pathname, resolved_pathname, SUSFS_MAX_LEN_PATHNAME - 1); + cursor->info.i_uid = info.i_uid; + cursor->path_len = strlen(cursor->info.target_pathname); + SUSFS_LOGI("target_ino: '%lu', target_pathname: '%s', i_uid: '%u', is successfully updated to LH_SUS_PATH_LOOP\n", + cursor->info.target_ino, cursor->target_pathname, cursor->info.i_uid); + spin_unlock(&susfs_spin_lock); + goto out_set_sus_path; + } + } + new_list = kmalloc(sizeof(struct st_susfs_sus_path_list), GFP_KERNEL); + if (!new_list) { + err = -ENOMEM; + goto out_kfree_tmp_buf; + } + new_list->info.target_ino = info.target_ino; + strncpy(new_list->info.target_pathname, resolved_pathname, SUSFS_MAX_LEN_PATHNAME - 1); + strncpy(new_list->target_pathname, resolved_pathname, SUSFS_MAX_LEN_PATHNAME - 1); + new_list->info.i_uid = info.i_uid; + new_list->path_len = strlen(new_list->info.target_pathname); + INIT_LIST_HEAD(&new_list->list); + spin_lock(&susfs_spin_lock); + list_add_tail(&new_list->list, &LH_SUS_PATH_LOOP); + SUSFS_LOGI("target_ino: '%lu', target_pathname: '%s', i_uid: '%u', is successfully added to LH_SUS_PATH_LOOP\n", + new_list->info.target_ino, new_list->target_pathname, new_list->info.i_uid); + spin_unlock(&susfs_spin_lock); +out_set_sus_path: + spin_lock(&inode->i_lock); + set_bit(AS_FLAGS_SUS_PATH, &inode->i_mapping->flags); + SUSFS_LOGI("pathname: '%s', ino: '%lu', is flagged as AS_FLAGS_SUS_PATH\n", resolved_pathname, info.target_ino); + spin_unlock(&inode->i_lock); +out_kfree_tmp_buf: + kfree(tmp_buf); +out_path_put_path: + path_put(&path); + return err; +} + +void susfs_run_sus_path_loop(uid_t uid) { + struct st_susfs_sus_path_list *cursor = NULL, *temp = NULL; + struct path path; + struct inode *inode; + + list_for_each_entry_safe(cursor, temp, &LH_SUS_PATH_LOOP, list) { + if (!kern_path(cursor->target_pathname, 0, &path)) { + inode = path.dentry->d_inode; + spin_lock(&inode->i_lock); + set_bit(AS_FLAGS_SUS_PATH, &inode->i_mapping->flags); + spin_unlock(&inode->i_lock); + path_put(&path); + SUSFS_LOGI("re-flag '%s' as SUS_PATH for uid: %u\n", cursor->target_pathname, uid); + } + } +} + static inline bool is_i_uid_in_android_data_not_allowed(uid_t i_uid) { return (likely(susfs_is_current_non_root_user_app_proc()) && unlikely(current_uid().val != i_uid)); @@ -302,7 +408,7 @@ bool susfs_is_sus_sdcard_d_name_found(const char *d_name) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0) bool susfs_is_inode_sus_path(struct mnt_idmap* idmap, struct inode *inode) { - if (unlikely(inode->i_state & INODE_STATE_SUS_PATH && + if (unlikely(inode->i_mapping->flags & BIT_SUS_PATH && is_i_uid_not_allowed(i_uid_into_vfsuid(idmap, inode).val))) { SUSFS_LOGI("hiding path with ino '%lu'\n", inode->i_ino); @@ -312,7 +418,7 @@ bool susfs_is_inode_sus_path(struct mnt_idmap* idmap, struct inode *inode) { } #elif LINUX_VERSION_CODE >= KERNEL_VERSION(5, 15, 0) bool susfs_is_inode_sus_path(struct inode *inode) { - if (unlikely(inode->i_state & INODE_STATE_SUS_PATH && + if (unlikely(inode->i_mapping->flags & BIT_SUS_PATH && is_i_uid_not_allowed(i_uid_into_mnt(i_user_ns(inode), inode).val))) { SUSFS_LOGI("hiding path with ino '%lu'\n", inode->i_ino); @@ -322,7 +428,7 @@ bool susfs_is_inode_sus_path(struct inode *inode) { } #else bool susfs_is_inode_sus_path(struct inode *inode) { - if (unlikely(inode->i_state & INODE_STATE_SUS_PATH && + if (unlikely(inode->i_mapping->flags & BIT_SUS_PATH && is_i_uid_not_allowed(inode->i_uid.val))) { SUSFS_LOGI("hiding path with ino '%lu'\n", inode->i_ino); @@ -352,7 +458,7 @@ static void susfs_update_sus_mount_inode(char *target_pathname) { /* 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. + * instead of BIT_SUS_MOUNT. */ mnt = real_mount(p.mnt); if (mnt->mnt_group_id > 0 && // 0 means no peer group @@ -368,9 +474,9 @@ static void susfs_update_sus_mount_inode(char *target_pathname) { return; } - if (!(inode->i_state & INODE_STATE_SUS_MOUNT)) { + if (!(inode->i_mapping->flags & BIT_SUS_MOUNT)) { spin_lock(&inode->i_lock); - inode->i_state |= INODE_STATE_SUS_MOUNT; + set_bit(AS_FLAGS_SUS_MOUNT, &inode->i_mapping->flags); spin_unlock(&inode->i_lock); } path_put(&p); @@ -440,9 +546,9 @@ int susfs_auto_add_sus_bind_mount(const char *pathname, struct path *path_target } inode = path_target->dentry->d_inode; if (!inode) return 1; - if (!(inode->i_state & INODE_STATE_SUS_MOUNT)) { + if (!(inode->i_mapping->flags & BIT_SUS_MOUNT)) { spin_lock(&inode->i_lock); - inode->i_state |= INODE_STATE_SUS_MOUNT; + set_bit(AS_FLAGS_SUS_MOUNT, &inode->i_mapping->flags); spin_unlock(&inode->i_lock); SUSFS_LOGI("set SUS_MOUNT inode state for source bind mount path '%s'\n", pathname); } @@ -484,9 +590,9 @@ set_inode_sus_mount: goto out_path_put; return; } - if (!(inode->i_state & INODE_STATE_SUS_MOUNT)) { + if (!(inode->i_mapping->flags & BIT_SUS_MOUNT)) { spin_lock(&inode->i_lock); - inode->i_state |= INODE_STATE_SUS_MOUNT; + set_bit(AS_FLAGS_SUS_MOUNT, &inode->i_mapping->flags); spin_unlock(&inode->i_lock); SUSFS_LOGI("set SUS_MOUNT inode state for default KSU mount path '%s'\n", pathname); } @@ -506,7 +612,7 @@ static int susfs_update_sus_kstat_inode(char *target_pathname) { struct inode *inode = NULL; int err = 0; - err = kern_path(target_pathname, LOOKUP_FOLLOW, &p); + err = kern_path(target_pathname, 0, &p); if (err) { SUSFS_LOGE("Failed opening file '%s'\n", target_pathname); return 1; @@ -519,9 +625,9 @@ static int susfs_update_sus_kstat_inode(char *target_pathname) { return 1; } - if (!(inode->i_state & INODE_STATE_SUS_KSTAT)) { + if (!(inode->i_mapping->flags & BIT_SUS_KSTAT)) { spin_lock(&inode->i_lock); - inode->i_state |= INODE_STATE_SUS_KSTAT; + set_bit(AS_FLAGS_SUS_KSTAT, &inode->i_mapping->flags); spin_unlock(&inode->i_lock); } path_put(&p); @@ -771,8 +877,8 @@ void susfs_auto_add_try_umount_for_bind_mount(struct path *path) { #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"); + if (path->dentry->d_inode->i_mapping->flags & BIT_SUS_KSTAT) { + SUSFS_LOGI("skip adding path to try_umount list as its inode is flagged BIT_SUS_KSTAT already\n"); return; } #endif @@ -958,7 +1064,7 @@ static int susfs_update_open_redirect_inode(struct st_susfs_open_redirect_hlist } spin_lock(&inode_target->i_lock); - inode_target->i_state |= INODE_STATE_OPEN_REDIRECT; + set_bit(AS_FLAGS_OPEN_REDIRECT, &inode_target->i_mapping->flags); spin_unlock(&inode_target->i_lock); out_path_put_target: diff --git a/include/linux/susfs_def.h b/include/linux/susfs_def.h index 5c28df71ccf2..9de638e7c403 100644 --- a/include/linux/susfs_def.h +++ b/include/linux/susfs_def.h @@ -10,6 +10,7 @@ #define CMD_SUSFS_ADD_SUS_PATH 0x55550 #define CMD_SUSFS_SET_ANDROID_DATA_ROOT_PATH 0x55551 #define CMD_SUSFS_SET_SDCARD_ROOT_PATH 0x55552 +#define CMD_SUSFS_ADD_SUS_PATH_LOOP 0x55553 #define CMD_SUSFS_ADD_SUS_MOUNT 0x55560 #define CMD_SUSFS_HIDE_SUS_MNTS_FOR_ALL_PROCS 0x55561 #define CMD_SUSFS_UMOUNT_FOR_ZYGOTE_ISO_SERVICE 0x55562 @@ -44,24 +45,26 @@ #define DEFAULT_SUS_MNT_GROUP_ID 1000 /* used by mount->mnt_group_id */ /* - * 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 * inode->i_mapping->flags => storing flag 'AS_FLAGS_' * nd->state => storing flag 'ND_STATE_' * nd->flags => storing flag 'ND_FLAGS_' * task_struct->thread_info.flags => storing flag 'TIF_' */ -#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) - // thread_info->flags is unsigned long :D #define TIF_NON_ROOT_USER_APP_PROC 33 #define TIF_PROC_SU_NOT_ALLOWED 34 +#define AS_FLAGS_SUS_PATH 24 +#define AS_FLAGS_SUS_MOUNT 25 +#define AS_FLAGS_SUS_KSTAT 26 +#define AS_FLAGS_OPEN_REDIRECT 27 #define AS_FLAGS_ANDROID_DATA_ROOT_DIR 28 #define AS_FLAGS_SDCARD_ROOT_DIR 29 +#define BIT_SUS_PATH BIT(24) +#define BIT_SUS_MOUNT BIT(25) +#define BIT_SUS_KSTAT BIT(26) +#define BIT_OPEN_REDIRECT BIT(27) #define BIT_ANDROID_DATA_ROOT_DIR BIT(28) #define BIT_ANDROID_SDCARD_ROOT_DIR BIT(29) @@ -92,4 +95,11 @@ static inline void susfs_set_current_proc_su_not_allowed(void) { set_ti_thread_flag(¤t->thread_info, TIF_PROC_SU_NOT_ALLOWED); } +static inline bool susfs_starts_with(const char *str, const char *prefix) { + while (*prefix) { + if (*str++ != *prefix++) + return false; + } + return true; +} #endif // #ifndef KSU_SUSFS_DEF_H