11446e1dfSGabriel Krisman Bertazi // SPDX-License-Identifier: GPL-2.0
21446e1dfSGabriel Krisman Bertazi /*
31446e1dfSGabriel Krisman Bertazi  * Copyright (C) 2020 Collabora Ltd.
41446e1dfSGabriel Krisman Bertazi  */
51446e1dfSGabriel Krisman Bertazi #include <linux/sched.h>
61446e1dfSGabriel Krisman Bertazi #include <linux/prctl.h>
71446e1dfSGabriel Krisman Bertazi #include <linux/syscall_user_dispatch.h>
81446e1dfSGabriel Krisman Bertazi #include <linux/uaccess.h>
91446e1dfSGabriel Krisman Bertazi #include <linux/signal.h>
101446e1dfSGabriel Krisman Bertazi #include <linux/elf.h>
111446e1dfSGabriel Krisman Bertazi 
121446e1dfSGabriel Krisman Bertazi #include <linux/sched/signal.h>
131446e1dfSGabriel Krisman Bertazi #include <linux/sched/task_stack.h>
141446e1dfSGabriel Krisman Bertazi 
151446e1dfSGabriel Krisman Bertazi #include <asm/syscall.h>
161446e1dfSGabriel Krisman Bertazi 
171446e1dfSGabriel Krisman Bertazi #include "common.h"
181446e1dfSGabriel Krisman Bertazi 
191446e1dfSGabriel Krisman Bertazi static void trigger_sigsys(struct pt_regs *regs)
201446e1dfSGabriel Krisman Bertazi {
211446e1dfSGabriel Krisman Bertazi 	struct kernel_siginfo info;
221446e1dfSGabriel Krisman Bertazi 
231446e1dfSGabriel Krisman Bertazi 	clear_siginfo(&info);
241446e1dfSGabriel Krisman Bertazi 	info.si_signo = SIGSYS;
251446e1dfSGabriel Krisman Bertazi 	info.si_code = SYS_USER_DISPATCH;
261446e1dfSGabriel Krisman Bertazi 	info.si_call_addr = (void __user *)KSTK_EIP(current);
271446e1dfSGabriel Krisman Bertazi 	info.si_errno = 0;
281446e1dfSGabriel Krisman Bertazi 	info.si_arch = syscall_get_arch(current);
291446e1dfSGabriel Krisman Bertazi 	info.si_syscall = syscall_get_nr(current, regs);
301446e1dfSGabriel Krisman Bertazi 
311446e1dfSGabriel Krisman Bertazi 	force_sig_info(&info);
321446e1dfSGabriel Krisman Bertazi }
331446e1dfSGabriel Krisman Bertazi 
341446e1dfSGabriel Krisman Bertazi bool syscall_user_dispatch(struct pt_regs *regs)
351446e1dfSGabriel Krisman Bertazi {
361446e1dfSGabriel Krisman Bertazi 	struct syscall_user_dispatch *sd = &current->syscall_dispatch;
371446e1dfSGabriel Krisman Bertazi 	char state;
381446e1dfSGabriel Krisman Bertazi 
391446e1dfSGabriel Krisman Bertazi 	if (likely(instruction_pointer(regs) - sd->offset < sd->len))
401446e1dfSGabriel Krisman Bertazi 		return false;
411446e1dfSGabriel Krisman Bertazi 
421446e1dfSGabriel Krisman Bertazi 	if (unlikely(arch_syscall_is_vdso_sigreturn(regs)))
431446e1dfSGabriel Krisman Bertazi 		return false;
441446e1dfSGabriel Krisman Bertazi 
451446e1dfSGabriel Krisman Bertazi 	if (likely(sd->selector)) {
461446e1dfSGabriel Krisman Bertazi 		/*
471446e1dfSGabriel Krisman Bertazi 		 * access_ok() is performed once, at prctl time, when
481446e1dfSGabriel Krisman Bertazi 		 * the selector is loaded by userspace.
491446e1dfSGabriel Krisman Bertazi 		 */
50941edc5bSEric W. Biederman 		if (unlikely(__get_user(state, sd->selector))) {
51*fcb116bcSEric W. Biederman 			force_exit_sig(SIGSEGV);
52941edc5bSEric W. Biederman 			return true;
53941edc5bSEric W. Biederman 		}
541446e1dfSGabriel Krisman Bertazi 
5536a6c843SGabriel Krisman Bertazi 		if (likely(state == SYSCALL_DISPATCH_FILTER_ALLOW))
561446e1dfSGabriel Krisman Bertazi 			return false;
571446e1dfSGabriel Krisman Bertazi 
58941edc5bSEric W. Biederman 		if (state != SYSCALL_DISPATCH_FILTER_BLOCK) {
59*fcb116bcSEric W. Biederman 			force_exit_sig(SIGSYS);
60941edc5bSEric W. Biederman 			return true;
61941edc5bSEric W. Biederman 		}
621446e1dfSGabriel Krisman Bertazi 	}
631446e1dfSGabriel Krisman Bertazi 
641446e1dfSGabriel Krisman Bertazi 	sd->on_dispatch = true;
651446e1dfSGabriel Krisman Bertazi 	syscall_rollback(current, regs);
661446e1dfSGabriel Krisman Bertazi 	trigger_sigsys(regs);
671446e1dfSGabriel Krisman Bertazi 
681446e1dfSGabriel Krisman Bertazi 	return true;
691446e1dfSGabriel Krisman Bertazi }
701446e1dfSGabriel Krisman Bertazi 
711446e1dfSGabriel Krisman Bertazi int set_syscall_user_dispatch(unsigned long mode, unsigned long offset,
721446e1dfSGabriel Krisman Bertazi 			      unsigned long len, char __user *selector)
731446e1dfSGabriel Krisman Bertazi {
741446e1dfSGabriel Krisman Bertazi 	switch (mode) {
751446e1dfSGabriel Krisman Bertazi 	case PR_SYS_DISPATCH_OFF:
761446e1dfSGabriel Krisman Bertazi 		if (offset || len || selector)
771446e1dfSGabriel Krisman Bertazi 			return -EINVAL;
781446e1dfSGabriel Krisman Bertazi 		break;
791446e1dfSGabriel Krisman Bertazi 	case PR_SYS_DISPATCH_ON:
801446e1dfSGabriel Krisman Bertazi 		/*
811446e1dfSGabriel Krisman Bertazi 		 * Validate the direct dispatcher region just for basic
821446e1dfSGabriel Krisman Bertazi 		 * sanity against overflow and a 0-sized dispatcher
831446e1dfSGabriel Krisman Bertazi 		 * region.  If the user is able to submit a syscall from
841446e1dfSGabriel Krisman Bertazi 		 * an address, that address is obviously valid.
851446e1dfSGabriel Krisman Bertazi 		 */
861446e1dfSGabriel Krisman Bertazi 		if (offset && offset + len <= offset)
871446e1dfSGabriel Krisman Bertazi 			return -EINVAL;
881446e1dfSGabriel Krisman Bertazi 
891446e1dfSGabriel Krisman Bertazi 		if (selector && !access_ok(selector, sizeof(*selector)))
901446e1dfSGabriel Krisman Bertazi 			return -EFAULT;
911446e1dfSGabriel Krisman Bertazi 
921446e1dfSGabriel Krisman Bertazi 		break;
931446e1dfSGabriel Krisman Bertazi 	default:
941446e1dfSGabriel Krisman Bertazi 		return -EINVAL;
951446e1dfSGabriel Krisman Bertazi 	}
961446e1dfSGabriel Krisman Bertazi 
971446e1dfSGabriel Krisman Bertazi 	current->syscall_dispatch.selector = selector;
981446e1dfSGabriel Krisman Bertazi 	current->syscall_dispatch.offset = offset;
991446e1dfSGabriel Krisman Bertazi 	current->syscall_dispatch.len = len;
1001446e1dfSGabriel Krisman Bertazi 	current->syscall_dispatch.on_dispatch = false;
1011446e1dfSGabriel Krisman Bertazi 
1021446e1dfSGabriel Krisman Bertazi 	if (mode == PR_SYS_DISPATCH_ON)
1031446e1dfSGabriel Krisman Bertazi 		set_syscall_work(SYSCALL_USER_DISPATCH);
1041446e1dfSGabriel Krisman Bertazi 	else
1051446e1dfSGabriel Krisman Bertazi 		clear_syscall_work(SYSCALL_USER_DISPATCH);
1061446e1dfSGabriel Krisman Bertazi 
1071446e1dfSGabriel Krisman Bertazi 	return 0;
1081446e1dfSGabriel Krisman Bertazi }
109