Skip to content
38 changes: 5 additions & 33 deletions kernel/kernel_umount.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,24 +40,6 @@ 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;
}

extern int path_umount(struct path *path, int flags);

static void ksu_umount_mnt(struct path *path, int flags)
Expand All @@ -68,7 +50,7 @@ static void ksu_umount_mnt(struct path *path, int flags)
}
}

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);
Expand All @@ -82,12 +64,6 @@ 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(&path, flags);
}

Expand All @@ -104,14 +80,10 @@ static void umount_tw_func(struct callback_head *cb)
saved = override_creds(tw->old_cred);
}

// 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) {
try_umount(entry->umountable, entry->flags);
}

if (saved)
revert_creds(saved);
Expand Down
9 changes: 9 additions & 0 deletions kernel/kernel_umount.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,20 @@
#define __KSU_H_KERNEL_UMOUNT

#include <linux/types.h>
#include <linux/list.h>

void ksu_kernel_umount_init(void);
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;

#endif
103 changes: 103 additions & 0 deletions kernel/supercalls.c
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "klog.h" // IWYU pragma: keep
#include "ksu.h"
#include "ksud.h"
#include "kernel_umount.h"
#include "manager.h"
#include "selinux/selinux.h"
#include "objsec.h"
Expand Down Expand Up @@ -477,6 +478,107 @@ static int do_nuke_ext4_sysfs(void __user *arg)
return nuke_ext4_sysfs(mnt);
}

struct list_head mount_list = LIST_HEAD_INIT(mount_list);

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;
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);
}

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 = kmalloc(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;
}

// 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);
kfree(new_entry->umountable);
kfree(new_entry);
return -1;
}
}
/*
#define MNT_FORCE 0x00000001
#define MNT_DETACH 0x00000002
#define MNT_EXPIRE 0x00000004
#define UMOUNT_NOFOLLOW 0x00000008
#define UMOUNT_UNUSED 0x80000000
*/
// now check flags and add
if (cmd.flags)
new_entry->flags = cmd.flags;
else
new_entry->flags = 0;

// debug
list_add(&new_entry->list, &mount_list);
pr_info("cmd_add_try_umount: %s added!\n", buf);

return 0;
}

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';

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);
}
}

return 0;
}

default: {
pr_err("cmd_add_try_umount: invalid operation %u\n", cmd.mode);
return -EINVAL;
}


}

return 0;
}

// IOCTL handlers mapping table
static const struct ksu_ioctl_cmd_map ksu_ioctl_handlers[] = {
{ .cmd = KSU_IOCTL_GRANT_ROOT, .name = "GRANT_ROOT", .handler = do_grant_root, .perm_check = allowed_for_su },
Expand All @@ -496,6 +598,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 = 0, .name = NULL, .handler = NULL, .perm_check = NULL } // Sentinel
};

Expand Down
20 changes: 16 additions & 4 deletions kernel/supercalls.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,26 @@ struct ksu_manage_mark_cmd {
__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_MARK 2
#define KSU_MARK_UNMARK 3
#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


// 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)
Expand All @@ -111,6 +122,7 @@ struct ksu_nuke_ext4_sysfs_cmd {
#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_READ|_IOC_WRITE, 'K', 18, 0)

// IOCTL handler types
typedef int (*ksu_ioctl_handler_t)(void __user *arg);
Expand Down