[PATCH 1/2] kernel/entry: add CONFIG_SYSCALL_USER_DISPATCH to compile SUD out
From: Gregory Price
Date: Sat Jun 27 2026 - 16:56:12 EST
Syscall User Dispatch is built under CONFIG_GENERIC_SYSCALL and cannot
be disabled independent of the core syscall-entry machinery.
Native foreign-binary emulators (Wine/Proton) need it, but it should
be an optional for minimal/high security systems.
Add CONFIG_SYSCALL_USER_DISPATCH to make it optional.
Signed-off-by: Gregory Price <gourry@xxxxxxxxxx>
---
arch/Kconfig | 11 ++++++++
include/linux/entry-common.h | 6 ++---
include/linux/syscall_user_dispatch.h | 28 +++++++++++++++++++--
include/linux/syscall_user_dispatch_types.h | 2 +-
kernel/entry/Makefile | 3 ++-
5 files changed, 42 insertions(+), 8 deletions(-)
diff --git a/arch/Kconfig b/arch/Kconfig
index e86880045158..a40686e2ad5b 100644
--- a/arch/Kconfig
+++ b/arch/Kconfig
@@ -114,6 +114,17 @@ config GENERIC_ENTRY
select GENERIC_IRQ_ENTRY
select GENERIC_SYSCALL
+config SYSCALL_USER_DISPATCH
+ bool "Syscall User Dispatch (SUD)"
+ depends on GENERIC_ENTRY
+ default y
+ help
+ Syscall User Dispatch (SUD) lets a thread have its own system calls
+ redirected to a userspace handler. It is used by emulators that run
+ foreign binaries which issue system calls directly.
+
+ If unsure, say Y.
+
config KPROBES
bool "Kprobes"
depends on HAVE_KPROBES
diff --git a/include/linux/entry-common.h b/include/linux/entry-common.h
index 416a3352261f..9336516430a1 100644
--- a/include/linux/entry-common.h
+++ b/include/linux/entry-common.h
@@ -9,6 +9,7 @@
#include <linux/resume_user_mode.h>
#include <linux/seccomp.h>
#include <linux/sched.h>
+#include <linux/syscall_user_dispatch.h>
#include <asm/entry-common.h>
#include <asm/syscall.h>
@@ -55,7 +56,6 @@ static __always_inline int arch_ptrace_report_syscall_entry(struct pt_regs *regs
}
#endif
-bool syscall_user_dispatch(struct pt_regs *regs);
long trace_syscall_enter(struct pt_regs *regs, long syscall);
void trace_syscall_exit(struct pt_regs *regs, long ret);
@@ -232,10 +232,8 @@ static __always_inline void syscall_exit_work(struct pt_regs *regs, unsigned lon
* of these syscalls is unknown.
*/
if (work & SYSCALL_WORK_SYSCALL_USER_DISPATCH) {
- if (unlikely(current->syscall_dispatch.on_dispatch)) {
- current->syscall_dispatch.on_dispatch = false;
+ if (syscall_user_dispatch_clear_on_dispatch())
return;
- }
}
audit_syscall_exit(regs);
diff --git a/include/linux/syscall_user_dispatch.h b/include/linux/syscall_user_dispatch.h
index 3858a6ffdd5c..3dcb4c2dc544 100644
--- a/include/linux/syscall_user_dispatch.h
+++ b/include/linux/syscall_user_dispatch.h
@@ -7,8 +7,22 @@
#include <linux/thread_info.h>
#include <linux/syscall_user_dispatch_types.h>
+#include <linux/sched.h>
-#ifdef CONFIG_GENERIC_ENTRY
+struct pt_regs;
+
+#ifdef CONFIG_SYSCALL_USER_DISPATCH
+
+bool syscall_user_dispatch(struct pt_regs *regs);
+
+static inline bool syscall_user_dispatch_clear_on_dispatch(void)
+{
+ if (likely(!current->syscall_dispatch.on_dispatch))
+ return false;
+
+ current->syscall_dispatch.on_dispatch = false;
+ return true;
+}
int set_syscall_user_dispatch(unsigned long mode, unsigned long offset,
unsigned long len, char __user *selector);
@@ -24,6 +38,16 @@ int syscall_user_dispatch_set_config(struct task_struct *task, unsigned long siz
#else
+static inline bool syscall_user_dispatch(struct pt_regs *regs)
+{
+ return false;
+}
+
+static inline bool syscall_user_dispatch_clear_on_dispatch(void)
+{
+ return false;
+}
+
static inline int set_syscall_user_dispatch(unsigned long mode, unsigned long offset,
unsigned long len, char __user *selector)
{
@@ -46,6 +70,6 @@ static inline int syscall_user_dispatch_set_config(struct task_struct *task,
return -EINVAL;
}
-#endif /* CONFIG_GENERIC_ENTRY */
+#endif /* CONFIG_SYSCALL_USER_DISPATCH */
#endif /* _SYSCALL_USER_DISPATCH_H */
diff --git a/include/linux/syscall_user_dispatch_types.h b/include/linux/syscall_user_dispatch_types.h
index 3be36b06c7d7..c0bdd4f760d3 100644
--- a/include/linux/syscall_user_dispatch_types.h
+++ b/include/linux/syscall_user_dispatch_types.h
@@ -4,7 +4,7 @@
#include <linux/types.h>
-#ifdef CONFIG_GENERIC_ENTRY
+#ifdef CONFIG_SYSCALL_USER_DISPATCH
struct syscall_user_dispatch {
char __user *selector;
diff --git a/kernel/entry/Makefile b/kernel/entry/Makefile
index 2333d70802e4..f220bae86b12 100644
--- a/kernel/entry/Makefile
+++ b/kernel/entry/Makefile
@@ -13,5 +13,6 @@ CFLAGS_REMOVE_common.o = -fstack-protector -fstack-protector-strong
CFLAGS_common.o += -fno-stack-protector
obj-$(CONFIG_GENERIC_IRQ_ENTRY) += common.o
-obj-$(CONFIG_GENERIC_SYSCALL) += syscall-common.o syscall_user_dispatch.o
+obj-$(CONFIG_GENERIC_SYSCALL) += syscall-common.o
+obj-$(CONFIG_SYSCALL_USER_DISPATCH) += syscall_user_dispatch.o
obj-$(CONFIG_VIRT_XFER_TO_GUEST_WORK) += virt.o
--
2.54.0