Table of contents
- Embedding KernelSU & Patching SuSFS
- Part 1 — Embedding KernelSU
- Step 1: Check for and Clean an Existing KernelSU Installation
- Step 2: Embed KernelSU
- Step 3: Update the defconfig
- Step 4: Apply the Manual Hook Patch
- Step 5: Build
- Part 2 — Applying the SuSFS Patch
- Part 3 — Manual Patching in Detail
- Part 4 — Creating a Secondary Patch
- Part 5 — Build
- Part 6 — Ambiguous SuSFS Patch Hunks
- security/selinux/avc.c
- Form 1: (tsid, &scontext, &scontext_len)
- Form 2: (state, tsid, &scontext, &scontext_len)
- Form 3: (sad->state, sad->tsid, &scontext, &scontext_len)
- fs/namei.c
- Form 1: Only int generic_readlink(...) exists
- Form 2: Both int generic_readlink(...) and int vfs_readlink(...) exist
- Form 3: Only int vfs_readlink(...) exists
- fs/namespace.c
- static void mnt_free_id(struct mount *mnt)
- static int mnt_alloc_group_id(struct mount *mnt)
- void mnt_release_group_id(struct mount *mnt)
- alloc_vfsmnt(...)
- fs/proc/task_mmu.c
- fs/notify/fdinfo.c
- fs/stat.c
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
Embedding KernelSU & Patching SuSFS
Before pushing a kernel to the automated Action pipeline, it is usually a good idea to verify that KernelSU and SuSFS work correctly with a local build first.
SuSFS is a kernel module developed by simonpunk (GitLab). It provides several concealment features including mount information hiding, AVC audit log suppression, and kernel version spoofing. Because it is applied as a kernel patch rather than a loadable module, it must be integrated manually using patch scripts.
This guide uses ReSukiSU and the Xiaomi Mix2s kernel (evox_mix2s_a15) as the working example.
Part 1 — Embedding KernelSU
Step 1: Check for and Clean an Existing KernelSU Installation
Before embedding, verify whether KernelSU is already present in the kernel source:
- A folder named
KernelSUexists at the source root, or fs/exec.ccontains references toCONFIG_KSU
If either condition is true, clean the existing installation first:
cp NonGKI_Kernel_Build_2nd/bin/clean_hook.sh Kernel/evox_mix2s_a15/
cd Kernel/evox_mix2s_a15/
bash clean_hook.sh
Step 2: Embed KernelSU
Go to the ReSukiSU repository, navigate to the kernel/ folder, open setup.sh, click the Raw button in the top-right corner, and copy the URL.
cd Kernel/evox_mix2s_a15/
curl -LSs "<setup.sh raw URL>" | bash -s <branch-name>
Alternatively, download setup.sh manually, copy it into the kernel source directory, and run:
bash setup.sh <branch-name>
Step 3: Update the defconfig
Open the defconfig file (in this example: arch/arm64/configs/vendor/mi845_defconfig) and append the following lines at the end:
CONFIG_KSU=y
CONFIG_KSU_MANUAL_HOOK=y
If you are using ReSukiSU, you usually also need to add it:
CONFIG_KALLSYMS=y
CONFIG_KALLSYMS_ALL=y
ReSukiSU supports automatic hook patching — feel free to explore that option. It is not covered here.
Step 4: Apply the Manual Hook Patch
Two patch scripts are available — choose based on your setup:
| Script | When to use |
|---|---|
syscall_hook_patches.sh |
KernelSU without SuSFS |
susfs_inline_hook_patches.sh |
KernelSU with SuSFS |
This section covers the KernelSU-only case, so use syscall_hook_patches.sh. If you plan to add SuSFS, use susfs_inline_hook_patches.sh instead (see Part 2).
cp NonGKI_Kernel_Build_2nd/Patches/syscall_hook_patches.sh Kernel/evox_mix2s_a15/
cd Kernel/evox_mix2s_a15/
bash syscall_hook_patches.sh
Important: The script contain detection logic that checks for KernelSU. Always complete Step 2 after running the scripts.
Step 5: Build
Follow the same steps described in the Compiling the Kernel page.
Part 2 — Applying the SuSFS Patch
Due to the highly fragmented nature of Non-GKI kernels, there is no guarantee that SuSFS will work out of the box on every device. The compatibility patches provided here are based on simonpunk's original code and cannot cover every possible situation.
Step 1: Apply the SuSFS Patch File
First, confirm your kernel version.
VERSION = 4
PATCHLEVEL = 9
The naming convention follows the format VERSION.PATCHLEVEL; in this case, it would be 4.9.
If unsure, check the first two lines of the Makefile in the kernel source root.
cp NonGKI_Kernel_Build_2nd/Patches/Patch/susfs_patch_to_<version>.patch Kernel/evox_mix2s_a15/
cd Kernel/evox_mix2s_a15/
patch -p1 < susfs_patch_to_4.9.patch
If the patch applies cleanly with no .rej files, skip ahead to Part 5 — Build.
If you see errors like the following, manual patching is required:
X out of X hunk FAILED -- saving rejects to file XXX/XXX.c.rej
Step 2: Prepare for Manual Patching
Back up the kernel source directory before making any changes:
cp -r Kernel/evox_mix2s_a15 Kernel/evox_mix2s_a15_bak
cd Kernel/evox_mix2s_a15
Using 1 out of 1 hunk FAILED -- saving rejects to file kernel/sys.c.rej as our example, we need to work with both kernel/sys.c and kernel/sys.c.rej.
Open both files side by side in your preferred editor:
- KDE users: Kate is recommended
- General: Zed, VSCode, Fresh Editor, etc.
kate kernel/sys.c.rej kernel/sys.c
Part 3 — Manual Patching in Detail
Understanding the .rej File
A .rej file uses the standard patch format and records the hunks that failed to apply automatically — usually because the surrounding context in the target file does not match what the patch expected.
Example kernel/sys.c.rej:
--- kernel/sys.c
+++ kernel/sys.c
@@ -1240,12 +1240,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);
Lines beginning with + are the new code that needs to be inserted manually. The remaining lines are context used to locate where the insertion should go.
How to Locate the Insertion Point
- Search for a unique function name first —
SYSCALL_DEFINE1(newuname, ...)is almost always unique in the file - If multiple functions share the same name, the full parameter list narrows it down further
- If still unclear, search for a distinctive nearby code fragment such as
if (copy_to_user(name, &tmp, sizeof(tmp))) - Be flexible — exact context match is not required
Worked Example
The actual kernel/sys.c in this example looks like this:
SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name)
{
struct new_utsname tmp;
down_read(&uts_sem);
memcpy(&tmp, utsname(), sizeof(tmp));
if (current_uid().val == 0 &&
(!strncmp(current->comm, "bpfloader", 9) ||
!strncmp(current->comm, "netbpfload", 10) ||
!strncmp(current->comm, "netd", 4) ||
!strncmp(current->comm, "uprobestats", 11))) {
strcpy(tmp.release, "5.10.240");
pr_debug("fake uname: %s release=%s\n",
current->comm, tmp.release);
}
up_read(&uts_sem);
...
}
This example is from a 4.19 kernel which includes extra BPF spoofing code — that is what disrupted the automatic patching.
First insertion — add the extern declaration before the function definition (straightforward, position is unambiguous):
#ifdef CONFIG_KSU_SUSFS_SPOOF_UNAME
extern void susfs_spoof_uname(struct new_utsname* tmp);
#endif
SYSCALL_DEFINE1(newuname, struct new_utsname __user *, name)
{
...
}
Second insertion — insert the function call after memcpy. Ignore the extra BPF block; just find the nearest matching anchor point and insert after memcpy:
After patching, the result looks like this:
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
if (current_uid().val == 0 &&
(!strncmp(current->comm, "bpfloader", 9) ||
!strncmp(current->comm, "netbpfload", 10) ||
!strncmp(current->comm, "netd", 4) ||
!strncmp(current->comm, "uprobestats", 11))) {
strcpy(tmp.release, "5.10.240");
pr_debug("fake uname: %s release=%s\n",
current->comm, tmp.release);
}
up_read(&uts_sem);
...
}
Apply the same approach to all remaining .rej files.
Part 4 — Creating a Secondary Patch
If you need a reusable patch file for the automated Action pipeline, generate a diff against the backup:
git diff -u Kernel/evox_mix2s_a15_bak Kernel/evox_mix2s_a15 > susfs_fixed.patch
Open susfs_fixed.patch. It may look like this:
diff --git a/evox_mix2s_a15_bak/.git/index b/evox_mix2s_a15/.git/index
index 48cdcb2..bf7b3e7 100644
Binary files a/evox_mix2s_a15_bak/.git/index and b/evox_mix2s_a15/.git/index differ
diff --git a/evox_mix2s_a15_bak/kernel/sys.c b/evox_mix2s_a15/kernel/sys.c
index cdb8b16..3b26985 100644
--- a/evox_mix2s_a15_bak/kernel/sys.c
+++ b/evox_mix2s_a15/kernel/sys.c
@@ -1240,12 +1240,18 @@ ...
Two edits are required:
Edit 1 — Delete the first three lines (the .git/index binary diff entry — it serves no purpose here):
diff --git a/evox_mix2s_a15_bak/kernel/sys.c b/evox_mix2s_a15/kernel/sys.c
index cdb8b16..3b26985 100644
--- a/evox_mix2s_a15_bak/kernel/sys.c
+++ b/evox_mix2s_a15/kernel/sys.c
@@ -1240,12 +1240,18 @@ ...
Edit 2 — Strip the directory prefixes from all paths. Remove /evox_mix2s_a15_bak and /evox_mix2s_a15 from every a/ and b/ path so they are relative to the kernel source root:
diff --git a/kernel/sys.c b/kernel/sys.c
index cdb8b16..3b26985 100644
--- a/kernel/sys.c
+++ b/kernel/sys.c
@@ -1240,12 +1240,18 @@ ...
susfs_fixed.patch is now ready for use in the Action automated pipeline.
Finally, remove the backup directory:
rm -rf Kernel/evox_mix2s_a15_bak
Part 5 — Build
Before compiling, append the following SuSFS config entries to the defconfig file:
CONFIG_KSU_SUSFS=y
CONFIG_KSU_SUSFS_SUS_PATH=y
CONFIG_KSU_SUSFS_SUS_MOUNT=y
CONFIG_KSU_SUSFS_SUS_KSTAT=y
CONFIG_KSU_SUSFS_SPOOF_UNAME=y
CONFIG_KSU_SUSFS_ENABLE_LOG=y
CONFIG_KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS=y
CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG=y
CONFIG_KSU_SUSFS_OPEN_REDIRECT=y
CONFIG_KSU_SUSFS_SUS_MAP=y
Then follow the same build steps described in Compiling the Kernel.
Part 6 — Ambiguous SuSFS Patch Hunks
Some hunks require flexible judgement rather than mechanical application of what the patch file specifies. This section documents known cases where the correct fix depends on the kernel version.
security/selinux/avc.c
Locate the rc = security_sid_to_context(...) call and identify which parameter form is present. Choose the matching fix below.
Form 1: (tsid, &scontext, &scontext_len)
Common in: Kernel 4.9
The state parameter is absent, so no sad variable declaration is needed. Insert the following block at the appropriate location:
rc = security_sid_to_context(tsid, &scontext, &scontext_len);
#ifdef CONFIG_KSU_SUSFS
if (static_branch_likely(&susfs_avc_log_spoofing_key_true)) {
if (unlikely(tsid == susfs_ksu_sid)) {
if (rc)
audit_log_format(ab, " tsid=%d", susfs_priv_app_sid);
else
audit_log_format(ab, " tcontext=%s", "u:r:priv_app:s0:c512,c768");
goto bypass_orig_flow;
}
}
#endif
Form 2: (state, tsid, &scontext, &scontext_len)
Common in: Kernels 4.14 — 4.19
An incomplete state parameter is present, so a sad variable must be declared manually.
Step 1 — Add the declaration below u32 scontext_len;:
u32 scontext_len;
#ifdef CONFIG_KSU_SUSFS
struct selinux_audit_data sad;
#endif
Step 2 — Insert the following block at the appropriate location:
rc = security_sid_to_context(state, tsid, &scontext, &scontext_len);
#ifdef CONFIG_KSU_SUSFS
if (static_branch_likely(&susfs_avc_log_spoofing_key_true)) {
if (unlikely(sad.tsid == susfs_ksu_sid)) {
if (rc)
audit_log_format(ab, " tsid=%d", susfs_priv_app_sid);
else
audit_log_format(ab, " tcontext=%s", "u:r:priv_app:s0:c512,c768");
goto bypass_orig_flow;
}
}
#endif
Form 3: (sad->state, sad->tsid, &scontext, &scontext_len)
Common in: Kernel 5.4 and newer
This is the complete state definition. Apply the upstream change directly:
rc = security_sid_to_context(sad->state, sad->tsid, &scontext,
&scontext_len);
#ifdef CONFIG_KSU_SUSFS
if (static_branch_likely(&susfs_avc_log_spoofing_key_true)) {
if (unlikely(sad->tsid == susfs_ksu_sid)) {
if (rc)
audit_log_format(ab, " tsid=%d", susfs_priv_app_sid);
else
audit_log_format(ab, " tcontext=%s", "u:r:priv_app:s0:c512,c768");
goto bypass_orig_flow;
}
}
#endif
fs/namei.c
The upstream patch targets vfs_readlink, the res = readlink_copy(buffer, buflen, link); statement, and the if (unlikely(!(inode->i_opflags & IOP_DEFAULT_READLINK))) {...} check. However, older kernels may use different function names or be missing functions entirely — choose the correct fix based on what is actually present in your source tree.
Form 1: Only int generic_readlink(...) exists
Common in: Kernel 4.9
generic_readlink is effectively a partial implementation of vfs_readlink. Since only res = readlink_copy(buffer, buflen, link); is present, insert the following block immediately before that statement:
#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT
if (SUSFS_IS_INODE_OPEN_REDIRECT(inode)) {
res = susfs_open_redirect_spoof_vfs_readlink(inode, buffer, buflen);
if (!res) {
do_delayed_call(&done);
return res;
}
}
#endif
res = readlink_copy(buffer, buflen, link);
Form 2: Both int generic_readlink(...) and int vfs_readlink(...) exist
Common in: Kernel 4.14
The vfs_readlink here contains return generic_readlink(dentry, buffer, buflen); and still delegates to generic_readlink. After applying the Form 1 patch to generic_readlink, also replace the entire vfs_readlink body with the following:
int vfs_readlink(struct dentry *dentry, char __user *buffer, int buflen)
{
struct inode *inode = d_inode(dentry);
#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT
int res;
#endif
if (unlikely(!(inode->i_opflags & IOP_DEFAULT_READLINK))) {
if (unlikely(inode->i_op->readlink))
#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT
{
if (SUSFS_IS_INODE_OPEN_REDIRECT(inode)) {
res = susfs_open_redirect_spoof_vfs_readlink(inode, buffer, buflen);
if (!res)
return res;
}
return inode->i_op->readlink(dentry, buffer, buflen);
}
#else
return inode->i_op->readlink(dentry, buffer, buflen);
#endif
if (!d_is_symlink(dentry))
return -EINVAL;
spin_lock(&inode->i_lock);
inode->i_opflags |= IOP_DEFAULT_READLINK;
spin_unlock(&inode->i_lock);
}
return generic_readlink(dentry, buffer, buflen);
}
EXPORT_SYMBOL(vfs_readlink);
Form 3: Only int vfs_readlink(...) exists
Common in: Kernel 4.19 and newer
A complete vfs_readlink definition is present. Apply the upstream SuSFS patch directly — add the extern declaration before the function and replace the body with the following:
#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT
extern int susfs_open_redirect_spoof_vfs_readlink(struct inode *inode, char __user *buffer, int buflen);
#endif
int vfs_readlink(struct dentry *dentry, char __user *buffer, int buflen)
{
struct inode *inode = d_inode(dentry);
DEFINE_DELAYED_CALL(done);
const char *link;
int res;
if (unlikely(!(inode->i_opflags & IOP_DEFAULT_READLINK))) {
if (unlikely(inode->i_op->readlink))
#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT
{
if (SUSFS_IS_INODE_OPEN_REDIRECT(inode)) {
res = susfs_open_redirect_spoof_vfs_readlink(inode, buffer, buflen);
if (!res)
return res;
}
return inode->i_op->readlink(dentry, buffer, buflen);
}
#else
return inode->i_op->readlink(dentry, buffer, buflen);
#endif
if (!d_is_symlink(dentry))
return -EINVAL;
spin_lock(&inode->i_lock);
inode->i_opflags |= IOP_DEFAULT_READLINK;
spin_unlock(&inode->i_lock);
}
link = READ_ONCE(inode->i_link);
if (!link) {
link = inode->i_op->get_link(dentry, inode, &done);
if (IS_ERR(link))
return PTR_ERR(link);
}
#ifdef CONFIG_KSU_SUSFS_OPEN_REDIRECT
if (SUSFS_IS_INODE_OPEN_REDIRECT(inode)) {
res = susfs_open_redirect_spoof_vfs_readlink(inode, buffer, buflen);
if (!res) {
do_delayed_call(&done);
return res;
}
}
#endif
res = readlink_copy(buffer, buflen, link);
do_delayed_call(&done);
return res;
}
EXPORT_SYMBOL(vfs_readlink);
fs/namespace.c
This file has two pairs of function name differences that typically appear together:
| Older API (4.9 — 4.14) | Newer API (4.19+) |
|---|---|
ida_remove |
ida_free |
ida_pre_get + ida_get_new_above |
ida_alloc_min |
Note
: If the kernel does not support
ida_alloc_min, replace everyida_alloc_mincall inside SuSFS-related code withida_simple_get. For example, replaceres = ida_alloc_min(&susfs_mnt_id_ida, DEFAULT_KSU_MNT_ID, GFP_KERNEL);withres = ida_simple_get(&susfs_mnt_id_ida, DEFAULT_KSU_MNT_ID, 0, GFP_KERNEL);.
static void mnt_free_id(struct mount *mnt)
If ida_remove is present (kernels 4.9 — 4.14):
static void mnt_free_id(struct mount *mnt)
{
int id = mnt->mnt_id;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
if (id >= DEFAULT_KSU_MNT_ID) {
spin_lock(&mnt_id_lock);
ida_remove(&susfs_mnt_id_ida, id);
if (mnt_id_start > id)
mnt_id_start = id;
spin_unlock(&mnt_id_lock);
return;
}
if (mnt->mnt.mnt_flags & VFSMOUNT_MNT_FLAGS_KSU_UNSHARED_MNT) {
return;
}
#endif // #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
spin_lock(&mnt_id_lock);
ida_remove(&mnt_id_ida, id);
if (mnt_id_start > id)
mnt_id_start = id;
spin_unlock(&mnt_id_lock);
}
If ida_free is present (kernels 4.19+):
static void mnt_free_id(struct mount *mnt)
{
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
if (mnt->mnt_id >= DEFAULT_KSU_MNT_ID) {
ida_free(&susfs_mnt_id_ida, mnt->mnt_id);
return;
}
if (mnt->mnt.mnt_flags & VFSMOUNT_MNT_FLAGS_KSU_UNSHARED_MNT) {
return;
}
#endif // #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
ida_free(&mnt_id_ida, mnt->mnt_id);
}
static int mnt_alloc_group_id(struct mount *mnt)
If ida_pre_get is present (kernels 4.9 — 4.14):
static int mnt_alloc_group_id(struct mount *mnt)
{
int res;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
if (susfs_is_current_ksu_domain()) {
if (!ida_pre_get(&susfs_mnt_group_ida, GFP_KERNEL))
return -ENOMEM;
res = ida_get_new_above(&susfs_mnt_group_ida,
DEFAULT_KSU_MNT_GROUP_ID,
&mnt->mnt_group_id);
goto bypass_orig_flow;
}
if (!ida_pre_get(&mnt_group_ida, GFP_KERNEL))
return -ENOMEM;
res = ida_get_new_above(&mnt_group_ida,
mnt_group_start,
&mnt->mnt_group_id);
bypass_orig_flow:
#else
if (!ida_pre_get(&mnt_group_ida, GFP_KERNEL))
return -ENOMEM;
res = ida_get_new_above(&mnt_group_ida,
mnt_group_start,
&mnt->mnt_group_id);
#endif
if (!res)
mnt_group_start = mnt->mnt_group_id + 1;
return res;
}
If ida_alloc_min is present (kernels 4.19+):
static int mnt_alloc_group_id(struct mount *mnt)
{
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
int res;
/* - mnt_alloc_group_id will unlikely get called after screen is unlocked on reboot,
* so here we can persistently check if current is ksu domain, and assign a sus
* mnt_group_id if so.
* - Also we can re-use the original mnt_group_ida so there is no need to use
* another ida nor hook the mnt_release_group_id() function.
*/
if (susfs_is_current_ksu_domain()) {
res = ida_alloc_min(&susfs_mnt_group_ida, DEFAULT_KSU_MNT_GROUP_ID, GFP_KERNEL);
goto bypass_orig_flow;
}
res = ida_alloc_min(&mnt_group_ida, 1, GFP_KERNEL);
bypass_orig_flow:
#else
int res = ida_alloc_min(&mnt_group_ida, 1, GFP_KERNEL);
#endif
if (res < 0)
return res;
mnt->mnt_group_id = res;
return 0;
}
void mnt_release_group_id(struct mount *mnt)
If ida_remove is present (kernels 4.9 — 4.14):
void mnt_release_group_id(struct mount *mnt)
{
int id = mnt->mnt_group_id;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
if (id >= DEFAULT_KSU_MNT_GROUP_ID) {
ida_remove(&susfs_mnt_group_ida, id);
if (mnt_group_start > id)
mnt_group_start = id;
mnt->mnt_group_id = 0;
return;
}
#endif // #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
ida_remove(&mnt_group_ida, id);
if (mnt_group_start > id)
mnt_group_start = id;
mnt->mnt_group_id = 0;
}
If ida_free is present (kernels 4.19+):
void mnt_release_group_id(struct mount *mnt)
{
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
if (mnt->mnt_group_id >= DEFAULT_KSU_MNT_GROUP_ID) {
ida_free(&susfs_mnt_group_ida, mnt->mnt_group_id);
mnt->mnt_group_id = 0;
return;
}
#endif // #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
ida_free(&mnt_group_ida, mnt->mnt_group_id);
mnt->mnt_group_id = 0;
}
alloc_vfsmnt(...)
If the function in alloc_vfsmnt(...); is name::
struct vfsmount *
vfs_kern_mount(struct file_system_type *type, int flags, const char *name, void *data)
{
struct mount *mnt;
struct dentry *root;
if (!type)
return ERR_PTR(-ENODEV);
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
// - We will just stop checking for ksu process if /sdcard/Android is accessible,
// for the sake of performance
if (static_branch_unlikely(&susfs_set_sdcard_android_data_decrypted_key_false)) {
if (susfs_is_current_ksu_domain()) {
mnt = susfs_alloc_non_unshare_ksu_vfsmnt(name ?:"none");
goto bypass_orig_flow;
}
}
#endif
mnt = alloc_vfsmnt(name);
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
bypass_orig_flow:
#endif
if (!mnt)
return ERR_PTR(-ENOMEM);
If the function in alloc_vfsmnt(...); is fc->source ?: "none":
struct vfsmount *vfs_create_mount(struct fs_context *fc)
{
struct mount *mnt;
struct super_block *sb;
if (!fc->root)
return ERR_PTR(-EINVAL);
sb = fc->root->d_sb;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
// - We will just stop checking for ksu process if /sdcard/Android is accessible,
// for the sake of performance
if (static_branch_unlikely(&susfs_set_sdcard_android_data_decrypted_key_false)) {
if (susfs_is_current_ksu_domain()) {
mnt = susfs_alloc_non_unshare_ksu_vfsmnt(fc->source ?: "none");
goto bypass_orig_flow;
}
}
#endif // #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
mnt = alloc_vfsmnt(fc->source ?: "none");
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
bypass_orig_flow:
#endif // #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
if (!mnt)
return ERR_PTR(-ENOMEM);
fs/proc/task_mmu.c
- If
seq_put_hex_llis present in the file, apply the 4.19 — 5.4 patch variant - If it is absent, refer to the 4.9 — 4.14 patch variant instead
fs/notify/fdinfo.c
Some older kernels are missing the inotify_mark_user_mask function.
The key concern when patching this file is the seq_printf output: the function calls inside the SuSFS injection block and in the original flow must be identical, but the path.dentry references must be kept as-is — they are what SuSFS uses to spoof the reported path.
If your kernel has inotify_mark_user_mask, apply the following patch directly:
#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;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
struct mount *mnt = NULL;
#endif
if (!(mark->flags & FSNOTIFY_MARK_FLAG_ALIVE) ||
!(mark->flags & FSNOTIFY_MARK_FLAG_INODE))
return;
inode_mark = container_of(mark, struct inotify_inode_mark, fsn_mark);
inode = igrab(mark->inode);
if (inode) {
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
mnt = real_mount(file->f_path.mnt);
if (mnt->mnt_id >= DEFAULT_KSU_MNT_ID &&
likely(susfs_is_current_proc_umounted()))
{
struct path path;
char *pathname = kmalloc(PAGE_SIZE, GFP_KERNEL);
char *dpath;
if (!pathname) {
goto orig_flow;
}
dpath = d_path(&file->f_path, pathname, PAGE_SIZE);
if (!dpath) {
goto out_kfree;
}
if (kern_path(dpath, 0, &path)) {
goto out_kfree;
}
if (!path.dentry->d_inode) {
goto out_path_put;
}
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');
path_put(&path);
kfree(pathname);
iput(inode);
return;
out_path_put:
path_put(&path);
out_kfree:
kfree(pathname);
}
orig_flow:
#endif // #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
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));
show_mark_fhandle(m, inode);
seq_putc(m, '\n');
iput(inode);
}
}
If the kernel is missing inotify_mark_user_mask, replace every call to that function in the patch with whatever equivalent expression your kernel actually provides. Keep the format string and argument structure of both seq_printf calls consistent with each other, and leave all path.dentry references unchanged.
#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;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
struct mount *mnt = NULL;
#endif
if (!(mark->flags & FSNOTIFY_MARK_FLAG_ALIVE) ||
!(mark->flags & FSNOTIFY_MARK_FLAG_INODE))
return;
inode_mark = container_of(mark, struct inotify_inode_mark, fsn_mark);
inode = igrab(mark->inode);
if (inode) {
/*
* IN_ALL_EVENTS represents all of the mask bits
* that we expose to userspace. There is at
* least one bit (FS_EVENT_ON_CHILD) which is
* used only internally to the kernel.
*/
u32 mask = mark->mask & IN_ALL_EVENTS;
#ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
mnt = real_mount(file->f_path.mnt);
if (mnt->mnt_id >= DEFAULT_KSU_MNT_ID &&
likely(susfs_is_current_proc_umounted()))
{
struct path path;
char *pathname = kmalloc(PAGE_SIZE, GFP_KERNEL);
char *dpath;
if (!pathname) {
goto orig_flow;
}
dpath = d_path(&file->f_path, pathname, PAGE_SIZE);
if (!dpath) {
goto out_kfree;
}
if (kern_path(dpath, 0, &path)) {
goto out_kfree;
}
if (!path.dentry->d_inode) {
goto out_path_put;
}
seq_printf(m, "inotify wd:%x ino:%lx sdev:%x mask:%x ignored_mask:%x ",
inode_mark->wd, path.dentry->d_inode->i_ino, path.dentry->d_inode->i_sb->s_dev,
mask, mark->ignored_mask);
show_mark_fhandle(m, path.dentry->d_inode);
seq_putc(m, '\n');
path_put(&path);
kfree(pathname);
iput(inode);
return;
out_path_put:
path_put(&path);
out_kfree:
kfree(pathname);
}
orig_flow:
#endif // #ifdef CONFIG_KSU_SUSFS_SUS_MOUNT
seq_printf(m, "inotify wd:%x ino:%lx sdev:%x mask:%x ignored_mask:%x ",
inode_mark->wd, inode->i_ino, inode->i_sb->s_dev,
mask, mark->ignored_mask);
show_mark_fhandle(m, inode);
seq_putc(m, '\n');
iput(inode);
}
}
fs/stat.c
Some older kernels are missing the request_mask function. (Often done with vfs_statx backporting.)
The core controversy of the fix is the function implementation of getattr, which is essentially just consistent with the system.
If your kernel has request_mask, apply the following patch directly:
int vfs_getattr_nosec(const struct path *path, struct kstat *stat,
u32 request_mask, unsigned int query_flags)
{
struct inode *inode = d_backing_inode(path->dentry);
memset(stat, 0, sizeof(*stat));
stat->result_mask |= STATX_BASIC_STATS;
request_mask &= STATX_ALL;
query_flags &= KSTAT_QUERY_FLAGS;
if (inode->i_op->getattr)
#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT
{
int err = inode->i_op->getattr(path, stat, request_mask,
query_flags);
if (!err)
susfs_sus_kstat_spoof_generic_fillattr(inode, stat);
return err;
}
#else
return inode->i_op->getattr(path, stat, request_mask,
query_flags);
#endif
generic_fillattr(inode, stat);
return 0;
}
EXPORT_SYMBOL(vfs_getattr_nosec);
Otherwise remove the code involving the request_mask function and replace it with the kernel's old expression.
int vfs_getattr_nosec(struct path *path, struct kstat *stat)
{
struct inode *inode = d_backing_inode(path->dentry);
if (inode->i_op->getattr)
#ifdef CONFIG_KSU_SUSFS_SUS_KSTAT
{
int err = inode->i_op->getattr(path->mnt, path->dentry, stat);
if (!err)
susfs_sus_kstat_spoof_generic_fillattr(inode, stat);
return err;
}
#else
return inode->i_op->getattr(path->mnt, path->dentry, stat);
#endif
generic_fillattr(inode, stat);
return 0;
}
🌐 Language / 语言
📖 English
Local Build
- Setting Up the Environment
- Choosing a Toolchain
- Compiling the Kernel
- Compiling on a Mobile Device
- Compiling the Stock Vendor Kernel
- Embedding KernelSU & Patching SuSFS
- Packaging and Flashing
- Kernel Build FAQ
Build with Actions
- Fork the Repository
- Configuring the YML File
- Variable Reference
- Running and Downloading Artifacts
- Automated Release
Hide Root Trace
📖 中文
基础编译
利用 Action 构建内核
隐藏Root痕迹