Skip to content

Commit bbc6986

Browse files
Roland McGrathtorvalds
Roland McGrath
authored andcommitted
task_current_syscall
This adds the new function task_current_syscall() on machines where the asm/syscall.h interface is supported (CONFIG_HAVE_ARCH_TRACEHOOK). It's exported for modules to use in the future. This function safely samples the state of a blocked thread to collect what system call it is blocked in, and the six system call argument registers. Signed-off-by: Roland McGrath <[email protected]> Cc: Oleg Nesterov <[email protected]> Reviewed-by: Ingo Molnar <[email protected]> Signed-off-by: Andrew Morton <[email protected]> Signed-off-by: Linus Torvalds <[email protected]>
1 parent 85ba2d8 commit bbc6986

File tree

3 files changed

+81
-0
lines changed

3 files changed

+81
-0
lines changed

include/linux/ptrace.h

+4
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,10 @@ static inline void user_enable_block_step(struct task_struct *task)
314314
#define arch_ptrace_stop(code, info) do { } while (0)
315315
#endif
316316

317+
extern int task_current_syscall(struct task_struct *target, long *callno,
318+
unsigned long args[6], unsigned int maxargs,
319+
unsigned long *sp, unsigned long *pc);
320+
317321
#endif
318322

319323
#endif

lib/Makefile

+2
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ lib-$(CONFIG_GENERIC_BUG) += bug.o
7878

7979
obj-$(CONFIG_HAVE_LMB) += lmb.o
8080

81+
obj-$(CONFIG_HAVE_ARCH_TRACEHOOK) += syscall.o
82+
8183
hostprogs-y := gen_crc32table
8284
clean-files := crc32table.h
8385

lib/syscall.c

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#include <linux/ptrace.h>
2+
#include <linux/sched.h>
3+
#include <linux/module.h>
4+
#include <asm/syscall.h>
5+
6+
static int collect_syscall(struct task_struct *target, long *callno,
7+
unsigned long args[6], unsigned int maxargs,
8+
unsigned long *sp, unsigned long *pc)
9+
{
10+
struct pt_regs *regs = task_pt_regs(target);
11+
if (unlikely(!regs))
12+
return -EAGAIN;
13+
14+
*sp = user_stack_pointer(regs);
15+
*pc = instruction_pointer(regs);
16+
17+
*callno = syscall_get_nr(target, regs);
18+
if (*callno != -1L && maxargs > 0)
19+
syscall_get_arguments(target, regs, 0, maxargs, args);
20+
21+
return 0;
22+
}
23+
24+
/**
25+
* task_current_syscall - Discover what a blocked task is doing.
26+
* @target: thread to examine
27+
* @callno: filled with system call number or -1
28+
* @args: filled with @maxargs system call arguments
29+
* @maxargs: number of elements in @args to fill
30+
* @sp: filled with user stack pointer
31+
* @pc: filled with user PC
32+
*
33+
* If @target is blocked in a system call, returns zero with *@callno
34+
* set to the the call's number and @args filled in with its arguments.
35+
* Registers not used for system call arguments may not be available and
36+
* it is not kosher to use &struct user_regset calls while the system
37+
* call is still in progress. Note we may get this result if @target
38+
* has finished its system call but not yet returned to user mode, such
39+
* as when it's stopped for signal handling or syscall exit tracing.
40+
*
41+
* If @target is blocked in the kernel during a fault or exception,
42+
* returns zero with *@callno set to -1 and does not fill in @args.
43+
* If so, it's now safe to examine @target using &struct user_regset
44+
* get() calls as long as we're sure @target won't return to user mode.
45+
*
46+
* Returns -%EAGAIN if @target does not remain blocked.
47+
*
48+
* Returns -%EINVAL if @maxargs is too large (maximum is six).
49+
*/
50+
int task_current_syscall(struct task_struct *target, long *callno,
51+
unsigned long args[6], unsigned int maxargs,
52+
unsigned long *sp, unsigned long *pc)
53+
{
54+
long state;
55+
unsigned long ncsw;
56+
57+
if (unlikely(maxargs > 6))
58+
return -EINVAL;
59+
60+
if (target == current)
61+
return collect_syscall(target, callno, args, maxargs, sp, pc);
62+
63+
state = target->state;
64+
if (unlikely(!state))
65+
return -EAGAIN;
66+
67+
ncsw = wait_task_inactive(target, state);
68+
if (unlikely(!ncsw) ||
69+
unlikely(collect_syscall(target, callno, args, maxargs, sp, pc)) ||
70+
unlikely(wait_task_inactive(target, state) != ncsw))
71+
return -EAGAIN;
72+
73+
return 0;
74+
}
75+
EXPORT_SYMBOL_GPL(task_current_syscall);

0 commit comments

Comments
 (0)