feature: add devpts fd wrapper (#21)

This feature is intended to resolve devpts problem.
This commit is contained in:
5ec1cff
2025-11-06 23:29:58 +08:00
committed by ShirkNeko
parent f86c71efc5
commit 826661dffb
15 changed files with 526 additions and 22 deletions

View File

@@ -5,6 +5,7 @@ on:
jobs: jobs:
build-lkm: build-lkm:
strategy: strategy:
fail-fast: false
matrix: matrix:
kmi: kmi:
- android12-5.10 - android12-5.10

View File

@@ -11,6 +11,7 @@ kernelsu-objs += feature.o
kernelsu-objs += ksud.o kernelsu-objs += ksud.o
kernelsu-objs += embed_ksud.o kernelsu-objs += embed_ksud.o
kernelsu-objs += kernel_compat.o kernelsu-objs += kernel_compat.o
kernelsu-objs += file_wrapper.o
kernelsu-objs += throne_comm.o kernelsu-objs += throne_comm.o
kernelsu-objs += sulog.o kernelsu-objs += sulog.o

347
kernel/file_wrapper.c Normal file
View File

@@ -0,0 +1,347 @@
#include "linux/export.h"
#include <linux/anon_inodes.h>
#include <linux/capability.h>
#include <linux/cred.h>
#include <linux/err.h>
#include <linux/file.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/version.h>
#include "allowlist.h"
#include "klog.h" // IWYU pragma: keep
#include "ksu.h"
#include "ksud.h"
#include "manager.h"
#include "selinux/selinux.h"
#include "core_hook.h"
#include "objsec.h"
#include "file_wrapper.h"
static loff_t mksu_wrapper_llseek(struct file *fp, loff_t off, int flags) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
return orig->f_op->llseek(data->orig, off, flags);
}
static ssize_t mksu_wrapper_read(struct file *fp, char __user *ptr, size_t sz, loff_t *off) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
return orig->f_op->read(orig, ptr, sz, off);
}
static ssize_t mksu_wrapper_write(struct file *fp, const char __user *ptr, size_t sz, loff_t *off) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
return orig->f_op->write(orig, ptr, sz, off);
}
static ssize_t mksu_wrapper_read_iter(struct kiocb *iocb, struct iov_iter *iovi) {
struct ksu_file_wrapper* data = iocb->ki_filp->private_data;
struct file* orig = data->orig;
iocb->ki_filp = orig;
return orig->f_op->read_iter(iocb, iovi);
}
static ssize_t mksu_wrapper_write_iter (struct kiocb *iocb, struct iov_iter *iovi) {
struct ksu_file_wrapper* data = iocb->ki_filp->private_data;
struct file* orig = data->orig;
iocb->ki_filp = orig;
return orig->f_op->write_iter(iocb, iovi);
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 1, 0)
static int mksu_wrapper_iopoll(struct kiocb *kiocb, struct io_comp_batch* icb, unsigned int v) {
struct ksu_file_wrapper* data = kiocb->ki_filp->private_data;
struct file* orig = data->orig;
kiocb->ki_filp = orig;
return orig->f_op->iopoll(kiocb, icb, v);
}
#else
static int mksu_wrapper_iopoll(struct kiocb *kiocb, bool spin) {
struct ksu_file_wrapper* data = kiocb->ki_filp->private_data;
struct file* orig = data->orig;
kiocb->ki_filp = orig;
return orig->f_op->iopoll(kiocb, spin);
}
#endif
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
static int mksu_wrapper_iterate (struct file *fp, struct dir_context *dc) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
return orig->f_op->iterate(orig, dc);
}
#endif
static int mksu_wrapper_iterate_shared (struct file *fp, struct dir_context *dc) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
return orig->f_op->iterate_shared(orig, dc);
}
static __poll_t mksu_wrapper_poll (struct file *fp, struct poll_table_struct *pts) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
return orig->f_op->poll(orig, pts);
}
static long mksu_wrapper_unlocked_ioctl (struct file *fp, unsigned int cmd, unsigned long arg) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
return orig->f_op->unlocked_ioctl(orig, cmd, arg);
}
static long mksu_wrapper_compat_ioctl (struct file *fp, unsigned int cmd, unsigned long arg) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
return orig->f_op->compat_ioctl(orig, cmd, arg);
}
static int mksu_wrapper_mmap (struct file *fp, struct vm_area_struct * vma) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
return orig->f_op->mmap(orig, vma);
}
// static unsigned long mmap_supported_flags {}
static int mksu_wrapper_open(struct inode *ino, struct file *fp) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
struct inode *orig_ino = file_inode(orig);
return orig->f_op->open(orig_ino, orig);
}
static int mksu_wrapper_flush (struct file *fp, fl_owner_t id) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
return orig->f_op->flush(orig, id);
}
static int mksu_wrapper_fsync(struct file *fp, loff_t off1, loff_t off2, int datasync) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
return orig->f_op->fsync(orig, off1, off2, datasync);
}
static int mksu_wrapper_fasync(int arg, struct file *fp, int arg2) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
return orig->f_op->fasync(arg, orig, arg2);
}
static int mksu_wrapper_lock(struct file *fp, int arg1, struct file_lock *fl) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
return orig->f_op->lock(orig, arg1, fl);
}
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
static ssize_t mksu_wrapper_sendpage (struct file *fp, struct page *pg, int arg1, size_t sz, loff_t *off, int arg2) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
if (orig->f_op->sendpage) {
return orig->f_op->sendpage(orig, pg, arg1, sz, off, arg2);
}
return -EINVAL;
}
#endif
static unsigned long mksu_wrapper_get_unmapped_area(struct file *fp, unsigned long arg1, unsigned long arg2, unsigned long arg3, unsigned long arg4) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
if (orig->f_op->get_unmapped_area) {
return orig->f_op->get_unmapped_area(orig, arg1, arg2, arg3, arg4);
}
return -EINVAL;
}
// static int mksu_wrapper_check_flags(int arg) {}
static int mksu_wrapper_flock(struct file *fp, int arg1, struct file_lock *fl) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
if (orig->f_op->flock) {
return orig->f_op->flock(orig, arg1, fl);
}
return -EINVAL;
}
static ssize_t mksu_wrapper_splice_write(struct pipe_inode_info * pii, struct file *fp, loff_t *off, size_t sz, unsigned int arg1) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
if (orig->f_op->splice_write) {
return orig->f_op->splice_write(pii, orig, off, sz, arg1);
}
return -EINVAL;
}
static ssize_t mksu_wrapper_splice_read(struct file *fp, loff_t *off, struct pipe_inode_info *pii, size_t sz, unsigned int arg1) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
if (orig->f_op->splice_read) {
return orig->f_op->splice_read(orig, off, pii, sz, arg1);
}
return -EINVAL;
}
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
void mksu_wrapper_splice_eof(struct file *fp) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
if (orig->f_op->splice_eof) {
return orig->f_op->splice_eof(orig);
}
}
#endif
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0)
static int mksu_wrapper_setlease(struct file *fp, int arg1, struct file_lease **fl, void **p) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
if (orig->f_op->setlease) {
return orig->f_op->setlease(orig, arg1, fl, p);
}
return -EINVAL;
}
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
static int mksu_wrapper_setlease(struct file *fp, int arg1, struct file_lock **fl, void **p) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
if (orig->f_op->setlease) {
return orig->f_op->setlease(orig, arg1, fl, p);
}
return -EINVAL;
}
#else
static int mksu_wrapper_setlease(struct file *fp, long arg1, struct file_lock **fl, void **p) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
if (orig->f_op->setlease) {
return orig->f_op->setlease(orig, arg1, fl, p);
}
return -EINVAL;
}
#endif
static long mksu_wrapper_fallocate(struct file *fp, int mode, loff_t offset, loff_t len) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
if (orig->f_op->fallocate) {
return orig->f_op->fallocate(orig, mode, offset, len);
}
return -EINVAL;
}
static void mksu_wrapper_show_fdinfo(struct seq_file *m, struct file *f) {
struct ksu_file_wrapper* data = m->file->private_data;
struct file* orig = data->orig;
if (orig->f_op->show_fdinfo) {
orig->f_op->show_fdinfo(m, orig);
}
}
static ssize_t mksu_wrapper_copy_file_range(struct file *f1, loff_t off1, struct file *f2,
loff_t off2, size_t sz, unsigned int flags) {
// TODO: determine which file to use
struct ksu_file_wrapper* data = f1->private_data;
struct file* orig = data->orig;
if (orig->f_op->copy_file_range) {
return orig->f_op->copy_file_range(orig, off1, f2, off2, sz, flags);
}
return -EINVAL;
}
static loff_t mksu_wrapper_remap_file_range(struct file *file_in, loff_t pos_in,
struct file *file_out, loff_t pos_out,
loff_t len, unsigned int remap_flags) {
// TODO: determine which file to use
struct ksu_file_wrapper* data = file_in->private_data;
struct file* orig = data->orig;
if (orig->f_op->remap_file_range) {
return orig->f_op->remap_file_range(orig, pos_in, file_out, pos_out, len, remap_flags);
}
return -EINVAL;
}
static int mksu_wrapper_fadvise(struct file *fp, loff_t off1, loff_t off2, int flags) {
struct ksu_file_wrapper* data = fp->private_data;
struct file* orig = data->orig;
if (orig->f_op->fadvise) {
return orig->f_op->fadvise(orig, off1, off2, flags);
}
return -EINVAL;
}
static int mksu_wrapper_release(struct inode *inode, struct file *filp) {
mksu_delete_file_wrapper(filp->private_data);
return 0;
}
struct ksu_file_wrapper* mksu_create_file_wrapper(struct file* fp) {
struct ksu_file_wrapper* p = kcalloc(sizeof(struct ksu_file_wrapper), 1, GFP_KERNEL);
if (!p) {
return NULL;
}
get_file(fp);
p->orig = fp;
p->ops.owner = THIS_MODULE;
p->ops.llseek = fp->f_op->llseek ? mksu_wrapper_llseek : NULL;
p->ops.read = fp->f_op->read ? mksu_wrapper_read : NULL;
p->ops.write = fp->f_op->write ? mksu_wrapper_write : NULL;
p->ops.read_iter = fp->f_op->read_iter ? mksu_wrapper_read_iter : NULL;
p->ops.write_iter = fp->f_op->write_iter ? mksu_wrapper_write_iter : NULL;
p->ops.iopoll = fp->f_op->iopoll ? mksu_wrapper_iopoll : NULL;
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
p->ops.iterate = fp->f_op->iterate ? mksu_wrapper_iterate : NULL;
#endif
p->ops.iterate_shared = fp->f_op->iterate_shared ? mksu_wrapper_iterate_shared : NULL;
p->ops.poll = fp->f_op->poll ? mksu_wrapper_poll : NULL;
p->ops.unlocked_ioctl = fp->f_op->unlocked_ioctl ? mksu_wrapper_unlocked_ioctl : NULL;
p->ops.compat_ioctl = fp->f_op->compat_ioctl ? mksu_wrapper_compat_ioctl : NULL;
p->ops.mmap = fp->f_op->mmap ? mksu_wrapper_mmap : NULL;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 12, 0)
p->ops.fop_flags = fp->f_op->fop_flags;
#else
p->ops.mmap_supported_flags = fp->f_op->mmap_supported_flags;
#endif
p->ops.open = fp->f_op->open ? mksu_wrapper_open : NULL;
p->ops.flush = fp->f_op->flush ? mksu_wrapper_flush : NULL;
p->ops.release = mksu_wrapper_release;
p->ops.fsync = fp->f_op->fsync ? mksu_wrapper_fsync : NULL;
p->ops.fasync = fp->f_op->fasync ? mksu_wrapper_fasync : NULL;
p->ops.lock = fp->f_op->lock ? mksu_wrapper_lock : NULL;
#if LINUX_VERSION_CODE < KERNEL_VERSION(6, 6, 0)
p->ops.sendpage = fp->f_op->sendpage ? mksu_wrapper_sendpage : NULL;
#endif
p->ops.get_unmapped_area = fp->f_op->get_unmapped_area ? mksu_wrapper_get_unmapped_area : NULL;
p->ops.check_flags = fp->f_op->check_flags;
p->ops.flock = fp->f_op->flock ? mksu_wrapper_flock : NULL;
p->ops.splice_write = fp->f_op->splice_write ? mksu_wrapper_splice_write : NULL;
p->ops.splice_read = fp->f_op->splice_read ? mksu_wrapper_splice_read : NULL;
p->ops.setlease = fp->f_op->setlease ? mksu_wrapper_setlease : NULL;
p->ops.fallocate = fp->f_op->fallocate ? mksu_wrapper_fallocate : NULL;
p->ops.show_fdinfo = fp->f_op->show_fdinfo ? mksu_wrapper_show_fdinfo : NULL;
p->ops.copy_file_range = fp->f_op->copy_file_range ? mksu_wrapper_copy_file_range : NULL;
p->ops.remap_file_range = fp->f_op->remap_file_range ? mksu_wrapper_remap_file_range : NULL;
p->ops.fadvise = fp->f_op->fadvise ? mksu_wrapper_fadvise : NULL;
#if LINUX_VERSION_CODE >= KERNEL_VERSION(6, 6, 0)
p->ops.splice_eof = fp->f_op->splice_eof ? mksu_wrapper_splice_eof : NULL;
#endif
return p;
}
void mksu_delete_file_wrapper(struct ksu_file_wrapper* data) {
fput((struct file*) data->orig);
kfree(data);
}

10
kernel/file_wrapper.h Normal file
View File

@@ -0,0 +1,10 @@
#include <linux/file.h>
#include <linux/fs.h>
struct ksu_file_wrapper {
struct file* orig;
struct file_operations ops;
};
struct ksu_file_wrapper* mksu_create_file_wrapper(struct file* fp);
void mksu_delete_file_wrapper(struct ksu_file_wrapper* data);

View File

@@ -8,6 +8,24 @@
#define KERNEL_SU_OPTION 0xDEADBEEF #define KERNEL_SU_OPTION 0xDEADBEEF
extern bool ksu_uid_scanner_enabled; extern bool ksu_uid_scanner_enabled;
#define CMD_GRANT_ROOT 0
#define CMD_BECOME_MANAGER 1
#define CMD_GET_VERSION 2
#define CMD_ALLOW_SU 3
#define CMD_DENY_SU 4
#define CMD_GET_ALLOW_LIST 5
#define CMD_GET_DENY_LIST 6
#define CMD_REPORT_EVENT 7
#define CMD_SET_SEPOLICY 8
#define CMD_CHECK_SAFEMODE 9
#define CMD_GET_APP_PROFILE 10
#define CMD_SET_APP_PROFILE 11
#define CMD_UID_GRANTED_ROOT 12
#define CMD_UID_SHOULD_UMOUNT 13
#define CMD_IS_SU_ENABLED 14
#define CMD_ENABLE_SU 15
#define CMD_GET_MANAGER_UID 16
#define CMD_GET_WRAPPER_FD 10000
#define EVENT_POST_FS_DATA 1 #define EVENT_POST_FS_DATA 1
#define EVENT_BOOT_COMPLETED 2 #define EVENT_BOOT_COMPLETED 2

View File

@@ -60,7 +60,7 @@ bool ksu_input_hook __read_mostly = true;
#endif #endif
bool ksu_execveat_hook __read_mostly = true; bool ksu_execveat_hook __read_mostly = true;
u32 ksu_devpts_sid; u32 ksu_file_sid;
// Detect whether it is on or not // Detect whether it is on or not
static bool is_boot_phase = true; static bool is_boot_phase = true;
@@ -81,11 +81,11 @@ void on_post_fs_data(void)
// sanity check, this may influence the performance // sanity check, this may influence the performance
stop_input_hook(); stop_input_hook();
ksu_devpts_sid = ksu_get_devpts_sid();
pr_info("devpts sid: %d\n", ksu_devpts_sid);
// End of boot state // End of boot state
is_boot_phase = false; is_boot_phase = false;
ksu_file_sid = ksu_get_ksu_file_sid();
pr_info("ksu_file sid: %d\n", ksu_file_sid);
} }
// since _ksud handler only uses argv and envp for comparisons // since _ksud handler only uses argv and envp for comparisons

View File

@@ -10,7 +10,7 @@ void on_post_fs_data(void);
bool ksu_is_safe_mode(void); bool ksu_is_safe_mode(void);
extern u32 ksu_devpts_sid; extern u32 ksu_file_sid;
extern bool ksu_execveat_hook __read_mostly; extern bool ksu_execveat_hook __read_mostly;
extern int ksu_handle_pre_ksud(const char *filename); extern int ksu_handle_pre_ksud(const char *filename);

View File

@@ -164,15 +164,15 @@ bool is_zygote(const struct cred* cred)
return result; return result;
} }
#define DEVPTS_DOMAIN "u:object_r:ksu_file:s0" #define KSU_FILE_DOMAIN "u:object_r:ksu_file:s0"
u32 ksu_get_devpts_sid() u32 ksu_get_ksu_file_sid()
{ {
u32 devpts_sid = 0; u32 ksu_file_sid = 0;
int err = security_secctx_to_secid(DEVPTS_DOMAIN, strlen(DEVPTS_DOMAIN), int err = security_secctx_to_secid(KSU_FILE_DOMAIN, strlen(KSU_FILE_DOMAIN),
&devpts_sid); &ksu_file_sid);
if (err) { if (err) {
pr_info("get devpts sid err %d\n", err); pr_info("get ksufile sid err %d\n", err);
} }
return devpts_sid; return ksu_file_sid;
} }

View File

@@ -19,7 +19,7 @@ bool is_zygote(const struct cred* cred);
void apply_kernelsu_rules(); void apply_kernelsu_rules();
u32 ksu_get_devpts_sid(); u32 ksu_get_ksu_file_sid();
int handle_sepolicy(unsigned long arg3, void __user *arg4); int handle_sepolicy(unsigned long arg3, void __user *arg4);

View File

@@ -346,8 +346,8 @@ int __ksu_handle_devpts(struct inode *inode)
struct inode_security_struct *sec = selinux_inode(inode); struct inode_security_struct *sec = selinux_inode(inode);
if (ksu_devpts_sid && sec) if (ksu_file_sid && sec)
sec->sid = ksu_devpts_sid; sec->sid = ksu_file_sid;
return 0; return 0;
} }

View File

@@ -18,6 +18,8 @@
#include "sulog.h" #include "sulog.h"
#include "selinux/selinux.h" #include "selinux/selinux.h"
#include "core_hook.h" #include "core_hook.h"
#include "objsec.h"
#include "file_wrapper.h"
#include "kernel_compat.h" #include "kernel_compat.h"
#include "throne_comm.h" #include "throne_comm.h"
#include "dynamic_manager.h" #include "dynamic_manager.h"
@@ -355,6 +357,64 @@ static int do_set_feature(void __user *arg)
return 0; return 0;
} }
static int do_get_wrapper_fd(void __user *arg) {
if (!ksu_file_sid) {
return -1;
}
struct ksu_get_wrapper_fd_cmd cmd;
int ret;
if (copy_from_user(&cmd, arg, sizeof(cmd))) {
pr_err("get_wrapper_fd: copy_from_user failed\n");
return -EFAULT;
}
struct file* f = fget(cmd.fd);
if (!f) {
return -EBADF;
}
struct ksu_file_wrapper *data = mksu_create_file_wrapper(f);
if (data == NULL) {
ret = -ENOMEM;
goto put_orig_file;
}
struct file* pf = anon_inode_getfile("[mksu_fdwrapper]", &data->ops, data, f->f_flags);
if (IS_ERR(pf)) {
ret = PTR_ERR(pf);
pr_err("mksu_fdwrapper: anon_inode_getfile failed: %ld\n", PTR_ERR(pf));
goto put_wrapper_data;
}
struct inode* wrapper_inode = file_inode(pf);
struct inode_security_struct *sec = selinux_inode(wrapper_inode);
if (sec) {
sec->sid = ksu_file_sid;
}
ret = get_unused_fd_flags(cmd.flags);
if (ret < 0) {
pr_err("mksu_fdwrapper: get unused fd failed: %d\n", ret);
goto put_wrapper_file;
}
// pr_info("mksu_fdwrapper: installed wrapper fd for %p %d (flags=%d, mode=%d) to %p %d (flags=%d, mode=%d)", f, cmd.fd, f->f_flags, f->f_mode, pf, ret, pf->f_flags, pf->f_mode);
// pf->f_mode |= FMODE_READ | FMODE_CAN_READ | FMODE_WRITE | FMODE_CAN_WRITE;
fd_install(ret, pf);
goto put_orig_file;
put_wrapper_file:
fput(pf);
put_wrapper_data:
mksu_delete_file_wrapper(data);
put_orig_file:
fput(f);
return ret;
}
// 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)
{ {
@@ -572,6 +632,7 @@ static const struct ksu_ioctl_cmd_map ksu_ioctl_handlers[] = {
{ .cmd = KSU_IOCTL_SET_APP_PROFILE, .name = "SET_APP_PROFILE", .handler = do_set_app_profile, .perm_check = only_manager }, { .cmd = KSU_IOCTL_SET_APP_PROFILE, .name = "SET_APP_PROFILE", .handler = do_set_app_profile, .perm_check = only_manager },
{ .cmd = KSU_IOCTL_GET_FEATURE, .name = "GET_FEATURE", .handler = do_get_feature, .perm_check = manager_or_root }, { .cmd = KSU_IOCTL_GET_FEATURE, .name = "GET_FEATURE", .handler = do_get_feature, .perm_check = manager_or_root },
{ .cmd = KSU_IOCTL_SET_FEATURE, .name = "SET_FEATURE", .handler = do_set_feature, .perm_check = manager_or_root }, { .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_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},

View File

@@ -77,6 +77,11 @@ struct ksu_set_feature_cmd {
__u64 value; // Input: feature value/state to set __u64 value; // Input: feature value/state to set
}; };
struct ksu_get_wrapper_fd_cmd {
__u32 fd;
__u32 flags; // CLOEXEC
};
// 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
@@ -128,6 +133,7 @@ struct ksu_manual_su_cmd {
#define KSU_IOCTL_SET_APP_PROFILE _IOC(_IOC_WRITE, 'K', 12, 0) #define KSU_IOCTL_SET_APP_PROFILE _IOC(_IOC_WRITE, 'K', 12, 0)
#define KSU_IOCTL_GET_FEATURE _IOC(_IOC_READ|_IOC_WRITE, 'K', 13, 0) #define KSU_IOCTL_GET_FEATURE _IOC(_IOC_READ|_IOC_WRITE, 'K', 13, 0)
#define KSU_IOCTL_SET_FEATURE _IOC(_IOC_WRITE, 'K', 14, 0) #define KSU_IOCTL_SET_FEATURE _IOC(_IOC_WRITE, 'K', 14, 0)
#define KSU_IOCTL_GET_WRAPPER_FD _IOC(_IOC_NONE, 'K', 10000, 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)

View File

@@ -40,3 +40,5 @@ pub const BACKUP_FILENAME: &str = "stock_image.sha1";
pub const NO_TMPFS_PATH: &str = concatcp!(WORKING_DIR, ".notmpfs"); pub const NO_TMPFS_PATH: &str = concatcp!(WORKING_DIR, ".notmpfs");
pub const NO_MOUNT_PATH: &str = concatcp!(WORKING_DIR, ".nomount"); pub const NO_MOUNT_PATH: &str = concatcp!(WORKING_DIR, ".nomount");
pub const NO_FD_WRAPPER_PATH: &str = concatcp!(WORKING_DIR, ".nofdwrapper");

View File

@@ -1,4 +1,5 @@
use std::fs; use std::fs;
use std::os::fd::OwnedFd;
#[cfg(any(target_os = "linux", target_os = "android"))] #[cfg(any(target_os = "linux", target_os = "android"))]
use std::os::fd::RawFd; use std::os::fd::RawFd;
use std::sync::OnceLock; use std::sync::OnceLock;
@@ -15,6 +16,7 @@ const KSU_IOCTL_SET_SEPOLICY: u32 = 0xc0004b04; // _IOC(_IOC_READ|_IOC_WRITE, 'K
const KSU_IOCTL_CHECK_SAFEMODE: u32 = 0x80004b05; // _IOC(_IOC_READ, 'K', 5, 0) const KSU_IOCTL_CHECK_SAFEMODE: u32 = 0x80004b05; // _IOC(_IOC_READ, 'K', 5, 0)
const KSU_IOCTL_GET_FEATURE: u32 = 0xc0004b0d; // _IOC(_IOC_READ|_IOC_WRITE, 'K', 13, 0) const KSU_IOCTL_GET_FEATURE: u32 = 0xc0004b0d; // _IOC(_IOC_READ|_IOC_WRITE, 'K', 13, 0)
const KSU_IOCTL_SET_FEATURE: u32 = 0x40004b0e; // _IOC(_IOC_WRITE, 'K', 14, 0) const KSU_IOCTL_SET_FEATURE: u32 = 0x40004b0e; // _IOC(_IOC_WRITE, 'K', 14, 0)
const KSU_IOCTL_GET_WRAPPER_FD: u32 = 0x00006f10; // _IOC(_IOC_NONE, 'K', 10000, 0)
#[allow(dead_code)] #[allow(dead_code)]
const KSU_IOCTL_KPM: u32 = 0xc0004bc8; // _IOC(_IOC_READ|_IOC_WRITE, 'K', 200, 0) const KSU_IOCTL_KPM: u32 = 0xc0004bc8; // _IOC(_IOC_READ|_IOC_WRITE, 'K', 200, 0)
@@ -58,6 +60,13 @@ struct SetFeatureCmd {
value: u64, value: u64,
} }
#[repr(C)]
#[derive(Clone, Copy, Default)]
struct GetWrapperFdCmd {
fd: i32,
flags: u32,
}
// Global driver fd cache // Global driver fd cache
#[cfg(any(target_os = "linux", target_os = "android"))] #[cfg(any(target_os = "linux", target_os = "android"))]
static DRIVER_FD: OnceLock<RawFd> = OnceLock::new(); static DRIVER_FD: OnceLock<RawFd> = OnceLock::new();
@@ -223,6 +232,12 @@ pub fn set_feature(feature_id: u32, value: u64) -> std::io::Result<()> {
Ok(()) Ok(())
} }
pub fn get_wrapped_fd(fd: RawFd) -> std::io::Result<RawFd> {
let mut cmd = GetWrapperFdCmd { fd, flags: 0 };
let result = ksuctl(KSU_IOCTL_GET_WRAPPER_FD, &mut cmd as *mut _)?;
Ok(result)
}
#[repr(C)] #[repr(C)]
#[derive(Clone, Copy, Default)] #[derive(Clone, Copy, Default)]
#[allow(dead_code)] #[allow(dead_code)]

View File

@@ -1,20 +1,26 @@
use crate::{
defs,
utils::{self, umask},
};
use anyhow::{Context, Ok, Result, bail};
use getopts::Options;
use libc::c_int;
use log::{debug, error, info};
use procfs::process::FDTarget::Path;
use std::fs::File;
#[cfg(unix)] #[cfg(unix)]
use std::os::unix::process::CommandExt; use std::os::unix::process::CommandExt;
use std::{env, ffi::CStr, path::PathBuf, process::Command}; use std::{env, ffi::CStr, path::PathBuf, process::Command};
use anyhow::{Ok, Result}; use crate::defs::NO_FD_WRAPPER_PATH;
use getopts::Options; use crate::ksucalls::get_wrapped_fd;
#[cfg(any(target_os = "linux", target_os = "android"))] #[cfg(any(target_os = "linux", target_os = "android"))]
use rustix::{ use rustix::{
process::getuid, process::getuid,
thread::{Gid, Uid, set_thread_res_gid, set_thread_res_uid}, thread::{Gid, Uid, set_thread_res_gid, set_thread_res_uid},
}; };
use crate::{
defs,
utils::{self, umask},
};
#[cfg(any(target_os = "linux", target_os = "android"))] #[cfg(any(target_os = "linux", target_os = "android"))]
pub fn grant_root(global_mnt: bool) -> Result<()> { pub fn grant_root(global_mnt: bool) -> Result<()> {
crate::ksucalls::grant_root()?; crate::ksucalls::grant_root()?;
@@ -62,6 +68,29 @@ fn set_identity(uid: u32, gid: u32, groups: &[u32]) {
} }
} }
#[cfg(any(target_os = "android"))]
fn wrap_tty(fd: c_int) {
let inner_fn = move || -> Result<()> {
if unsafe { libc::isatty(fd) != 1 } {
debug!("not a tty: {fd}");
return Ok(());
}
let new_fd = get_wrapped_fd(fd).context("get_wrapped_fd")?;
if unsafe { libc::dup2(new_fd, fd) } == -1 {
bail!("dup {new_fd} -> {fd} errno: {}", unsafe {
*libc::__errno()
});
} else {
unsafe { libc::close(new_fd) };
Ok(())
}
};
if let Err(e) = inner_fn() {
error!("wrap tty {fd}: {e:?}");
}
}
#[cfg(not(any(target_os = "linux", target_os = "android")))] #[cfg(not(any(target_os = "linux", target_os = "android")))]
pub fn root_shell() -> Result<()> { pub fn root_shell() -> Result<()> {
unimplemented!() unimplemented!()
@@ -122,6 +151,8 @@ pub fn root_shell() -> Result<()> {
"Specify a supplementary group. The first specified supplementary group is also used as a primary group if the option -g is not specified.", "Specify a supplementary group. The first specified supplementary group is also used as a primary group if the option -g is not specified.",
"GROUP", "GROUP",
); );
opts.optflag("w", "wrapper", "Use mksu fd wrapper");
opts.optflag("W", "no-wrapper", "Don't use mksu fd wrapper");
// Replace -cn with -z, -mm with -M for supporting getopt_long // Replace -cn with -z, -mm with -M for supporting getopt_long
let args = args let args = args
@@ -165,6 +196,11 @@ pub fn root_shell() -> Result<()> {
let mut is_login = matches.opt_present("l"); let mut is_login = matches.opt_present("l");
let preserve_env = matches.opt_present("p"); let preserve_env = matches.opt_present("p");
let mount_master = matches.opt_present("M"); let mount_master = matches.opt_present("M");
let use_fd_wrapper = (!std::path::Path::new(NO_FD_WRAPPER_PATH).exists()
|| matches.opt_present("w"))
&& !matches.opt_present("W");
info!("use_fd_wrapper={use_fd_wrapper}");
let groups = matches let groups = matches
.opt_strs("G") .opt_strs("G")
@@ -263,6 +299,13 @@ pub fn root_shell() -> Result<()> {
let _ = utils::switch_mnt_ns(1); let _ = utils::switch_mnt_ns(1);
} }
#[cfg(any(target_os = "android"))]
if use_fd_wrapper {
wrap_tty(0);
wrap_tty(1);
wrap_tty(2);
}
set_identity(uid, gid, &groups); set_identity(uid, gid, &groups);
Result::Ok(()) Result::Ok(())