mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
104 lines
2.4 KiB
104 lines
2.4 KiB
// SPDX-License-Identifier: GPL-2.0 |
|
/* |
|
* Copyright (C) 2020 Collabora Ltd. |
|
*/ |
|
#include <linux/sched.h> |
|
#include <linux/prctl.h> |
|
#include <linux/syscall_user_dispatch.h> |
|
#include <linux/uaccess.h> |
|
#include <linux/signal.h> |
|
#include <linux/elf.h> |
|
|
|
#include <linux/sched/signal.h> |
|
#include <linux/sched/task_stack.h> |
|
|
|
#include <asm/syscall.h> |
|
|
|
#include "common.h" |
|
|
|
static void trigger_sigsys(struct pt_regs *regs) |
|
{ |
|
struct kernel_siginfo info; |
|
|
|
clear_siginfo(&info); |
|
info.si_signo = SIGSYS; |
|
info.si_code = SYS_USER_DISPATCH; |
|
info.si_call_addr = (void __user *)KSTK_EIP(current); |
|
info.si_errno = 0; |
|
info.si_arch = syscall_get_arch(current); |
|
info.si_syscall = syscall_get_nr(current, regs); |
|
|
|
force_sig_info(&info); |
|
} |
|
|
|
bool syscall_user_dispatch(struct pt_regs *regs) |
|
{ |
|
struct syscall_user_dispatch *sd = ¤t->syscall_dispatch; |
|
char state; |
|
|
|
if (likely(instruction_pointer(regs) - sd->offset < sd->len)) |
|
return false; |
|
|
|
if (unlikely(arch_syscall_is_vdso_sigreturn(regs))) |
|
return false; |
|
|
|
if (likely(sd->selector)) { |
|
/* |
|
* access_ok() is performed once, at prctl time, when |
|
* the selector is loaded by userspace. |
|
*/ |
|
if (unlikely(__get_user(state, sd->selector))) |
|
do_exit(SIGSEGV); |
|
|
|
if (likely(state == SYSCALL_DISPATCH_FILTER_ALLOW)) |
|
return false; |
|
|
|
if (state != SYSCALL_DISPATCH_FILTER_BLOCK) |
|
do_exit(SIGSYS); |
|
} |
|
|
|
sd->on_dispatch = true; |
|
syscall_rollback(current, regs); |
|
trigger_sigsys(regs); |
|
|
|
return true; |
|
} |
|
|
|
int set_syscall_user_dispatch(unsigned long mode, unsigned long offset, |
|
unsigned long len, char __user *selector) |
|
{ |
|
switch (mode) { |
|
case PR_SYS_DISPATCH_OFF: |
|
if (offset || len || selector) |
|
return -EINVAL; |
|
break; |
|
case PR_SYS_DISPATCH_ON: |
|
/* |
|
* Validate the direct dispatcher region just for basic |
|
* sanity against overflow and a 0-sized dispatcher |
|
* region. If the user is able to submit a syscall from |
|
* an address, that address is obviously valid. |
|
*/ |
|
if (offset && offset + len <= offset) |
|
return -EINVAL; |
|
|
|
if (selector && !access_ok(selector, sizeof(*selector))) |
|
return -EFAULT; |
|
|
|
break; |
|
default: |
|
return -EINVAL; |
|
} |
|
|
|
current->syscall_dispatch.selector = selector; |
|
current->syscall_dispatch.offset = offset; |
|
current->syscall_dispatch.len = len; |
|
current->syscall_dispatch.on_dispatch = false; |
|
|
|
if (mode == PR_SYS_DISPATCH_ON) |
|
set_syscall_work(SYSCALL_USER_DISPATCH); |
|
else |
|
clear_syscall_work(SYSCALL_USER_DISPATCH); |
|
|
|
return 0; |
|
}
|
|
|