syscall_user_dispatch: Add kernel.syscall_user_dispatch sysctl

Add a matching sysctl to go with CONFIG_SYSCALL_USER_DISPATCH.

kernel.syscall_user_dispatch (default 1 - allow) controls whether
userspace may arm syscall user dispatch (both via prctl and ptrace).

Disarming is always permitted - same semantics as comparable knobs.

Disabling while a task has armed syscall user dispatch does not
cause it to become inactive - instead it remains active until the
user attempts to disable/re-enable via prctl or ptrace.  On the
next attempt to re-enable, the prctl/ptrace call fails gracefully.

The alternative would cause programs translating non-linux syscalls
to interpret those syscalls as linux syscalls, resulting in undefined
userland behavior.

Signed-off-by: Gregory Price <gourry@gourry.net>
Signed-off-by: Thomas Gleixner <tglx@kernel.org>
Link: https://patch.msgid.link/20260706140020.873735-3-gourry@gourry.net
This commit is contained in:
Gregory Price
2026-07-08 10:22:00 +02:00
committed by Thomas Gleixner
parent ee935e8dc7
commit 5b6e32ba7b
2 changed files with 48 additions and 7 deletions
@@ -1402,6 +1402,23 @@ Note that if you change this from 0 to 1, already created segments
without users and with a dead originative process will be destroyed.
syscall_user_dispatch
=====================
Controls whether userspace may arm Syscall User Dispatch via
``prctl(PR_SET_SYSCALL_USER_DISPATCH, ...)`` or the
``PTRACE_SET_SYSCALL_USER_DISPATCH_CONFIG`` ptrace request:
== ===================================================================
0 Arming syscall user dispatch is denied with ``-EPERM``. Tasks that
already armed it keep it, and disabling it is always permitted.
1 (default) Arming syscall user dispatch is permitted.
== ===================================================================
Only present when the kernel is built with ``CONFIG_SYSCALL_USER_DISPATCH``
and ``CONFIG_PROC_SYSCTL``.
sysctl_writes_strict
====================
+31 -7
View File
@@ -2,21 +2,22 @@
/*
* Copyright (C) 2020 Collabora Ltd.
*/
#include <linux/elf.h>
#include <linux/entry-common.h>
#include <linux/sched.h>
#include <linux/prctl.h>
#include <linux/ptrace.h>
#include <linux/syscall_user_dispatch.h>
#include <linux/uaccess.h>
#include <linux/signal.h>
#include <linux/elf.h>
#include <linux/sched.h>
#include <linux/sched/signal.h>
#include <linux/sched/task_stack.h>
#include <linux/signal.h>
#include <linux/syscall_user_dispatch.h>
#include <linux/sysctl.h>
#include <linux/uaccess.h>
#include <asm/syscall.h>
static bool syscall_user_dispatch_allowed __read_mostly = true;
static void trigger_sigsys(struct pt_regs *regs)
{
struct kernel_siginfo info;
@@ -102,6 +103,10 @@ static int task_set_syscall_user_dispatch(struct task_struct *task, unsigned lon
return -EINVAL;
}
/* Arming can be denied at runtime via sysctl, disarming is allowed */
if (mode != PR_SYS_DISPATCH_OFF && !syscall_user_dispatch_allowed)
return -EPERM;
/*
* access_ok() will clear memory tags for tagged addresses
* if current has memory tagging enabled.
@@ -172,3 +177,22 @@ int syscall_user_dispatch_set_config(struct task_struct *task, unsigned long siz
return task_set_syscall_user_dispatch(task, cfg.mode, cfg.offset, cfg.len,
(char __user *)(uintptr_t)cfg.selector);
}
#ifdef CONFIG_PROC_SYSCTL
static const struct ctl_table syscall_user_dispatch_sysctls[] = {
{
.procname = "syscall_user_dispatch",
.data = &syscall_user_dispatch_allowed,
.maxlen = sizeof(syscall_user_dispatch_allowed),
.mode = 0644,
.proc_handler = proc_dobool,
},
};
static int __init syscall_user_dispatch_sysctl_init(void)
{
register_sysctl_init("kernel", syscall_user_dispatch_sysctls);
return 0;
}
late_initcall(syscall_user_dispatch_sysctl_init);
#endif /* CONFIG_PROC_SYSCTL */