[PATCH] ptrace: add ability to retrieve signals without removing from a queue (v2)
From: Andrey Vagin <hidden>
Date: 2013-02-25 10:08:30
Also in:
lkml
Subsystem:
ptrace support, the rest · Maintainers:
Oleg Nesterov, Linus Torvalds
This patch adds a new ptrace request PTRACE_PEEKSIGINFO.
This request is used to retrieve information about signals starting with
the specified sequence number. Siginfo_t structures are copied from the
child into the buffer starting at "data".
The argument "addr" is a pointer to struct ptrace_peeksiginfo_args.
struct ptrace_peeksiginfo_args {
u64 off; /* from which siginfo to start */
u32 nr; /* how may siginfos to take */
u32 flags;
};
Currently here is only one flag PTRACE_PEEKSIGINFO_SHARED for dumping
signals from process-wide shared queue. If this flag is not set, a
signal is read from a per-thread queue. A result siginfo contains a
kernel part of si_code which usually striped, but it's required for
queuing the same siginfo back during restore of pending signals.
The request PTRACE_PEEKSIGINFO returns a number of dumped signals.
If a signal with the specified sequence number doesn't exist, ptrace
returns 0.
This functionality is required for checkpointing pending signals.
The prototype of this code was developed by Oleg Nesterov.
v2: * don't wrapped on CONFIG_CHECKPOINT_RESTORE. This functionality is
going to be used in gdb.
* use ptrace_peeksiginfo_args, because addr is too small for
encoding a signal number and flags.
Cc: Roland McGrath <redacted>
Cc: Oleg Nesterov <redacted>
Cc: Andrew Morton <akpm-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b@public.gmane.org>
Cc: "Paul E. McKenney" <redacted>
Cc: David Howells <redacted>
Cc: Dave Jones <redacted>
Cc: "Michael Kerrisk (man-pages)" <redacted>
Cc: Pavel Emelyanov <redacted>
Cc: Linus Torvalds <torvalds-de/tnXTf+JLsfHDXvbKv3WD2FQJk+8+b@public.gmane.org>
Cc: Pedro Alves <redacted>
Signed-off-by: Andrey Vagin <redacted>
---
include/uapi/linux/ptrace.h | 12 +++++++
kernel/ptrace.c | 76 +++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 88 insertions(+)
diff --git a/include/uapi/linux/ptrace.h b/include/uapi/linux/ptrace.h
index 022ab18..01b4bd8 100644
--- a/include/uapi/linux/ptrace.h
+++ b/include/uapi/linux/ptrace.h@@ -5,6 +5,7 @@ /* has the defines to get at the registers. */ +#include <linux/types.h> #define PTRACE_TRACEME 0 #define PTRACE_PEEKTEXT 1
@@ -52,6 +53,17 @@ #define PTRACE_INTERRUPT 0x4207 #define PTRACE_LISTEN 0x4208 +#define PTRACE_PEEKSIGINFO 0x4209 + +struct ptrace_peeksiginfo_args { + __u64 off; /* from which siginfo to start */ + __u32 nr; /* how may siginfos to take */ + __u32 flags; +}; + +/* Read signals from a shared (process wide) queue */ +#define PTRACE_PEEKSIGINFO_SHARED (1 << 0) + /* Wait extended result codes for the above trace options. */ #define PTRACE_EVENT_FORK 1 #define PTRACE_EVENT_VFORK 2
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 6cbeaae..ceecdcd 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c@@ -24,6 +24,7 @@ #include <linux/regset.h> #include <linux/hw_breakpoint.h> #include <linux/cn_proc.h> +#include <linux/compat.h> static int ptrace_trapping_sleep_fn(void *flags)
@@ -618,6 +619,77 @@ static int ptrace_setsiginfo(struct task_struct *child, const siginfo_t *info) return error; } +static int ptrace_peek_siginfo(struct task_struct *child, + unsigned long addr, + unsigned long data) +{ + struct ptrace_peeksiginfo_args arg; + struct sigpending *pending; + struct sigqueue *q; + siginfo_t info; + long long off; + int ret, i; + + ret = copy_from_user(&arg, (void __user *) addr, + sizeof(struct ptrace_peeksiginfo_args)); + if (ret) + return ret; + + if (arg.flags & PTRACE_PEEKSIGINFO_SHARED) + pending = &child->signal->shared_pending; + else + pending = &child->pending; + + if (arg.flags & ~PTRACE_PEEKSIGINFO_SHARED) + return -EINVAL; /* unknown flags */ + + for (i = 0; i < arg.nr; i++) { + off = arg.off + i; + + spin_lock_irq(&child->sighand->siglock); + list_for_each_entry(q, &pending->list, list) { + if (!off--) { + copy_siginfo(&info, &q->info); + break; + } + } + spin_unlock_irq(&child->sighand->siglock); + + if (off >= 0) + break; + +#ifdef CONFIG_COMPAT + if (unlikely(is_compat_task())) { + compat_siginfo_t __user *uinfo = compat_ptr(data); + + ret = copy_siginfo_to_user32(uinfo, &info); + if (!ret) + ret = __put_user(info.si_code, &uinfo->si_code); + } else +#endif + { + siginfo_t __user *uinfo = (siginfo_t __user *) data; + + ret = copy_siginfo_to_user(uinfo, &info); + if (!ret) + ret = __put_user(info.si_code, &uinfo->si_code); + } + + if (ret) + break; + + data += sizeof(siginfo_t); + + if (signal_pending(current)) { + i++; + break; + } + + cond_resched(); + } + + return i ? : ret; +} #ifdef PTRACE_SINGLESTEP #define is_singlestep(request) ((request) == PTRACE_SINGLESTEP)
@@ -742,6 +814,10 @@ int ptrace_request(struct task_struct *child, long request, ret = put_user(child->ptrace_message, datalp); break; + case PTRACE_PEEKSIGINFO: + ret = ptrace_peek_siginfo(child, addr, data); + break; + case PTRACE_GETSIGINFO: ret = ptrace_getsiginfo(child, &siginfo); if (!ret)
--
1.7.11.7