Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.15.
  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/syscall_user_dispatch.h>
  8#include <linux/uaccess.h>
  9#include <linux/signal.h>
 10#include <linux/elf.h>
 11
 12#include <linux/sched/signal.h>
 13#include <linux/sched/task_stack.h>
 14
 15#include <asm/syscall.h>
 16
 17#include "common.h"
 18
 19static void trigger_sigsys(struct pt_regs *regs)
 20{
 21	struct kernel_siginfo info;
 22
 23	clear_siginfo(&info);
 24	info.si_signo = SIGSYS;
 25	info.si_code = SYS_USER_DISPATCH;
 26	info.si_call_addr = (void __user *)KSTK_EIP(current);
 27	info.si_errno = 0;
 28	info.si_arch = syscall_get_arch(current);
 29	info.si_syscall = syscall_get_nr(current, regs);
 30
 31	force_sig_info(&info);
 32}
 33
 34bool syscall_user_dispatch(struct pt_regs *regs)
 35{
 36	struct syscall_user_dispatch *sd = &current->syscall_dispatch;
 37	char state;
 38
 39	if (likely(instruction_pointer(regs) - sd->offset < sd->len))
 40		return false;
 41
 42	if (unlikely(arch_syscall_is_vdso_sigreturn(regs)))
 43		return false;
 44
 45	if (likely(sd->selector)) {
 46		/*
 47		 * access_ok() is performed once, at prctl time, when
 48		 * the selector is loaded by userspace.
 49		 */
 50		if (unlikely(__get_user(state, sd->selector))) {
 51			force_exit_sig(SIGSEGV);
 52			return true;
 53		}
 54
 55		if (likely(state == SYSCALL_DISPATCH_FILTER_ALLOW))
 56			return false;
 57
 58		if (state != SYSCALL_DISPATCH_FILTER_BLOCK) {
 59			force_exit_sig(SIGSYS);
 60			return true;
 61		}
 62	}
 63
 64	sd->on_dispatch = true;
 65	syscall_rollback(current, regs);
 66	trigger_sigsys(regs);
 67
 68	return true;
 69}
 70
 71int set_syscall_user_dispatch(unsigned long mode, unsigned long offset,
 72			      unsigned long len, char __user *selector)
 73{
 74	switch (mode) {
 75	case PR_SYS_DISPATCH_OFF:
 76		if (offset || len || selector)
 77			return -EINVAL;
 78		break;
 79	case PR_SYS_DISPATCH_ON:
 80		/*
 81		 * Validate the direct dispatcher region just for basic
 82		 * sanity against overflow and a 0-sized dispatcher
 83		 * region.  If the user is able to submit a syscall from
 84		 * an address, that address is obviously valid.
 85		 */
 86		if (offset && offset + len <= offset)
 87			return -EINVAL;
 88
 89		if (selector && !access_ok(selector, sizeof(*selector)))
 90			return -EFAULT;
 91
 92		break;
 93	default:
 94		return -EINVAL;
 95	}
 96
 97	current->syscall_dispatch.selector = selector;
 98	current->syscall_dispatch.offset = offset;
 99	current->syscall_dispatch.len = len;
100	current->syscall_dispatch.on_dispatch = false;
101
102	if (mode == PR_SYS_DISPATCH_ON)
103		set_syscall_work(SYSCALL_USER_DISPATCH);
104	else
105		clear_syscall_work(SYSCALL_USER_DISPATCH);
106
107	return 0;
108}