kernel: expose umount list to ioctl interface (#2950)
This idea is borrowed from simonpunk's susfs4ksu. What we see here is that, yeah well, lets just have userspace send us what it wants unmounted, this is better than hardcoding everything. This also solves that issue where MNT_DETACH fails, as long as we send unmountables in proper order. A small anti-duplicate mechanism is also added. While in-kernel umount is a bit worse than zygisk-provider-based ones, this can still serve as a healthy alternative. --------- - Remove duplicate checks Signed-off-by: backslashxx <118538522+backslashxx@users.noreply.github.com> Co-authored-by: weishu <twsxtd@gmail.com> Co-authored-by: ShirkNeko <109797057+ShirkNeko@users.noreply.github.com>
This commit is contained in:
@@ -52,7 +52,6 @@ static const struct ksu_feature_handler kernel_umount_handler = {
|
|||||||
|
|
||||||
#ifdef CONFIG_KSU_SUSFS
|
#ifdef CONFIG_KSU_SUSFS
|
||||||
extern bool susfs_is_mnt_devname_ksu(struct path *path);
|
extern bool susfs_is_mnt_devname_ksu(struct path *path);
|
||||||
|
|
||||||
#if defined(CONFIG_KSU_SUSFS_TRY_UMOUNT) && defined(CONFIG_KSU_SUSFS_ENABLE_LOG)
|
#if defined(CONFIG_KSU_SUSFS_TRY_UMOUNT) && defined(CONFIG_KSU_SUSFS_ENABLE_LOG)
|
||||||
extern bool susfs_is_log_enabled;
|
extern bool susfs_is_log_enabled;
|
||||||
#endif // #if defined(CONFIG_KSU_SUSFS_TRY_UMOUNT) && defined(CONFIG_KSU_SUSFS_ENABLE_LOG)
|
#endif // #if defined(CONFIG_KSU_SUSFS_TRY_UMOUNT) && defined(CONFIG_KSU_SUSFS_ENABLE_LOG)
|
||||||
@@ -61,27 +60,6 @@ extern void susfs_try_umount(void);
|
|||||||
#endif // #ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT
|
#endif // #ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT
|
||||||
#endif // #ifdef CONFIG_KSU_SUSFS
|
#endif // #ifdef CONFIG_KSU_SUSFS
|
||||||
|
|
||||||
static bool should_umount(struct path *path)
|
|
||||||
{
|
|
||||||
if (!path) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#ifdef CONFIG_KSU_SUSFS
|
|
||||||
return susfs_is_mnt_devname_ksu(path);
|
|
||||||
#else
|
|
||||||
|
|
||||||
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;
|
|
||||||
#endif // #ifdef CONFIG_KSU_SUSFS
|
|
||||||
}
|
|
||||||
|
|
||||||
#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)
|
||||||
static int ksu_path_umount(struct path *path, int flags)
|
static int ksu_path_umount(struct path *path, int flags)
|
||||||
@@ -119,9 +97,9 @@ static int ksu_sys_umount(const char *mnt, int flags)
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT
|
#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT
|
||||||
void try_umount(const char *mnt, bool check_mnt, int flags)
|
void try_umount(const char *mnt, int flags)
|
||||||
#else
|
#else
|
||||||
static void try_umount(const char *mnt, bool check_mnt, int flags)
|
static void try_umount(const char *mnt, int flags)
|
||||||
#endif // #ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT
|
#endif // #ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT
|
||||||
{
|
{
|
||||||
struct path path;
|
struct path path;
|
||||||
@@ -137,12 +115,6 @@ static void try_umount(const char *mnt, bool check_mnt, int flags)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// we are only interest in some specific mounts
|
|
||||||
if (check_mnt && !should_umount(&path)) {
|
|
||||||
path_put(&path);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(CONFIG_KSU_SUSFS_TRY_UMOUNT) && defined(CONFIG_KSU_SUSFS_ENABLE_LOG)
|
#if defined(CONFIG_KSU_SUSFS_TRY_UMOUNT) && defined(CONFIG_KSU_SUSFS_ENABLE_LOG)
|
||||||
if (susfs_is_log_enabled) {
|
if (susfs_is_log_enabled) {
|
||||||
pr_info("susfs: umounting '%s'\n", mnt);
|
pr_info("susfs: umounting '%s'\n", mnt);
|
||||||
@@ -184,16 +156,13 @@ static void umount_tw_func(struct callback_head *cb)
|
|||||||
saved = override_creds(tw->old_cred);
|
saved = override_creds(tw->old_cred);
|
||||||
}
|
}
|
||||||
|
|
||||||
// fixme: use `collect_mounts` and `iterate_mount` to iterate all mountpoint and
|
struct mount_entry *entry;
|
||||||
// filter the mountpoint whose target is `/data/adb`
|
down_read(&mount_list_lock);
|
||||||
try_umount("/odm", true, 0);
|
list_for_each_entry(entry, &mount_list, list) {
|
||||||
try_umount("/system", true, 0);
|
pr_info("%s: unmounting: %s flags 0x%x\n", __func__, entry->umountable, entry->flags);
|
||||||
try_umount("/vendor", true, 0);
|
try_umount(entry->umountable, entry->flags);
|
||||||
try_umount("/product", true, 0);
|
}
|
||||||
try_umount("/system_ext", true, 0);
|
up_read(&mount_list_lock);
|
||||||
try_umount("/data/adb/modules", false, MNT_DETACH);
|
|
||||||
// try umount ksu temp path
|
|
||||||
try_umount("/debug_ramdisk", false, MNT_DETACH);
|
|
||||||
|
|
||||||
if (saved)
|
if (saved)
|
||||||
revert_creds(saved);
|
revert_creds(saved);
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
#define __KSU_H_KERNEL_UMOUNT
|
#define __KSU_H_KERNEL_UMOUNT
|
||||||
|
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
|
#include <linux/list.h>
|
||||||
|
#include <linux/rwsem.h>
|
||||||
|
|
||||||
void ksu_kernel_umount_init(void);
|
void ksu_kernel_umount_init(void);
|
||||||
void ksu_kernel_umount_exit(void);
|
void ksu_kernel_umount_exit(void);
|
||||||
@@ -11,4 +13,13 @@ void ksu_kernel_umount_exit(void);
|
|||||||
int ksu_handle_umount(uid_t old_uid, uid_t new_uid);
|
int ksu_handle_umount(uid_t old_uid, uid_t new_uid);
|
||||||
#endif // #ifndef CONFIG_KSU_SUSFS
|
#endif // #ifndef CONFIG_KSU_SUSFS
|
||||||
|
|
||||||
|
// 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
|
#endif
|
||||||
@@ -593,6 +593,111 @@ static int do_nuke_ext4_sysfs(void __user *arg)
|
|||||||
return nuke_ext4_sysfs(mnt);
|
return nuke_ext4_sysfs(mnt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
// 100. GET_FULL_VERSION - Get full version string
|
||||||
static int do_get_full_version(void __user *arg)
|
static int do_get_full_version(void __user *arg)
|
||||||
{
|
{
|
||||||
@@ -814,6 +919,7 @@ static const struct ksu_ioctl_cmd_map ksu_ioctl_handlers[] = {
|
|||||||
{ .cmd = KSU_IOCTL_GET_WRAPPER_FD, .name = "GET_WRAPPER_FD", .handler = do_get_wrapper_fd, .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_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_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_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_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},
|
{ .cmd = KSU_IOCTL_ENABLE_KPM, .name = "GET_ENABLE_KPM", .handler = do_enable_kpm, .perm_check = manager_or_root},
|
||||||
|
|||||||
@@ -89,15 +89,26 @@ struct ksu_manage_mark_cmd {
|
|||||||
__u32 result; // Output: for get operation - mark status or reg_count
|
__u32 result; // Output: for get operation - mark status or reg_count
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ksu_nuke_ext4_sysfs_cmd {
|
|
||||||
__aligned_u64 arg; // Input: mnt pointer
|
|
||||||
};
|
|
||||||
|
|
||||||
#define KSU_MARK_GET 1
|
#define KSU_MARK_GET 1
|
||||||
#define KSU_MARK_MARK 2
|
#define KSU_MARK_MARK 2
|
||||||
#define KSU_MARK_UNMARK 3
|
#define KSU_MARK_UNMARK 3
|
||||||
#define KSU_MARK_REFRESH 4
|
#define KSU_MARK_REFRESH 4
|
||||||
|
|
||||||
|
struct ksu_nuke_ext4_sysfs_cmd {
|
||||||
|
__aligned_u64 arg; // Input: mnt pointer
|
||||||
|
};
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
// Other command structures
|
// Other command structures
|
||||||
struct ksu_get_full_version_cmd {
|
struct ksu_get_full_version_cmd {
|
||||||
char version_full[KSU_FULL_VERSION_STRING]; // Output: full version string
|
char version_full[KSU_FULL_VERSION_STRING]; // Output: full version string
|
||||||
@@ -152,6 +163,7 @@ struct ksu_manual_su_cmd {
|
|||||||
#define KSU_IOCTL_GET_WRAPPER_FD _IOC(_IOC_WRITE, 'K', 15, 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_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_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
|
// Other IOCTL command definitions
|
||||||
#define KSU_IOCTL_GET_FULL_VERSION _IOC(_IOC_READ, 'K', 100, 0)
|
#define KSU_IOCTL_GET_FULL_VERSION _IOC(_IOC_READ, 'K', 100, 0)
|
||||||
#define KSU_IOCTL_HOOK_TYPE _IOC(_IOC_READ, 'K', 101, 0)
|
#define KSU_IOCTL_HOOK_TYPE _IOC(_IOC_READ, 'K', 101, 0)
|
||||||
|
|||||||
Reference in New Issue
Block a user