Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  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_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
 91		/*
 92		 * access_ok() will clear memory tags for tagged addresses
 93		 * if current has memory tagging enabled.
 94
 95		 * To enable a tracer to set a tracees selector the
 96		 * selector address must be untagged for access_ok(),
 97		 * otherwise an untagged tracer will always fail to set a
 98		 * tagged tracees selector.
 99		 */
100		if (selector && !access_ok(untagged_addr(selector), sizeof(*selector)))
101			return -EFAULT;
102
103		break;
104	default:
105		return -EINVAL;
106	}
107
108	task->syscall_dispatch.selector = selector;
109	task->syscall_dispatch.offset = offset;
110	task->syscall_dispatch.len = len;
111	task->syscall_dispatch.on_dispatch = false;
112
113	if (mode == PR_SYS_DISPATCH_ON)
114		set_task_syscall_work(task, SYSCALL_USER_DISPATCH);
115	else
116		clear_task_syscall_work(task, SYSCALL_USER_DISPATCH);
117
118	return 0;
119}
120
121int set_syscall_user_dispatch(unsigned long mode, unsigned long offset,
122			      unsigned long len, char __user *selector)
123{
124	return task_set_syscall_user_dispatch(current, mode, offset, len, selector);
125}
126
127int syscall_user_dispatch_get_config(struct task_struct *task, unsigned long size,
128				     void __user *data)
129{
130	struct syscall_user_dispatch *sd = &task->syscall_dispatch;
131	struct ptrace_sud_config cfg;
132
133	if (size != sizeof(cfg))
134		return -EINVAL;
135
136	if (test_task_syscall_work(task, SYSCALL_USER_DISPATCH))
137		cfg.mode = PR_SYS_DISPATCH_ON;
138	else
139		cfg.mode = PR_SYS_DISPATCH_OFF;
140
141	cfg.offset = sd->offset;
142	cfg.len = sd->len;
143	cfg.selector = (__u64)(uintptr_t)sd->selector;
144
145	if (copy_to_user(data, &cfg, sizeof(cfg)))
146		return -EFAULT;
147
148	return 0;
149}
150
151int syscall_user_dispatch_set_config(struct task_struct *task, unsigned long size,
152				     void __user *data)
153{
154	struct ptrace_sud_config cfg;
155
156	if (size != sizeof(cfg))
157		return -EINVAL;
158
159	if (copy_from_user(&cfg, data, sizeof(cfg)))
160		return -EFAULT;
161
162	return task_set_syscall_user_dispatch(task, cfg.mode, cfg.offset, cfg.len,
163					      (char __user *)(uintptr_t)cfg.selector);
164}