162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2020 Collabora Ltd. 462306a36Sopenharmony_ci */ 562306a36Sopenharmony_ci#include <linux/sched.h> 662306a36Sopenharmony_ci#include <linux/prctl.h> 762306a36Sopenharmony_ci#include <linux/ptrace.h> 862306a36Sopenharmony_ci#include <linux/syscall_user_dispatch.h> 962306a36Sopenharmony_ci#include <linux/uaccess.h> 1062306a36Sopenharmony_ci#include <linux/signal.h> 1162306a36Sopenharmony_ci#include <linux/elf.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include <linux/sched/signal.h> 1462306a36Sopenharmony_ci#include <linux/sched/task_stack.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <asm/syscall.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#include "common.h" 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_cistatic void trigger_sigsys(struct pt_regs *regs) 2162306a36Sopenharmony_ci{ 2262306a36Sopenharmony_ci struct kernel_siginfo info; 2362306a36Sopenharmony_ci 2462306a36Sopenharmony_ci clear_siginfo(&info); 2562306a36Sopenharmony_ci info.si_signo = SIGSYS; 2662306a36Sopenharmony_ci info.si_code = SYS_USER_DISPATCH; 2762306a36Sopenharmony_ci info.si_call_addr = (void __user *)KSTK_EIP(current); 2862306a36Sopenharmony_ci info.si_errno = 0; 2962306a36Sopenharmony_ci info.si_arch = syscall_get_arch(current); 3062306a36Sopenharmony_ci info.si_syscall = syscall_get_nr(current, regs); 3162306a36Sopenharmony_ci 3262306a36Sopenharmony_ci force_sig_info(&info); 3362306a36Sopenharmony_ci} 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cibool syscall_user_dispatch(struct pt_regs *regs) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci struct syscall_user_dispatch *sd = ¤t->syscall_dispatch; 3862306a36Sopenharmony_ci char state; 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci if (likely(instruction_pointer(regs) - sd->offset < sd->len)) 4162306a36Sopenharmony_ci return false; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_ci if (unlikely(arch_syscall_is_vdso_sigreturn(regs))) 4462306a36Sopenharmony_ci return false; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci if (likely(sd->selector)) { 4762306a36Sopenharmony_ci /* 4862306a36Sopenharmony_ci * access_ok() is performed once, at prctl time, when 4962306a36Sopenharmony_ci * the selector is loaded by userspace. 5062306a36Sopenharmony_ci */ 5162306a36Sopenharmony_ci if (unlikely(__get_user(state, sd->selector))) { 5262306a36Sopenharmony_ci force_exit_sig(SIGSEGV); 5362306a36Sopenharmony_ci return true; 5462306a36Sopenharmony_ci } 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci if (likely(state == SYSCALL_DISPATCH_FILTER_ALLOW)) 5762306a36Sopenharmony_ci return false; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci if (state != SYSCALL_DISPATCH_FILTER_BLOCK) { 6062306a36Sopenharmony_ci force_exit_sig(SIGSYS); 6162306a36Sopenharmony_ci return true; 6262306a36Sopenharmony_ci } 6362306a36Sopenharmony_ci } 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci sd->on_dispatch = true; 6662306a36Sopenharmony_ci syscall_rollback(current, regs); 6762306a36Sopenharmony_ci trigger_sigsys(regs); 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci return true; 7062306a36Sopenharmony_ci} 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic int task_set_syscall_user_dispatch(struct task_struct *task, unsigned long mode, 7362306a36Sopenharmony_ci unsigned long offset, unsigned long len, 7462306a36Sopenharmony_ci char __user *selector) 7562306a36Sopenharmony_ci{ 7662306a36Sopenharmony_ci switch (mode) { 7762306a36Sopenharmony_ci case PR_SYS_DISPATCH_OFF: 7862306a36Sopenharmony_ci if (offset || len || selector) 7962306a36Sopenharmony_ci return -EINVAL; 8062306a36Sopenharmony_ci break; 8162306a36Sopenharmony_ci case PR_SYS_DISPATCH_ON: 8262306a36Sopenharmony_ci /* 8362306a36Sopenharmony_ci * Validate the direct dispatcher region just for basic 8462306a36Sopenharmony_ci * sanity against overflow and a 0-sized dispatcher 8562306a36Sopenharmony_ci * region. If the user is able to submit a syscall from 8662306a36Sopenharmony_ci * an address, that address is obviously valid. 8762306a36Sopenharmony_ci */ 8862306a36Sopenharmony_ci if (offset && offset + len <= offset) 8962306a36Sopenharmony_ci return -EINVAL; 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci /* 9262306a36Sopenharmony_ci * access_ok() will clear memory tags for tagged addresses 9362306a36Sopenharmony_ci * if current has memory tagging enabled. 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_ci * To enable a tracer to set a tracees selector the 9662306a36Sopenharmony_ci * selector address must be untagged for access_ok(), 9762306a36Sopenharmony_ci * otherwise an untagged tracer will always fail to set a 9862306a36Sopenharmony_ci * tagged tracees selector. 9962306a36Sopenharmony_ci */ 10062306a36Sopenharmony_ci if (selector && !access_ok(untagged_addr(selector), sizeof(*selector))) 10162306a36Sopenharmony_ci return -EFAULT; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci break; 10462306a36Sopenharmony_ci default: 10562306a36Sopenharmony_ci return -EINVAL; 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci task->syscall_dispatch.selector = selector; 10962306a36Sopenharmony_ci task->syscall_dispatch.offset = offset; 11062306a36Sopenharmony_ci task->syscall_dispatch.len = len; 11162306a36Sopenharmony_ci task->syscall_dispatch.on_dispatch = false; 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci if (mode == PR_SYS_DISPATCH_ON) 11462306a36Sopenharmony_ci set_task_syscall_work(task, SYSCALL_USER_DISPATCH); 11562306a36Sopenharmony_ci else 11662306a36Sopenharmony_ci clear_task_syscall_work(task, SYSCALL_USER_DISPATCH); 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci return 0; 11962306a36Sopenharmony_ci} 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ciint set_syscall_user_dispatch(unsigned long mode, unsigned long offset, 12262306a36Sopenharmony_ci unsigned long len, char __user *selector) 12362306a36Sopenharmony_ci{ 12462306a36Sopenharmony_ci return task_set_syscall_user_dispatch(current, mode, offset, len, selector); 12562306a36Sopenharmony_ci} 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ciint syscall_user_dispatch_get_config(struct task_struct *task, unsigned long size, 12862306a36Sopenharmony_ci void __user *data) 12962306a36Sopenharmony_ci{ 13062306a36Sopenharmony_ci struct syscall_user_dispatch *sd = &task->syscall_dispatch; 13162306a36Sopenharmony_ci struct ptrace_sud_config cfg; 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci if (size != sizeof(cfg)) 13462306a36Sopenharmony_ci return -EINVAL; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci if (test_task_syscall_work(task, SYSCALL_USER_DISPATCH)) 13762306a36Sopenharmony_ci cfg.mode = PR_SYS_DISPATCH_ON; 13862306a36Sopenharmony_ci else 13962306a36Sopenharmony_ci cfg.mode = PR_SYS_DISPATCH_OFF; 14062306a36Sopenharmony_ci 14162306a36Sopenharmony_ci cfg.offset = sd->offset; 14262306a36Sopenharmony_ci cfg.len = sd->len; 14362306a36Sopenharmony_ci cfg.selector = (__u64)(uintptr_t)sd->selector; 14462306a36Sopenharmony_ci 14562306a36Sopenharmony_ci if (copy_to_user(data, &cfg, sizeof(cfg))) 14662306a36Sopenharmony_ci return -EFAULT; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci return 0; 14962306a36Sopenharmony_ci} 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ciint syscall_user_dispatch_set_config(struct task_struct *task, unsigned long size, 15262306a36Sopenharmony_ci void __user *data) 15362306a36Sopenharmony_ci{ 15462306a36Sopenharmony_ci struct ptrace_sud_config cfg; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci if (size != sizeof(cfg)) 15762306a36Sopenharmony_ci return -EINVAL; 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci if (copy_from_user(&cfg, data, sizeof(cfg))) 16062306a36Sopenharmony_ci return -EFAULT; 16162306a36Sopenharmony_ci 16262306a36Sopenharmony_ci return task_set_syscall_user_dispatch(task, cfg.mode, cfg.offset, cfg.len, 16362306a36Sopenharmony_ci (char __user *)(uintptr_t)cfg.selector); 16462306a36Sopenharmony_ci} 165