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:
backslashxx
2025-11-18 11:10:44 +08:00
committed by ShirkNeko
parent 44eed59c46
commit 02c3f70ab5
4 changed files with 143 additions and 45 deletions

View File

@@ -593,6 +593,111 @@ static int do_nuke_ext4_sysfs(void __user *arg)
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
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_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},