en Embedding KernelSU and Patching SuSFS - JackA1ltman/NonGKI_Kernel_Build_2nd GitHub Wiki

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 KernelSU exists at the source root, or
  • fs/exec.c contains references to CONFIG_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

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/
cp NonGKI_Kernel_Build_2nd/Patches/backport_patches.sh Kernel/evox_mix2s_a15/
cd Kernel/evox_mix2s_a15/
bash syscall_hook_patches.sh
bash backport_patches.sh

The two scripts can be run in either order: syscall_hook_patches.sh injects the hook points, and backport_patches.sh backports certain kernel functions.

Important: Both scripts contain detection logic that checks for KernelSU. Always complete Step 2 before 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 every ida_alloc_min call inside SuSFS-related code with ida_simple_get. For example, replace res = ida_alloc_min(&susfs_mnt_id_ida, DEFAULT_KSU_MNT_ID, GFP_KERNEL); with res = 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_ll is 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);
	}
}
⚠️ **GitHub.com Fallback** ⚠️