diff --git a/kernel/kernel_umount.c b/kernel/kernel_umount.c index c052115a..e4cd1f89 100644 --- a/kernel/kernel_umount.c +++ b/kernel/kernel_umount.c @@ -44,28 +44,11 @@ static const struct ksu_feature_handler kernel_umount_handler = { .set_handler = kernel_umount_feature_set, }; -static bool should_umount(struct path *path) -{ - if (!path) { - return false; - } - - if (current->nsproxy->mnt_ns == init_nsproxy.mnt_ns) { - pr_info("ignore global mnt namespace process: %d\n", - current_uid().val); - return false; - } - - if (path->mnt && path->mnt->mnt_sb && path->mnt->mnt_sb->s_type) { - const char *fstype = path->mnt->mnt_sb->s_type->name; - return strcmp(fstype, "overlay") == 0; - } - return false; -} - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) || defined(KSU_HAS_PATH_UMOUNT) +#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 9, 0) || \ + defined(KSU_HAS_PATH_UMOUNT) extern int path_umount(struct path *path, int flags); -static void ksu_umount_mnt(const char *__never_use_mnt, struct path *path, int flags) +static void ksu_umount_mnt(const char *__never_use_mnt, struct path *path, + int flags) { int err = path_umount(path, flags); if (err) { @@ -91,12 +74,12 @@ static void ksu_sys_umount(const char *mnt, int flags) #define ksu_umount_mnt(mnt, __unused, flags) \ ({ \ path_put(__unused); \ - ksu_sys_umount(mnt, flags); \ + ksu_sys_umount(mnt, flags); \ }) #endif -static void try_umount(const char *mnt, bool check_mnt, int flags) +static void try_umount(const char *mnt, int flags) { struct path path; int err = kern_path(mnt, 0, &path); @@ -110,25 +93,17 @@ static void try_umount(const char *mnt, bool check_mnt, int flags) return; } - // we are only interest in some specific mounts - if (check_mnt && !should_umount(&path)) { - path_put(&path); - return; - } - ksu_umount_mnt(mnt, &path, flags); } -static inline void do_ksu_umount_lists(void) +static inline void do_umount_work(void) { - // fixme: use `collect_mounts` and `iterate_mount` to iterate all mountpoint and - // filter the mountpoint whose target is `/data/adb` - try_umount("/odm", true, 0); - try_umount("/system", true, 0); - try_umount("/vendor", true, 0); - try_umount("/product", true, 0); - try_umount("/system_ext", true, 0); - try_umount("/data/adb/modules", false, MNT_DETACH); + struct mount_entry *entry; + list_for_each_entry (entry, &mount_list, list) { + pr_info("%s: unmounting: %s flags 0x%x\n", __func__, + entry->umountable, entry->flags); + try_umount(entry->umountable, entry->flags); + } } #ifdef KSU_SHOULD_USE_NEW_TP @@ -145,7 +120,9 @@ static void umount_tw_func(struct callback_head *cb) saved = override_creds(tw->old_cred); } - do_ksu_umount_lists(); + down_read(&mount_list_lock); + do_umount_work(); + up_read(&mount_list_lock); if (saved) revert_creds(saved); @@ -208,7 +185,9 @@ int ksu_handle_umount(uid_t old_uid, uid_t new_uid) } #else // Using task work for non-kp context is expansive? - do_ksu_umount_lists(); + down_read(&mount_list_lock); + do_umount_work(); + up_read(&mount_list_lock); #endif return 0; diff --git a/kernel/kernel_umount.h b/kernel/kernel_umount.h index 3f7a426d..46486f8e 100644 --- a/kernel/kernel_umount.h +++ b/kernel/kernel_umount.h @@ -2,6 +2,8 @@ #define __KSU_H_KERNEL_UMOUNT #include +#include +#include void ksu_kernel_umount_init(void); void ksu_kernel_umount_exit(void); @@ -9,4 +11,13 @@ void ksu_kernel_umount_exit(void); // Handler function to be called from setresuid hook int ksu_handle_umount(uid_t old_uid, uid_t new_uid); +// for the umount list +struct mount_entry { + char *umountable; + unsigned int flags; + struct list_head list; +}; +extern struct list_head mount_list; +extern struct rw_semaphore mount_list_lock; + #endif diff --git a/kernel/supercalls.c b/kernel/supercalls.c index da65d408..b5da1bd3 100644 --- a/kernel/supercalls.c +++ b/kernel/supercalls.c @@ -19,6 +19,8 @@ #include "feature.h" #include "klog.h" // IWYU pragma: keep #include "ksud.h" +#include "kernel_compat.h" +#include "kernel_umount.h" #include "manager.h" #include "selinux/selinux.h" #include "objsec.h" @@ -475,6 +477,116 @@ static int do_manage_mark(void __user *arg) return 0; } +struct list_head mount_list = LIST_HEAD_INIT(mount_list); +DECLARE_RWSEM(mount_list_lock); + +static int add_try_umount(void __user *arg) +{ + struct mount_entry *new_entry, *entry, *tmp; + struct ksu_add_try_umount_cmd cmd; + char buf[256] = { 0 }; + + if (copy_from_user(&cmd, arg, sizeof cmd)) + return -EFAULT; + + switch (cmd.mode) { + case KSU_UMOUNT_WIPE: { + struct mount_entry *entry, *tmp; + down_write(&mount_list_lock); + list_for_each_entry_safe (entry, tmp, &mount_list, list) { + pr_info("wipe_umount_list: removing entry: %s\n", + entry->umountable); + list_del(&entry->list); + kfree(entry->umountable); + kfree(entry); + } + up_write(&mount_list_lock); + + return 0; + } + + case KSU_UMOUNT_ADD: { + long len = strncpy_from_user(buf, (const char __user *)cmd.arg, + 256); + if (len <= 0) + return -EFAULT; + + buf[sizeof(buf) - 1] = '\0'; + + new_entry = kzalloc(sizeof(*new_entry), GFP_KERNEL); + if (!new_entry) + return -ENOMEM; + + new_entry->umountable = kstrdup(buf, GFP_KERNEL); + if (!new_entry->umountable) { + kfree(new_entry); + return -1; + } + + down_write(&mount_list_lock); + + // disallow dupes + // if this gets too many, we can consider moving this whole task to a kthread + list_for_each_entry (entry, &mount_list, list) { + if (!strcmp(entry->umountable, buf)) { + pr_info("cmd_add_try_umount: %s is already here!\n", + buf); + up_write(&mount_list_lock); + kfree(new_entry->umountable); + kfree(new_entry); + return -1; + } + } + + // now check flags and add + // this also serves as a null check + if (cmd.flags) + new_entry->flags = cmd.flags; + else + new_entry->flags = 0; + + // debug + list_add(&new_entry->list, &mount_list); + up_write(&mount_list_lock); + pr_info("cmd_add_try_umount: %s added!\n", buf); + + return 0; + } + + // this is just strcmp'd wipe anyway + case KSU_UMOUNT_DEL: { + long len = strncpy_from_user(buf, (const char __user *)cmd.arg, + sizeof(buf) - 1); + if (len <= 0) + return -EFAULT; + + buf[sizeof(buf) - 1] = '\0'; + + down_write(&mount_list_lock); + list_for_each_entry_safe (entry, tmp, &mount_list, list) { + if (!strcmp(entry->umountable, buf)) { + pr_info("cmd_add_try_umount: entry removed: %s\n", + entry->umountable); + list_del(&entry->list); + kfree(entry->umountable); + kfree(entry); + } + } + up_write(&mount_list_lock); + + return 0; + } + + default: { + pr_err("cmd_add_try_umount: invalid operation %u\n", cmd.mode); + return -EINVAL; + } + + } // switch(cmd.mode) + + return 0; +} + // 100. GET_FULL_VERSION - Get full version string static int do_get_full_version(void __user *arg) { @@ -651,6 +763,8 @@ static const struct ksu_ioctl_cmd_map ksu_ioctl_handlers[] = { { .cmd = KSU_IOCTL_SET_FEATURE, .name = "SET_FEATURE", .handler = do_set_feature, .perm_check = manager_or_root }, { .cmd = KSU_IOCTL_GET_WRAPPER_FD, .name = "GET_WRAPPER_FD", .handler = do_get_wrapper_fd, .perm_check = manager_or_root }, { .cmd = KSU_IOCTL_MANAGE_MARK, .name = "MANAGE_MARK", .handler = do_manage_mark, .perm_check = manager_or_root }, + //{ .cmd = KSU_IOCTL_NUKE_EXT4_SYSFS, .name = "NUKE_EXT4_SYSFS", .handler = do_nuke_ext4_sysfs, .perm_check = manager_or_root }, + { .cmd = KSU_IOCTL_ADD_TRY_UMOUNT, .name = "ADD_TRY_UMOUNT", .handler = add_try_umount, .perm_check = manager_or_root }, { .cmd = KSU_IOCTL_GET_FULL_VERSION,.name = "GET_FULL_VERSION", .handler = do_get_full_version, .perm_check = always_allow}, { .cmd = KSU_IOCTL_HOOK_TYPE,.name = "GET_HOOK_TYPE", .handler = do_get_hook_type, .perm_check = manager_or_root}, { .cmd = KSU_IOCTL_ENABLE_KPM, .name = "GET_ENABLE_KPM", .handler = do_enable_kpm, .perm_check = manager_or_root}, diff --git a/kernel/supercalls.h b/kernel/supercalls.h index 584091ec..df997bea 100644 --- a/kernel/supercalls.h +++ b/kernel/supercalls.h @@ -121,6 +121,16 @@ struct ksu_manage_mark_cmd { #define KSU_MARK_UNMARK 3 #define KSU_MARK_REFRESH 4 +struct ksu_add_try_umount_cmd { + __aligned_u64 arg; // char ptr, this is the mountpoint + __u32 flags; // this is the flag we use for it + __u8 mode; // denotes what to do with it 0:wipe_list 1:add_to_list 2:delete_entry +}; + +#define KSU_UMOUNT_WIPE 0 // ignore everything and wipe list +#define KSU_UMOUNT_ADD 1 // add entry (path + flags) +#define KSU_UMOUNT_DEL 2 // delete entry, strcmp + // IOCTL command definitions #define KSU_IOCTL_GRANT_ROOT _IOC(_IOC_NONE, 'K', 1, 0) #define KSU_IOCTL_GET_INFO _IOC(_IOC_READ, 'K', 2, 0) @@ -138,6 +148,8 @@ struct ksu_manage_mark_cmd { #define KSU_IOCTL_SET_FEATURE _IOC(_IOC_WRITE, 'K', 14, 0) #define KSU_IOCTL_GET_WRAPPER_FD _IOC(_IOC_WRITE, 'K', 15, 0) #define KSU_IOCTL_MANAGE_MARK _IOC(_IOC_READ | _IOC_WRITE, 'K', 16, 0) +// #define KSU_IOCTL_NUKE_EXT4_SYSFS _IOC(_IOC_WRITE, 'K', 17, 0) +#define KSU_IOCTL_ADD_TRY_UMOUNT _IOC(_IOC_WRITE, 'K', 18, 0) // Other IOCTL command definitions #define KSU_IOCTL_GET_FULL_VERSION _IOC(_IOC_READ, 'K', 100, 0) #define KSU_IOCTL_HOOK_TYPE _IOC(_IOC_READ, 'K', 101, 0)