Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions kernel/Kbuild
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ endif
ifeq ($(CONFIG_KSU_DEBUG),y)
ccflags-y += -DCONFIG_KSU_DEBUG=1
endif
ifeq ($(CONFIG_KSU_X86_PATCH_SYSCALL_DISPATCHER),y)
ccflags-y += -DCONFIG_KSU_X86_PATCH_SYSCALL_DISPATCHER=1
endif
endif

ccflags-y += -I$(srctree)/security/selinux -I$(srctree)/security/selinux/include
Expand Down
8 changes: 8 additions & 0 deletions kernel/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,12 @@ config KSU_DISABLE_POLICY
Escalation will always use the default full root profile, and
non-root handling will follow the global umount policy only.

config KSU_X86_PATCH_SYSCALL_DISPATCHER
bool "Dynamically patch x64's hardened syscall dispatcher to support syscall hooks"
depends on KSU && X86_64
default n
help
Dynamically patch x64's hardened syscall dispatcher to support syscall hooks. This is a
replacement for a kernel source code patch, and is useful for x86_64 LKM mode.

endmenu
4 changes: 2 additions & 2 deletions kernel/core/init.c
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
#include "feature/selinux_hide.h"
#include "infra/symbol_resolver.h"

#if defined(__x86_64__)
#if defined(__x86_64__) && !defined(CONFIG_KSU_X86_PATCH_SYSCALL_DISPATCHER)
#include <asm/cpufeature.h>
#include <linux/version.h>
#ifndef X86_FEATURE_INDIRECT_SAFE
Expand Down Expand Up @@ -85,7 +85,7 @@ module_param_named(norc, ksu_no_custom_rc, bool, 0);

int __init kernelsu_init(void)
{
#if defined(__x86_64__)
#if defined(__x86_64__) && !defined(CONFIG_KSU_X86_PATCH_SYSCALL_DISPATCHER)
// If the kernel has the hardening patch, X86_FEATURE_INDIRECT_SAFE must be set
if (!boot_cpu_has(X86_FEATURE_INDIRECT_SAFE)) {
pr_alert("*************************************************************");
Expand Down
2 changes: 1 addition & 1 deletion kernel/hook/patch_memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
#include "asm/insn.h" // IWYU pragma: keep
#endif
#elif __x86_64__
#include "asm/text-patching.h" // IWYU pragma: keep
#include <asm/ptrace.h>
#else
#error "Unsupported arch"
#endif
Expand Down
132 changes: 130 additions & 2 deletions kernel/hook/x86_64/syscall_hook.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include <linux/kallsyms.h>
#include <linux/mutex.h>
#include <linux/nospec.h>
#include <asm/cacheflush.h>
#include "infra/symbol_resolver.h"
#include "../patch_memory.h"
Expand All @@ -13,6 +14,10 @@
sys_call_ptr_t *ksu_syscall_table = NULL;
int ksu_dispatcher_nr = -1;

#ifndef __NR_syscalls
#define __NR_syscalls (__NR_syscall_max + 1)
#endif
Comment thread
5ec1cff marked this conversation as resolved.

// Hook registration table — read with READ_ONCE from tracepoint/dispatcher
// context, written with WRITE_ONCE from init/exit context.
static ksu_syscall_hook_fn syscall_hooks[__NR_syscalls];
Expand Down Expand Up @@ -198,18 +203,120 @@ bool ksu_has_syscall_hook(int nr)
return READ_ONCE(syscall_hooks[nr]) != NULL;
}

void __init ksu_syscall_hook_init(void)
// https://github.com/torvalds/linux/commit/1e3ad78334a69b36e107232e337f9d693dcc9df2
// harden syscall table was introduced in 6.9, but it was backported to almost
// all of GKI kernel except 5.10
#ifdef CONFIG_KSU_X86_PATCH_SYSCALL_DISPATCHER
static void *x64_sys_call_patch_addr;
static char x64_sys_call_patch_orig_insn[14];

static long my_x64_sys_call(const struct pt_regs *regs, unsigned int nr)
{
return ksu_syscall_table[nr](regs);
}
Comment thread
5ec1cff marked this conversation as resolved.

// avd 13-5.15 missing this commit:
// https://github.com/torvalds/linux/commit/fb13b11d53875e28e7fbf0c26b288e4ea676aa9f
// we need to patch the whole do_syscall_64
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0)
static void *do_syscall_64_patch_addr;
static char do_syscall_64_orig_insn[14];

static long (*syscall_enter_from_user_mode_fn)(struct pt_regs *regs, long syscall);
static void (*syscall_exit_to_user_mode_fn)(struct pt_regs *regs);

static __always_inline bool my_do_syscall_x64(struct pt_regs *regs, int nr)
{
/*
* Convert negative numbers to very high and thus out of range
* numbers for comparisons.
*/
unsigned int unr = nr;

if (likely(unr < NR_syscalls)) {
unr = array_index_nospec(unr, NR_syscalls);
regs->ax = ksu_syscall_table[unr](regs);
return true;
}
return false;
}

static void __nocfi my_do_syscall_64(struct pt_regs *regs, int nr)
{
nr = syscall_enter_from_user_mode_fn(regs, nr);
nr = syscall_get_nr(current, regs);

// AVD doesn't have x32 support after A13, we can ignore do_syscall_x32

if (!my_do_syscall_x64(regs, nr) && nr != -1) {
/* Invalid system call, but still a system call. */
regs->ax = -ENOSYS;
}

syscall_exit_to_user_mode_fn(regs);
}
#endif
#endif

static void patch_abs_jump(const char *sym, void **patch_addr, void *target, char backup[14])
{
*patch_addr = (void *)find_kernel_symbol_exact(sym);
pr_info("%s=0x%lx\n", sym, (unsigned long)*patch_addr);
if (*patch_addr) {
pr_info("patching %s\n", sym);
// skip endbr64
static const char endbr64_insn[] = {
// clang-format off
0xf3, 0x0f, 0x1e, 0xfa
// clang-format on
};
if (memcmp((void *)*patch_addr, endbr64_insn, sizeof(endbr64_insn)) == 0) {
pr_info("%s: skip endbr64\n", sym);
*patch_addr = (void *)((char *)(*patch_addr) + 4);
}
// clang-format off
char buf[] = {
// jmp *(%rip) = addr
0xff, 0x25, 0x00, 0x00, 0x00, 0x00,
// addr: .quad 0
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
};
// clang-format on
*((void **)(buf + 6)) = target;
memcpy(backup, *patch_addr, sizeof(buf));
int ret = ksu_patch_text(*patch_addr, buf, sizeof(buf), KSU_PATCH_TEXT_FLUSH_ICACHE);
if (ret) {
pr_err("patch %s err: %d\n", sym, ret);
*patch_addr = NULL;
}
}
}

void __init __nocfi ksu_syscall_hook_init(void)
{
int ni_slot;

memset(syscall_hooks, 0, sizeof(syscall_hooks));

ksu_syscall_table = (sys_call_ptr_t *)ksu_resolve_symbol_for_functable_hook("sys_call_table");
pr_info("sys_call_table=0x%lx", (unsigned long)ksu_syscall_table);
pr_info("sys_call_table=0x%lx\n", (unsigned long)ksu_syscall_table);

if (!ksu_syscall_table)
return;

#ifdef CONFIG_KSU_X86_PATCH_SYSCALL_DISPATCHER
patch_abs_jump("x64_sys_call", &x64_sys_call_patch_addr, my_x64_sys_call, x64_sys_call_patch_orig_insn);
#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0)
syscall_enter_from_user_mode_fn = find_kernel_symbol_exact("syscall_enter_from_user_mode");
syscall_exit_to_user_mode_fn = find_kernel_symbol_exact("syscall_exit_to_user_mode");
pr_info("syscall_enter_from_user_mode: 0x%lx, syscall_exit_to_user_mode: 0x%lx\n",
(unsigned long)syscall_enter_from_user_mode_fn, (unsigned long)syscall_exit_to_user_mode_fn);
if (syscall_enter_from_user_mode_fn && syscall_exit_to_user_mode_fn) {
patch_abs_jump("do_syscall_64", &do_syscall_64_patch_addr, my_do_syscall_64, do_syscall_64_orig_insn);
}
#endif
#endif

// Find one ni_syscall slot for the dispatcher
if (ksu_find_ni_syscall_slots(&ni_slot, 1) < 1) {
pr_err("failed to find ni_syscall slot for dispatcher\n");
Expand All @@ -225,6 +332,27 @@ void __exit ksu_syscall_hook_exit(void)
{
int i;

#ifdef CONFIG_KSU_X86_PATCH_SYSCALL_DISPATCHER
int ret;
if (x64_sys_call_patch_addr) {
ret = ksu_patch_text((void *)x64_sys_call_patch_addr, x64_sys_call_patch_orig_insn,
sizeof(x64_sys_call_patch_orig_insn), KSU_PATCH_TEXT_FLUSH_ICACHE);
if (ret) {
pr_err("restore x64_sys_call err: %d\n", ret);
}
}

#if LINUX_VERSION_CODE < KERNEL_VERSION(5, 16, 0)
if (do_syscall_64_patch_addr) {
ret = ksu_patch_text((void *)do_syscall_64_patch_addr, do_syscall_64_orig_insn, sizeof(do_syscall_64_orig_insn),
KSU_PATCH_TEXT_FLUSH_ICACHE);
if (ret) {
pr_err("restore x64_sys_call err: %d\n", ret);
}
}
#endif
#endif

if (!ksu_syscall_table)
goto clear_state;

Expand Down
1 change: 1 addition & 0 deletions website/docs/.vitepress/locales/id_ID.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ function sidebarGuide() {
{ text: 'Metamodule', link: '/id_ID/guide/metamodule.md' },
{ text: 'Konfigurasi Modul', link: '/id_ID/guide/module-config.md' },
{ text: 'Antisipasi dari bootloop', link: '/id_ID/guide/rescue-from-bootloop.md' },
{ text: 'Dukungan x86_64', link: '/id_ID/guide/x86_64-support' },
{ text: 'FAQ', link: '/id_ID/guide/faq' },
]
}
Expand Down
1 change: 1 addition & 0 deletions website/docs/.vitepress/locales/ja_JP.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ function sidebarGuide() {
{ text: 'メタモジュール', link: '/ja_JP/guide/metamodule.md' },
{ text: 'モジュール設定', link: '/ja_JP/guide/module-config.md' },
{ text: 'ブートループからの復旧', link: '/ja_JP/guide/rescue-from-bootloop.md' },
{ text: 'x86_64 サポート', link: '/ja_JP/guide/x86_64-support' },
{ text: 'よくある質問', link: '/ja_JP/guide/faq' },
{ text: '隠し機能', link: '/ja_JP/guide/hidden-features' },
]
Expand Down
1 change: 1 addition & 0 deletions website/docs/.vitepress/locales/pt_BR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ function sidebarGuide() {
{ text: 'Configuração de Módulo', link: '/pt_BR/guide/module-config.md' },
{ text: 'Perfil do Aplicativo', link: '/pt_BR/guide/app-profile.md' },
{ text: 'Resgate do bootloop', link: '/pt_BR/guide/rescue-from-bootloop.md' },
{ text: 'Suporte a x86_64', link: '/pt_BR/guide/x86_64-support' },
{ text: 'Perguntas frequentes', link: '/pt_BR/guide/faq' },
{ text: 'Recursos ocultos', link: '/pt_BR/guide/hidden-features' },
]
Expand Down
1 change: 1 addition & 0 deletions website/docs/.vitepress/locales/ru_RU.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ function sidebarGuide() {
{ text: 'Конфигурация модулей', link: '/ru_RU/guide/module-config.md' },
{ text: 'Профиль приложений', link: '/ru_RU/guide/app-profile.md' },
{ text: 'Выход из циклической загрузки', link: '/ru_RU/guide/rescue-from-bootloop.md' },
{ text: 'Поддержка x86_64', link: '/ru_RU/guide/x86_64-support' },
{ text: 'FAQ', link: '/ru_RU/guide/faq' },
{ text: 'Скрытые возможности', link: '/ru_RU/guide/hidden-features' },
]
Expand Down
1 change: 1 addition & 0 deletions website/docs/.vitepress/locales/vi_VN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ function sidebarGuide() {
{ text: 'Thiết bị hỗ trợ không chính thức', link: '/vi_VN/guide/unofficially-support-devices.md' },
{ text: 'Metamodule', link: '/vi_VN/guide/metamodule.md' },
{ text: 'Cấu hình module', link: '/vi_VN/guide/module-config.md' },
{ text: 'Hỗ trợ x86_64', link: '/vi_VN/guide/x86_64-support' },
{ text: 'FAQ - Câu hỏi thường gặp', link: '/vi_VN/guide/faq' },
]
}
Expand Down
1 change: 1 addition & 0 deletions website/docs/.vitepress/locales/zh_CN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ function sidebarGuide() {
{ text: '模块配置', link: '/zh_CN/guide/module-config.md' },
{ text: 'App Profile', link: '/zh_CN/guide/app-profile.md' },
{ text: '救砖', link: '/zh_CN/guide/rescue-from-bootloop.md' },
{ text: 'x86_64 支持', link: '/zh_CN/guide/x86_64-support' },
{ text: '常见问题', link: '/zh_CN/guide/faq' },
{ text: '隐藏功能', link: '/zh_CN/guide/hidden-features' },
]
Expand Down
1 change: 1 addition & 0 deletions website/docs/.vitepress/locales/zh_TW.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ function sidebarGuide() {
{ text: '模組配置', link: '/zh_TW/guide/module-config.md' },
{ text: 'App Profile', link: '/zh_TW/guide/app-profile.md' },
{ text: '搶救開機迴圈', link: '/zh_TW/guide/rescue-from-bootloop.md' },
{ text: 'x86_64 支援', link: '/zh_TW/guide/x86_64-support' },
{ text: '常見問題', link: '/zh_TW/guide/faq' },
{ text: '隱藏功能', link: '/zh_TW/guide/hidden-features' },
]
Expand Down
35 changes: 29 additions & 6 deletions website/docs/guide/x86_64-support.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,41 @@
# x86_64 Support

KernelSU fully supports the `x86_64` architecture. However, due to recent upstream kernel security changes, integrating KernelSU on modern `x86_64` kernels requires manual patching to allow our unified syscall dispatcher to function correctly.
KernelSU fully supports the `x86_64` architecture. However, due to recent upstream kernel security changes, integrating KernelSU on modern `x86_64` kernels requires additional handling so our unified syscall dispatcher can function correctly.

## Why did it break?

In newer kernel versions, a [commit](https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=1e3ad78334a69b36e107232e337f9d693dcc9df2) was introduced to harden the syscall table. This change converted indirect branches within the system call path into a series of direct conditional branches.

KernelSU's `syscall_hook` mechanism relies on an indirect jump to route intercepted system calls to our unified dispatcher. Because the new hardening forces direct calls, the kernel blocks our dispatcher. If KernelSU attempts to load and hook the syscall table without the proper mitigations disabled, it will fail to route the calls and cleanly abort initialization to prevent a kernel panic.
KernelSU's `syscall_hook` mechanism relies on modifying entries in the syscall table so intercepted system calls can be routed to our unified dispatcher. Because the new hardening changes the syscall path, the kernel ignores those syscall table modifications. If KernelSU attempts to load and hook the syscall table without handling this limitation correctly, it will fail to route the calls and cleanly abort initialization to prevent a kernel panic.

## How to fix it?

To make KernelSU work on these newer kernels, you must apply a patch that allows you to bypass this specific syscall hardening.
There are two supported ways to handle this syscall hook issue on `x86_64`:

1. Enable the kernel build option `KSU_X86_PATCH_SYSCALL_DISPATCHER`.
2. Continue using the original kernel source patch method.

You only need to use one of them. Do not apply both at the same time.

### Option 1: Enable `KSU_X86_PATCH_SYSCALL_DISPATCHER`

KernelSU 3.2.6 introduced an official mechanism for `x86_64`: the build option `KSU_X86_PATCH_SYSCALL_DISPATCHER`.

When this option is enabled, KernelSU dynamically patches the hardened syscall dispatcher at runtime so the syscall hook can work without requiring the previous kernel source patch set. This is the recommended approach if you are building a kernel with KernelSU 3.2.6 or newer.

### Option 2: Apply the original kernel source patches

If you do not want to enable `KSU_X86_PATCH_SYSCALL_DISPATCHER`, you can continue to use the original kernel patch approach.

To make KernelSU work on these newer kernels, apply a patch that allows you to bypass this specific syscall hardening.

::: danger SECURITY WARNING
By applying these patches and disabling syscall hardening, you are intentionally bypassing a mitigation designed to protect against speculative execution vulnerabilities.
By using either of these two solutions, you are intentionally bypassing or weakening a mitigation designed to protect against speculative execution vulnerabilities.

This re-opens the indirect branch attack surface for system calls. **Do not apply these patches if you are running a production server or a system where strict side-channel security is critical.** This is intended for testing environments where root access via KernelSU is prioritized over this specific hardware vulnerability mitigation.
This re-opens the indirect branch attack surface for system calls. **Do not use either solution if you are running a production server or a system where strict side-channel security is critical.** These approaches are intended for testing environments where root access via KernelSU is prioritized over this specific hardware vulnerability mitigation.
:::

Choose & apply the patches that match your kernel version below. These patches will create a feature called `X86_FEATURE_INDIRECT_SAFE` and can be activated using the cmdline `syscall_hardening=off`.
Choose and apply the patches that match your kernel version below. These patches create a feature called `X86_FEATURE_INDIRECT_SAFE` and can be activated using the kernel cmdline `syscall_hardening=off`.

```
For kernel 6.6:
Expand All @@ -33,3 +50,9 @@ For kernel 6.18:
https://github.com/android-generic/kernel-zenith/commit/40b1c323d1ad29c86e041d665c7f089b9a3ccfb5
https://github.com/android-generic/kernel-zenith/commit/f5813e10b7630e1ccd86fc2c4cf30eef60b64a82
```

## Which method should I choose?

- If you are using KernelSU 3.2.6 or newer and can change the KernelSU build configuration, enable `KSU_X86_PATCH_SYSCALL_DISPATCHER`.
- If you prefer to keep your current kernel-side patch workflow, continue using the original source patches above.

Loading
Loading