| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * Restartable sequences system call |
| * |
| * Copyright (C) 2015, Google, Inc., |
| * Paul Turner <pjt@google.com> and Andrew Hunter <ahh@google.com> |
| * Copyright (C) 2015-2018, EfficiOS Inc., |
| * Mathieu Desnoyers <mathieu.desnoyers@efficios.com> |
| */ |
| |
| /* |
| * Restartable sequences are a lightweight interface that allows |
| * user-level code to be executed atomically relative to scheduler |
| * preemption and signal delivery. Typically used for implementing |
| * per-cpu operations. |
| * |
| * It allows user-space to perform update operations on per-cpu data |
| * without requiring heavy-weight atomic operations. |
| * |
| * Detailed algorithm of rseq user-space assembly sequences: |
| * |
| * init(rseq_cs) |
| * cpu = TLS->rseq::cpu_id_start |
| * [1] TLS->rseq::rseq_cs = rseq_cs |
| * [start_ip] ---------------------------- |
| * [2] if (cpu != TLS->rseq::cpu_id) |
| * goto abort_ip; |
| * [3] <last_instruction_in_cs> |
| * [post_commit_ip] ---------------------------- |
| * |
| * The address of jump target abort_ip must be outside the critical |
| * region, i.e.: |
| * |
| * [abort_ip] < [start_ip] || [abort_ip] >= [post_commit_ip] |
| * |
| * Steps [2]-[3] (inclusive) need to be a sequence of instructions in |
| * userspace that can handle being interrupted between any of those |
| * instructions, and then resumed to the abort_ip. |
| * |
| * 1. Userspace stores the address of the struct rseq_cs assembly |
| * block descriptor into the rseq_cs field of the registered |
| * struct rseq TLS area. This update is performed through a single |
| * store within the inline assembly instruction sequence. |
| * [start_ip] |
| * |
| * 2. Userspace tests to check whether the current cpu_id field match |
| * the cpu number loaded before start_ip, branching to abort_ip |
| * in case of a mismatch. |
| * |
| * If the sequence is preempted or interrupted by a signal |
| * at or after start_ip and before post_commit_ip, then the kernel |
| * clears TLS->__rseq_abi::rseq_cs, and sets the user-space return |
| * ip to abort_ip before returning to user-space, so the preempted |
| * execution resumes at abort_ip. |
| * |
| * 3. Userspace critical section final instruction before |
| * post_commit_ip is the commit. The critical section is |
| * self-terminating. |
| * [post_commit_ip] |
| * |
| * 4. <success> |
| * |
| * On failure at [2], or if interrupted by preempt or signal delivery |
| * between [1] and [3]: |
| * |
| * [abort_ip] |
| * F1. <failure> |
| */ |
| |
| /* Required to select the proper per_cpu ops for rseq_stats_inc() */ |
| #define RSEQ_BUILD_SLOW_PATH |
| |
| #include <linux/debugfs.h> |
| #include <linux/ratelimit.h> |
| #include <linux/rseq_entry.h> |
| #include <linux/sched.h> |
| #include <linux/syscalls.h> |
| #include <linux/uaccess.h> |
| #include <linux/types.h> |
| #include <asm/ptrace.h> |
| |
| #define CREATE_TRACE_POINTS |
| #include <trace/events/rseq.h> |
| |
| DEFINE_STATIC_KEY_MAYBE(CONFIG_RSEQ_DEBUG_DEFAULT_ENABLE, rseq_debug_enabled); |
| |
| static inline void rseq_control_debug(bool on) |
| { |
| if (on) |
| static_branch_enable(&rseq_debug_enabled); |
| else |
| static_branch_disable(&rseq_debug_enabled); |
| } |
| |
| static int __init rseq_setup_debug(char *str) |
| { |
| bool on; |
| |
| if (kstrtobool(str, &on)) |
| return -EINVAL; |
| rseq_control_debug(on); |
| return 1; |
| } |
| __setup("rseq_debug=", rseq_setup_debug); |
| |
| #ifdef CONFIG_TRACEPOINTS |
| /* |
| * Out of line, so the actual update functions can be in a header to be |
| * inlined into the exit to user code. |
| */ |
| void __rseq_trace_update(struct task_struct *t) |
| { |
| trace_rseq_update(t); |
| } |
| |
| void __rseq_trace_ip_fixup(unsigned long ip, unsigned long start_ip, |
| unsigned long offset, unsigned long abort_ip) |
| { |
| trace_rseq_ip_fixup(ip, start_ip, offset, abort_ip); |
| } |
| #endif /* CONFIG_TRACEPOINTS */ |
| |
| #ifdef CONFIG_DEBUG_FS |
| #ifdef CONFIG_RSEQ_STATS |
| DEFINE_PER_CPU(struct rseq_stats, rseq_stats); |
| |
| static int rseq_stats_show(struct seq_file *m, void *p) |
| { |
| struct rseq_stats stats = { }; |
| unsigned int cpu; |
| |
| for_each_possible_cpu(cpu) { |
| stats.exit += data_race(per_cpu(rseq_stats.exit, cpu)); |
| stats.signal += data_race(per_cpu(rseq_stats.signal, cpu)); |
| stats.slowpath += data_race(per_cpu(rseq_stats.slowpath, cpu)); |
| stats.fastpath += data_race(per_cpu(rseq_stats.fastpath, cpu)); |
| stats.ids += data_race(per_cpu(rseq_stats.ids, cpu)); |
| stats.cs += data_race(per_cpu(rseq_stats.cs, cpu)); |
| stats.clear += data_race(per_cpu(rseq_stats.clear, cpu)); |
| stats.fixup += data_race(per_cpu(rseq_stats.fixup, cpu)); |
| } |
| |
| seq_printf(m, "exit: %16lu\n", stats.exit); |
| seq_printf(m, "signal: %16lu\n", stats.signal); |
| seq_printf(m, "slowp: %16lu\n", stats.slowpath); |
| seq_printf(m, "fastp: %16lu\n", stats.fastpath); |
| seq_printf(m, "ids: %16lu\n", stats.ids); |
| seq_printf(m, "cs: %16lu\n", stats.cs); |
| seq_printf(m, "clear: %16lu\n", stats.clear); |
| seq_printf(m, "fixup: %16lu\n", stats.fixup); |
| return 0; |
| } |
| |
| static int rseq_stats_open(struct inode *inode, struct file *file) |
| { |
| return single_open(file, rseq_stats_show, inode->i_private); |
| } |
| |
| static const struct file_operations stat_ops = { |
| .open = rseq_stats_open, |
| .read = seq_read, |
| .llseek = seq_lseek, |
| .release = single_release, |
| }; |
| |
| static int __init rseq_stats_init(struct dentry *root_dir) |
| { |
| debugfs_create_file("stats", 0444, root_dir, NULL, &stat_ops); |
| return 0; |
| } |
| #else |
| static inline void rseq_stats_init(struct dentry *root_dir) { } |
| #endif /* CONFIG_RSEQ_STATS */ |
| |
| static int rseq_debug_show(struct seq_file *m, void *p) |
| { |
| bool on = static_branch_unlikely(&rseq_debug_enabled); |
| |
| seq_printf(m, "%d\n", on); |
| return 0; |
| } |
| |
| static ssize_t rseq_debug_write(struct file *file, const char __user *ubuf, |
| size_t count, loff_t *ppos) |
| { |
| bool on; |
| |
| if (kstrtobool_from_user(ubuf, count, &on)) |
| return -EINVAL; |
| |
| rseq_control_debug(on); |
| return count; |
| } |
| |
| static int rseq_debug_open(struct inode *inode, struct file *file) |
| { |
| return single_open(file, rseq_debug_show, inode->i_private); |
| } |
| |
| static const struct file_operations debug_ops = { |
| .open = rseq_debug_open, |
| .read = seq_read, |
| .write = rseq_debug_write, |
| .llseek = seq_lseek, |
| .release = single_release, |
| }; |
| |
| static int __init rseq_debugfs_init(void) |
| { |
| struct dentry *root_dir = debugfs_create_dir("rseq", NULL); |
| |
| debugfs_create_file("debug", 0644, root_dir, NULL, &debug_ops); |
| rseq_stats_init(root_dir); |
| return 0; |
| } |
| __initcall(rseq_debugfs_init); |
| #endif /* CONFIG_DEBUG_FS */ |
| |
| static bool rseq_set_ids(struct task_struct *t, struct rseq_ids *ids, u32 node_id) |
| { |
| return rseq_set_ids_get_csaddr(t, ids, node_id, NULL); |
| } |
| |
| static bool rseq_handle_cs(struct task_struct *t, struct pt_regs *regs) |
| { |
| struct rseq __user *urseq = t->rseq.usrptr; |
| u64 csaddr; |
| |
| scoped_user_read_access(urseq, efault) |
| unsafe_get_user(csaddr, &urseq->rseq_cs, efault); |
| if (likely(!csaddr)) |
| return true; |
| return rseq_update_user_cs(t, regs, csaddr); |
| efault: |
| return false; |
| } |
| |
| static void rseq_slowpath_update_usr(struct pt_regs *regs) |
| { |
| /* |
| * Preserve rseq state and user_irq state. The generic entry code |
| * clears user_irq on the way out, the non-generic entry |
| * architectures are not having user_irq. |
| */ |
| const struct rseq_event evt_mask = { .has_rseq = true, .user_irq = true, }; |
| struct task_struct *t = current; |
| struct rseq_ids ids; |
| u32 node_id; |
| bool event; |
| |
| if (unlikely(t->flags & PF_EXITING)) |
| return; |
| |
| rseq_stat_inc(rseq_stats.slowpath); |
| |
| /* |
| * Read and clear the event pending bit first. If the task |
| * was not preempted or migrated or a signal is on the way, |
| * there is no point in doing any of the heavy lifting here |
| * on production kernels. In that case TIF_NOTIFY_RESUME |
| * was raised by some other functionality. |
| * |
| * This is correct because the read/clear operation is |
| * guarded against scheduler preemption, which makes it CPU |
| * local atomic. If the task is preempted right after |
| * re-enabling preemption then TIF_NOTIFY_RESUME is set |
| * again and this function is invoked another time _before_ |
| * the task is able to return to user mode. |
| * |
| * On a debug kernel, invoke the fixup code unconditionally |
| * with the result handed in to allow the detection of |
| * inconsistencies. |
| */ |
| scoped_guard(irq) { |
| event = t->rseq.event.sched_switch; |
| t->rseq.event.all &= evt_mask.all; |
| ids.cpu_id = task_cpu(t); |
| ids.mm_cid = task_mm_cid(t); |
| } |
| |
| if (!event) |
| return; |
| |
| node_id = cpu_to_node(ids.cpu_id); |
| |
| if (unlikely(!rseq_update_usr(t, regs, &ids, node_id))) { |
| /* |
| * Clear the errors just in case this might survive magically, but |
| * leave the rest intact. |
| */ |
| t->rseq.event.error = 0; |
| force_sig(SIGSEGV); |
| } |
| } |
| |
| void __rseq_handle_slowpath(struct pt_regs *regs) |
| { |
| /* |
| * If invoked from hypervisors before entering the guest via |
| * resume_user_mode_work(), then @regs is a NULL pointer. |
| * |
| * resume_user_mode_work() clears TIF_NOTIFY_RESUME and re-raises |
| * it before returning from the ioctl() to user space when |
| * rseq_event.sched_switch is set. |
| * |
| * So it's safe to ignore here instead of pointlessly updating it |
| * in the vcpu_run() loop. |
| */ |
| if (!regs) |
| return; |
| |
| rseq_slowpath_update_usr(regs); |
| } |
| |
| void __rseq_signal_deliver(int sig, struct pt_regs *regs) |
| { |
| rseq_stat_inc(rseq_stats.signal); |
| /* |
| * Don't update IDs, they are handled on exit to user if |
| * necessary. The important thing is to abort a critical section of |
| * the interrupted context as after this point the instruction |
| * pointer in @regs points to the signal handler. |
| */ |
| if (unlikely(!rseq_handle_cs(current, regs))) { |
| /* |
| * Clear the errors just in case this might survive |
| * magically, but leave the rest intact. |
| */ |
| current->rseq.event.error = 0; |
| force_sigsegv(sig); |
| } |
| } |
| |
| /* |
| * Terminate the process if a syscall is issued within a restartable |
| * sequence. |
| */ |
| void __rseq_debug_syscall_return(struct pt_regs *regs) |
| { |
| struct task_struct *t = current; |
| u64 csaddr; |
| |
| if (!t->rseq.event.has_rseq) |
| return; |
| if (get_user(csaddr, &t->rseq.usrptr->rseq_cs)) |
| goto fail; |
| if (likely(!csaddr)) |
| return; |
| if (unlikely(csaddr >= TASK_SIZE)) |
| goto fail; |
| if (rseq_debug_update_user_cs(t, regs, csaddr)) |
| return; |
| fail: |
| force_sig(SIGSEGV); |
| } |
| |
| #ifdef CONFIG_DEBUG_RSEQ |
| /* Kept around to keep GENERIC_ENTRY=n architectures supported. */ |
| void rseq_syscall(struct pt_regs *regs) |
| { |
| __rseq_debug_syscall_return(regs); |
| } |
| #endif |
| |
| static bool rseq_reset_ids(void) |
| { |
| struct rseq_ids ids = { |
| .cpu_id = RSEQ_CPU_ID_UNINITIALIZED, |
| .mm_cid = 0, |
| }; |
| |
| /* |
| * If this fails, terminate it because this leaves the kernel in |
| * stupid state as exit to user space will try to fixup the ids |
| * again. |
| */ |
| if (rseq_set_ids(current, &ids, 0)) |
| return true; |
| |
| force_sig(SIGSEGV); |
| return false; |
| } |
| |
| /* The original rseq structure size (including padding) is 32 bytes. */ |
| #define ORIG_RSEQ_SIZE 32 |
| |
| /* |
| * sys_rseq - setup restartable sequences for caller thread. |
| */ |
| SYSCALL_DEFINE4(rseq, struct rseq __user *, rseq, u32, rseq_len, int, flags, u32, sig) |
| { |
| if (flags & RSEQ_FLAG_UNREGISTER) { |
| if (flags & ~RSEQ_FLAG_UNREGISTER) |
| return -EINVAL; |
| /* Unregister rseq for current thread. */ |
| if (current->rseq.usrptr != rseq || !current->rseq.usrptr) |
| return -EINVAL; |
| if (rseq_len != current->rseq.len) |
| return -EINVAL; |
| if (current->rseq.sig != sig) |
| return -EPERM; |
| if (!rseq_reset_ids()) |
| return -EFAULT; |
| rseq_reset(current); |
| return 0; |
| } |
| |
| if (unlikely(flags)) |
| return -EINVAL; |
| |
| if (current->rseq.usrptr) { |
| /* |
| * If rseq is already registered, check whether |
| * the provided address differs from the prior |
| * one. |
| */ |
| if (current->rseq.usrptr != rseq || rseq_len != current->rseq.len) |
| return -EINVAL; |
| if (current->rseq.sig != sig) |
| return -EPERM; |
| /* Already registered. */ |
| return -EBUSY; |
| } |
| |
| /* |
| * If there was no rseq previously registered, ensure the provided rseq |
| * is properly aligned, as communcated to user-space through the ELF |
| * auxiliary vector AT_RSEQ_ALIGN. If rseq_len is the original rseq |
| * size, the required alignment is the original struct rseq alignment. |
| * |
| * In order to be valid, rseq_len is either the original rseq size, or |
| * large enough to contain all supported fields, as communicated to |
| * user-space through the ELF auxiliary vector AT_RSEQ_FEATURE_SIZE. |
| */ |
| if (rseq_len < ORIG_RSEQ_SIZE || |
| (rseq_len == ORIG_RSEQ_SIZE && !IS_ALIGNED((unsigned long)rseq, ORIG_RSEQ_SIZE)) || |
| (rseq_len != ORIG_RSEQ_SIZE && (!IS_ALIGNED((unsigned long)rseq, __alignof__(*rseq)) || |
| rseq_len < offsetof(struct rseq, end)))) |
| return -EINVAL; |
| if (!access_ok(rseq, rseq_len)) |
| return -EFAULT; |
| |
| scoped_user_write_access(rseq, efault) { |
| /* |
| * If the rseq_cs pointer is non-NULL on registration, clear it to |
| * avoid a potential segfault on return to user-space. The proper thing |
| * to do would have been to fail the registration but this would break |
| * older libcs that reuse the rseq area for new threads without |
| * clearing the fields. Don't bother reading it, just reset it. |
| */ |
| unsafe_put_user(0UL, &rseq->rseq_cs, efault); |
| /* Initialize IDs in user space */ |
| unsafe_put_user(RSEQ_CPU_ID_UNINITIALIZED, &rseq->cpu_id_start, efault); |
| unsafe_put_user(RSEQ_CPU_ID_UNINITIALIZED, &rseq->cpu_id, efault); |
| unsafe_put_user(0U, &rseq->node_id, efault); |
| unsafe_put_user(0U, &rseq->mm_cid, efault); |
| } |
| |
| /* |
| * Activate the registration by setting the rseq area address, length |
| * and signature in the task struct. |
| */ |
| current->rseq.usrptr = rseq; |
| current->rseq.len = rseq_len; |
| current->rseq.sig = sig; |
| |
| /* |
| * If rseq was previously inactive, and has just been |
| * registered, ensure the cpu_id_start and cpu_id fields |
| * are updated before returning to user-space. |
| */ |
| current->rseq.event.has_rseq = true; |
| rseq_force_update(); |
| return 0; |
| |
| efault: |
| return -EFAULT; |
| } |