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. --------- Signed-off-by: backslashxx <118538522+backslashxx@users.noreply.github.com> Co-authored-by: weishu <twsxtd@gmail.com> Signed-off-by: fc5b87cf <rissu.ntk@gmail.com>
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#define __KSU_H_KERNEL_UMOUNT
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/rwsem.h>
|
||||
|
||||
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
|
||||
|
||||
@@ -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},
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user