File Index
Files and directories
Sign in
FILE

syscall_user_dispatch.c

File preview with syntax highlighting.

linux-6.19.14/kernel/entry/syscall_user_dispatch.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Copyright (C) 2020 Collabora Ltd.
  4 */
  5#include <linux/sched.h>
  6#include <linux/prctl.h>
  7#include <linux/ptrace.h>
  8#include <linux/syscall_user_dispatch.h>
  9#include <linux/uaccess.h>
 10#include <linux/signal.h>
 11#include <linux/elf.h>
 12
 13#include <linux/sched/signal.h>
 14#include <linux/sched/task_stack.h>
 15
 16#include <asm/syscall.h>
 17
 18#include "common.h"
 19
 20static void trigger_sigsys(struct pt_regs *regs)
 21{
 22	struct kernel_siginfo info;
 23
 24	clear_siginfo(&info);
 25	info.si_signo = SIGSYS;
 26	info.si_code = SYS_USER_DISPATCH;
 27	info.si_call_addr = (void __user *)KSTK_EIP(current);
 28	info.si_errno = 0;
 29	info.si_arch = syscall_get_arch(current);
 30	info.si_syscall = syscall_get_nr(current, regs);
 31
 32	force_sig_info(&info);
 33}
 34
 35bool syscall_user_dispatch(struct pt_regs *regs)
 36{
 37	struct syscall_user_dispatch *sd = &current->syscall_dispatch;
 38	char state;
 39
 40	if (likely(instruction_pointer(regs) - sd->offset < sd->len))
 41		return false;
 42
 43	if (unlikely(arch_syscall_is_vdso_sigreturn(regs)))
 44		return false;
 45
 46	if (likely(sd->selector)) {
 47		/*
 48		 * access_ok() is performed once, at prctl time, when
 49		 * the selector is loaded by userspace.
 50		 */
 51		if (unlikely(__get_user(state, sd->selector))) {
 52			force_exit_sig(SIGSEGV);
 53			return true;
 54		}
 55
 56		if (likely(state == SYSCALL_DISPATCH_FILTER_ALLOW))
 57			return false;
 58
 59		if (state != SYSCALL_DISPATCH_FILTER_BLOCK) {
 60			force_exit_sig(SIGSYS);
 61			return true;
 62		}
 63	}
 64
 65	sd->on_dispatch = true;
 66	syscall_rollback(current, regs);
 67	trigger_sigsys(regs);
 68
 69	return true;
 70}
 71
 72static int task_set_syscall_user_dispatch(struct task_struct *task, unsigned long mode,
 73					  unsigned long offset, unsigned long len,
 74					  char __user *selector)
 75{
 76	switch (mode) {
 77	case PR_SYS_DISPATCH_OFF:
 78		if (offset || len || selector)
 79			return -EINVAL;
 80		break;
 81	case PR_SYS_DISPATCH_EXCLUSIVE_ON:
 82		/*
 83		 * Validate the direct dispatcher region just for basic
 84		 * sanity against overflow and a 0-sized dispatcher
 85		 * region.  If the user is able to submit a syscall from
 86		 * an address, that address is obviously valid.
 87		 */
 88		if (offset && offset + len <= offset)
 89			return -EINVAL;
 90		break;
 91	case PR_SYS_DISPATCH_INCLUSIVE_ON:
 92		if (len == 0 || offset + len <= offset)
 93			return -EINVAL;
 94		/*
 95		 * Invert the range, the check in syscall_user_dispatch()
 96		 * supports wrap-around.
 97		 */
 98		offset = offset + len;
 99		len = -len;
100		break;
101	default:
102		return -EINVAL;
103	}
104
105	/*
106	 * access_ok() will clear memory tags for tagged addresses
107	 * if current has memory tagging enabled.
108	 *
109	 * To enable a tracer to set a tracees selector the
110	 * selector address must be untagged for access_ok(),
111	 * otherwise an untagged tracer will always fail to set a
112	 * tagged tracees selector.
113	 */
114	if (mode != PR_SYS_DISPATCH_OFF && selector &&
115		!access_ok(untagged_addr(selector), sizeof(*selector)))
116		return -EFAULT;
117
118	task->syscall_dispatch.selector = selector;
119	task->syscall_dispatch.offset = offset;
120	task->syscall_dispatch.len = len;
121	task->syscall_dispatch.on_dispatch = false;
122
123	if (mode != PR_SYS_DISPATCH_OFF)
124		set_task_syscall_work(task, SYSCALL_USER_DISPATCH);
125	else
126		clear_task_syscall_work(task, SYSCALL_USER_DISPATCH);
127
128	return 0;
129}
130
131int set_syscall_user_dispatch(unsigned long mode, unsigned long offset,
132			      unsigned long len, char __user *selector)
133{
134	return task_set_syscall_user_dispatch(current, mode, offset, len, selector);
135}
136
137int syscall_user_dispatch_get_config(struct task_struct *task, unsigned long size,
138				     void __user *data)
139{
140	struct syscall_user_dispatch *sd = &task->syscall_dispatch;
141	struct ptrace_sud_config cfg;
142
143	if (size != sizeof(cfg))
144		return -EINVAL;
145
146	if (test_task_syscall_work(task, SYSCALL_USER_DISPATCH))
147		cfg.mode = PR_SYS_DISPATCH_ON;
148	else
149		cfg.mode = PR_SYS_DISPATCH_OFF;
150
151	cfg.offset = sd->offset;
152	cfg.len = sd->len;
153	cfg.selector = (__u64)(uintptr_t)sd->selector;
154
155	if (copy_to_user(data, &cfg, sizeof(cfg)))
156		return -EFAULT;
157
158	return 0;
159}
160
161int syscall_user_dispatch_set_config(struct task_struct *task, unsigned long size,
162				     void __user *data)
163{
164	struct ptrace_sud_config cfg;
165
166	if (size != sizeof(cfg))
167		return -EINVAL;
168
169	if (copy_from_user(&cfg, data, sizeof(cfg)))
170		return -EFAULT;
171
172	return task_set_syscall_user_dispatch(task, cfg.mode, cfg.offset, cfg.len,
173					      (char __user *)(uintptr_t)cfg.selector);
174}
~/ Read-only file preview.