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 = ¤t->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