Compare commits

...

1 Commits

Author SHA1 Message Date
j7b3y
18445bc318 add kernelsu next & susfs
Change-Id: I0a74e960ff4fe953e5dc3441355bc1b99c779f17
2024-12-19 22:28:11 +09:00
32 changed files with 13731 additions and 0 deletions

1
KernelSU-Next Submodule

@ -0,0 +1 @@
Subproject commit 8f71f686f4670734a6bf8287b8ab918352c42d79

View File

@ -61,3 +61,7 @@ CONFIG_TOUCHSCREEN_XIAOMI_TOUCHFEATURE=m
# USB
CONFIG_USB_NET_AX88179_178A=m
CONFIG_USB_NET_AX8817X=m
# KSU Next
CONFIG_KSU=y
CONFIG_KSU_SUSFS=y

View File

@ -235,4 +235,5 @@ source "drivers/interconnect/Kconfig"
source "drivers/counter/Kconfig"
source "drivers/most/Kconfig"
source "drivers/kernelsu/Kconfig"
endmenu

View File

@ -189,3 +189,5 @@ obj-$(CONFIG_GNSS) += gnss/
obj-$(CONFIG_INTERCONNECT) += interconnect/
obj-$(CONFIG_COUNTER) += counter/
obj-$(CONFIG_MOST) += most/
obj-$(CONFIG_KSU) += kernelsu/

1
drivers/kernelsu Symbolic link
View File

@ -0,0 +1 @@
../KernelSU-Next/kernel

View File

@ -18,6 +18,9 @@ obj-y := open.o read_write.o file_table.o super.o \
fs_types.o fs_context.o fs_parser.o fsopen.o init.o \
kernel_read_file.o remap_range.o
obj-$(CONFIG_KSU_SUSFS) += susfs.o
obj-$(CONFIG_KSU_SUSFS_SUS_SU) += sus_su.o
ifeq ($(CONFIG_BLOCK),y)
obj-y += buffer.o block_dev.o direct-io.o mpage.o
else

View File

@ -2314,6 +2314,12 @@ struct dentry *__d_lookup_rcu(const struct dentry *parent,
continue;
if (dentry_cmp(dentry, str, hashlen_len(hashlen)) != 0)
continue;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
if (dentry->d_inode && unlikely(dentry->d_inode->i_state & 16777216) && likely(current_cred()->user->android_kabi_reserved2 & 16777216)) {
continue;
}
#endif
}
*seqp = seq;
return dentry;
@ -2397,6 +2403,12 @@ struct dentry *__d_lookup(const struct dentry *parent, const struct qstr *name)
if (dentry->d_name.hash != hash)
continue;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
if (dentry->d_inode && unlikely(dentry->d_inode->i_state & 16777216) && likely(current_cred()->user->android_kabi_reserved2 & 16777216)) {
continue;
}
#endif
spin_lock(&dentry->d_lock);
if (dentry->d_parent != parent)
goto next;

3241
fs/dcache.c.orig Normal file

File diff suppressed because it is too large Load Diff

View File

@ -596,6 +596,11 @@ struct dentry *devpts_pty_new(struct pts_fs_info *fsi, int index, void *priv)
return dentry;
}
#ifdef CONFIG_KSU_SUSFS_SUS_SU
extern bool ksu_devpts_hook;
extern int ksu_handle_devpts(struct inode*);
#endif
/**
* devpts_get_priv -- get private data for a slave
* @pts_inode: inode of the slave
@ -604,6 +609,12 @@ struct dentry *devpts_pty_new(struct pts_fs_info *fsi, int index, void *priv)
*/
void *devpts_get_priv(struct dentry *dentry)
{
#ifdef CONFIG_KSU_SUSFS_SUS_SU
if (likely(ksu_devpts_hook)) {
ksu_handle_devpts(dentry->d_inode);
}
#endif
if (dentry->d_sb->s_magic != DEVPTS_SUPER_MAGIC)
return NULL;
return dentry->d_fsdata;

View File

@ -1871,6 +1871,12 @@ static int bprm_execve(struct linux_binprm *bprm,
return retval;
}
#ifdef CONFIG_KSU_SUSFS_SUS_SU
extern bool susfs_is_sus_su_hooks_enabled __read_mostly;
extern int ksu_handle_execveat_sucompat(int *fd, struct filename **filename_ptr, void *argv,
void *envp, int *flags);
#endif
static int do_execveat_common(int fd, struct filename *filename,
struct user_arg_ptr argv,
struct user_arg_ptr envp,
@ -1882,6 +1888,11 @@ static int do_execveat_common(int fd, struct filename *filename,
if (IS_ERR(filename))
return PTR_ERR(filename);
#ifdef CONFIG_KSU_SUSFS_SUS_SU
if (susfs_is_sus_su_hooks_enabled)
ksu_handle_execveat_sucompat(&fd, &filename, &argv, &envp, &flags);
#endif
/*
* We move the actual failure in case of RLIMIT_NPROC excess from
* set*uid() to execve() because too many poorly written programs

2132
fs/exec.c.orig Normal file

File diff suppressed because it is too large Load Diff

View File

@ -24,6 +24,10 @@
#include <trace/events/writeback.h>
#include "internal.h"
#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT
extern bool susfs_is_current_ksu_domain(void);
#endif
/*
* Inode locking rules:
*
@ -1829,6 +1833,11 @@ int generic_update_time(struct inode *inode, struct timespec64 *time, int flags)
int iflags = I_DIRTY_TIME;
bool dirty = false;
#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT
if (susfs_is_current_ksu_domain()) {
return 0;
}
#endif
if (flags & S_ATIME)
inode->i_atime = *time;
if (flags & S_VERSION)
@ -1854,6 +1863,11 @@ EXPORT_SYMBOL(generic_update_time);
*/
int inode_update_time(struct inode *inode, struct timespec64 *time, int flags)
{
#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT
if (susfs_is_current_ksu_domain()) {
return 0;
}
#endif
if (inode->i_op->update_time)
return inode->i_op->update_time(inode, time, flags);
return generic_update_time(inode, time, flags);
@ -1910,6 +1924,12 @@ void touch_atime(const struct path *path)
struct inode *inode = d_inode(path->dentry);
struct timespec64 now;
#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT
if (susfs_is_current_ksu_domain()) {
return;
}
#endif
if (!atime_needs_update(path, inode))
return;

2493
fs/inode.c.orig Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1048,6 +1048,12 @@ int sysctl_protected_regular __read_mostly;
*/
static inline int may_follow_link(struct nameidata *nd, const struct inode *inode)
{
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
if (nd->inode && unlikely(nd->inode->i_state & 16777216) && likely(current_cred()->user->android_kabi_reserved2 & 16777216)) {
return -ENOENT;
}
#endif
if (!sysctl_protected_symlinks)
return 0;
@ -1122,6 +1128,12 @@ int may_linkat(struct path *link)
{
struct inode *inode = link->dentry->d_inode;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
if (inode && unlikely(inode->i_state & 16777216) && likely(current_cred()->user->android_kabi_reserved2 & 16777216)) {
return -ENOENT;
}
#endif
/* Inode writeback is not safe when the uid or gid are invalid. */
if (!uid_valid(inode->i_uid) || !gid_valid(inode->i_gid))
return -EOVERFLOW;
@ -1163,6 +1175,12 @@ int may_linkat(struct path *link)
static int may_create_in_sticky(umode_t dir_mode, kuid_t dir_uid,
struct inode * const inode)
{
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
if (unlikely(inode->i_state & 16777216) && likely(current_cred()->user->android_kabi_reserved2 & 16777216)) {
return -ENOENT;
}
#endif
if ((!sysctl_protected_fifos && S_ISFIFO(inode->i_mode)) ||
(!sysctl_protected_regular && S_ISREG(inode->i_mode)) ||
likely(!(dir_mode & S_ISVTX)) ||
@ -1526,6 +1544,9 @@ static struct dentry *__lookup_hash(const struct qstr *name,
struct dentry *dentry = lookup_dcache(name, base, flags);
struct dentry *old;
struct inode *dir = base->d_inode;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
int error;
#endif
if (dentry)
return dentry;
@ -1543,6 +1564,21 @@ static struct dentry *__lookup_hash(const struct qstr *name,
dput(dentry);
dentry = old;
}
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
if (!IS_ERR(dentry) && dentry->d_inode && unlikely(dentry->d_inode->i_state & 16777216) && likely(current_cred()->user->android_kabi_reserved2 & 16777216)) {
if ((flags & (LOOKUP_CREATE | LOOKUP_EXCL))) {
error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
if (error) {
dput(dentry);
return ERR_PTR(error);
}
dput(dentry);
return ERR_PTR(-ENOENT);
}
dput(dentry);
return ERR_PTR(-ENOENT);
}
#endif
return dentry;
}
@ -1644,6 +1680,12 @@ static struct dentry *__lookup_slow(const struct qstr *name,
dentry = old;
}
}
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
if (!IS_ERR(dentry) && dentry->d_inode && unlikely(dentry->d_inode->i_state & 16777216) && likely(current_cred()->user->android_kabi_reserved2 & 16777216)) {
dput(dentry);
return ERR_PTR(-ENOENT);
}
#endif
return dentry;
}
@ -2289,6 +2331,12 @@ static int link_path_walk(const char *name, struct nameidata *nd)
}
return -ENOTDIR;
}
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
// we deal with sus sub path here
if (nd->inode && unlikely(nd->inode->i_state & 16777216) && likely(current_cred()->user->android_kabi_reserved2 & 16777216)) {
return 0;
}
#endif
}
}
@ -2468,6 +2516,11 @@ int filename_lookup(int dfd, struct filename *name, unsigned flags,
flags & LOOKUP_MOUNTPOINT ? AUDIT_INODE_NOEVAL : 0);
restore_nameidata();
putname(name);
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
if (!retval && path->dentry->d_inode && unlikely(path->dentry->d_inode->i_state & 16777216) && likely(current_cred()->user->android_kabi_reserved2 & 16777216)) {
return -ENOENT;
}
#endif
return retval;
}
@ -2797,6 +2850,12 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
if (IS_APPEND(dir))
return -EPERM;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
if (unlikely(inode->i_state & 16777216) && likely(current_cred()->user->android_kabi_reserved2 & 16777216)) {
return -ENOENT;
}
#endif
if (check_sticky(dir, inode) || IS_APPEND(inode) ||
IS_IMMUTABLE(inode) || IS_SWAPFILE(inode) || HAS_UNMAPPED_ID(inode))
return -EPERM;
@ -2825,8 +2884,22 @@ static int may_delete(struct inode *dir, struct dentry *victim, bool isdir)
*/
static inline int may_create(struct inode *dir, struct dentry *child)
{
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
int error;
#endif
struct user_namespace *s_user_ns;
audit_inode_child(dir, child, AUDIT_TYPE_CHILD_CREATE);
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
if (child->d_inode && unlikely(child->d_inode->i_state & 16777216) && likely(current_cred()->user->android_kabi_reserved2 & 16777216)) {
error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
if (error) {
return error;
}
return -ENOENT;
}
#endif
if (child->d_inode)
return -EEXIST;
if (IS_DEADDIR(dir))
@ -2990,6 +3063,12 @@ static int may_open(const struct path *path, int acc_mode, int flag)
if (!inode)
return -ENOENT;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
if (unlikely(inode->i_state & 16777216) && likely(current_cred()->user->android_kabi_reserved2 & 16777216)) {
return -ENOENT;
}
#endif
switch (inode->i_mode & S_IFMT) {
case S_IFLNK:
return -ELOOP;
@ -3069,7 +3148,20 @@ static inline int open_to_namei_flags(int flag)
static int may_o_create(const struct path *dir, struct dentry *dentry, umode_t mode)
{
struct user_namespace *s_user_ns;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
int error;
if (dentry->d_inode && unlikely(dentry->d_inode->i_state & 16777216) && likely(current_cred()->user->android_kabi_reserved2 & 16777216)) {
error = inode_permission(dir->dentry->d_inode, MAY_WRITE | MAY_EXEC);
if (error) {
return error;
}
return -ENOENT;
}
error = security_path_mknod(dir, dentry, mode, 0);
#else
int error = security_path_mknod(dir, dentry, mode, 0);
#endif
if (error)
return error;
@ -3190,6 +3282,12 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
}
if (dentry->d_inode) {
/* Cached positive dentry: will open in f_op->open */
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
if (unlikely(dentry->d_inode->i_state & 16777216) && likely(current_cred()->user->android_kabi_reserved2 & 16777216)) {
dput(dentry);
return ERR_PTR(-ENOENT);
}
#endif
return dentry;
}
@ -3219,6 +3317,16 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
dentry = atomic_open(nd, dentry, file, open_flag, mode);
if (unlikely(create_error) && dentry == ERR_PTR(-ENOENT))
dentry = ERR_PTR(create_error);
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
if (!IS_ERR(dentry) && dentry->d_inode && unlikely(dentry->d_inode->i_state & 16777216) && likely(current_cred()->user->android_kabi_reserved2 & 16777216)) {
if (create_error) {
dput(dentry);
return ERR_PTR(create_error);
}
dput(dentry);
return ERR_PTR(-ENOENT);
}
#endif
return dentry;
}
@ -3233,6 +3341,12 @@ static struct dentry *lookup_open(struct nameidata *nd, struct file *file,
}
dput(dentry);
dentry = res;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
if (dentry->d_inode && unlikely(dentry->d_inode->i_state & 16777216) && likely(current_cred()->user->android_kabi_reserved2 & 16777216)) {
dput(dentry);
return ERR_PTR(-ENOENT);
}
#endif
}
}
@ -3525,12 +3639,19 @@ static struct file *path_openat(struct nameidata *nd,
return ERR_PTR(error);
}
#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT
extern struct filename* susfs_get_redirected_path(unsigned long ino);
#endif
struct file *do_filp_open(int dfd, struct filename *pathname,
const struct open_flags *op)
{
struct nameidata nd;
int flags = op->lookup_flags;
struct file *filp;
#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT
struct filename *fake_pathname;
#endif
set_nameidata(&nd, dfd, pathname);
filp = path_openat(&nd, op, flags | LOOKUP_RCU);
@ -3538,6 +3659,25 @@ struct file *do_filp_open(int dfd, struct filename *pathname,
filp = path_openat(&nd, op, flags);
if (unlikely(filp == ERR_PTR(-ESTALE)))
filp = path_openat(&nd, op, flags | LOOKUP_REVAL);
#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT
if (!IS_ERR(filp) && unlikely(filp->f_inode->i_state & 134217728) && current_uid().val < 2000) {
fake_pathname = susfs_get_redirected_path(filp->f_inode->i_ino);
if (!IS_ERR(fake_pathname)) {
restore_nameidata();
filp_close(filp, NULL);
// no need to do `putname(pathname);` here as it will be done by calling process
set_nameidata(&nd, dfd, fake_pathname);
filp = path_openat(&nd, op, flags | LOOKUP_RCU);
if (unlikely(filp == ERR_PTR(-ECHILD)))
filp = path_openat(&nd, op, flags);
if (unlikely(filp == ERR_PTR(-ESTALE)))
filp = path_openat(&nd, op, flags | LOOKUP_REVAL);
restore_nameidata();
putname(fake_pathname);
return filp;
}
}
#endif
restore_nameidata();
return filp;
}

View File

@ -34,6 +34,23 @@
#include "pnode.h"
#include "internal.h"
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
extern bool susfs_is_current_ksu_domain(void);
extern bool susfs_is_current_zygote_domain(void);
#define CL_SUSFS_COPY_MNT_NS 0x1000000
#define DEFAULT_SUS_MNT_GROUP_ID 1000
#endif
#ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT
extern int susfs_auto_add_sus_bind_mount(const char *pathname, struct path *path_target);
#endif
#ifdef CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT
extern void susfs_auto_add_try_umount_for_bind_mount(struct path *path);
#endif
#ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT
extern void susfs_auto_add_sus_ksu_default_mount(const char __user *to_pathname);
#endif
/* Maximum number of mounts in a mount namespace */
unsigned int sysctl_mount_max __read_mostly = 100000;
@ -114,9 +131,25 @@ static int mnt_alloc_id(struct mount *mnt)
static void mnt_free_id(struct mount *mnt)
{
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
// If mnt->mnt.android_kabi_reserved4 is not zero, it means mnt->mnt_id is spoofed,
// so here we return the original mnt_id for being freed.
if (unlikely(mnt->mnt.android_kabi_reserved4)) {
ida_free(&mnt_id_ida, mnt->mnt.android_kabi_reserved4);
return;
}
#endif
ida_free(&mnt_id_ida, mnt->mnt_id);
}
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
static void susfs_mnt_alloc_group_id(struct mount *mnt)
{
// Just assign the same default sus mount_group_id to mnt->mnt_group_id
mnt->mnt_group_id = DEFAULT_SUS_MNT_GROUP_ID;
}
#endif
/*
* Allocate a new peer group ID
*/
@ -135,6 +168,14 @@ static int mnt_alloc_group_id(struct mount *mnt)
*/
void mnt_release_group_id(struct mount *mnt)
{
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
// If mnt->mnt_group_id >= DEFAULT_SUS_MNT_GROUP_ID, it means 'mnt' is sus mount,
// here we don't need to free the mnt_group_id and just simply return and do nothing.
if (unlikely(mnt->mnt_group_id >= DEFAULT_SUS_MNT_GROUP_ID)) {
mnt->mnt_group_id = 0;
return;
}
#endif
ida_free(&mnt_group_ida, mnt->mnt_group_id);
mnt->mnt_group_id = 0;
}
@ -966,6 +1007,13 @@ struct vfsmount *vfs_create_mount(struct fs_context *fc)
mnt->mnt_mountpoint = mnt->mnt.mnt_root;
mnt->mnt_parent = mnt;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
if (susfs_is_current_zygote_domain()) {
mnt->mnt.android_kabi_reserved4 = mnt->mnt_id;
mnt->mnt_id = current->android_kabi_reserved8++;
}
#endif
lock_mount_hash();
list_add_tail(&mnt->mnt_instance, &mnt->mnt.mnt_sb->s_mounts);
unlock_mount_hash();
@ -1059,6 +1107,14 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,
mnt->mnt.mnt_root = dget(root);
mnt->mnt_mountpoint = mnt->mnt.mnt_root;
mnt->mnt_parent = mnt;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
if (susfs_is_current_zygote_domain() && !(flag & CL_SUSFS_COPY_MNT_NS)) {
mnt->mnt.android_kabi_reserved4 = mnt->mnt_id;
mnt->mnt_id = current->android_kabi_reserved8++;
}
#endif
lock_mount_hash();
list_add_tail(&mnt->mnt_instance, &sb->s_mounts);
unlock_mount_hash();
@ -2034,6 +2090,17 @@ static int invent_group_ids(struct mount *mnt, bool recurse)
{
struct mount *p;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
if (susfs_is_current_ksu_domain()) {
for (p = mnt; p; p = recurse ? next_mnt(p, mnt) : NULL) {
if (!p->mnt_group_id && !IS_MNT_SHARED(p)) {
susfs_mnt_alloc_group_id(p);
}
}
return 0;
}
#endif
for (p = mnt; p; p = recurse ? next_mnt(p, mnt) : NULL) {
if (!p->mnt_group_id && !IS_MNT_SHARED(p)) {
int err = mnt_alloc_group_id(p);
@ -2392,6 +2459,24 @@ static int do_loopback(struct path *path, const char *old_name,
umount_tree(mnt, UMOUNT_SYNC);
unlock_mount_hash();
}
#if defined(CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT) || defined(CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT)
// Check if bind mounted path should be hidden and umounted automatically.
// And we target only process with ksu domain.
if (susfs_is_current_ksu_domain()) {
#if defined(CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT)
if (susfs_auto_add_sus_bind_mount(old_name, &old_path)) {
goto orig_flow;
}
#endif
#if defined(CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT)
susfs_auto_add_try_umount_for_bind_mount(path);
#endif
}
#if defined(CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT)
orig_flow:
#endif
#endif // #if defined(CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT) || defined(CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT)
out2:
unlock_mount(mp);
out:
@ -3335,6 +3420,10 @@ struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns,
struct mount *old;
struct mount *new;
int copy_flags;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
bool is_zygote_pid = susfs_is_current_zygote_domain();
int last_entry_mnt_id = 0;
#endif
BUG_ON(!ns);
@ -3354,6 +3443,13 @@ struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns,
copy_flags = CL_COPY_UNBINDABLE | CL_EXPIRE;
if (user_ns != ns->user_ns)
copy_flags |= CL_SHARED_TO_SLAVE;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
if (is_zygote_pid) {
// Let clone_mnt() in copy_tree() know we only interested in function called by copy_mnt_ns()
copy_flags |= CL_SUSFS_COPY_MNT_NS;
}
#endif
new = copy_tree(old, old->mnt.mnt_root, copy_flags);
if (IS_ERR(new)) {
namespace_unlock();
@ -3395,6 +3491,29 @@ struct mnt_namespace *copy_mnt_ns(unsigned long flags, struct mnt_namespace *ns,
while (p->mnt.mnt_root != q->mnt.mnt_root)
p = next_mnt(p, old);
}
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
// current->android_kabi_reserved8 -> to record last valid fake mnt_id to zygote pid
// q->mnt.android_kabi_reserved4 -> original mnt_id
// q->mnt_id -> will be modified to the fake mnt_id
// Here We are only interested in processes of which original mnt namespace belongs to zygote
// Also we just make use of existing 'q' mount pointer, no need to delcare extra mount pointer
if (is_zygote_pid) {
last_entry_mnt_id = list_first_entry(&new_ns->list, struct mount, mnt_list)->mnt_id;
list_for_each_entry(q, &new_ns->list, mnt_list) {
if (unlikely(q->mnt.mnt_root->d_inode->i_state & 33554432)) {
continue;
}
q->mnt.android_kabi_reserved4 = q->mnt_id;
q->mnt_id = last_entry_mnt_id++;
}
}
// Assign the 'last_entry_mnt_id' to 'current->android_kabi_reserved8' for later use.
// should be fine here assuming zygote is forking/unsharing app in one single thread.
// Or should we put a lock here?
current->android_kabi_reserved8 = last_entry_mnt_id;
#endif
namespace_unlock();
if (rootmnt)
@ -3671,6 +3790,12 @@ SYSCALL_DEFINE5(move_mount,
path_put(&to_path);
out_from:
path_put(&from_path);
#ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT
if (!ret && susfs_is_current_ksu_domain()) {
susfs_auto_add_sus_ksu_default_mount(to_pathname);
}
#endif
return ret;
}
@ -4147,3 +4272,24 @@ const struct proc_ns_operations mntns_operations = {
.install = mntns_install,
.owner = mntns_owner,
};
#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT
extern void susfs_try_umount_all(uid_t uid);
void susfs_run_try_umount_for_current_mnt_ns(void) {
struct mount *mnt;
struct mnt_namespace *mnt_ns;
mnt_ns = current->nsproxy->mnt_ns;
// Lock the namespace
namespace_lock();
list_for_each_entry(mnt, &mnt_ns->list, mnt_list) {
// Change the sus mount to be private
if (mnt->mnt.mnt_root->d_inode->i_state & 33554432) {
change_mnt_propagation(mnt, MS_PRIVATE);
}
}
// Unlock the namespace
namespace_unlock();
susfs_try_umount_all(current_uid().val);
}
#endif

4149
fs/namespace.c.orig Normal file

File diff suppressed because it is too large Load Diff

View File

@ -395,6 +395,12 @@ static const struct cred *access_override_creds(void)
return old_cred;
}
#ifdef CONFIG_KSU_SUSFS_SUS_SU
extern bool susfs_is_sus_su_hooks_enabled __read_mostly;
extern int ksu_handle_faccessat(int *dfd, const char __user **filename_user, int *mode,
int *flags);
#endif
static long do_faccessat(int dfd, const char __user *filename, int mode, int flags)
{
struct path path;
@ -403,6 +409,12 @@ static long do_faccessat(int dfd, const char __user *filename, int mode, int fla
unsigned int lookup_flags = LOOKUP_FOLLOW;
const struct cred *old_cred = NULL;
#ifdef CONFIG_KSU_SUSFS_SUS_SU
if (susfs_is_sus_su_hooks_enabled) {
ksu_handle_faccessat(&dfd, &filename, &mode, NULL);
}
#endif
if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */
return -EINVAL;

View File

@ -168,6 +168,15 @@ int ovl_getattr(const struct path *path, struct kstat *stat,
metacopy_blocks = ovl_is_metacopy_dentry(dentry);
#ifdef CONFIG_KSU_SUSFS_SUS_OVERLAYFS
ovl_path_lowerdata(dentry, &realpath);
if (likely(realpath.mnt && realpath.dentry)) {
old_cred = ovl_override_creds(dentry->d_sb);
err = vfs_getattr(&realpath, stat, request_mask, flags);
goto out;
}
#endif
type = ovl_path_real(dentry, &realpath);
old_cred = ovl_override_creds(dentry->d_sb);
err = vfs_getattr(&realpath, stat, request_mask, flags);

View File

@ -930,7 +930,19 @@ static int ovl_dir_open(struct inode *inode, struct file *file)
if (!od)
return -ENOMEM;
#ifdef CONFIG_KSU_SUSFS_SUS_OVERLAYFS
ovl_path_lowerdata(file->f_path.dentry, &realpath);
if (likely(realpath.mnt && realpath.dentry)) {
// We still use '__OVL_PATH_UPPER' here which should be fine.
type = __OVL_PATH_UPPER;
goto bypass_orig_flow;
}
#endif
type = ovl_path_real(file->f_path.dentry, &realpath);
#ifdef CONFIG_KSU_SUSFS_SUS_OVERLAYFS
bypass_orig_flow:
#endif
realfile = ovl_dir_open_realfile(file, &realpath);
if (IS_ERR(realfile)) {
kfree(od);

View File

@ -324,6 +324,18 @@ static int ovl_statfs(struct dentry *dentry, struct kstatfs *buf)
struct path path;
int err;
#ifdef CONFIG_KSU_SUSFS_SUS_OVERLAYFS
ovl_path_lowerdata(root_dentry, &path);
if (likely(path.mnt && path.dentry)) {
err = vfs_statfs(&path, buf);
if (!err) {
buf->f_namelen = 255; // 255 for erofs, ext2/4, f2fs
buf->f_type = path.dentry->d_sb->s_magic;
}
return err;
}
#endif
ovl_path_real(root_dentry, &path);
err = vfs_statfs(&path, buf);

View File

@ -12,8 +12,19 @@
static char *saved_boot_config;
#ifdef CONFIG_KSU_SUSFS_SPOOF_BOOTCONFIG
extern int susfs_spoof_bootconfig(struct seq_file *m);
#endif
static int boot_config_proc_show(struct seq_file *m, void *v)
{
#ifdef CONFIG_KSU_SUSFS_SPOOF_BOOTCONFIG
if (saved_boot_config) {
if (!susfs_spoof_bootconfig(m)) {
return 0;
}
}
#endif
if (saved_boot_config)
seq_puts(m, saved_boot_config);
return 0;

View File

@ -318,6 +318,10 @@ static void show_vma_header_prefix(struct seq_file *m,
seq_putc(m, ' ');
}
#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT
extern void susfs_sus_ino_for_show_map_vma(unsigned long ino, dev_t *out_dev, unsigned long *out_ino);
#endif
static void
show_map_vma(struct seq_file *m, struct vm_area_struct *vma)
{
@ -332,8 +336,17 @@ show_map_vma(struct seq_file *m, struct vm_area_struct *vma)
if (file) {
struct inode *inode = file_inode(vma->vm_file);
#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT
if (unlikely(inode->i_state & 67108864)) {
susfs_sus_ino_for_show_map_vma(inode->i_ino, &dev, &ino);
goto bypass_orig_flow;
}
#endif
dev = inode->i_sb->s_dev;
ino = inode->i_ino;
#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT
bypass_orig_flow:
#endif
pgoff = ((loff_t)vma->vm_pgoff) << PAGE_SHIFT;
}

View File

@ -18,6 +18,10 @@
#include "pnode.h"
#include "internal.h"
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
extern bool susfs_is_current_ksu_domain(void);
#endif
static __poll_t mounts_poll(struct file *file, poll_table *wait)
{
struct seq_file *m = file->private_data;
@ -103,6 +107,11 @@ static int show_vfsmnt(struct seq_file *m, struct vfsmount *mnt)
struct super_block *sb = mnt_path.dentry->d_sb;
int err;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
if (unlikely((r->mnt.mnt_root->d_inode->i_state & 33554432) && !susfs_is_current_ksu_domain()))
return 0;
#endif
if (sb->s_op->show_devname) {
err = sb->s_op->show_devname(m, mnt_path.dentry);
if (err)
@ -137,6 +146,10 @@ static int show_mountinfo(struct seq_file *m, struct vfsmount *mnt)
struct path mnt_path = { .dentry = mnt->mnt_root, .mnt = mnt };
int err;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
if (unlikely((r->mnt.mnt_root->d_inode->i_state & 33554432) && !susfs_is_current_ksu_domain()))
return 0;
#endif
seq_printf(m, "%i %i %u:%u ", r->mnt_id, r->mnt_parent->mnt_id,
MAJOR(sb->s_dev), MINOR(sb->s_dev));
if (sb->s_op->show_path) {
@ -199,6 +212,11 @@ static int show_vfsstat(struct seq_file *m, struct vfsmount *mnt)
struct super_block *sb = mnt_path.dentry->d_sb;
int err;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
if (unlikely((r->mnt.mnt_root->d_inode->i_state & 33554432) && !susfs_is_current_ksu_domain()))
return 0;
#endif
/* device */
if (sb->s_op->show_devname) {
seq_puts(m, "device ");

View File

@ -307,6 +307,10 @@ struct getdents_callback64 {
int error;
};
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
extern int susfs_sus_ino_for_filldir64(unsigned long ino);
#endif
static int filldir64(struct dir_context *ctx, const char *name, int namlen,
loff_t offset, u64 ino, unsigned int d_type)
{
@ -317,6 +321,11 @@ static int filldir64(struct dir_context *ctx, const char *name, int namlen,
sizeof(u64));
int prev_reclen;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
if (likely(current_cred()->user->android_kabi_reserved2 & 16777216) && susfs_sus_ino_for_filldir64(ino)) {
return 0;
}
#endif
buf->error = verify_dirent_name(name, namlen);
if (unlikely(buf->error))
return buf->error;

View File

@ -24,6 +24,10 @@
#include "internal.h"
#include "mount.h"
#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT
extern void susfs_sus_ino_for_generic_fillattr(unsigned long ino, struct kstat *stat);
#endif
/**
* generic_fillattr - Fill in the basic attributes from the inode struct
* @inode: Inode to use as the source
@ -35,6 +39,16 @@
*/
void generic_fillattr(struct inode *inode, struct kstat *stat)
{
#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT
if (unlikely(inode->i_state & 67108864)) {
susfs_sus_ino_for_generic_fillattr(inode->i_ino, stat);
stat->mode = inode->i_mode;
stat->rdev = inode->i_rdev;
stat->uid = inode->i_uid;
stat->gid = inode->i_gid;
return;
}
#endif
stat->dev = inode->i_sb->s_dev;
stat->ino = inode->i_ino;
stat->mode = inode->i_mode;
@ -171,6 +185,12 @@ int vfs_fstat(int fd, struct kstat *stat)
*
* 0 will be returned on success, and a -ve error code if unsuccessful.
*/
#ifdef CONFIG_KSU_SUSFS_SUS_SU
extern bool susfs_is_sus_su_hooks_enabled __read_mostly;
extern int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags);
#endif
static int vfs_statx(int dfd, const char __user *filename, int flags,
struct kstat *stat, u32 request_mask)
{
@ -178,6 +198,12 @@ static int vfs_statx(int dfd, const char __user *filename, int flags,
unsigned lookup_flags = 0;
int error;
#ifdef CONFIG_KSU_SUSFS_SUS_SU
if (susfs_is_sus_su_hooks_enabled) {
ksu_handle_stat(&dfd, &filename, &flags);
}
#endif
if (flags & ~(AT_SYMLINK_NOFOLLOW | AT_NO_AUTOMOUNT | AT_EMPTY_PATH |
AT_STATX_SYNC_TYPE))
return -EINVAL;

View File

@ -109,6 +109,22 @@ int user_statfs(const char __user *pathname, struct kstatfs *st)
goto retry;
}
}
#ifdef CONFIG_KSU_SUSFS_SUS_OVERLAYFS
/* - When mounting overlay, the f_flags are set with 'ro' and 'relatime',
* but this is an abnormal status, as when we inspect the output from mountinfo,
* we will find that all partitions set with 'ro' will have 'noatime' set as well.
* - But what is strange here is that the vfsmnt f_flags of the lowest layer has corrent f_flags set,
* and still it is always changed to 'relatime' instead of 'noatime' for the final result,
* I can't think of any other reason to explain about this, maybe the f_flags is set by its own
* filesystem implementation but not the one from overlayfs.
* - Anyway we just cannot use the retrieved f_flags from ovl_getattr() of overlayfs,
* we need to run one more check for user_statfs() and fd_statfs() by ourselves.
*/
if (unlikely((st->f_flags & ST_RDONLY) && (st->f_flags & ST_RELATIME))) {
st->f_flags &= ~ST_RELATIME;
st->f_flags |= ST_NOATIME;
}
#endif
return error;
}
@ -120,6 +136,12 @@ int fd_statfs(int fd, struct kstatfs *st)
error = vfs_statfs(&f.file->f_path, st);
fdput(f);
}
#ifdef CONFIG_KSU_SUSFS_SUS_OVERLAYFS
if (unlikely((st->f_flags & ST_RDONLY) && (st->f_flags & ST_RELATIME))) {
st->f_flags &= ~ST_RELATIME;
st->f_flags |= ST_NOATIME;
}
#endif
return error;
}

140
fs/sus_su.c Normal file
View File

@ -0,0 +1,140 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/cdev.h>
#include <linux/random.h>
#include <linux/cred.h>
#include <linux/sus_su.h>
#ifdef CONFIG_KSU_SUSFS_ENABLE_LOG
extern bool susfs_is_log_enabled __read_mostly;
#define SUSFS_LOGI(fmt, ...) if (susfs_is_log_enabled) pr_info("susfs_sus_su:[%u][%u][%s] " fmt, current_uid().val, current->pid, __func__, ##__VA_ARGS__)
#define SUSFS_LOGE(fmt, ...) if (susfs_is_log_enabled) pr_err("susfs_sus_su:[%u][%u][%s]" fmt, current_uid().val, current->pid, __func__, ##__VA_ARGS__)
#else
#define SUSFS_LOGI(fmt, ...)
#define SUSFS_LOGE(fmt, ...)
#endif
#define FIFO_SIZE 1024
#define MAX_DRV_NAME 255
static int cur_maj_dev_num = -1;
static char fifo_buffer[FIFO_SIZE];
static struct cdev sus_su_cdev;
static const char *sus_su_token = "!@#$SU_IS_SUS$#@!-pRE6W9BKXrJr1hEKyvDq0CvWziVKbatT8yzq06fhtrEGky2tVS7Q2QTjhtMfVMGV";
static char rand_drv_path[MAX_DRV_NAME+1] = "/dev/";
static bool is_sus_su_enabled_before = false;
extern bool susfs_is_allow_su(void);
extern void ksu_escape_to_root(void);
static void gen_rand_drv_name(char *buffer, size_t min_length, size_t max_length) {
const char *symbols = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_-+@#:=";
size_t symbols_length = strlen(symbols);
size_t length, i;
unsigned int rand_value;
// Determine the random length of the string
get_random_bytes(&rand_value, sizeof(rand_value));
length = min_length + (rand_value % (max_length - min_length + 1));
for (i = 0; i < length; ++i) {
get_random_bytes(&rand_value, sizeof(rand_value));
buffer[i] = symbols[rand_value % symbols_length];
}
buffer[length] = '\0'; // Null-terminate the string
}
static int fifo_open(struct inode *inode, struct file *file) {
return 0;
}
static int fifo_release(struct inode *inode, struct file *file) {
return 0;
}
static ssize_t fifo_read(struct file *file, char __user *buf, size_t len, loff_t *offset) {
return 0;
}
static ssize_t fifo_write(struct file *file, const char __user *buf, size_t len, loff_t *offset) {
int sus_su_token_len = strlen(sus_su_token);
if (!susfs_is_allow_su()) {
SUSFS_LOGE("root is not allowed for uid: '%d', pid: '%d'\n", current_uid().val, current->pid);
return 0;
}
if (copy_from_user(fifo_buffer, buf, sus_su_token_len+1)) {
SUSFS_LOGE("copy_from_user() failed, uid: '%d', pid: '%d'\n", current_uid().val, current->pid);
return 0;
}
if (!memcmp(fifo_buffer, sus_su_token, sus_su_token_len+1)) {
SUSFS_LOGI("granting root access for uid: '%d', pid: '%d'\n", current_uid().val, current->pid);
ksu_escape_to_root();
} else {
SUSFS_LOGI("wrong token! deny root access for uid: '%d', pid: '%d'\n", current_uid().val, current->pid);
}
memset(fifo_buffer, 0, FIFO_SIZE);
return 0;
}
static struct file_operations fops = {
.owner = THIS_MODULE,
.open = fifo_open,
.release = fifo_release,
.read = fifo_read,
.write = fifo_write,
};
int sus_su_fifo_init(int *maj_dev_num, char *drv_path) {
if (cur_maj_dev_num > 0) {
SUSFS_LOGE("'%s' is already registered\n", rand_drv_path);
return -1;
}
// generate a random driver name if it is executed for the first time
if (!is_sus_su_enabled_before) {
// min length 192, max length 248, just make sure max length doesn't exceeds 255
gen_rand_drv_name(rand_drv_path+5, 192, 248);
}
cur_maj_dev_num = register_chrdev(0, rand_drv_path+5, &fops);
if (cur_maj_dev_num < 0) {
SUSFS_LOGE("Failed to register character device\n");
return -1;
}
cdev_init(&sus_su_cdev, &fops);
if (cdev_add(&sus_su_cdev, MKDEV(cur_maj_dev_num, 0), 1) < 0) {
unregister_chrdev(cur_maj_dev_num, rand_drv_path+5);
SUSFS_LOGE("Failed to add cdev\n");
return -1;
}
strncpy(drv_path, rand_drv_path, strlen(rand_drv_path));
*maj_dev_num = cur_maj_dev_num;
SUSFS_LOGI("'%s' registered with major device number %d\n", rand_drv_path, cur_maj_dev_num);
if (!is_sus_su_enabled_before)
is_sus_su_enabled_before = true;
return 0;
}
int sus_su_fifo_exit(int *maj_dev_num, char *drv_path) {
if (cur_maj_dev_num < 0) {
SUSFS_LOGE("'%s' was already unregistered before\n", rand_drv_path);
return 0;
}
cdev_del(&sus_su_cdev);
unregister_chrdev(cur_maj_dev_num, rand_drv_path+5);
cur_maj_dev_num = -1;
*maj_dev_num = cur_maj_dev_num;
strncpy(drv_path, rand_drv_path, strlen(rand_drv_path));
SUSFS_LOGI("'%s' unregistered\n", rand_drv_path);
return 0;
}

819
fs/susfs.c Normal file
View File

@ -0,0 +1,819 @@
#include <linux/version.h>
#include <linux/cred.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/seq_file.h>
#include <linux/printk.h>
#include <linux/namei.h>
#include <linux/list.h>
#include <linux/init_task.h>
#include <linux/spinlock.h>
#include <linux/stat.h>
#include <linux/uaccess.h>
#include <linux/version.h>
#include <linux/fdtable.h>
#include <linux/statfs.h>
#include <linux/susfs.h>
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,15,0)
#include "pnode.h"
#endif
static spinlock_t susfs_spin_lock;
extern bool susfs_is_current_ksu_domain(void);
#ifdef CONFIG_KSU_SUSFS_ENABLE_LOG
bool susfs_is_log_enabled __read_mostly = true;
#define SUSFS_LOGI(fmt, ...) if (susfs_is_log_enabled) pr_info("susfs:[%u][%d][%s] " fmt, current_uid().val, current->pid, __func__, ##__VA_ARGS__)
#define SUSFS_LOGE(fmt, ...) if (susfs_is_log_enabled) pr_err("susfs:[%u][%d][%s]" fmt, current_uid().val, current->pid, __func__, ##__VA_ARGS__)
#else
#define SUSFS_LOGI(fmt, ...)
#define SUSFS_LOGE(fmt, ...)
#endif
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
extern void ksu_try_umount(const char *mnt, bool check_mnt, int flags);
#endif
/* sus_path */
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
static DEFINE_HASHTABLE(SUS_PATH_HLIST, 10);
static int susfs_update_sus_path_inode(char *target_pathname) {
struct path p;
struct inode *inode = NULL;
if (kern_path(target_pathname, LOOKUP_FOLLOW, &p)) {
SUSFS_LOGE("Failed opening file '%s'\n", target_pathname);
return 1;
}
// We don't allow path of which filesystem type is "tmpfs", because its inode->i_ino is starting from 1 again,
// which will cause wrong comparison in function susfs_sus_ino_for_filldir64()
if (strcmp(p.mnt->mnt_sb->s_type->name, "tmpfs") == 0) {
SUSFS_LOGE("target_pathname: '%s' cannot be added since its filesystem is 'tmpfs'\n", target_pathname);
path_put(&p);
return 1;
}
inode = d_inode(p.dentry);
if (!inode) {
SUSFS_LOGE("inode is NULL\n");
path_put(&p);
return 1;
}
spin_lock(&inode->i_lock);
inode->i_state |= INODE_STATE_SUS_PATH;
spin_unlock(&inode->i_lock);
path_put(&p);
return 0;
}
int susfs_add_sus_path(struct st_susfs_sus_path* __user user_info) {
struct st_susfs_sus_path info;
struct st_susfs_sus_path_hlist *new_entry, *tmp_entry;
struct hlist_node *tmp_node;
int bkt;
bool update_hlist = false;
if (copy_from_user(&info, user_info, sizeof(info))) {
SUSFS_LOGE("failed copying from userspace\n");
return 1;
}
spin_lock(&susfs_spin_lock);
hash_for_each_safe(SUS_PATH_HLIST, bkt, tmp_node, tmp_entry, node) {
if (!strcmp(tmp_entry->target_pathname, info.target_pathname)) {
hash_del(&tmp_entry->node);
kfree(tmp_entry);
update_hlist = true;
break;
}
}
spin_unlock(&susfs_spin_lock);
new_entry = kmalloc(sizeof(struct st_susfs_sus_path_hlist), GFP_KERNEL);
if (!new_entry) {
SUSFS_LOGE("no enough memory\n");
return 1;
}
new_entry->target_ino = info.target_ino;
strncpy(new_entry->target_pathname, info.target_pathname, SUSFS_MAX_LEN_PATHNAME-1);
if (susfs_update_sus_path_inode(new_entry->target_pathname)) {
kfree(new_entry);
return 1;
}
spin_lock(&susfs_spin_lock);
hash_add(SUS_PATH_HLIST, &new_entry->node, info.target_ino);
if (update_hlist) {
SUSFS_LOGI("target_ino: '%lu', target_pathname: '%s' is successfully updated to SUS_PATH_HLIST\n",
new_entry->target_ino, new_entry->target_pathname);
} else {
SUSFS_LOGI("target_ino: '%lu', target_pathname: '%s' is successfully added to SUS_PATH_HLIST\n",
new_entry->target_ino, new_entry->target_pathname);
}
spin_unlock(&susfs_spin_lock);
return 0;
}
int susfs_sus_ino_for_filldir64(unsigned long ino) {
struct st_susfs_sus_path_hlist *entry;
hash_for_each_possible(SUS_PATH_HLIST, entry, node, ino) {
if (entry->target_ino == ino)
return 1;
}
return 0;
}
#endif // #ifdef CONFIG_KSU_SUSFS_SUS_PATH
/* sus_mount */
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
static LIST_HEAD(LH_SUS_MOUNT);
static void susfs_update_sus_mount_inode(char *target_pathname) {
struct path p;
struct inode *inode = NULL;
int err = 0;
err = kern_path(target_pathname, LOOKUP_FOLLOW, &p);
if (err) {
SUSFS_LOGE("Failed opening file '%s'\n", target_pathname);
return;
}
inode = d_inode(p.dentry);
if (!inode) {
path_put(&p);
SUSFS_LOGE("inode is NULL\n");
return;
}
spin_lock(&inode->i_lock);
inode->i_state |= INODE_STATE_SUS_MOUNT;
spin_unlock(&inode->i_lock);
path_put(&p);
}
int susfs_add_sus_mount(struct st_susfs_sus_mount* __user user_info) {
struct st_susfs_sus_mount_list *cursor = NULL, *temp = NULL;
struct st_susfs_sus_mount_list *new_list = NULL;
struct st_susfs_sus_mount info;
if (copy_from_user(&info, user_info, sizeof(info))) {
SUSFS_LOGE("failed copying from userspace\n");
return 1;
}
#if defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_COMPAT_STAT64)
#ifdef CONFIG_MIPS
info.target_dev = new_decode_dev(info.target_dev);
#else
info.target_dev = huge_decode_dev(info.target_dev);
#endif /* CONFIG_MIPS */
#else
info.target_dev = old_decode_dev(info.target_dev);
#endif /* defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_COMPAT_STAT64) */
list_for_each_entry_safe(cursor, temp, &LH_SUS_MOUNT, list) {
if (unlikely(!strcmp(cursor->info.target_pathname, info.target_pathname))) {
spin_lock(&susfs_spin_lock);
memcpy(&cursor->info, &info, sizeof(info));
susfs_update_sus_mount_inode(cursor->info.target_pathname);
SUSFS_LOGI("target_pathname: '%s', target_dev: '%lu', is successfully updated to LH_SUS_MOUNT\n",
cursor->info.target_pathname, cursor->info.target_dev);
spin_unlock(&susfs_spin_lock);
return 0;
}
}
new_list = kmalloc(sizeof(struct st_susfs_sus_mount_list), GFP_KERNEL);
if (!new_list) {
SUSFS_LOGE("no enough memory\n");
return 1;
}
memcpy(&new_list->info, &info, sizeof(info));
susfs_update_sus_mount_inode(new_list->info.target_pathname);
INIT_LIST_HEAD(&new_list->list);
spin_lock(&susfs_spin_lock);
list_add_tail(&new_list->list, &LH_SUS_MOUNT);
SUSFS_LOGI("target_pathname: '%s', target_dev: '%lu', is successfully added to LH_SUS_MOUNT\n",
new_list->info.target_pathname, new_list->info.target_dev);
spin_unlock(&susfs_spin_lock);
return 0;
}
#ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT
int susfs_auto_add_sus_bind_mount(const char *pathname, struct path *path_target) {
struct inode *inode;
// Only source mount path starting with '/data/adb/' will be hidden
if (strncmp(pathname, "/data/adb/", 10)) {
SUSFS_LOGE("skip setting SUS_MOUNT inode state for source bind mount path '%s'\n", pathname);
return 1;
}
inode = path_target->dentry->d_inode;
if (!inode) return 1;
spin_lock(&inode->i_lock);
inode->i_state |= INODE_STATE_SUS_MOUNT;
SUSFS_LOGI("set SUS_MOUNT inode state for source bind mount path '%s'\n", pathname);
spin_unlock(&inode->i_lock);
return 0;
}
#endif // #ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT
#ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT
void susfs_auto_add_sus_ksu_default_mount(const char __user *to_pathname) {
char pathname[SUSFS_MAX_LEN_PATHNAME];
struct path path;
struct inode *inode;
// Here we need to re-retrieve the struct path as we want the new struct path, not the old one
if (strncpy_from_user(pathname, to_pathname, SUSFS_MAX_LEN_PATHNAME-1) < 0)
return;
//SUSFS_LOGI("pathname: '%s'\n", pathname);
if ((!strncmp(pathname, "/data/adb/modules", 17) ||
!strncmp(pathname, "/debug_ramdisk", 14) ||
!strncmp(pathname, "/system", 7) ||
!strncmp(pathname, "/system_ext", 11) ||
!strncmp(pathname, "/vendor", 7) ||
!strncmp(pathname, "/product", 8)) &&
!kern_path(pathname, LOOKUP_FOLLOW, &path)) {
goto set_inode_sus_mount;
}
return;
set_inode_sus_mount:
inode = path.dentry->d_inode;
if (!inode) return;
spin_lock(&inode->i_lock);
inode->i_state |= INODE_STATE_SUS_MOUNT;
SUSFS_LOGI("set SUS_MOUNT inode state for default KSU mount path '%s'\n", pathname);
spin_unlock(&inode->i_lock);
path_put(&path);
}
#endif // #ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT
#endif // #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
/* sus_kstat */
#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT
static DEFINE_HASHTABLE(SUS_KSTAT_HLIST, 10);
static int susfs_update_sus_kstat_inode(char *target_pathname) {
struct path p;
struct inode *inode = NULL;
int err = 0;
err = kern_path(target_pathname, LOOKUP_FOLLOW, &p);
if (err) {
SUSFS_LOGE("Failed opening file '%s'\n", target_pathname);
return 1;
}
// We don't allow path of which filesystem type is "tmpfs", because its inode->i_ino is starting from 1 again,
// which will cause wrong comparison in function susfs_sus_ino_for_filldir64()
if (strcmp(p.mnt->mnt_sb->s_type->name, "tmpfs") == 0) {
SUSFS_LOGE("target_pathname: '%s' cannot be added since its filesystem is 'tmpfs'\n", target_pathname);
path_put(&p);
return 1;
}
inode = d_inode(p.dentry);
if (!inode) {
path_put(&p);
SUSFS_LOGE("inode is NULL\n");
return 1;
}
spin_lock(&inode->i_lock);
inode->i_state |= INODE_STATE_SUS_KSTAT;
spin_unlock(&inode->i_lock);
path_put(&p);
return 0;
}
int susfs_add_sus_kstat(struct st_susfs_sus_kstat* __user user_info) {
struct st_susfs_sus_kstat info;
struct st_susfs_sus_kstat_hlist *new_entry, *tmp_entry;
struct hlist_node *tmp_node;
int bkt;
bool update_hlist = false;
if (copy_from_user(&info, user_info, sizeof(info))) {
SUSFS_LOGE("failed copying from userspace\n");
return 1;
}
if (strlen(info.target_pathname) == 0) {
SUSFS_LOGE("target_pathname is an empty string\n");
return 1;
}
spin_lock(&susfs_spin_lock);
hash_for_each_safe(SUS_KSTAT_HLIST, bkt, tmp_node, tmp_entry, node) {
if (!strcmp(tmp_entry->info.target_pathname, info.target_pathname)) {
hash_del(&tmp_entry->node);
kfree(tmp_entry);
update_hlist = true;
break;
}
}
spin_unlock(&susfs_spin_lock);
new_entry = kmalloc(sizeof(struct st_susfs_sus_kstat_hlist), GFP_KERNEL);
if (!new_entry) {
SUSFS_LOGE("no enough memory\n");
return 1;
}
#if defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_COMPAT_STAT64)
#ifdef CONFIG_MIPS
info.spoofed_dev = new_decode_dev(info.spoofed_dev);
#else
info.spoofed_dev = huge_decode_dev(info.spoofed_dev);
#endif /* CONFIG_MIPS */
#else
info.spoofed_dev = old_decode_dev(info.spoofed_dev);
#endif /* defined(__ARCH_WANT_STAT64) || defined(__ARCH_WANT_COMPAT_STAT64) */
new_entry->target_ino = info.target_ino;
memcpy(&new_entry->info, &info, sizeof(info));
// only if the target is added statically needs to have flag INODE_STATE_SUS_KSTAT set here,
// otherwise the flag INODE_STATE_SUS_KSTAT should be set in function susfs_update_sus_kstat()
if (new_entry->info.is_statically && susfs_update_sus_kstat_inode(new_entry->info.target_pathname)) {
kfree(new_entry);
return 1;
}
spin_lock(&susfs_spin_lock);
hash_add(SUS_KSTAT_HLIST, &new_entry->node, info.target_ino);
if (update_hlist) {
SUSFS_LOGI("is_statically: '%d', target_ino: '%lu', target_pathname: '%s', spoofed_ino: '%lu', spoofed_dev: '%lu', spoofed_nlink: '%u', spoofed_size: '%u', spoofed_atime_tv_sec: '%ld', spoofed_mtime_tv_sec: '%ld', spoofed_ctime_tv_sec: '%ld', spoofed_atime_tv_nsec: '%ld', spoofed_mtime_tv_nsec: '%ld', spoofed_ctime_tv_nsec: '%ld', spoofed_blksize: '%lu', spoofed_blocks: '%llu', is successfully added to SUS_KSTAT_HLIST\n",
new_entry->info.is_statically, new_entry->info.target_ino, new_entry->info.target_pathname,
new_entry->info.spoofed_ino, new_entry->info.spoofed_dev,
new_entry->info.spoofed_nlink, new_entry->info.spoofed_size,
new_entry->info.spoofed_atime_tv_sec, new_entry->info.spoofed_mtime_tv_sec, new_entry->info.spoofed_ctime_tv_sec,
new_entry->info.spoofed_atime_tv_nsec, new_entry->info.spoofed_mtime_tv_nsec, new_entry->info.spoofed_ctime_tv_nsec,
new_entry->info.spoofed_blksize, new_entry->info.spoofed_blocks);
} else {
SUSFS_LOGI("is_statically: '%d', target_ino: '%lu', target_pathname: '%s', spoofed_ino: '%lu', spoofed_dev: '%lu', spoofed_nlink: '%u', spoofed_size: '%u', spoofed_atime_tv_sec: '%ld', spoofed_mtime_tv_sec: '%ld', spoofed_ctime_tv_sec: '%ld', spoofed_atime_tv_nsec: '%ld', spoofed_mtime_tv_nsec: '%ld', spoofed_ctime_tv_nsec: '%ld', spoofed_blksize: '%lu', spoofed_blocks: '%llu', is successfully updated to SUS_KSTAT_HLIST\n",
new_entry->info.is_statically, new_entry->info.target_ino, new_entry->info.target_pathname,
new_entry->info.spoofed_ino, new_entry->info.spoofed_dev,
new_entry->info.spoofed_nlink, new_entry->info.spoofed_size,
new_entry->info.spoofed_atime_tv_sec, new_entry->info.spoofed_mtime_tv_sec, new_entry->info.spoofed_ctime_tv_sec,
new_entry->info.spoofed_atime_tv_nsec, new_entry->info.spoofed_mtime_tv_nsec, new_entry->info.spoofed_ctime_tv_nsec,
new_entry->info.spoofed_blksize, new_entry->info.spoofed_blocks);
}
spin_unlock(&susfs_spin_lock);
return 0;
}
int susfs_update_sus_kstat(struct st_susfs_sus_kstat* __user user_info) {
struct st_susfs_sus_kstat info;
struct st_susfs_sus_kstat_hlist *new_entry, *tmp_entry;
struct hlist_node *tmp_node;
int bkt;
int err = 0;
if (copy_from_user(&info, user_info, sizeof(info))) {
SUSFS_LOGE("failed copying from userspace\n");
return 1;
}
spin_lock(&susfs_spin_lock);
hash_for_each_safe(SUS_KSTAT_HLIST, bkt, tmp_node, tmp_entry, node) {
if (!strcmp(tmp_entry->info.target_pathname, info.target_pathname)) {
if (susfs_update_sus_kstat_inode(tmp_entry->info.target_pathname)) {
err = 1;
goto out_spin_unlock;
}
new_entry = kmalloc(sizeof(struct st_susfs_sus_kstat_hlist), GFP_KERNEL);
if (!new_entry) {
SUSFS_LOGE("no enough memory\n");
err = 1;
goto out_spin_unlock;
}
memcpy(&new_entry->info, &tmp_entry->info, sizeof(tmp_entry->info));
SUSFS_LOGI("updating target_ino from '%lu' to '%lu' for pathname: '%s' in SUS_KSTAT_HLIST\n",
new_entry->info.target_ino, info.target_ino, info.target_pathname);
new_entry->target_ino = info.target_ino;
new_entry->info.target_ino = info.target_ino;
if (info.spoofed_size > 0) {
SUSFS_LOGI("updating spoofed_size from '%lld' to '%lld' for pathname: '%s' in SUS_KSTAT_HLIST\n",
new_entry->info.spoofed_size, info.spoofed_size, info.target_pathname);
new_entry->info.spoofed_size = info.spoofed_size;
}
if (info.spoofed_blocks > 0) {
SUSFS_LOGI("updating spoofed_blocks from '%llu' to '%llu' for pathname: '%s' in SUS_KSTAT_HLIST\n",
new_entry->info.spoofed_blocks, info.spoofed_blocks, info.target_pathname);
new_entry->info.spoofed_blocks = info.spoofed_blocks;
}
hash_del(&tmp_entry->node);
kfree(tmp_entry);
hash_add(SUS_KSTAT_HLIST, &new_entry->node, info.target_ino);
goto out_spin_unlock;
}
}
out_spin_unlock:
spin_unlock(&susfs_spin_lock);
return err;
}
void susfs_sus_ino_for_generic_fillattr(unsigned long ino, struct kstat *stat) {
struct st_susfs_sus_kstat_hlist *entry;
hash_for_each_possible(SUS_KSTAT_HLIST, entry, node, ino) {
if (entry->target_ino == ino) {
stat->dev = entry->info.spoofed_dev;
stat->ino = entry->info.spoofed_ino;
stat->nlink = entry->info.spoofed_nlink;
stat->size = entry->info.spoofed_size;
stat->atime.tv_sec = entry->info.spoofed_atime_tv_sec;
stat->atime.tv_nsec = entry->info.spoofed_atime_tv_nsec;
stat->mtime.tv_sec = entry->info.spoofed_mtime_tv_sec;
stat->mtime.tv_nsec = entry->info.spoofed_mtime_tv_nsec;
stat->ctime.tv_sec = entry->info.spoofed_ctime_tv_sec;
stat->ctime.tv_nsec = entry->info.spoofed_ctime_tv_nsec;
stat->blocks = entry->info.spoofed_blocks;
stat->blksize = entry->info.spoofed_blksize;
return;
}
}
}
void susfs_sus_ino_for_show_map_vma(unsigned long ino, dev_t *out_dev, unsigned long *out_ino) {
struct st_susfs_sus_kstat_hlist *entry;
hash_for_each_possible(SUS_KSTAT_HLIST, entry, node, ino) {
if (entry->target_ino == ino) {
*out_dev = entry->info.spoofed_dev;
*out_ino = entry->info.spoofed_ino;
return;
}
}
}
#endif // #ifdef CONFIG_KSU_SUSFS_SUS_KSTAT
/* try_umount */
#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT
static LIST_HEAD(LH_TRY_UMOUNT_PATH);
int susfs_add_try_umount(struct st_susfs_try_umount* __user user_info) {
struct st_susfs_try_umount_list *cursor = NULL, *temp = NULL;
struct st_susfs_try_umount_list *new_list = NULL;
struct st_susfs_try_umount info;
if (copy_from_user(&info, user_info, sizeof(info))) {
SUSFS_LOGE("failed copying from userspace\n");
return 1;
}
list_for_each_entry_safe(cursor, temp, &LH_TRY_UMOUNT_PATH, list) {
if (unlikely(!strcmp(info.target_pathname, cursor->info.target_pathname))) {
SUSFS_LOGE("target_pathname: '%s' is already created in LH_TRY_UMOUNT_PATH\n", info.target_pathname);
return 1;
}
}
new_list = kmalloc(sizeof(struct st_susfs_try_umount_list), GFP_KERNEL);
if (!new_list) {
SUSFS_LOGE("no enough memory\n");
return 1;
}
memcpy(&new_list->info, &info, sizeof(info));
INIT_LIST_HEAD(&new_list->list);
spin_lock(&susfs_spin_lock);
list_add_tail(&new_list->list, &LH_TRY_UMOUNT_PATH);
spin_unlock(&susfs_spin_lock);
SUSFS_LOGI("target_pathname: '%s', mnt_mode: %d, is successfully added to LH_TRY_UMOUNT_PATH\n", new_list->info.target_pathname, new_list->info.mnt_mode);
return 0;
}
void susfs_try_umount(uid_t target_uid) {
struct st_susfs_try_umount_list *cursor = NULL, *temp = NULL;
list_for_each_entry_safe(cursor, temp, &LH_TRY_UMOUNT_PATH, list) {
SUSFS_LOGI("umounting '%s' for uid: %d\n", cursor->info.target_pathname, target_uid);
if (cursor->info.mnt_mode == TRY_UMOUNT_DEFAULT) {
ksu_try_umount(cursor->info.target_pathname, false, 0);
} else if (cursor->info.mnt_mode == TRY_UMOUNT_DETACH) {
ksu_try_umount(cursor->info.target_pathname, false, MNT_DETACH);
} else {
SUSFS_LOGE("failed umounting '%s' for uid: %d, mnt_mode '%d' not supported\n",
cursor->info.target_pathname, target_uid, cursor->info.mnt_mode);
}
}
}
#ifdef CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT
void susfs_auto_add_try_umount_for_bind_mount(struct path *path) {
struct st_susfs_try_umount_list *cursor = NULL, *temp = NULL;
struct st_susfs_try_umount_list *new_list = NULL;
char *pathname = NULL, *dpath = NULL;
pathname = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!pathname) {
SUSFS_LOGE("no enough memory\n");
return;
}
dpath = d_path(path, pathname, PAGE_SIZE);
if (!dpath) {
SUSFS_LOGE("dpath is NULL\n");
goto out_free_pathname;
}
list_for_each_entry_safe(cursor, temp, &LH_TRY_UMOUNT_PATH, list) {
if (unlikely(!strcmp(dpath, cursor->info.target_pathname))) {
SUSFS_LOGE("target_pathname: '%s' is already created in LH_TRY_UMOUNT_PATH\n", dpath);
goto out_free_pathname;
}
}
new_list = kmalloc(sizeof(struct st_susfs_try_umount_list), GFP_KERNEL);
if (!new_list) {
SUSFS_LOGE("no enough memory\n");
goto out_free_pathname;
}
strncpy(new_list->info.target_pathname, dpath, SUSFS_MAX_LEN_PATHNAME-1);
new_list->info.mnt_mode = TRY_UMOUNT_DETACH;
INIT_LIST_HEAD(&new_list->list);
spin_lock(&susfs_spin_lock);
list_add_tail(&new_list->list, &LH_TRY_UMOUNT_PATH);
spin_unlock(&susfs_spin_lock);
SUSFS_LOGI("target_pathname: '%s', mnt_mode: %d, is successfully added to LH_TRY_UMOUNT_PATH\n", new_list->info.target_pathname, new_list->info.mnt_mode);
out_free_pathname:
kfree(pathname);
}
#endif // #ifdef CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT
#endif // #ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT
/* spoof_uname */
#ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME
static spinlock_t susfs_uname_spin_lock;
static struct st_susfs_uname my_uname;
static void susfs_my_uname_init(void) {
memset(&my_uname, 0, sizeof(my_uname));
}
int susfs_set_uname(struct st_susfs_uname* __user user_info) {
struct st_susfs_uname info;
if (copy_from_user(&info, user_info, sizeof(struct st_susfs_uname))) {
SUSFS_LOGE("failed copying from userspace.\n");
return 1;
}
spin_lock(&susfs_uname_spin_lock);
if (!strcmp(info.release, "default")) {
strncpy(my_uname.release, utsname()->release, __NEW_UTS_LEN);
} else {
strncpy(my_uname.release, info.release, __NEW_UTS_LEN);
}
if (!strcmp(info.version, "default")) {
strncpy(my_uname.version, utsname()->version, __NEW_UTS_LEN);
} else {
strncpy(my_uname.version, info.version, __NEW_UTS_LEN);
}
spin_unlock(&susfs_uname_spin_lock);
SUSFS_LOGI("setting spoofed release: '%s', version: '%s'\n",
my_uname.release, my_uname.version);
return 0;
}
int susfs_spoof_uname(struct new_utsname* tmp) {
if (unlikely(my_uname.release[0] == '\0' || spin_is_locked(&susfs_uname_spin_lock)))
return 1;
strncpy(tmp->sysname, utsname()->sysname, __NEW_UTS_LEN);
strncpy(tmp->nodename, utsname()->nodename, __NEW_UTS_LEN);
strncpy(tmp->release, my_uname.release, __NEW_UTS_LEN);
strncpy(tmp->version, my_uname.version, __NEW_UTS_LEN);
strncpy(tmp->machine, utsname()->machine, __NEW_UTS_LEN);
strncpy(tmp->domainname, utsname()->domainname, __NEW_UTS_LEN);
return 0;
}
#endif // #ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME
/* set_log */
#ifdef CONFIG_KSU_SUSFS_ENABLE_LOG
void susfs_set_log(bool enabled) {
spin_lock(&susfs_spin_lock);
susfs_is_log_enabled = enabled;
spin_unlock(&susfs_spin_lock);
if (susfs_is_log_enabled) {
pr_info("susfs: enable logging to kernel");
} else {
pr_info("susfs: disable logging to kernel");
}
}
#endif // #ifdef CONFIG_KSU_SUSFS_ENABLE_LOG
/* spoof_bootconfig */
#ifdef CONFIG_KSU_SUSFS_SPOOF_BOOTCONFIG
static char *fake_boot_config = NULL;
int susfs_set_bootconfig(char* __user user_fake_boot_config) {
int res;
if (!fake_boot_config) {
// 4096 is enough I guess
fake_boot_config = kmalloc(SUSFS_FAKE_BOOT_CONFIG_SIZE, GFP_KERNEL);
if (!fake_boot_config) {
SUSFS_LOGE("no enough memory\n");
return -ENOMEM;
}
}
spin_lock(&susfs_spin_lock);
memset(fake_boot_config, 0, SUSFS_FAKE_BOOT_CONFIG_SIZE);
res = strncpy_from_user(fake_boot_config, user_fake_boot_config, SUSFS_FAKE_BOOT_CONFIG_SIZE-1);
spin_unlock(&susfs_spin_lock);
if (res > 0) {
SUSFS_LOGI("fake_boot_config is set, length of string: %u\n", strlen(fake_boot_config));
return 0;
}
SUSFS_LOGI("failed setting fake_boot_config\n");
return res;
}
int susfs_spoof_bootconfig(struct seq_file *m) {
if (fake_boot_config != NULL) {
seq_puts(m, fake_boot_config);
return 0;
}
return 1;
}
#endif
/* open_redirect */
#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT
static DEFINE_HASHTABLE(OPEN_REDIRECT_HLIST, 10);
static int susfs_update_open_redirect_inode(struct st_susfs_open_redirect_hlist *new_entry) {
struct path path_target;
struct inode *inode_target;
int err = 0;
err = kern_path(new_entry->target_pathname, LOOKUP_FOLLOW, &path_target);
if (err) {
SUSFS_LOGE("Failed opening file '%s'\n", new_entry->target_pathname);
return err;
}
inode_target = d_inode(path_target.dentry);
if (!inode_target) {
SUSFS_LOGE("inode_target is NULL\n");
err = 1;
goto out_path_put_target;
}
spin_lock(&inode_target->i_lock);
inode_target->i_state |= INODE_STATE_OPEN_REDIRECT;
spin_unlock(&inode_target->i_lock);
out_path_put_target:
path_put(&path_target);
return err;
}
int susfs_add_open_redirect(struct st_susfs_open_redirect* __user user_info) {
struct st_susfs_open_redirect info;
struct st_susfs_open_redirect_hlist *new_entry, *tmp_entry;
struct hlist_node *tmp_node;
int bkt;
bool update_hlist = false;
if (copy_from_user(&info, user_info, sizeof(info))) {
SUSFS_LOGE("failed copying from userspace\n");
return 1;
}
spin_lock(&susfs_spin_lock);
hash_for_each_safe(OPEN_REDIRECT_HLIST, bkt, tmp_node, tmp_entry, node) {
if (!strcmp(tmp_entry->target_pathname, info.target_pathname)) {
hash_del(&tmp_entry->node);
kfree(tmp_entry);
update_hlist = true;
break;
}
}
spin_unlock(&susfs_spin_lock);
new_entry = kmalloc(sizeof(struct st_susfs_open_redirect_hlist), GFP_KERNEL);
if (!new_entry) {
SUSFS_LOGE("no enough memory\n");
return 1;
}
new_entry->target_ino = info.target_ino;
strncpy(new_entry->target_pathname, info.target_pathname, SUSFS_MAX_LEN_PATHNAME-1);
strncpy(new_entry->redirected_pathname, info.redirected_pathname, SUSFS_MAX_LEN_PATHNAME-1);
if (susfs_update_open_redirect_inode(new_entry)) {
SUSFS_LOGE("failed adding path '%s' to OPEN_REDIRECT_HLIST\n", new_entry->target_pathname);
kfree(new_entry);
return 1;
}
spin_lock(&susfs_spin_lock);
hash_add(OPEN_REDIRECT_HLIST, &new_entry->node, info.target_ino);
if (update_hlist) {
SUSFS_LOGI("target_ino: '%lu', target_pathname: '%s', redirected_pathname: '%s', is successfully updated to OPEN_REDIRECT_HLIST\n",
new_entry->target_ino, new_entry->target_pathname, new_entry->redirected_pathname);
} else {
SUSFS_LOGI("target_ino: '%lu', target_pathname: '%s' redirected_pathname: '%s', is successfully added to OPEN_REDIRECT_HLIST\n",
new_entry->target_ino, new_entry->target_pathname, new_entry->redirected_pathname);
}
spin_unlock(&susfs_spin_lock);
return 0;
}
struct filename* susfs_get_redirected_path(unsigned long ino) {
struct st_susfs_open_redirect_hlist *entry;
hash_for_each_possible(OPEN_REDIRECT_HLIST, entry, node, ino) {
if (entry->target_ino == ino) {
SUSFS_LOGI("Redirect for ino: %lu\n", ino);
return getname_kernel(entry->redirected_pathname);
}
}
return ERR_PTR(-ENOENT);
}
#endif // #ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT
/* sus_su */
#ifdef CONFIG_KSU_SUSFS_SUS_SU
bool susfs_is_sus_su_hooks_enabled __read_mostly = false;
static int susfs_sus_su_working_mode = 0;
extern void ksu_susfs_enable_sus_su(void);
extern void ksu_susfs_disable_sus_su(void);
int susfs_get_sus_su_working_mode(void) {
return susfs_sus_su_working_mode;
}
int susfs_sus_su(struct st_sus_su* __user user_info) {
struct st_sus_su info;
int last_working_mode = susfs_sus_su_working_mode;
if (copy_from_user(&info, user_info, sizeof(struct st_sus_su))) {
SUSFS_LOGE("failed copying from userspace\n");
return 1;
}
if (info.mode == SUS_SU_WITH_HOOKS) {
if (last_working_mode == SUS_SU_WITH_HOOKS) {
SUSFS_LOGE("current sus_su mode is already %d\n", SUS_SU_WITH_HOOKS);
return 1;
}
if (last_working_mode != SUS_SU_DISABLED) {
SUSFS_LOGE("please make sure the current sus_su mode is %d first\n", SUS_SU_DISABLED);
return 2;
}
ksu_susfs_enable_sus_su();
susfs_sus_su_working_mode = SUS_SU_WITH_HOOKS;
susfs_is_sus_su_hooks_enabled = true;
SUSFS_LOGI("core kprobe hooks for ksu are disabled!\n");
SUSFS_LOGI("non-kprobe hook sus_su is enabled!\n");
SUSFS_LOGI("sus_su mode: %d\n", SUS_SU_WITH_HOOKS);
return 0;
} else if (info.mode == SUS_SU_DISABLED) {
if (last_working_mode == SUS_SU_DISABLED) {
SUSFS_LOGE("current sus_su mode is already %d\n", SUS_SU_DISABLED);
return 1;
}
susfs_is_sus_su_hooks_enabled = false;
ksu_susfs_disable_sus_su();
susfs_sus_su_working_mode = SUS_SU_DISABLED;
if (last_working_mode == SUS_SU_WITH_HOOKS) {
SUSFS_LOGI("core kprobe hooks for ksu are enabled!\n");
goto out;
}
out:
if (copy_to_user(user_info, &info, sizeof(info)))
SUSFS_LOGE("copy_to_user() failed\n");
return 0;
} else if (info.mode == SUS_SU_WITH_OVERLAY) {
SUSFS_LOGE("sus_su mode %d is deprecated\n", SUS_SU_WITH_OVERLAY);
return 1;
}
return 1;
}
#endif // #ifdef CONFIG_KSU_SUSFS_SUS_SU
/* susfs_init */
void susfs_init(void) {
spin_lock_init(&susfs_spin_lock);
#ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME
spin_lock_init(&susfs_uname_spin_lock);
susfs_my_uname_init();
#endif
SUSFS_LOGI("susfs is initialized! version: " SUSFS_VERSION " \n");
}
/* No module exit is needed becuase it should never be a loadable kernel module */
//void __init susfs_exit(void)

9
include/linux/sus_su.h Normal file
View File

@ -0,0 +1,9 @@
#ifndef __KSU_H_SUS_SU
#define __KSU_H_SUS_SU
#include "../../drivers/kernelsu/core_hook.h"
int sus_su_fifo_init(int *maj_dev_num, char *drv_path);
int sus_su_fifo_exit(int *maj_dev_num, char *drv_path);
#endif

231
include/linux/susfs.h Normal file
View File

@ -0,0 +1,231 @@
#ifndef KSU_SUSFS_H
#define KSU_SUSFS_H
#include <linux/version.h>
#include <linux/types.h>
#include <linux/utsname.h>
#include <linux/hashtable.h>
#include <linux/path.h>
#define SUSFS_VERSION "v1.5.3"
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,0,0)
#define SUSFS_VARIANT "NON-GKI"
#else
#define SUSFS_VARIANT "GKI"
#endif
/********/
/* ENUM */
/********/
/* shared with userspace ksu_susfs tool */
#define CMD_SUSFS_ADD_SUS_PATH 0x55550
#define CMD_SUSFS_ADD_SUS_MOUNT 0x55560
#define CMD_SUSFS_ADD_SUS_KSTAT 0x55570
#define CMD_SUSFS_UPDATE_SUS_KSTAT 0x55571
#define CMD_SUSFS_ADD_SUS_KSTAT_STATICALLY 0x55572
#define CMD_SUSFS_ADD_TRY_UMOUNT 0x55580
#define CMD_SUSFS_SET_UNAME 0x55590
#define CMD_SUSFS_ENABLE_LOG 0x555a0
#define CMD_SUSFS_SET_BOOTCONFIG 0x555b0
#define CMD_SUSFS_ADD_OPEN_REDIRECT 0x555c0
#define CMD_SUSFS_RUN_UMOUNT_FOR_CURRENT_MNT_NS 0x555d0
#define CMD_SUSFS_SHOW_VERSION 0x555e1
#define CMD_SUSFS_SHOW_ENABLED_FEATURES 0x555e2
#define CMD_SUSFS_SHOW_VARIANT 0x555e3
#define CMD_SUSFS_SHOW_SUS_SU_WORKING_MODE 0x555e4
#define CMD_SUSFS_IS_SUS_SU_READY 0x555f0
#define CMD_SUSFS_SUS_SU 0x60000
#define SUSFS_MAX_LEN_PATHNAME 256 // 256 should address many paths already unless you are doing some strange experimental stuff, then set your own desired length
#define SUSFS_FAKE_BOOT_CONFIG_SIZE 4096
#define TRY_UMOUNT_DEFAULT 0
#define TRY_UMOUNT_DETACH 1
#define SUS_SU_DISABLED 0
#define SUS_SU_WITH_OVERLAY 1 /* deprecated */
#define SUS_SU_WITH_HOOKS 2
/*
* inode->i_state => storing flag 'INODE_STATE_'
* mount->mnt.android_kabi_reserved4 => storing original mnt_id
* task_struct->android_kabi_reserved8 => storing last valid fake mnt_id
* user_struct->android_kabi_reserved2 => storing flag 'USER_STRUCT_KABI2_'
*/
#define INODE_STATE_SUS_PATH 16777216 // 1 << 24
#define INODE_STATE_SUS_MOUNT 33554432 // 1 << 25
#define INODE_STATE_SUS_KSTAT 67108864 // 1 << 26
#define INODE_STATE_OPEN_REDIRECT 134217728 // 1 << 27
#define USER_STRUCT_KABI2_NON_ROOT_USER_APP_PROFILE 16777216 // 1 << 24, for distinguishing root/no-root granted user app process
/*********/
/* MACRO */
/*********/
#define getname_safe(name) (name == NULL ? ERR_PTR(-EINVAL) : getname(name))
#define putname_safe(name) (IS_ERR(name) ? NULL : putname(name))
/**********/
/* STRUCT */
/**********/
/* sus_path */
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
struct st_susfs_sus_path {
unsigned long target_ino;
char target_pathname[SUSFS_MAX_LEN_PATHNAME];
};
struct st_susfs_sus_path_hlist {
unsigned long target_ino;
char target_pathname[SUSFS_MAX_LEN_PATHNAME];
struct hlist_node node;
};
#endif
/* sus_mount */
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
struct st_susfs_sus_mount {
char target_pathname[SUSFS_MAX_LEN_PATHNAME];
unsigned long target_dev;
};
struct st_susfs_sus_mount_list {
struct list_head list;
struct st_susfs_sus_mount info;
};
#endif
/* sus_kstat */
#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT
struct st_susfs_sus_kstat {
int is_statically;
unsigned long target_ino; // the ino after bind mounted or overlayed
char target_pathname[SUSFS_MAX_LEN_PATHNAME];
unsigned long spoofed_ino;
unsigned long spoofed_dev;
unsigned int spoofed_nlink;
long long spoofed_size;
long spoofed_atime_tv_sec;
long spoofed_mtime_tv_sec;
long spoofed_ctime_tv_sec;
long spoofed_atime_tv_nsec;
long spoofed_mtime_tv_nsec;
long spoofed_ctime_tv_nsec;
unsigned long spoofed_blksize;
unsigned long long spoofed_blocks;
};
struct st_susfs_sus_kstat_hlist {
unsigned long target_ino;
struct st_susfs_sus_kstat info;
struct hlist_node node;
};
#endif
/* try_umount */
#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT
struct st_susfs_try_umount {
char target_pathname[SUSFS_MAX_LEN_PATHNAME];
int mnt_mode;
};
struct st_susfs_try_umount_list {
struct list_head list;
struct st_susfs_try_umount info;
};
#endif
/* spoof_uname */
#ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME
struct st_susfs_uname {
char release[__NEW_UTS_LEN+1];
char version[__NEW_UTS_LEN+1];
};
#endif
/* open_redirect */
#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT
struct st_susfs_open_redirect {
unsigned long target_ino;
char target_pathname[SUSFS_MAX_LEN_PATHNAME];
char redirected_pathname[SUSFS_MAX_LEN_PATHNAME];
};
struct st_susfs_open_redirect_hlist {
unsigned long target_ino;
char target_pathname[SUSFS_MAX_LEN_PATHNAME];
char redirected_pathname[SUSFS_MAX_LEN_PATHNAME];
struct hlist_node node;
};
#endif
/* sus_su */
#ifdef CONFIG_KSU_SUSFS_SUS_SU
struct st_sus_su {
int mode;
};
#endif
/***********************/
/* FORWARD DECLARATION */
/***********************/
/* sus_path */
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
int susfs_add_sus_path(struct st_susfs_sus_path* __user user_info);
int susfs_sus_ino_for_filldir64(unsigned long ino);
#endif
/* sus_mount */
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
int susfs_add_sus_mount(struct st_susfs_sus_mount* __user user_info);
#ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT
int susfs_auto_add_sus_bind_mount(const char *pathname, struct path *path_target);
#endif // #ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT
#ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT
void susfs_auto_add_sus_ksu_default_mount(const char __user *to_pathname);
#endif // #ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT
#endif // #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
/* sus_kstat */
#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT
int susfs_add_sus_kstat(struct st_susfs_sus_kstat* __user user_info);
int susfs_update_sus_kstat(struct st_susfs_sus_kstat* __user user_info);
void susfs_sus_ino_for_generic_fillattr(unsigned long ino, struct kstat *stat);
void susfs_sus_ino_for_show_map_vma(unsigned long ino, dev_t *out_dev, unsigned long *out_ino);
#endif
/* try_umount */
#ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT
int susfs_add_try_umount(struct st_susfs_try_umount* __user user_info);
void susfs_try_umount(uid_t target_uid);
#ifdef CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT
void susfs_auto_add_try_umount_for_bind_mount(struct path *path);
#endif // #ifdef CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT
#endif // #ifdef CONFIG_KSU_SUSFS_TRY_UMOUNT
/* spoof_uname */
#ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME
int susfs_set_uname(struct st_susfs_uname* __user user_info);
int susfs_spoof_uname(struct new_utsname* tmp);
#endif
/* set_log */
#ifdef CONFIG_KSU_SUSFS_ENABLE_LOG
void susfs_set_log(bool enabled);
#endif
/* spoof_bootconfig */
#ifdef CONFIG_KSU_SUSFS_SPOOF_BOOTCONFIG
int susfs_set_bootconfig(char* __user user_fake_boot_config);
int susfs_spoof_bootconfig(struct seq_file *m);
#endif
/* open_redirect */
#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT
int susfs_add_open_redirect(struct st_susfs_open_redirect* __user user_info);
struct filename* susfs_get_redirected_path(unsigned long ino);
#endif
/* sus_su */
#ifdef CONFIG_KSU_SUSFS_SUS_SU
int susfs_get_sus_su_working_mode(void);
int susfs_sus_su(struct st_sus_su* __user user_info);
#endif
/* susfs_init */
void susfs_init(void);
#endif

View File

@ -692,8 +692,18 @@ static int s_show(struct seq_file *m, void *p)
seq_printf(m, "%px %c %s\t[%s]\n", value,
type, iter->name, iter->module_name);
} else
#ifndef CONFIG_KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS
seq_printf(m, "%px %c %s\n", value,
iter->type, iter->name);
#else
{
if (strstr(iter->name, "ksu_") || !strncmp(iter->name, "susfs_", 6) || !strncmp(iter->name, "ksud", 4)) {
return 0;
}
seq_printf(m, "%px %c %s\n", value,
iter->type, iter->name);
}
#endif
return 0;
}

View File

@ -1271,12 +1271,23 @@ static int override_release(char __user *release, size_t len)
return ret;
}
#ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME
extern int susfs_spoof_uname(struct new_utsname* tmp);
#endif
SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name)
{
struct new_utsname tmp;
down_read(&uts_sem);
#ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME
if (likely(!susfs_spoof_uname(&tmp)))
goto bypass_orig_flow;
#endif
memcpy(&tmp, utsname(), sizeof(tmp));
#ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME
bypass_orig_flow:
#endif
up_read(&uts_sem);
if (copy_to_user(name, &tmp, sizeof(tmp)))
return -EFAULT;