Blame view

lib/syscall.c 2.65 KB
b24413180   Greg Kroah-Hartman   License cleanup: ...
1
  // SPDX-License-Identifier: GPL-2.0
bbc698636   Roland McGrath   task_current_syscall
2
3
  #include <linux/ptrace.h>
  #include <linux/sched.h>
68db0cf10   Ingo Molnar   sched/headers: Pr...
4
  #include <linux/sched/task_stack.h>
8bc3bcc93   Paul Gortmaker   lib: reduce the u...
5
  #include <linux/export.h>
bbc698636   Roland McGrath   task_current_syscall
6
7
8
9
10
11
  #include <asm/syscall.h>
  
  static int collect_syscall(struct task_struct *target, long *callno,
  			   unsigned long args[6], unsigned int maxargs,
  			   unsigned long *sp, unsigned long *pc)
  {
aa1f1a639   Andy Lutomirski   lib/syscall: Pin ...
12
13
14
15
  	struct pt_regs *regs;
  
  	if (!try_get_task_stack(target)) {
  		/* Task has no stack, so the task isn't in a syscall. */
854fbd6e5   Kees Cook   lib/syscall: Clea...
16
  		*sp = *pc = 0;
aa1f1a639   Andy Lutomirski   lib/syscall: Pin ...
17
18
19
20
21
22
23
  		*callno = -1;
  		return 0;
  	}
  
  	regs = task_pt_regs(target);
  	if (unlikely(!regs)) {
  		put_task_stack(target);
bbc698636   Roland McGrath   task_current_syscall
24
  		return -EAGAIN;
aa1f1a639   Andy Lutomirski   lib/syscall: Pin ...
25
  	}
bbc698636   Roland McGrath   task_current_syscall
26
27
28
29
30
31
32
  
  	*sp = user_stack_pointer(regs);
  	*pc = instruction_pointer(regs);
  
  	*callno = syscall_get_nr(target, regs);
  	if (*callno != -1L && maxargs > 0)
  		syscall_get_arguments(target, regs, 0, maxargs, args);
aa1f1a639   Andy Lutomirski   lib/syscall: Pin ...
33
  	put_task_stack(target);
bbc698636   Roland McGrath   task_current_syscall
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
  	return 0;
  }
  
  /**
   * task_current_syscall - Discover what a blocked task is doing.
   * @target:		thread to examine
   * @callno:		filled with system call number or -1
   * @args:		filled with @maxargs system call arguments
   * @maxargs:		number of elements in @args to fill
   * @sp:			filled with user stack pointer
   * @pc:			filled with user PC
   *
   * If @target is blocked in a system call, returns zero with *@callno
   * set to the the call's number and @args filled in with its arguments.
   * Registers not used for system call arguments may not be available and
   * it is not kosher to use &struct user_regset calls while the system
   * call is still in progress.  Note we may get this result if @target
   * has finished its system call but not yet returned to user mode, such
   * as when it's stopped for signal handling or syscall exit tracing.
   *
   * If @target is blocked in the kernel during a fault or exception,
   * returns zero with *@callno set to -1 and does not fill in @args.
   * If so, it's now safe to examine @target using &struct user_regset
   * get() calls as long as we're sure @target won't return to user mode.
   *
   * Returns -%EAGAIN if @target does not remain blocked.
   *
   * Returns -%EINVAL if @maxargs is too large (maximum is six).
   */
  int task_current_syscall(struct task_struct *target, long *callno,
  			 unsigned long args[6], unsigned int maxargs,
  			 unsigned long *sp, unsigned long *pc)
  {
  	long state;
  	unsigned long ncsw;
  
  	if (unlikely(maxargs > 6))
  		return -EINVAL;
  
  	if (target == current)
  		return collect_syscall(target, callno, args, maxargs, sp, pc);
  
  	state = target->state;
  	if (unlikely(!state))
  		return -EAGAIN;
  
  	ncsw = wait_task_inactive(target, state);
  	if (unlikely(!ncsw) ||
  	    unlikely(collect_syscall(target, callno, args, maxargs, sp, pc)) ||
  	    unlikely(wait_task_inactive(target, state) != ncsw))
  		return -EAGAIN;
  
  	return 0;
  }