1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2bbc69863SRoland McGrath #include <linux/ptrace.h>
3bbc69863SRoland McGrath #include <linux/sched.h>
468db0cf1SIngo Molnar #include <linux/sched/task_stack.h>
58bc3bcc9SPaul Gortmaker #include <linux/export.h>
6bbc69863SRoland McGrath #include <asm/syscall.h>
7bbc69863SRoland McGrath
collect_syscall(struct task_struct * target,struct syscall_info * info)8631b7abaSSteven Rostedt (Red Hat) static int collect_syscall(struct task_struct *target, struct syscall_info *info)
9bbc69863SRoland McGrath {
104f134b89SWilly Tarreau unsigned long args[6] = { };
11aa1f1a63SAndy Lutomirski struct pt_regs *regs;
12aa1f1a63SAndy Lutomirski
13aa1f1a63SAndy Lutomirski if (!try_get_task_stack(target)) {
14aa1f1a63SAndy Lutomirski /* Task has no stack, so the task isn't in a syscall. */
15631b7abaSSteven Rostedt (Red Hat) memset(info, 0, sizeof(*info));
16631b7abaSSteven Rostedt (Red Hat) info->data.nr = -1;
17aa1f1a63SAndy Lutomirski return 0;
18aa1f1a63SAndy Lutomirski }
19aa1f1a63SAndy Lutomirski
20aa1f1a63SAndy Lutomirski regs = task_pt_regs(target);
21aa1f1a63SAndy Lutomirski if (unlikely(!regs)) {
22aa1f1a63SAndy Lutomirski put_task_stack(target);
23bbc69863SRoland McGrath return -EAGAIN;
24aa1f1a63SAndy Lutomirski }
25bbc69863SRoland McGrath
26631b7abaSSteven Rostedt (Red Hat) info->sp = user_stack_pointer(regs);
27631b7abaSSteven Rostedt (Red Hat) info->data.instruction_pointer = instruction_pointer(regs);
28bbc69863SRoland McGrath
29631b7abaSSteven Rostedt (Red Hat) info->data.nr = syscall_get_nr(target, regs);
30631b7abaSSteven Rostedt (Red Hat) if (info->data.nr != -1L)
314f134b89SWilly Tarreau syscall_get_arguments(target, regs, args);
324f134b89SWilly Tarreau
334f134b89SWilly Tarreau info->data.args[0] = args[0];
344f134b89SWilly Tarreau info->data.args[1] = args[1];
354f134b89SWilly Tarreau info->data.args[2] = args[2];
364f134b89SWilly Tarreau info->data.args[3] = args[3];
374f134b89SWilly Tarreau info->data.args[4] = args[4];
384f134b89SWilly Tarreau info->data.args[5] = args[5];
39bbc69863SRoland McGrath
40aa1f1a63SAndy Lutomirski put_task_stack(target);
41bbc69863SRoland McGrath return 0;
42bbc69863SRoland McGrath }
43bbc69863SRoland McGrath
44bbc69863SRoland McGrath /**
45bbc69863SRoland McGrath * task_current_syscall - Discover what a blocked task is doing.
46bbc69863SRoland McGrath * @target: thread to examine
47631b7abaSSteven Rostedt (Red Hat) * @info: structure with the following fields:
48631b7abaSSteven Rostedt (Red Hat) * .sp - filled with user stack pointer
49631b7abaSSteven Rostedt (Red Hat) * .data.nr - filled with system call number or -1
50631b7abaSSteven Rostedt (Red Hat) * .data.args - filled with @maxargs system call arguments
51631b7abaSSteven Rostedt (Red Hat) * .data.instruction_pointer - filled with user PC
52bbc69863SRoland McGrath *
53631b7abaSSteven Rostedt (Red Hat) * If @target is blocked in a system call, returns zero with @info.data.nr
54408a93a2SRandy Dunlap * set to the call's number and @info.data.args filled in with its
55631b7abaSSteven Rostedt (Red Hat) * arguments. Registers not used for system call arguments may not be available
56631b7abaSSteven Rostedt (Red Hat) * and it is not kosher to use &struct user_regset calls while the system
57bbc69863SRoland McGrath * call is still in progress. Note we may get this result if @target
58bbc69863SRoland McGrath * has finished its system call but not yet returned to user mode, such
59bbc69863SRoland McGrath * as when it's stopped for signal handling or syscall exit tracing.
60bbc69863SRoland McGrath *
61bbc69863SRoland McGrath * If @target is blocked in the kernel during a fault or exception,
62631b7abaSSteven Rostedt (Red Hat) * returns zero with *@info.data.nr set to -1 and does not fill in
63631b7abaSSteven Rostedt (Red Hat) * @info.data.args. If so, it's now safe to examine @target using
64631b7abaSSteven Rostedt (Red Hat) * &struct user_regset get() calls as long as we're sure @target won't return
65631b7abaSSteven Rostedt (Red Hat) * to user mode.
66bbc69863SRoland McGrath *
67bbc69863SRoland McGrath * Returns -%EAGAIN if @target does not remain blocked.
68bbc69863SRoland McGrath */
task_current_syscall(struct task_struct * target,struct syscall_info * info)69631b7abaSSteven Rostedt (Red Hat) int task_current_syscall(struct task_struct *target, struct syscall_info *info)
70bbc69863SRoland McGrath {
71bbc69863SRoland McGrath unsigned long ncsw;
72*2f064a59SPeter Zijlstra unsigned int state;
73bbc69863SRoland McGrath
74bbc69863SRoland McGrath if (target == current)
75631b7abaSSteven Rostedt (Red Hat) return collect_syscall(target, info);
76bbc69863SRoland McGrath
77*2f064a59SPeter Zijlstra state = READ_ONCE(target->__state);
78bbc69863SRoland McGrath if (unlikely(!state))
79bbc69863SRoland McGrath return -EAGAIN;
80bbc69863SRoland McGrath
81bbc69863SRoland McGrath ncsw = wait_task_inactive(target, state);
82bbc69863SRoland McGrath if (unlikely(!ncsw) ||
83631b7abaSSteven Rostedt (Red Hat) unlikely(collect_syscall(target, info)) ||
84bbc69863SRoland McGrath unlikely(wait_task_inactive(target, state) != ncsw))
85bbc69863SRoland McGrath return -EAGAIN;
86bbc69863SRoland McGrath
87bbc69863SRoland McGrath return 0;
88bbc69863SRoland McGrath }
89