fs: implement susfs v1.5.12

- This is a heavily modified version of susfs v1.5.12
- It does not comply with the upstream offical susfs v1.5.12
- sus_mount functionality still remain in v1.5.5 as backporting it to the latest version will result a mount detection leak in some apps/detectors
- Increase susfs_open_redirect UID limit to <11000
- susfs magic mount support is still implemented and enabled
- sus_map is implemented and complied with the upstream v1.5.12 codebase

This commit requires a bunch of backports commits from v4.19 and v5.x to make sus_map working:

0a8cbf3725edbacc5f1ead33eeae7e4d78823b5a proc: less memory for /proc/*/map_files readdir
37ae2444584654f6785f2cc49181f05af788c9b2 mm: smaps: split PSS into components
49a5115e11350ee68f6a5fbd56b3e817bf9e5aac fs/task_mmu: add pkeys header
6f94042bed51121f8f28a5e572cda20c21fed2e1 mm/pkeys: Add an empty arch_pkeys_enabled()
bbd5aec12b32097a71dc6a0097194a18f3ee9a17 mm/pkeys, powerpc, x86: Provide an empty vma_pkey() in linux/pkeys.h
849ca8ce954d9dbb082dcf83c98af861e98e5635 mm: /proc/pid/smaps_rollup: convert to single value seq_file
6071a482c8e603be25895cc2cac5f0eab61c4051 mm: /proc/pid/smaps: factor out common stats printing
03fd2fbe9c40da8128cec5c69ef54755c0f38c6c mm: /proc/pid/smaps: factor out mem stats gathering
95f8be4c8a86a491a1c2ac9bfe470aef9e1baa8f mm: /proc/pid/*maps remove is_pid and related wrappers
27956d255e3b012372951dd6131e07c106d2daae procfs: add seq_put_hex_ll to speed up /proc/pid/maps
7f2847d02cdc4491b5ee6d4a0043854cbd6c7a1a proc: add seq_put_decimal_ull_width to speed up /proc/pid/smaps

For KernelSU side patches for this commit you need the sidex15's KernelSU-Next fork:
https://github.com/sidex15/KernelSU-Next/tree/n3x7g3n-kernel

Or if you want to patch on your own here's the commit patch of susfs in the KernelSU-Next:
13b1dfd6e2

Co-authored-by: simonpunk <simonpunk2016@gmail.com>
Signed-off-by: Pranav Vashi <neobuddy89@gmail.com>
This commit is contained in:
sidex15
2025-11-17 09:17:56 +08:00
committed by Pranav Vashi
parent 22d95a657d
commit 1219b14df9
20 changed files with 2814 additions and 5 deletions

View File

@@ -14,6 +14,8 @@ obj-y := open.o read_write.o file_table.o super.o \
pnode.o splice.o sync.o utimes.o \
stack.o fs_struct.o statfs.o fs_pin.o nsfs.o
obj-$(CONFIG_KSU_SUSFS) += susfs.o
ifeq ($(CONFIG_BLOCK),y)
obj-y += buffer.o block_dev.o direct-io.o mpage.o
else

View File

@@ -39,6 +39,9 @@
#include <linux/bitops.h>
#include <linux/init_task.h>
#include <linux/uaccess.h>
#if defined(CONFIG_KSU_SUSFS_SUS_PATH) || defined(CONFIG_KSU_SUSFS_OPEN_REDIRECT)
#include <linux/susfs_def.h>
#endif
#include "internal.h"
#include "mount.h"
@@ -46,6 +49,15 @@
#define CREATE_TRACE_POINTS
#include <trace/events/namei.h>
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
extern bool susfs_is_sus_android_data_d_name_found(const char *d_name);
extern bool susfs_is_sus_sdcard_d_name_found(const char *d_name);
extern bool susfs_is_inode_sus_path(struct inode *inode);
extern bool susfs_is_base_dentry_android_data_dir(struct dentry* base);
extern bool susfs_is_base_dentry_sdcard_dir(struct dentry* base);
extern const struct qstr susfs_fake_qstr_name;
#endif
/* [Feb-1997 T. Schoebel-Theuer]
* Fundamental changes in the pathname lookup mechanisms (namei)
* were necessary because of omirr. The reason is that omirr needs
@@ -528,6 +540,9 @@ struct nameidata {
struct path root;
struct inode *inode; /* path.dentry.d_inode */
unsigned int flags;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
unsigned int state;
#endif
unsigned seq, m_seq;
int last_type;
unsigned depth;
@@ -554,6 +569,9 @@ static void set_nameidata(struct nameidata *p, int dfd, struct filename *name)
p->total_link_count = old ? old->total_link_count : 0;
p->saved = old;
current->nameidata = p;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
p->state = 0;
#endif
}
static void restore_nameidata(void)
@@ -1662,18 +1680,63 @@ static struct dentry *lookup_real(struct inode *dir, struct dentry *dentry,
static struct dentry *__lookup_hash(const struct qstr *name,
struct dentry *base, unsigned int flags)
{
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
struct dentry *dentry;
bool found_sus_path = false;
if (base && base->d_inode && !found_sus_path) {
if (susfs_is_base_dentry_android_data_dir(base) &&
susfs_is_sus_android_data_d_name_found(name->name))
{
dentry = lookup_dcache(&susfs_fake_qstr_name, base, flags);
found_sus_path = true;
goto retry;
} else if (susfs_is_base_dentry_sdcard_dir(base) &&
susfs_is_sus_sdcard_d_name_found(name->name))
{
dentry = lookup_dcache(&susfs_fake_qstr_name, base, flags);
found_sus_path = true;
goto retry;
}
}
dentry = lookup_dcache(name, base, flags);
retry:
#else
struct dentry *dentry = lookup_dcache(name, base, flags);
#endif
if (dentry)
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
{
if (!found_sus_path && !IS_ERR(dentry) && dentry->d_inode && susfs_is_inode_sus_path(dentry->d_inode)) {
dput(dentry);
dentry = lookup_dcache(&susfs_fake_qstr_name, base, flags);
found_sus_path = true;
goto retry;
}
return dentry;
}
#else
return dentry;
#endif
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
if (found_sus_path) {
dentry = d_alloc(base, &susfs_fake_qstr_name);
goto skip_orig_flow;
}
#endif
dentry = d_alloc(base, name);
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
skip_orig_flow:
#endif
if (unlikely(!dentry))
return ERR_PTR(-ENOMEM);
return lookup_real(base->d_inode, dentry, flags);
}
static int lookup_fast(struct nameidata *nd,
struct path *path, struct inode **inode,
unsigned *seqp)
@@ -1682,6 +1745,9 @@ static int lookup_fast(struct nameidata *nd,
struct dentry *dentry, *parent = nd->path.dentry;
int status = 1;
int err;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
bool is_nd_state_lookup_last_and_open_last = (nd->state & ND_STATE_LOOKUP_LAST || nd->state & ND_STATE_OPEN_LAST);
#endif
/*
* Rename seqlock is not required here because in the off chance
@@ -1691,7 +1757,33 @@ static int lookup_fast(struct nameidata *nd,
if (nd->flags & LOOKUP_RCU) {
unsigned seq;
bool negative;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
unsigned backup_next_seq;
if (is_nd_state_lookup_last_and_open_last && parent->d_inode) {
if (susfs_is_base_dentry_android_data_dir(parent) &&
susfs_is_sus_android_data_d_name_found(nd->last.name))
{
dentry = __d_lookup_rcu(parent, &susfs_fake_qstr_name, &seq);
goto skip_orig_flow1;
} else if (susfs_is_base_dentry_sdcard_dir(parent) &&
susfs_is_sus_sdcard_d_name_found(nd->last.name))
{
dentry = __d_lookup_rcu(parent, &susfs_fake_qstr_name, &seq);
goto skip_orig_flow1;
}
}
#endif
dentry = __d_lookup_rcu(parent, &nd->last, &seq);
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
if (is_nd_state_lookup_last_and_open_last && dentry && !IS_ERR(dentry) && dentry->d_inode && parent->d_inode) {
if (susfs_is_inode_sus_path(dentry->d_inode)) {
dput(dentry);
dentry = __d_lookup_rcu(parent, &susfs_fake_qstr_name, &backup_next_seq);
}
}
skip_orig_flow1:
#endif
if (unlikely(!dentry)) {
if (unlazy_walk(nd))
return -ECHILD;
@@ -1737,7 +1829,31 @@ static int lookup_fast(struct nameidata *nd,
/* we'd been told to redo it in non-rcu mode */
status = d_revalidate(dentry, nd->flags);
} else {
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
if (is_nd_state_lookup_last_and_open_last && parent->d_inode) {
if (susfs_is_base_dentry_android_data_dir(parent) &&
susfs_is_sus_android_data_d_name_found(nd->last.name))
{
dentry = __d_lookup(parent, &susfs_fake_qstr_name);
goto skip_orig_flow2;
} else if (susfs_is_base_dentry_sdcard_dir(parent) &&
susfs_is_sus_sdcard_d_name_found(nd->last.name))
{
dentry = __d_lookup(parent, &susfs_fake_qstr_name);
goto skip_orig_flow2;
}
}
#endif
dentry = __d_lookup(parent, &nd->last);
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
if (is_nd_state_lookup_last_and_open_last && dentry && !IS_ERR(dentry) && dentry->d_inode && parent->d_inode) {
if (susfs_is_inode_sus_path(dentry->d_inode)) {
dput(dentry);
dentry = __d_lookup(parent, &susfs_fake_qstr_name);
}
}
skip_orig_flow2:
#endif
if (unlikely(!dentry))
return 0;
status = d_revalidate(dentry, nd->flags);
@@ -1769,13 +1885,42 @@ static struct dentry *lookup_slow(const struct qstr *name,
struct dentry *dentry = ERR_PTR(-ENOENT), *old;
struct inode *inode = dir->d_inode;
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(sus_wq);
bool found_sus_path = false;
bool is_nd_flags_lookup_last = (flags & ND_FLAGS_LOOKUP_LAST);
#endif
inode_lock_shared(inode);
/* Don't go there if it's already dead */
if (unlikely(IS_DEADDIR(inode)))
goto out;
again:
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
if (found_sus_path) {
dentry = d_alloc_parallel(dir, &susfs_fake_qstr_name, &sus_wq);
goto retry;
}
if (is_nd_flags_lookup_last && !found_sus_path) {
if (susfs_is_base_dentry_android_data_dir(dir) &&
susfs_is_sus_android_data_d_name_found(name->name))
{
dentry = d_alloc_parallel(dir, &susfs_fake_qstr_name, &sus_wq);
found_sus_path = true;
goto retry;
} else if (susfs_is_base_dentry_sdcard_dir(dir) &&
susfs_is_sus_sdcard_d_name_found(name->name))
{
dentry = d_alloc_parallel(dir, &susfs_fake_qstr_name, &sus_wq);
found_sus_path = true;
goto retry;
}
}
#endif
dentry = d_alloc_parallel(dir, name, &wq);
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
retry:
#endif
if (IS_ERR(dentry))
goto out;
if (unlikely(!d_in_lookup(dentry))) {
@@ -1799,6 +1944,18 @@ again:
dentry = old;
}
}
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
if (is_nd_flags_lookup_last && !found_sus_path) {
if (dentry && !IS_ERR(dentry) && dentry->d_inode) {
if (susfs_is_inode_sus_path(dentry->d_inode)) {
dput(dentry);
dentry = d_alloc_parallel(dir, &susfs_fake_qstr_name, &sus_wq);
found_sus_path = true;
goto retry;
}
}
}
#endif
out:
inode_unlock_shared(inode);
return dentry;
@@ -1921,6 +2078,11 @@ static int walk_component(struct nameidata *nd, int flags)
if (unlikely(err <= 0)) {
if (err < 0)
return err;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
if (nd->state & ND_STATE_LOOKUP_LAST) {
nd->flags |= ND_FLAGS_LOOKUP_LAST;
}
#endif
path.dentry = lookup_slow(&nd->last, nd->path.dentry,
nd->flags);
if (IS_ERR(path.dentry))
@@ -2192,10 +2354,20 @@ static int link_path_walk(const char *name, struct nameidata *nd)
for(;;) {
u64 hash_len;
int type;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
struct dentry *dentry;
#endif
err = may_lookup(nd);
if (err)
return err;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
dentry = nd->path.dentry;
if (dentry->d_inode && susfs_is_inode_sus_path(dentry->d_inode)) {
// return -ENOENT here since it is walking the sub path of sus path
return -ENOENT;
}
#endif
hash_len = hash_name(nd->path.dentry, name);
@@ -2221,6 +2393,23 @@ static int link_path_walk(const char *name, struct nameidata *nd)
hash_len = this.hash_len;
name = this.name;
}
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
if (nd->state & ND_STATE_LAST_SDCARD_SUS_PATH) {
// return -ENOENT here since it is walking the sub path of sus sdcard path
return -ENOENT;
}
if (parent->d_inode) {
if (susfs_is_base_dentry_android_data_dir(parent) &&
susfs_is_sus_android_data_d_name_found(name))
{
nd->state |= ND_STATE_LAST_SDCARD_SUS_PATH;
} else if (susfs_is_base_dentry_sdcard_dir(parent) &&
susfs_is_sus_sdcard_d_name_found(name))
{
nd->state |= ND_STATE_LAST_SDCARD_SUS_PATH;
}
}
#endif
}
nd->last.hash_len = hash_len;
@@ -2387,7 +2576,9 @@ static inline int lookup_last(struct nameidata *nd)
{
if (nd->last_type == LAST_NORM && nd->last.name[nd->last.len])
nd->flags |= LOOKUP_FOLLOW | LOOKUP_DIRECTORY;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
nd->state |= ND_STATE_LOOKUP_LAST;
#endif
nd->flags &= ~LOOKUP_PARENT;
return walk_component(nd, 0);
}
@@ -3278,15 +3469,55 @@ static int lookup_open(struct nameidata *nd, struct path *path,
int error, create_error = 0;
umode_t mode = op->mode;
DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq);
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
bool found_sus_path = false;
bool is_nd_state_open_last = (nd->state & ND_STATE_OPEN_LAST);
#endif
if (unlikely(IS_DEADDIR(dir_inode)))
return -ENOENT;
*opened &= ~FILE_CREATED;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
if (is_nd_state_open_last) {
if (susfs_is_base_dentry_android_data_dir(dir) &&
susfs_is_sus_android_data_d_name_found(nd->last.name))
{
dentry = d_lookup(dir, &susfs_fake_qstr_name);
found_sus_path = true;
goto skip_orig_flow1;
} else if (susfs_is_base_dentry_sdcard_dir(dir) &&
susfs_is_sus_sdcard_d_name_found(nd->last.name))
{
dentry = d_lookup(dir, &susfs_fake_qstr_name);
found_sus_path = true;
goto skip_orig_flow1;
}
}
#endif
dentry = d_lookup(dir, &nd->last);
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
if (is_nd_state_open_last && dentry && !IS_ERR(dentry) && dentry->d_inode) {
if (susfs_is_inode_sus_path(dentry->d_inode)) {
dput(dentry);
dentry = d_lookup(dir, &susfs_fake_qstr_name);
found_sus_path = true;
}
}
skip_orig_flow1:
#endif
for (;;) {
if (!dentry) {
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
if (found_sus_path) {
dentry = d_alloc_parallel(dir, &susfs_fake_qstr_name, &wq);
goto skip_orig_flow2;
}
#endif
dentry = d_alloc_parallel(dir, &nd->last, &wq);
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
skip_orig_flow2:
#endif
if (IS_ERR(dentry))
return PTR_ERR(dentry);
}
@@ -3412,6 +3643,9 @@ static int do_last(struct nameidata *nd,
struct path path;
int error;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
nd->state |= ND_STATE_OPEN_LAST;
#endif
nd->flags &= ~LOOKUP_PARENT;
nd->flags |= op->intent;
@@ -3731,12 +3965,19 @@ out2:
return file;
}
#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);
@@ -3744,6 +3985,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_mapping->flags & BIT_OPEN_REDIRECT) && current_uid().val < 11000) {
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

@@ -27,10 +27,39 @@
#include <linux/bootmem.h>
#include <linux/task_work.h>
#include <linux/sched/task.h>
#if defined(CONFIG_KSU_SUSFS_SUS_MOUNT) || defined(CONFIG_KSU_SUSFS_TRY_UMOUNT)
#include <linux/susfs_def.h>
#endif
#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);
static DEFINE_IDA(susfs_mnt_id_ida);
static DEFINE_IDA(susfs_mnt_group_ida);
static int susfs_mnt_id_start = DEFAULT_SUS_MNT_ID;
static int susfs_mnt_group_start = DEFAULT_SUS_MNT_GROUP_ID;
#define CL_ZYGOTE_COPY_MNT_NS BIT(24) /* used by copy_mnt_ns() */
#define CL_COPY_MNT_NS BIT(25) /* used by copy_mnt_ns() */
#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);
bool susfs_is_auto_add_sus_ksu_default_mount_enabled = true;
#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);
bool susfs_is_auto_add_sus_bind_mount_enabled = true;
#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);
bool susfs_is_auto_add_try_umount_for_bind_mount_enabled = true;
#endif
/* Maximum number of mounts in a mount namespace */
unsigned int sysctl_mount_max __read_mostly = 100000;
@@ -100,6 +129,25 @@ static inline struct hlist_head *mp_hash(struct dentry *dentry)
return &mountpoint_hashtable[tmp & mp_hash_mask];
}
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
// Our own mnt_alloc_id() that assigns mnt_id starting from DEFAULT_SUS_MNT_ID
static int susfs_mnt_alloc_id(struct mount *mnt)
{
int res;
retry:
ida_pre_get(&susfs_mnt_id_ida, GFP_KERNEL);
spin_lock(&mnt_id_lock);
res = ida_get_new_above(&susfs_mnt_id_ida, susfs_mnt_id_start, &mnt->mnt_id);
if (!res)
susfs_mnt_id_start = mnt->mnt_id + 1;
spin_unlock(&mnt_id_lock);
if (res == -EAGAIN)
goto retry;
return res;
}
#endif
static int mnt_alloc_id(struct mount *mnt)
{
int res;
@@ -120,6 +168,35 @@ retry:
static void mnt_free_id(struct mount *mnt)
{
int id = mnt->mnt_id;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
int mnt_id_backup = mnt->mnt.susfs_mnt_id_backup;
// We should first check the 'mnt->mnt.susfs_mnt_id_backup', see if it is DEFAULT_SUS_MNT_ID_FOR_KSU_PROC_UNSHARE
// if so, these mnt_id were not assigned by mnt_alloc_id() so we don't need to free it.
if (unlikely(mnt_id_backup == DEFAULT_SUS_MNT_ID_FOR_KSU_PROC_UNSHARE)) {
return;
}
// Now we can check if its mnt_id is sus
if (unlikely(mnt->mnt_id >= DEFAULT_SUS_MNT_ID)) {
spin_lock(&mnt_id_lock);
ida_remove(&susfs_mnt_id_ida, id);
if (susfs_mnt_id_start > id)
susfs_mnt_id_start = id;
spin_unlock(&mnt_id_lock);
return;
}
// Lastly if 'mnt->mnt.susfs_mnt_id_backup' is not 0, then it contains a backup origin mnt_id
// so we free it in the original way
if (likely(mnt_id_backup)) {
// If mnt->mnt.susfs_mnt_id_backup is not zero, it means mnt->mnt_id is spoofed,
// so here we return the original mnt_id for being freed.
spin_lock(&mnt_id_lock);
ida_remove(&mnt_id_ida, mnt_id_backup);
if (mnt_id_start > mnt_id_backup)
mnt_id_start = mnt_id_backup;
spin_unlock(&mnt_id_lock);
return;
}
#endif
spin_lock(&mnt_id_lock);
ida_remove(&mnt_id_ida, id);
if (mnt_id_start > id)
@@ -136,6 +213,19 @@ static int mnt_alloc_group_id(struct mount *mnt)
{
int res;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
if (mnt->mnt_id >= DEFAULT_SUS_MNT_ID) {
if (!ida_pre_get(&susfs_mnt_group_ida, GFP_KERNEL))
return -ENOMEM;
// If so, assign a sus mnt_group id DEFAULT_SUS_MNT_GROUP_ID from susfs_mnt_group_ida
res = ida_get_new_above(&susfs_mnt_group_ida,
susfs_mnt_group_start,
&mnt->mnt_group_id);
if (!res)
susfs_mnt_group_start = mnt->mnt_group_id + 1;
return res;
}
#endif
if (!ida_pre_get(&mnt_group_ida, GFP_KERNEL))
return -ENOMEM;
@@ -154,6 +244,17 @@ static int mnt_alloc_group_id(struct mount *mnt)
void mnt_release_group_id(struct mount *mnt)
{
int id = mnt->mnt_group_id;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
// If mnt->mnt_group_id >= DEFAULT_SUS_MNT_GROUP_ID, it means 'mnt' is also sus mount,
// then we free the mnt->mnt_group_id from susfs_mnt_group_ida
if (id >= DEFAULT_SUS_MNT_GROUP_ID) {
ida_remove(&susfs_mnt_group_ida, id);
if (susfs_mnt_group_start > id)
susfs_mnt_group_start = id;
mnt->mnt_group_id = 0;
return;
}
#endif
ida_remove(&mnt_group_ida, id);
if (mnt_group_start > id)
mnt_group_start = id;
@@ -201,13 +302,31 @@ static void drop_mountpoint(struct fs_pin *p)
mntput(&m->mnt);
}
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
static struct mount *alloc_vfsmnt(const char *name, bool should_spoof, int custom_mnt_id)
#else
static struct mount *alloc_vfsmnt(const char *name)
#endif
{
struct mount *mnt = kmem_cache_zalloc(mnt_cache, GFP_KERNEL);
if (mnt) {
int err;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
if (should_spoof) {
if (!custom_mnt_id) {
err = susfs_mnt_alloc_id(mnt);
} else {
mnt->mnt_id = custom_mnt_id;
err = 0;
}
goto bypass_orig_flow;
}
#endif
err = mnt_alloc_id(mnt);
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
bypass_orig_flow:
#endif
if (err)
goto out_free_cache;
@@ -1039,7 +1158,17 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void
if (!type)
return ERR_PTR(-ENODEV);
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
// For newly created mounts, the only caller process we care is KSU
if (unlikely(susfs_is_current_ksu_domain())) {
mnt = alloc_vfsmnt(name, true, 0);
goto bypass_orig_flow;
}
mnt = alloc_vfsmnt(name, false, 0);
bypass_orig_flow:
#else
mnt = alloc_vfsmnt(name);
#endif
if (!mnt)
return ERR_PTR(-ENOMEM);
@@ -1065,6 +1194,15 @@ vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void
mnt->mnt.mnt_sb = root->d_sb;
mnt->mnt_mountpoint = mnt->mnt.mnt_root;
mnt->mnt_parent = mnt;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
// If caller process is zygote, then it is a normal mount, so we just reorder the mnt_id
if (susfs_is_current_zygote_domain()) {
mnt->mnt.susfs_mnt_id_backup = mnt->mnt_id;
mnt->mnt_id = current->susfs_last_fake_mnt_id++;
}
#endif
lock_mount_hash();
list_add_tail(&mnt->mnt_instance, &root->d_sb->s_mounts);
unlock_mount_hash();
@@ -1093,8 +1231,52 @@ static struct mount *clone_mnt(struct mount *old, struct dentry *root,
struct super_block *sb = old->mnt.mnt_sb;
struct mount *mnt;
int err;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
bool is_current_ksu_domain = susfs_is_current_ksu_domain();
bool is_current_zygote_domain = susfs_is_current_zygote_domain();
/* - It is very important that we need to use CL_COPY_MNT_NS to identify whether
* the clone is a copy_tree() or single mount like called by __do_loopback()
* - if caller process is KSU, consider the following situation:
* 1. it is NOT doing unshare => call alloc_vfsmnt() to assign a new sus mnt_id
* 2. it is doing unshare => spoof the new mnt_id with the old mnt_id
* - If caller process is zygote and old mnt_id is sus => call alloc_vfsmnt() to assign a new sus mnt_id
* - For the rest of caller process that doing unshare => call alloc_vfsmnt() to assign a new sus mnt_id only for old sus mount
*/
// Firstly, check if it is KSU process
if (unlikely(is_current_ksu_domain)) {
// if it is doing single clone
if (!(flag & CL_COPY_MNT_NS)) {
mnt = alloc_vfsmnt(old->mnt_devname, true, 0);
goto bypass_orig_flow;
}
// if it is doing unshare
mnt = alloc_vfsmnt(old->mnt_devname, true, old->mnt_id);
if (mnt) {
mnt->mnt.susfs_mnt_id_backup = DEFAULT_SUS_MNT_ID_FOR_KSU_PROC_UNSHARE;
}
goto bypass_orig_flow;
}
// Secondly, check if it is zygote process and no matter it is doing unshare or not
if (likely(is_current_zygote_domain) && (old->mnt_id >= DEFAULT_SUS_MNT_ID)) {
/* Important Note:
* - Here we can't determine whether the unshare is called zygisk or not,
* so we can only patch out the unshare code in zygisk source code for now
* - But at least we can deal with old sus mounts using alloc_vfsmnt()
*/
mnt = alloc_vfsmnt(old->mnt_devname, true, 0);
goto bypass_orig_flow;
}
// Lastly, for other process that is doing unshare operation, but only deal with old sus mount
if ((flag & CL_COPY_MNT_NS) && (old->mnt_id >= DEFAULT_SUS_MNT_ID)) {
mnt = alloc_vfsmnt(old->mnt_devname, true, 0);
goto bypass_orig_flow;
}
mnt = alloc_vfsmnt(old->mnt_devname, false, 0);
bypass_orig_flow:
#else
mnt = alloc_vfsmnt(old->mnt_devname);
#endif
if (!mnt)
return ERR_PTR(-ENOMEM);
@@ -1146,6 +1328,15 @@ 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 caller process is zygote and not doing unshare, so we just reorder the mnt_id
if (likely(is_current_zygote_domain) && !(flag & CL_ZYGOTE_COPY_MNT_NS)) {
mnt->mnt.susfs_mnt_id_backup = mnt->mnt_id;
mnt->mnt_id = current->susfs_last_fake_mnt_id++;
}
#endif
lock_mount_hash();
list_add_tail(&mnt->mnt_instance, &sb->s_mounts);
unlock_mount_hash();
@@ -1916,6 +2107,7 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry,
p = mnt;
list_for_each_entry(r, &mnt->mnt_mounts, mnt_child) {
struct mount *s;
if (!is_subdir(r->mnt_mountpoint, dentry))
continue;
@@ -1947,7 +2139,7 @@ struct mount *copy_tree(struct mount *mnt, struct dentry *dentry,
goto out;
lock_mount_hash();
list_add_tail(&q->mnt_list, &res->mnt_list);
attach_mnt(q, parent, p->mnt_mp);
attach_mnt(q, parent, p->mnt_mp);
unlock_mount_hash();
}
}
@@ -2410,6 +2602,27 @@ 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_is_auto_add_sus_bind_mount_enabled &&
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)
if (susfs_is_auto_add_try_umount_for_bind_mount_enabled) {
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:
@@ -3009,6 +3222,15 @@ long do_mount(const char *dev_name, const char __user *dir_name,
else
retval = do_new_mount(&path, type_page, sb_flags, mnt_flags,
dev_name, data_page);
#ifdef CONFIG_KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT
// For both Legacy and Magic Mount KernelSU
if (!retval && susfs_is_auto_add_sus_ksu_default_mount_enabled &&
(!(flags & (MS_REMOUNT | MS_BIND | MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)))) {
if (susfs_is_current_ksu_domain()) {
susfs_auto_add_sus_ksu_default_mount(dir_name);
}
}
#endif
dput_out:
path_put(&path);
return retval;
@@ -3086,6 +3308,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);
@@ -3105,6 +3331,14 @@ 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 | CL_UNPRIVILEGED;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
// Always let clone_mnt() in copy_tree() know it is from copy_mnt_ns()
copy_flags |= CL_COPY_MNT_NS;
if (is_zygote_pid) {
// Let clone_mnt() in copy_tree() know copy_mnt_ns() is run by zygote process
copy_flags |= CL_ZYGOTE_COPY_MNT_NS;
}
#endif
new = copy_tree(old, old->mnt.mnt_root, copy_flags);
if (IS_ERR(new)) {
namespace_unlock();
@@ -3141,6 +3375,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->susfs_last_fake_mnt_id -> to record last valid fake mnt_id to zygote pid
// q->mnt.susfs_mnt_id_backup -> 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_id >= DEFAULT_SUS_MNT_ID)) {
continue;
}
q->mnt.susfs_mnt_id_backup = q->mnt_id;
q->mnt_id = last_entry_mnt_id++;
}
}
// Assign the 'last_entry_mnt_id' to 'current->susfs_last_fake_mnt_id' 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->susfs_last_fake_mnt_id = last_entry_mnt_id;
#endif
namespace_unlock();
if (rootmnt)
@@ -3681,3 +3938,37 @@ 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_id >= DEFAULT_SUS_MNT_ID) {
change_mnt_propagation(mnt, MS_PRIVATE);
}
}
// Unlock the namespace
namespace_unlock();
susfs_try_umount_all(current_uid().val);
}
#endif
#ifdef CONFIG_KSU_SUSFS
bool susfs_is_mnt_devname_ksu(struct path *path) {
struct mount *mnt;
if (path && path->mnt) {
mnt = real_mount(path->mnt);
if (mnt && mnt->mnt_devname && !strcmp(mnt->mnt_devname, "KSU")) {
return true;
}
}
return false;
}
#endif

View File

@@ -13,6 +13,9 @@
#include <linux/seq_file.h>
#include <linux/proc_fs.h>
#include <linux/exportfs.h>
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
#include <linux/susfs_def.h>
#endif
#include "inotify/inotify.h"
#include "../fs/mount.h"
@@ -21,16 +24,27 @@
#if defined(CONFIG_INOTIFY_USER) || defined(CONFIG_FANOTIFY)
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
static void show_fdinfo(struct seq_file *m, struct file *f,
void (*show)(struct seq_file *m,
struct fsnotify_mark *mark,
struct file *file))
#else
static void show_fdinfo(struct seq_file *m, struct file *f,
void (*show)(struct seq_file *m,
struct fsnotify_mark *mark))
#endif
{
struct fsnotify_group *group = f->private_data;
struct fsnotify_mark *mark;
mutex_lock(&group->mark_mutex);
list_for_each_entry(mark, &group->marks_list, g_list) {
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
show(m, mark, f);
#else
show(m, mark);
#endif
if (seq_has_overflowed(m))
break;
}
@@ -72,7 +86,11 @@ static void show_mark_fhandle(struct seq_file *m, struct inode *inode)
#ifdef CONFIG_INOTIFY_USER
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
static void inotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark, struct file *file)
#else
static void inotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark)
#endif
{
struct inotify_inode_mark *inode_mark;
struct inode *inode;
@@ -83,6 +101,36 @@ static void inotify_fdinfo(struct seq_file *m, struct fsnotify_mark *mark)
inode_mark = container_of(mark, struct inotify_inode_mark, fsn_mark);
inode = igrab(mark->connector->inode);
if (inode) {
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
if (likely(susfs_is_current_proc_umounted()) &&
unlikely(inode->i_mapping->flags & BIT_SUS_KSTAT)) {
struct path path;
char *pathname = kmalloc(PAGE_SIZE, GFP_KERNEL);
char *dpath;
if (!pathname) {
goto out_seq_printf;
}
dpath = d_path(&file->f_path, pathname, PAGE_SIZE);
if (!dpath) {
goto out_free_pathname;
}
if (kern_path(dpath, 0, &path)) {
goto out_free_pathname;
}
seq_printf(m, "inotify wd:%x ino:%lx sdev:%x mask:%x ignored_mask:0 ",
inode_mark->wd, path.dentry->d_inode->i_ino, path.dentry->d_inode->i_sb->s_dev,
inotify_mark_user_mask(mark));
show_mark_fhandle(m, path.dentry->d_inode);
seq_putc(m, '\n');
iput(inode);
path_put(&path);
kfree(pathname);
return;
out_free_pathname:
kfree(pathname);
}
out_seq_printf:
#endif
seq_printf(m, "inotify wd:%x ino:%lx sdev:%x mask:%x ignored_mask:0 ",
inode_mark->wd, inode->i_ino, inode->i_sb->s_dev,
inotify_mark_user_mask(mark));

View File

@@ -101,6 +101,9 @@
#include <trace/events/oom.h>
#include "internal.h"
#include "fd.h"
#ifdef CONFIG_KSU_SUSFS_SUS_MAP
#include <linux/susfs_def.h>
#endif
#include "../../lib/kstrtox.h"
@@ -822,6 +825,9 @@ static ssize_t mem_rw(struct file *file, char __user *buf,
ssize_t copied;
char *page;
unsigned int flags;
#ifdef CONFIG_KSU_SUSFS_SUS_MAP
struct vm_area_struct *vma;
#endif
if (!mm)
return 0;
@@ -838,7 +844,20 @@ static ssize_t mem_rw(struct file *file, char __user *buf,
while (count > 0) {
size_t this_len = min_t(size_t, count, PAGE_SIZE);
#ifdef CONFIG_KSU_SUSFS_SUS_MAP
vma = find_vma(mm, addr);
if (vma && vma->vm_file) {
struct inode *inode = file_inode(vma->vm_file);
if (unlikely(inode->i_mapping->flags & BIT_SUS_MAPS) && susfs_is_current_proc_umounted()) {
if (write) {
copied = -EFAULT;
} else {
copied = -EIO;
}
break;
}
}
#endif
if (write && copy_from_user(page, buf, this_len)) {
copied = -EFAULT;
break;
@@ -2406,6 +2425,13 @@ proc_map_files_readdir(struct file *file, struct dir_context *ctx)
vma = vma->vm_next) {
if (!vma->vm_file)
continue;
#ifdef CONFIG_KSU_SUSFS_SUS_MAP
if (unlikely(file_inode(vma->vm_file)->i_mapping->flags & BIT_SUS_MAPS) &&
susfs_is_current_proc_umounted())
{
continue;
}
#endif
if (++pos <= ctx->pos)
continue;

View File

@@ -27,8 +27,18 @@ static void proc_command_line_init(void) {
}
#endif
#ifdef CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG
extern int susfs_spoof_cmdline_or_bootconfig(struct seq_file *m);
#endif
static int cmdline_proc_show(struct seq_file *m, void *v)
{
#ifdef CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG
if (!susfs_spoof_cmdline_or_bootconfig(m)) {
seq_putc(m, '\n');
return 0;
}
#endif
#ifdef CONFIG_INITRAMFS_IGNORE_SKIP_FLAG
seq_printf(m, "%s\n", proc_command_line);
#else

View File

@@ -12,6 +12,9 @@
#include <linux/fs.h>
#include <linux/proc_fs.h>
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
#include <linux/susfs_def.h>
#endif
#include "../mount.h"
#include "internal.h"
@@ -23,6 +26,9 @@ static int seq_show(struct seq_file *m, void *v)
int f_flags = 0, ret = -ENOENT;
struct file *file = NULL;
struct task_struct *task;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
struct mount *mnt = NULL;
#endif
task = get_proc_task(m->private);
if (!task)
@@ -53,9 +59,20 @@ static int seq_show(struct seq_file *m, void *v)
if (ret)
return ret;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
mnt = real_mount(file->f_path.mnt);
if (likely(susfs_is_current_proc_umounted()) &&
mnt->mnt_id >= DEFAULT_SUS_MNT_ID) {
for (; mnt->mnt_id >= DEFAULT_SUS_MNT_ID; mnt = mnt->mnt_parent) { }
}
seq_printf(m, "pos:\t%lli\nflags:\t0%o\nmnt_id:\t%i\n",
(long long)file->f_pos, f_flags,
mnt->mnt_id);
#else
seq_printf(m, "pos:\t%lli\nflags:\t0%o\nmnt_id:\t%i\n",
(long long)file->f_pos, f_flags,
real_mount(file->f_path.mnt)->mnt_id);
#endif
show_fd_locks(m, file, files);
if (seq_has_overflowed(m))

View File

@@ -21,12 +21,19 @@
#include <linux/mm_inline.h>
#include <linux/ctype.h>
#include <linux/pkeys.h>
#if defined(CONFIG_KSU_SUSFS_SUS_KSTAT) || defined(CONFIG_KSU_SUSFS_SUS_MAP)
#include <linux/susfs_def.h>
#endif
#include <asm/elf.h>
#include <asm/tlb.h>
#include <asm/tlbflush.h>
#include "internal.h"
#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
#define SEQ_PUT_DEC(str, val) \
seq_put_decimal_ull_width(m, str, (val) << (PAGE_SHIFT-10), 8)
void task_mem(struct seq_file *m, struct mm_struct *mm)
@@ -355,8 +362,35 @@ 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_MAP
if (unlikely(inode->i_mapping->flags & BIT_SUS_MAPS) && susfs_is_current_proc_umounted()) {
seq_setwidth(m, 25 + sizeof(void *) * 6 - 1);
seq_put_hex_ll(m, NULL, vma->vm_start, 8);
seq_put_hex_ll(m, "-", vma->vm_end, 8);
seq_putc(m, ' ');
seq_putc(m, '-');
seq_putc(m, '-');
seq_putc(m, '-');
seq_putc(m, 'p');
seq_put_hex_ll(m, " ", pgoff, 8);
seq_put_hex_ll(m, " ", MAJOR(dev), 2);
seq_put_hex_ll(m, ":", MINOR(dev), 2);
seq_put_decimal_ull(m, " ", ino);
seq_putc(m, ' ');
goto done;
}
#endif
#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT
if (unlikely(inode->i_mapping->flags & BIT_SUS_KSTAT)) {
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;
}
@@ -871,6 +905,25 @@ static int show_smap(struct seq_file *m, void *v)
memset(&mss, 0, sizeof(mss));
#ifdef CONFIG_KSU_SUSFS_SUS_MAP
if (vma->vm_file &&
unlikely(file_inode(vma->vm_file)->i_mapping->flags & BIT_SUS_MAPS) &&
susfs_is_current_proc_umounted())
{
show_map_vma(m, vma);
SEQ_PUT_DEC("Size: ", vma->vm_end - vma->vm_start);
SEQ_PUT_DEC(" kB\nKernelPageSize: ", 4);
SEQ_PUT_DEC(" kB\nMMUPageSize: ", 4);
seq_puts(m, " kB\n");
__show_smap(m, &mss, false);
if (arch_pkeys_enabled())
seq_printf(m, "ProtectionKey: %8u\n", vma_pkey(vma));
seq_puts(m, "VmFlags: mr mw me");
seq_putc(m, '\n');
goto bypass_orig_flow;
}
#endif
smap_gather_stats(vma, &mss);
show_map_vma(m, vma);
@@ -891,8 +944,11 @@ static int show_smap(struct seq_file *m, void *v)
seq_printf(m, "ProtectionKey: %8u\n", vma_pkey(vma));
show_smap_vma_flags(m, vma);
#ifdef CONFIG_KSU_SUSFS_SUS_MAP
bypass_orig_flow:
#endif
m_cache_vma(m, vma);
return 0;
}
@@ -921,7 +977,19 @@ static int show_smaps_rollup(struct seq_file *m, void *v)
hold_task_mempolicy(priv);
for (vma = priv->mm->mmap; vma; vma = vma->vm_next) {
#ifdef CONFIG_KSU_SUSFS_SUS_MAP
if (vma->vm_file &&
unlikely(file_inode(vma->vm_file)->i_mapping->flags & BIT_SUS_MAPS) &&
susfs_is_current_proc_umounted())
{
memset(&mss, 0, sizeof(mss));
goto bypass_orig_flow;
}
#endif
smap_gather_stats(vma, &mss);
#ifdef CONFIG_KSU_SUSFS_SUS_MAP
bypass_orig_flow:
#endif
last_vma_end = vma->vm_end;
}
@@ -1567,6 +1635,9 @@ static ssize_t pagemap_read(struct file *file, char __user *buf,
unsigned long start_vaddr;
unsigned long end_vaddr;
int ret = 0, copied = 0;
#ifdef CONFIG_KSU_SUSFS_SUS_MAP
struct vm_area_struct *vma;
#endif
if (!mm || !mmget_not_zero(mm))
goto out;
@@ -1625,6 +1696,16 @@ static ssize_t pagemap_read(struct file *file, char __user *buf,
down_read(&mm->mmap_sem);
ret = walk_page_range(start_vaddr, end, &pagemap_walk);
up_read(&mm->mmap_sem);
#ifdef CONFIG_KSU_SUSFS_SUS_MAP
vma = find_vma(mm, start_vaddr);
if (vma && vma->vm_file) {
struct inode *inode = file_inode(vma->vm_file);
if (unlikely(inode->i_mapping->flags & BIT_SUS_MAPS) && susfs_is_current_proc_umounted()) {
pm.show_pfn = false;
pm.buffer->pme = 0;
}
}
#endif
start_vaddr = end;
len = min(count, PM_ENTRY_BYTES * pm.pos);

View File

@@ -12,12 +12,20 @@
#include <linux/security.h>
#include <linux/fs_struct.h>
#include <linux/sched/task.h>
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
#include <linux/susfs_def.h>
#endif
#include "proc/internal.h" /* only for get_proc_task() in ->open() */
#include "pnode.h"
#include "internal.h"
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
extern bool susfs_is_current_ksu_domain(void);
bool susfs_hide_sus_mnts_for_all_procs = true; // hide sus mounts for all processes by default
#endif
static unsigned mounts_poll(struct file *file, poll_table *wait)
{
struct seq_file *m = file->private_data;
@@ -102,6 +110,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 (susfs_hide_sus_mnts_for_all_procs && r->mnt_id >= DEFAULT_SUS_MNT_ID)
return 0;
#endif
if (sb->s_op->show_devname) {
err = sb->s_op->show_devname(m, mnt_path.dentry);
if (err)
@@ -138,6 +151,11 @@ 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 (susfs_hide_sus_mnts_for_all_procs && r->mnt_id >= DEFAULT_SUS_MNT_ID)
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) {
@@ -202,6 +220,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 (susfs_hide_sus_mnts_for_all_procs && r->mnt_id >= DEFAULT_SUS_MNT_ID)
return 0;
#endif
/* device */
if (sb->s_op->show_devname) {
seq_puts(m, "device ");

View File

@@ -23,6 +23,14 @@
#include <linux/uaccess.h>
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
#include <linux/susfs_def.h>
extern bool susfs_is_inode_sus_path(struct inode *inode);
extern bool susfs_is_sus_android_data_d_name_found(const char *d_name);
extern bool susfs_is_sus_sdcard_d_name_found(const char *d_name);
extern bool susfs_is_base_dentry_android_data_dir(struct dentry* base);
extern bool susfs_is_base_dentry_sdcard_dir(struct dentry* base);
#endif
int iterate_dir(struct file *file, struct dir_context *ctx)
{
struct inode *inode = file_inode(file);
@@ -120,6 +128,11 @@ struct old_linux_dirent {
struct readdir_callback {
struct dir_context ctx;
struct old_linux_dirent __user * dirent;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
struct super_block *sb;
bool is_base_dentry_android_data_root_dir;
bool is_base_dentry_sdcard_root_dir;
#endif
int result;
};
@@ -130,6 +143,9 @@ static int fillonedir(struct dir_context *ctx, const char *name, int namlen,
container_of(ctx, struct readdir_callback, ctx);
struct old_linux_dirent __user * dirent;
unsigned long d_ino;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
struct inode *inode;
#endif
if (buf->result)
return -EINVAL;
@@ -141,6 +157,28 @@ static int fillonedir(struct dir_context *ctx, const char *name, int namlen,
buf->result = -EOVERFLOW;
return -EOVERFLOW;
}
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
if (buf->is_base_dentry_android_data_root_dir) {
if (susfs_is_sus_android_data_d_name_found(name)) {
return 0;
}
} else if (buf->is_base_dentry_sdcard_root_dir) {
if (susfs_is_sus_sdcard_d_name_found(name)) {
return 0;
}
}
inode = ilookup(buf->sb, ino);
if (!inode) {
goto orig_flow;
}
if (susfs_is_inode_sus_path(inode)) {
iput(inode);
return 0;
}
iput(inode);
orig_flow:
#endif
buf->result++;
dirent = buf->dirent;
if (!access_ok(VERIFY_WRITE, dirent,
@@ -168,9 +206,33 @@ SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
.ctx.actor = fillonedir,
.dirent = dirent
};
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
struct inode *inode;
#endif
if (!f.file)
return -EBADF;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
buf.sb = f.file->f_inode->i_sb;
inode = f.file->f_path.dentry->d_inode;
if (f.file->f_path.dentry && inode) {
if (susfs_is_base_dentry_android_data_dir(f.file->f_path.dentry))
{
buf.is_base_dentry_android_data_root_dir = true;
buf.is_base_dentry_sdcard_root_dir = false;
goto orig_flow;
}
if (susfs_is_base_dentry_sdcard_dir(f.file->f_path.dentry))
{
buf.is_base_dentry_sdcard_root_dir = true;
buf.is_base_dentry_android_data_root_dir = false;
goto orig_flow;
}
}
buf.is_base_dentry_android_data_root_dir = false;
buf.is_base_dentry_sdcard_root_dir = false;
orig_flow:
#endif
error = iterate_dir(f.file, &buf.ctx);
if (buf.result)
@@ -197,6 +259,11 @@ struct getdents_callback {
struct dir_context ctx;
struct linux_dirent __user * current_dir;
struct linux_dirent __user * previous;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
struct super_block *sb;
bool is_base_dentry_android_data_root_dir;
bool is_base_dentry_sdcard_root_dir;
#endif
int count;
int error;
};
@@ -210,6 +277,9 @@ static int filldir(struct dir_context *ctx, const char *name, int namlen,
unsigned long d_ino;
int reclen = ALIGN(offsetof(struct linux_dirent, d_name) + namlen + 2,
sizeof(long));
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
struct inode *inode;
#endif
buf->error = verify_dirent_name(name, namlen);
if (unlikely(buf->error))
@@ -222,6 +292,28 @@ static int filldir(struct dir_context *ctx, const char *name, int namlen,
buf->error = -EOVERFLOW;
return -EOVERFLOW;
}
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
if (buf->is_base_dentry_android_data_root_dir) {
if (susfs_is_sus_android_data_d_name_found(name)) {
return 0;
}
} else if (buf->is_base_dentry_sdcard_root_dir) {
if (susfs_is_sus_sdcard_d_name_found(name)) {
return 0;
}
}
inode = ilookup(buf->sb, ino);
if (!inode) {
goto orig_flow;
}
if (susfs_is_inode_sus_path(inode)) {
iput(inode);
return 0;
}
iput(inode);
orig_flow:
#endif
dirent = buf->previous;
if (dirent) {
if (signal_pending(current))
@@ -261,6 +353,9 @@ SYSCALL_DEFINE3(getdents, unsigned int, fd,
.current_dir = dirent
};
int error;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
struct inode *inode;
#endif
if (!access_ok(VERIFY_WRITE, dirent, count))
return -EFAULT;
@@ -268,6 +363,27 @@ SYSCALL_DEFINE3(getdents, unsigned int, fd,
f = fdget_pos(fd);
if (!f.file)
return -EBADF;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
buf.sb = f.file->f_inode->i_sb;
inode = f.file->f_path.dentry->d_inode;
if (f.file->f_path.dentry && inode) {
if (susfs_is_base_dentry_android_data_dir(f.file->f_path.dentry))
{
buf.is_base_dentry_android_data_root_dir = true;
buf.is_base_dentry_sdcard_root_dir = false;
goto orig_flow;
}
if (susfs_is_base_dentry_sdcard_dir(f.file->f_path.dentry))
{
buf.is_base_dentry_sdcard_root_dir = true;
buf.is_base_dentry_android_data_root_dir = false;
goto orig_flow;
}
}
buf.is_base_dentry_android_data_root_dir = false;
buf.is_base_dentry_sdcard_root_dir = false;
orig_flow:
#endif
error = iterate_dir(f.file, &buf.ctx);
if (error >= 0)
@@ -287,6 +403,11 @@ struct getdents_callback64 {
struct dir_context ctx;
struct linux_dirent64 __user * current_dir;
struct linux_dirent64 __user * previous;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
struct super_block *sb;
bool is_base_dentry_android_data_root_dir;
bool is_base_dentry_sdcard_root_dir;
#endif
int count;
int error;
};
@@ -299,6 +420,9 @@ static int filldir64(struct dir_context *ctx, const char *name, int namlen,
container_of(ctx, struct getdents_callback64, ctx);
int reclen = ALIGN(offsetof(struct linux_dirent64, d_name) + namlen + 1,
sizeof(u64));
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
struct inode *inode;
#endif
buf->error = verify_dirent_name(name, namlen);
if (unlikely(buf->error))
@@ -313,6 +437,28 @@ static int filldir64(struct dir_context *ctx, const char *name, int namlen,
if (__put_user(offset, &dirent->d_off))
goto efault;
}
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
if (buf->is_base_dentry_android_data_root_dir) {
if (susfs_is_sus_android_data_d_name_found(name)) {
return 0;
}
} else if (buf->is_base_dentry_sdcard_root_dir) {
if (susfs_is_sus_sdcard_d_name_found(name)) {
return 0;
}
}
inode = ilookup(buf->sb, ino);
if (!inode) {
goto orig_flow;
}
if (susfs_is_inode_sus_path(inode)) {
iput(inode);
return 0;
}
iput(inode);
orig_flow:
#endif
dirent = buf->current_dir;
if (__put_user(ino, &dirent->d_ino))
goto efault;
@@ -347,6 +493,9 @@ SYSCALL_DEFINE3(getdents64, unsigned int, fd,
.current_dir = dirent
};
int error;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
struct inode *inode;
#endif
if (!access_ok(VERIFY_WRITE, dirent, count))
return -EFAULT;
@@ -354,7 +503,27 @@ SYSCALL_DEFINE3(getdents64, unsigned int, fd,
f = fdget_pos(fd);
if (!f.file)
return -EBADF;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
buf.sb = f.file->f_inode->i_sb;
inode = f.file->f_path.dentry->d_inode;
if (f.file->f_path.dentry && inode) {
if (susfs_is_base_dentry_android_data_dir(f.file->f_path.dentry))
{
buf.is_base_dentry_android_data_root_dir = true;
buf.is_base_dentry_sdcard_root_dir = false;
goto orig_flow;
}
if (susfs_is_base_dentry_sdcard_dir(f.file->f_path.dentry))
{
buf.is_base_dentry_sdcard_root_dir = true;
buf.is_base_dentry_android_data_root_dir = false;
goto orig_flow;
}
}
buf.is_base_dentry_android_data_root_dir = false;
buf.is_base_dentry_sdcard_root_dir = false;
orig_flow:
#endif
error = iterate_dir(f.file, &buf.ctx);
if (error >= 0)
error = buf.error;
@@ -381,6 +550,11 @@ struct compat_old_linux_dirent {
struct compat_readdir_callback {
struct dir_context ctx;
struct compat_old_linux_dirent __user *dirent;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
struct super_block *sb;
bool is_base_dentry_android_data_root_dir;
bool is_base_dentry_sdcard_root_dir;
#endif
int result;
};
@@ -392,6 +566,9 @@ static int compat_fillonedir(struct dir_context *ctx, const char *name,
container_of(ctx, struct compat_readdir_callback, ctx);
struct compat_old_linux_dirent __user *dirent;
compat_ulong_t d_ino;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
struct inode *inode;
#endif
if (buf->result)
return -EINVAL;
@@ -403,6 +580,28 @@ static int compat_fillonedir(struct dir_context *ctx, const char *name,
buf->result = -EOVERFLOW;
return -EOVERFLOW;
}
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
if (buf->is_base_dentry_android_data_root_dir) {
if (susfs_is_sus_android_data_d_name_found(name)) {
return 0;
}
} else if (buf->is_base_dentry_sdcard_root_dir) {
if (susfs_is_sus_sdcard_d_name_found(name)) {
return 0;
}
}
inode = ilookup(buf->sb, ino);
if (!inode) {
goto orig_flow;
}
if (susfs_is_inode_sus_path(inode)) {
iput(inode);
return 0;
}
iput(inode);
orig_flow:
#endif
buf->result++;
dirent = buf->dirent;
if (!access_ok(VERIFY_WRITE, dirent,
@@ -430,9 +629,33 @@ COMPAT_SYSCALL_DEFINE3(old_readdir, unsigned int, fd,
.ctx.actor = compat_fillonedir,
.dirent = dirent
};
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
struct inode *inode;
#endif
if (!f.file)
return -EBADF;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
buf.sb = f.file->f_inode->i_sb;
inode = f.file->f_path.dentry->d_inode;
if (f.file->f_path.dentry && inode) {
if (susfs_is_base_dentry_android_data_dir(f.file->f_path.dentry))
{
buf.is_base_dentry_android_data_root_dir = true;
buf.is_base_dentry_sdcard_root_dir = false;
goto orig_flow;
}
if (susfs_is_base_dentry_sdcard_dir(f.file->f_path.dentry))
{
buf.is_base_dentry_sdcard_root_dir = true;
buf.is_base_dentry_android_data_root_dir = false;
goto orig_flow;
}
}
buf.is_base_dentry_android_data_root_dir = false;
buf.is_base_dentry_sdcard_root_dir = false;
orig_flow:
#endif
error = iterate_dir(f.file, &buf.ctx);
if (buf.result)
@@ -453,6 +676,11 @@ struct compat_getdents_callback {
struct dir_context ctx;
struct compat_linux_dirent __user *current_dir;
struct compat_linux_dirent __user *previous;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
struct super_block *sb;
bool is_base_dentry_android_data_root_dir;
bool is_base_dentry_sdcard_root_dir;
#endif
int count;
int error;
};
@@ -466,6 +694,9 @@ static int compat_filldir(struct dir_context *ctx, const char *name, int namlen,
compat_ulong_t d_ino;
int reclen = ALIGN(offsetof(struct compat_linux_dirent, d_name) +
namlen + 2, sizeof(compat_long_t));
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
struct inode *inode;
#endif
buf->error = -EINVAL; /* only used if we fail.. */
if (reclen > buf->count)
@@ -475,6 +706,28 @@ static int compat_filldir(struct dir_context *ctx, const char *name, int namlen,
buf->error = -EOVERFLOW;
return -EOVERFLOW;
}
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
if (buf->is_base_dentry_android_data_root_dir) {
if (susfs_is_sus_android_data_d_name_found(name)) {
return 0;
}
} else if (buf->is_base_dentry_sdcard_root_dir) {
if (susfs_is_sus_sdcard_d_name_found(name)) {
return 0;
}
}
inode = ilookup(buf->sb, ino);
if (!inode) {
goto orig_flow;
}
if (susfs_is_inode_sus_path(inode)) {
iput(inode);
return 0;
}
iput(inode);
orig_flow:
#endif
dirent = buf->previous;
if (dirent) {
if (signal_pending(current))
@@ -514,6 +767,9 @@ COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd,
.count = count
};
int error;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
struct inode *inode;
#endif
if (!access_ok(VERIFY_WRITE, dirent, count))
return -EFAULT;
@@ -521,6 +777,27 @@ COMPAT_SYSCALL_DEFINE3(getdents, unsigned int, fd,
f = fdget_pos(fd);
if (!f.file)
return -EBADF;
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
buf.sb = f.file->f_inode->i_sb;
inode = f.file->f_path.dentry->d_inode;
if (f.file->f_path.dentry && inode) {
if (susfs_is_base_dentry_android_data_dir(f.file->f_path.dentry))
{
buf.is_base_dentry_android_data_root_dir = true;
buf.is_base_dentry_sdcard_root_dir = false;
goto orig_flow;
}
if (susfs_is_base_dentry_sdcard_dir(f.file->f_path.dentry))
{
buf.is_base_dentry_sdcard_root_dir = true;
buf.is_base_dentry_android_data_root_dir = false;
goto orig_flow;
}
}
buf.is_base_dentry_android_data_root_dir = false;
buf.is_base_dentry_sdcard_root_dir = false;
orig_flow:
#endif
error = iterate_dir(f.file, &buf.ctx);
if (error >= 0)

View File

@@ -17,6 +17,9 @@
#include <linux/syscalls.h>
#include <linux/pagemap.h>
#include <linux/compat.h>
#if defined(CONFIG_KSU_SUSFS_SUS_KSTAT) || defined(CONFIG_KSU_SUSFS_SUS_MOUNT)
#include <linux/susfs_def.h>
#endif
#include <linux/uaccess.h>
#include <asm/unistd.h>
@@ -30,8 +33,23 @@
* found on the VFS inode structure. This is the default if no getattr inode
* operation is supplied.
*/
#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT
extern void susfs_sus_ino_for_generic_fillattr(unsigned long ino, struct kstat *stat);
#endif
void generic_fillattr(struct inode *inode, struct kstat *stat)
{
#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT
if (likely(susfs_is_current_proc_umounted()) &&
unlikely(inode->i_mapping->flags & BIT_SUS_KSTAT)) {
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;

View File

@@ -9,6 +9,10 @@
#include <linux/security.h>
#include <linux/uaccess.h>
#include <linux/compat.h>
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
#include <linux/susfs_def.h>
#include "mount.h"
#endif
#include "internal.h"
static int flags_by_mnt(int mnt_flags)
@@ -70,11 +74,23 @@ static int statfs_by_dentry(struct dentry *dentry, struct kstatfs *buf)
int vfs_statfs(const struct path *path, struct kstatfs *buf)
{
int error;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
struct mount *mnt;
mnt = real_mount(path->mnt);
if (likely(susfs_is_current_proc_umounted())) {
for (; mnt->mnt_id >= DEFAULT_SUS_MNT_ID; mnt = mnt->mnt_parent) {}
}
error = statfs_by_dentry(mnt->mnt.mnt_root, buf);
if (!error)
buf->f_flags = calculate_f_flags(&mnt->mnt);
return error;
#else
error = statfs_by_dentry(path->dentry, buf);
if (!error)
buf->f_flags = calculate_f_flags(path->mnt);
return error;
#endif
}
EXPORT_SYMBOL(vfs_statfs);
@@ -224,6 +240,11 @@ int vfs_ustat(dev_t dev, struct kstatfs *sbuf)
if (!s)
return -EINVAL;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
if (unlikely(s->s_root->d_inode->i_mapping->flags & BIT_SUS_MOUNT)) {
return -EINVAL;
}
#endif
err = statfs_by_dentry(s->s_root, sbuf);
drop_super(s);
return err;

1362
fs/susfs.c Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -69,6 +69,9 @@ struct vfsmount {
struct super_block *mnt_sb; /* pointer to superblock */
int mnt_flags;
void *data;
#ifdef CONFIG_KSU_SUSFS
u64 susfs_mnt_id_backup;
#endif
} __randomize_layout;
struct file; /* forward dec */

View File

@@ -1372,6 +1372,10 @@ struct task_struct {
* New fields for task_struct should be added above here, so that
* they are included in the randomized portion of task_struct.
*/
#ifdef CONFIG_KSU_SUSFS
u64 susfs_task_state;
u64 susfs_last_fake_mnt_id;
#endif
randomized_struct_fields_end
/* CPU-specific state of this task: */

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

@@ -0,0 +1,213 @@
#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>
#include <linux/susfs_def.h>
#define SUSFS_VERSION "v1.5.12"
#if LINUX_VERSION_CODE < KERNEL_VERSION(5,0,0)
#define SUSFS_VARIANT "NON-GKI"
#else
#define SUSFS_VARIANT "GKI"
#endif
/*********/
/* 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];
unsigned int i_uid;
};
struct st_susfs_sus_path_list {
struct list_head list;
struct st_susfs_sus_path info;
char target_pathname[SUSFS_MAX_LEN_PATHNAME];
size_t path_len;
};
struct st_android_data_path {
char pathname[SUSFS_MAX_LEN_PATHNAME];
bool is_inited;
};
struct st_sdcard_path {
char pathname[SUSFS_MAX_LEN_PATHNAME];
bool is_inited;
};
#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
/* sus_map */
#ifdef CONFIG_KSU_SUSFS_SUS_MAP
struct st_susfs_sus_map {
char target_pathname[SUSFS_MAX_LEN_PATHNAME];
};
#endif
/***********************/
/* FORWARD DECLARATION */
/***********************/
/* sus_path */
#ifdef CONFIG_KSU_SUSFS_SUS_PATH
int susfs_set_i_state_on_external_dir(char __user* user_info, int cmd);
int susfs_add_sus_path(struct st_susfs_sus_path* __user user_info);
#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);
void susfs_spoof_uname(struct new_utsname* tmp);
#endif
/* set_log */
#ifdef CONFIG_KSU_SUSFS_ENABLE_LOG
void susfs_set_log(bool enabled);
#endif
/* spoof_cmdline_or_bootconfig */
#ifdef CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG
int susfs_set_cmdline_or_bootconfig(char* __user user_fake_boot_config);
int susfs_spoof_cmdline_or_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
/* sus_map */
#ifdef CONFIG_KSU_SUSFS_SUS_MAP
int susfs_add_sus_map(struct st_susfs_sus_map* __user user_info);
#endif
int susfs_get_enabled_features(char __user* buf, size_t bufsize);
void susfs_set_avc_log_spoofing(bool enabled);
/* susfs_init */
void susfs_init(void);
#endif

92
include/linux/susfs_def.h Normal file
View File

@@ -0,0 +1,92 @@
#ifndef KSU_SUSFS_DEF_H
#define KSU_SUSFS_DEF_H
#include <linux/bits.h>
/********/
/* ENUM */
/********/
/* shared with userspace ksu_susfs tool */
#define CMD_SUSFS_ADD_SUS_PATH 0x55550
#define CMD_SUSFS_SET_ANDROID_DATA_ROOT_PATH 0x55551
#define CMD_SUSFS_SET_SDCARD_ROOT_PATH 0x55552
#define CMD_SUSFS_ADD_SUS_PATH_LOOP 0x55553
#define CMD_SUSFS_ADD_SUS_MOUNT 0x55560
#define CMD_SUSFS_HIDE_SUS_MNTS_FOR_ALL_PROCS 0x55561
#define CMD_SUSFS_UMOUNT_FOR_ZYGOTE_ISO_SERVICE 0x55562
#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_CMDLINE_OR_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 CMD_SUSFS_ENABLE_AVC_LOG_SPOOFING 0x60010
#define CMD_SUSFS_ADD_SUS_MAP 0x60020
#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_CMDLINE_OR_BOOTCONFIG_SIZE 4096
#define TRY_UMOUNT_DEFAULT 0 /* used by susfs_try_umount() */
#define TRY_UMOUNT_DETACH 1 /* used by susfs_try_umount() */
#define SUS_SU_DISABLED 0
#define SUS_SU_WITH_OVERLAY 1 /* deprecated */
#define SUS_SU_WITH_HOOKS 2
#define DEFAULT_SUS_MNT_ID 300000 /* used by mount->mnt_id */
#define DEFAULT_SUS_MNT_ID_FOR_KSU_PROC_UNSHARE 1000000 /* used by vfsmount->susfs_mnt_id_backup */
#define DEFAULT_SUS_MNT_GROUP_ID 3000 /* used by mount->mnt_group_id */
/*
* mount->mnt.susfs_mnt_id_backup => storing original mount's mnt_id
* inode->i_mapping->flags => storing flag 'AS_FLAGS_'
* nd->state => storing flag 'ND_STATE_'
* nd->flags => storing flag 'ND_FLAGS_'
* task_struct->thread_info.flags => storing flag 'TIF_'
*/
// thread_info->flags is unsigned long :D
#define TIF_PROC_UMOUNTED 33
#define AS_FLAGS_SUS_PATH 24
#define AS_FLAGS_SUS_MOUNT 25
#define AS_FLAGS_SUS_KSTAT 26
#define AS_FLAGS_OPEN_REDIRECT 27
#define AS_FLAGS_ANDROID_DATA_ROOT_DIR 28
#define AS_FLAGS_SDCARD_ROOT_DIR 29
#define AS_FLAGS_SUS_MAP 30
#define BIT_SUS_PATH BIT(24)
#define BIT_SUS_MOUNT BIT(25)
#define BIT_SUS_KSTAT BIT(26)
#define BIT_OPEN_REDIRECT BIT(27)
#define BIT_ANDROID_DATA_ROOT_DIR BIT(28)
#define BIT_ANDROID_SDCARD_ROOT_DIR BIT(29)
#define BIT_SUS_MAPS BIT(30)
#define ND_STATE_LOOKUP_LAST 32
#define ND_STATE_OPEN_LAST 64
#define ND_STATE_LAST_SDCARD_SUS_PATH 128
#define ND_FLAGS_LOOKUP_LAST 0x2000000
#define MAGIC_MOUNT_WORKDIR "/debug_ramdisk/workdir"
#define DATA_ADB_UMOUNT_FOR_ZYGOTE_SYSTEM_PROCESS "/data/adb/susfs_umount_for_zygote_system_process"
#define DATA_ADB_NO_AUTO_ADD_SUS_BIND_MOUNT "/data/adb/susfs_no_auto_add_sus_bind_mount"
#define DATA_ADB_NO_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT "/data/adb/susfs_no_auto_add_sus_ksu_default_mount"
#define DATA_ADB_NO_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT "/data/adb/susfs_no_auto_add_try_umount_for_bind_mount"
static inline bool susfs_is_current_proc_umounted(void) {
return test_ti_thread_flag(&current->thread_info, TIF_PROC_UMOUNTED);
}
static inline void susfs_set_current_proc_umounted(void) {
set_ti_thread_flag(&current->thread_info, TIF_PROC_UMOUNTED);
}
#endif // #ifndef KSU_SUSFS_DEF_H

View File

@@ -592,6 +592,11 @@ static void s_stop(struct seq_file *m, void *p)
{
}
#ifdef CONFIG_KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS
extern bool susfs_starts_with(const char *str, const char *prefix);
#endif
static int s_show(struct seq_file *m, void *p)
{
struct kallsym_iter *iter = m->private;
@@ -612,8 +617,38 @@ static int s_show(struct seq_file *m, void *p)
seq_printf(m, "%pK %c %s\t[%s]\n", (void *)iter->value,
type, iter->name, iter->module_name);
} else
#ifndef CONFIG_KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS
seq_printf(m, "%pK %c %s\n", (void *)iter->value,
iter->type, iter->name);
#else
{
if (susfs_starts_with(iter->name, "ksu_") ||
susfs_starts_with(iter->name, "__ksu_") ||
susfs_starts_with(iter->name, "susfs_") ||
susfs_starts_with(iter->name, "ksud") ||
susfs_starts_with(iter->name, "is_ksu_") ||
susfs_starts_with(iter->name, "is_manager_") ||
susfs_starts_with(iter->name, "escape_to_") ||
susfs_starts_with(iter->name, "setup_selinux") ||
susfs_starts_with(iter->name, "track_throne") ||
susfs_starts_with(iter->name, "on_post_fs_data") ||
susfs_starts_with(iter->name, "try_umount") ||
susfs_starts_with(iter->name, "kernelsu") ||
susfs_starts_with(iter->name, "__initcall__kmod_kernelsu") ||
susfs_starts_with(iter->name, "apply_kernelsu") ||
susfs_starts_with(iter->name, "handle_sepolicy") ||
susfs_starts_with(iter->name, "getenforce") ||
susfs_starts_with(iter->name, "setenforce") ||
susfs_starts_with(iter->name, "is_zygote"))
{
return 0;
}
seq_printf(m, "%pK %c %s\n", (void *)iter->value,
iter->type, iter->name);
}
#endif
return 0;
}

View File

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

View File

@@ -44,6 +44,13 @@
#define avc_cache_stats_incr(field) do {} while (0)
#endif
#ifdef CONFIG_KSU_SUSFS
extern u32 susfs_ksu_sid;
extern u32 susfs_kernel_sid;
bool susfs_is_avc_log_spoofing_enabled = false;
#endif
struct avc_entry {
u32 ssid;
u32 tsid;
@@ -187,6 +194,16 @@ static void avc_dump_query(struct audit_buffer *ab, struct selinux_state *state,
}
rc = security_sid_to_context(state, tsid, &scontext, &scontext_len);
#ifdef CONFIG_KSU_SUSFS
if (unlikely(tsid == susfs_ksu_sid && susfs_is_avc_log_spoofing_enabled)) {
if (rc)
audit_log_format(ab, " tsid=%d", susfs_kernel_sid);
else
audit_log_format(ab, " tcontext=%s", "u:r:kernel:s0");
goto bypass_orig_flow;
}
#endif
if (rc)
audit_log_format(ab, " tsid=%d", tsid);
else {
@@ -194,6 +211,9 @@ static void avc_dump_query(struct audit_buffer *ab, struct selinux_state *state,
kfree(scontext);
}
#ifdef CONFIG_KSU_SUSFS
bypass_orig_flow:
#endif
BUG_ON(!tclass || tclass >= ARRAY_SIZE(secclass_map));
audit_log_format(ab, " tclass=%s", secclass_map[tclass-1].name);
}