[PATCH 06/11] ptrace: make group stop state visible via PTRACE_GETSIGINFO

From: Tejun Heo
Date: Sun May 08 2011 - 11:50:42 EST


Ptracer can detect tracee entering group stop by watching for the
group stop trap; however, there is no reliable way to find out when
group stop ends - SIGCONT may be processed by another thread and
signal delivery is blocked while tracee is trapped.

This patch adds siginfo.si_pt_flags and uses PTRACE_SI_STOPPED flag to
indicate whether group stop is in effect or not. While tracee is
trapped for anything other than signal delivery and group stop itself,
tracer can use PTRACE_GETSIGINFO to access this information. Note
that it's only available if tracee was seized.

This doesn't address notification and tracer has to put tracee in an
appropriate trap and poll the flag. Later patches will deal with
notification and trap transition.

This essentially is a simple addition of a flag but seems complicated
thanks to siginfo_t convolution. _sigtrap struct, which contains all
the fields used by ptrace_notify[_locked]() and the new _pt_flags, is
added to siginfo._sifields union along with the field abbreviation
macro si_pt_flags; then, __SI_TRAP is defined to implement copying of
the new field to userland.

Two architectures - ia64 and mips - define their own versions of
siginfo_t and ia64 implements its own copy_siginfo_to_user(). Also,
x86, mips, parisc, powerpc, s390, sparc and tile have compat_siginfo_t
and copy_siginfo_to_user32() for 32bit compatibility. All are updated
such that [compat_]siginfo_t have _sigtrap and all the fields are
copied out.

x86 is tested. Affected code in mips, powerpc, s390 and sparc are
compile tested. mips and tile are untested.

Test program follows.

#define PTRACE_SEIZE 0x4206
#define PTRACE_INTERRUPT 0x4207

#define PTRACE_SEIZE_DEVEL 0x80000000

static const struct timespec ts1s = { .tv_sec = 1 };

int main(int argc, char **argv)
{
pid_t tracee, tracer;
int i;

tracee = fork();
if (!tracee)
while (1)
nanosleep(&ts1s, NULL);

tracer = fork();
if (!tracer) {
int last_stopped = 0, stopped;
siginfo_t si;

ptrace(PTRACE_SEIZE, tracee, NULL,
(void *)(unsigned long)PTRACE_SEIZE_DEVEL);
repeat:
waitid(P_PID, tracee, NULL, WSTOPPED);

if (!ptrace(PTRACE_GETSIGINFO, tracee, NULL, &si)) {
if (si.si_code) {
stopped = !!si.si_status;
if (stopped != last_stopped)
printf("tracer: stopped=%d\n", stopped);
last_stopped = stopped;
ptrace(PTRACE_CONT, tracee, NULL, NULL);
} else {
printf("tracer: SIG %d\n", si.si_signo);
ptrace(PTRACE_CONT, tracee, NULL,
(void *)(unsigned long)si.si_signo);
}
} else
ptrace(PTRACE_CONT, tracee, NULL, NULL);
ptrace(PTRACE_INTERRUPT, tracee, NULL, NULL);
goto repeat;
}

for (i = 0; i < 3; i++) {
nanosleep(&ts1s, NULL);
printf("mother: SIGSTOP\n");
kill(tracee, SIGSTOP);
nanosleep(&ts1s, NULL);
printf("mother: SIGCONT\n");
kill(tracee, SIGCONT);
}
nanosleep(&ts1s, NULL);

kill(tracer, SIGKILL);
kill(tracee, SIGKILL);
return 0;
}

Tracer delivers signal, resumes group stop, induces INTERRUPT traps
and reports group stop state change in busy loop. Mother sends
SIGSTOP or CONT to tracee on each second. Note that si_pt_flags and
flag testing are replaced with si_status testing. si_status occupies
the same offset as si_pt_flags and PTRACE_SI_STOPPED is the only flag
defined, so I took a dirty short cut.

# ./test-stopped
mother: SIGSTOP
tracer: SIG 19
tracer: stopped=1
mother: SIGCONT
tracer: SIG 18
tracer: stopped=0
mother: SIGSTOP
tracer: SIG 19
tracer: stopped=1
mother: SIGCONT
tracer: stopped=0
tracer: SIG 18
mother: SIGSTOP
tracer: SIG 19
tracer: stopped=1
mother: SIGCONT
tracer: SIG 18
tracer: stopped=0

Signed-off-by: Tejun Heo <tj@xxxxxxxxxx>
Cc: Tony Luck <tony.luck@xxxxxxxxx>
Cc: Fenghua Yu <fenghua.yu@xxxxxxxxx>
Cc: Ralf Baechle <ralf@xxxxxxxxxxxxxx>
Cc: Kyle McMartin <kyle@xxxxxxxxxxx>
Cc: Helge Deller <deller@xxxxxx>
Cc: "James E.J. Bottomley" <jejb@xxxxxxxxxxxxxxxx>
Cc: Benjamin Herrenschmidt <benh@xxxxxxxxxxxxxxxxxxx>
Cc: Paul Mackerras <paulus@xxxxxxxxx>
Cc: Martin Schwidefsky <schwidefsky@xxxxxxxxxx>
Cc: Heiko Carstens <heiko.carstens@xxxxxxxxxx>
Cc: "David S. Miller" <davem@xxxxxxxxxxxxx>
Cc: Chris Metcalf <cmetcalf@xxxxxxxxxx>
Cc: x86@xxxxxxxxxx
---
arch/ia64/include/asm/siginfo.h | 7 +++++++
arch/ia64/kernel/signal.c | 5 +++++
arch/mips/include/asm/compat-signal.h | 7 +++++++
arch/mips/include/asm/siginfo.h | 7 +++++++
arch/mips/kernel/signal32.c | 5 +++++
arch/parisc/kernel/signal32.c | 5 +++++
arch/parisc/kernel/signal32.h | 7 +++++++
arch/powerpc/kernel/ppc32.h | 7 +++++++
arch/powerpc/kernel/signal_32.c | 5 +++++
arch/s390/kernel/compat_linux.h | 7 +++++++
arch/s390/kernel/compat_signal.c | 5 +++++
arch/sparc/kernel/signal32.c | 12 ++++++++++++
arch/tile/kernel/compat_signal.c | 11 +++++++++++
arch/x86/ia32/ia32_signal.c | 4 ++++
arch/x86/include/asm/ia32.h | 7 +++++++
include/asm-generic/siginfo.h | 10 ++++++++++
include/linux/ptrace.h | 3 +++
kernel/ptrace.c | 13 +++++++++++++
kernel/signal.c | 7 ++++++-
19 files changed, 133 insertions(+), 1 deletions(-)

diff --git a/arch/ia64/include/asm/siginfo.h b/arch/ia64/include/asm/siginfo.h
index c8fcaa2..2cff1ce 100644
--- a/arch/ia64/include/asm/siginfo.h
+++ b/arch/ia64/include/asm/siginfo.h
@@ -70,6 +70,13 @@ typedef struct siginfo {
long _band; /* POLL_IN, POLL_OUT, POLL_MSG (XPG requires a "long") */
int _fd;
} _sigpoll;
+
+ /* SIGTRAP */
+ struct {
+ pid_t _pid; /* sender's pid */
+ uid_t _uid; /* sender's uid */
+ unsigned int _pt_flags;
+ } _sigtrap;
} _sifields;
} siginfo_t;

diff --git a/arch/ia64/kernel/signal.c b/arch/ia64/kernel/signal.c
index 7bdafc8..ee18366 100644
--- a/arch/ia64/kernel/signal.c
+++ b/arch/ia64/kernel/signal.c
@@ -142,6 +142,11 @@ copy_siginfo_to_user (siginfo_t __user *to, siginfo_t *from)
err |= __put_user(from->si_addr, &to->si_addr);
err |= __put_user(from->si_imm, &to->si_imm);
break;
+ case __SI_TRAP >> 16:
+ err |= __put_user(from->si_uid, &to->si_uid);
+ err |= __put_user(from->si_pid, &to->si_pid);
+ err |= __put_user(from->si_pt_flags, &to->si_pt_flags);
+ break;
case __SI_TIMER >> 16:
err |= __put_user(from->si_tid, &to->si_tid);
err |= __put_user(from->si_overrun, &to->si_overrun);
diff --git a/arch/mips/include/asm/compat-signal.h b/arch/mips/include/asm/compat-signal.h
index 368a99e..47b2e4f 100644
--- a/arch/mips/include/asm/compat-signal.h
+++ b/arch/mips/include/asm/compat-signal.h
@@ -54,6 +54,13 @@ typedef struct compat_siginfo {
int _fd;
} _sigpoll;

+ /* SIGTRAP */
+ struct {
+ compat_pid_t _pid; /* sender's pid */
+ compat_uid_t _uid; /* sender's uid */
+ unsigned int _pt_flags;
+ } _sigtrap;
+
/* POSIX.1b timers */
struct {
timer_t _tid; /* timer id */
diff --git a/arch/mips/include/asm/siginfo.h b/arch/mips/include/asm/siginfo.h
index 1ca64b4..232920f 100644
--- a/arch/mips/include/asm/siginfo.h
+++ b/arch/mips/include/asm/siginfo.h
@@ -96,6 +96,13 @@ typedef struct siginfo {
__ARCH_SI_BAND_T _band; /* POLL_IN, POLL_OUT, POLL_MSG */
int _fd;
} _sigpoll;
+
+ /* SIGTRAP */
+ struct {
+ pid_t _pid; /* sender's pid */
+ __ARCH_SI_UID_T _uid; /* sender's uid */
+ unsigned int _pt_flags;
+ } _sigtrap;
} _sifields;
} siginfo_t;

diff --git a/arch/mips/kernel/signal32.c b/arch/mips/kernel/signal32.c
index aae9866..7e392e1 100644
--- a/arch/mips/kernel/signal32.c
+++ b/arch/mips/kernel/signal32.c
@@ -452,6 +452,11 @@ int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from)
err |= __put_user(from->si_band, &to->si_band);
err |= __put_user(from->si_fd, &to->si_fd);
break;
+ case __SI_TRAP >> 16:
+ err |= __put_user(from->si_pid, &to->si_pid);
+ err |= __put_user(from->si_uid, &to->si_uid);
+ err |= __put_user(from->si_pt_flags, &to->si_pt_flags);
+ break;
case __SI_RT >> 16: /* This is not generated by the kernel as of now. */
case __SI_MESGQ >> 16:
err |= __put_user(from->si_pid, &to->si_pid);
diff --git a/arch/parisc/kernel/signal32.c b/arch/parisc/kernel/signal32.c
index e141324..ead8ca4 100644
--- a/arch/parisc/kernel/signal32.c
+++ b/arch/parisc/kernel/signal32.c
@@ -482,6 +482,11 @@ copy_siginfo_to_user32 (compat_siginfo_t __user *to, siginfo_t *from)
err |= __put_user(from->si_band, &to->si_band);
err |= __put_user(from->si_fd, &to->si_fd);
break;
+ case __SI_TRAP >> 16:
+ err |= __put_user(from->si_pid, &to->si_pid);
+ err |= __put_user(from->si_uid, &to->si_uid);
+ err |= __put_user(from->si_pt_flags, &to->si_pt_flags);
+ break;
case __SI_TIMER >> 16:
err |= __put_user(from->si_tid, &to->si_tid);
err |= __put_user(from->si_overrun, &to->si_overrun);
diff --git a/arch/parisc/kernel/signal32.h b/arch/parisc/kernel/signal32.h
index c780084..8016f51 100644
--- a/arch/parisc/kernel/signal32.h
+++ b/arch/parisc/kernel/signal32.h
@@ -104,6 +104,13 @@ typedef struct compat_siginfo {
int _band; /* POLL_IN, POLL_OUT, POLL_MSG */
int _fd;
} _sigpoll;
+
+ /* SIGTRAP */
+ struct {
+ unsigned int _pid; /* sender's pid */
+ unsigned int _uid; /* sender's uid */
+ unsigned int _pt_flags;
+ } _sigtrap;
} _sifields;
} compat_siginfo_t;

diff --git a/arch/powerpc/kernel/ppc32.h b/arch/powerpc/kernel/ppc32.h
index dc16aef..4293542 100644
--- a/arch/powerpc/kernel/ppc32.h
+++ b/arch/powerpc/kernel/ppc32.h
@@ -64,6 +64,13 @@ typedef struct compat_siginfo {
int _band; /* POLL_IN, POLL_OUT, POLL_MSG */
int _fd;
} _sigpoll;
+
+ /* SIGTRAP */
+ struct {
+ compat_pid_t _pid; /* sender's pid */
+ compat_uid_t _uid; /* sender's uid */
+ unsigned int _pt_flags;
+ } _sigtrap;
} _sifields;
} compat_siginfo_t;

diff --git a/arch/powerpc/kernel/signal_32.c b/arch/powerpc/kernel/signal_32.c
index b96a3a0..d072458 100644
--- a/arch/powerpc/kernel/signal_32.c
+++ b/arch/powerpc/kernel/signal_32.c
@@ -716,6 +716,11 @@ int copy_siginfo_to_user32(struct compat_siginfo __user *d, siginfo_t *s)
err |= __put_user(s->si_band, &d->si_band);
err |= __put_user(s->si_fd, &d->si_fd);
break;
+ case __SI_TRAP:
+ err |= __put_user(s->si_pid, &d->si_pid);
+ err |= __put_user(s->si_uid, &d->si_uid);
+ err |= __put_user(s->si_pt_flags, &d->si_pt_flags);
+ break;
case __SI_TIMER >> 16:
err |= __put_user(s->si_tid, &d->si_tid);
err |= __put_user(s->si_overrun, &d->si_overrun);
diff --git a/arch/s390/kernel/compat_linux.h b/arch/s390/kernel/compat_linux.h
index 9635d75..f8c973f 100644
--- a/arch/s390/kernel/compat_linux.h
+++ b/arch/s390/kernel/compat_linux.h
@@ -72,6 +72,13 @@ typedef struct compat_siginfo {
int _band; /* POLL_IN, POLL_OUT, POLL_MSG */
int _fd;
} _sigpoll;
+
+ /* SIGTRAP */
+ struct {
+ pid_t _pid; /* sender's pid */
+ uid_t _uid; /* sender's uid */
+ unsigned int _pt_flags;
+ } _sigtrap;
} _sifields;
} compat_siginfo_t;

diff --git a/arch/s390/kernel/compat_signal.c b/arch/s390/kernel/compat_signal.c
index eee9998..b3c9f6b 100644
--- a/arch/s390/kernel/compat_signal.c
+++ b/arch/s390/kernel/compat_signal.c
@@ -96,6 +96,11 @@ int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from)
err |= __put_user(from->si_band, &to->si_band);
err |= __put_user(from->si_fd, &to->si_fd);
break;
+ case __SI_TRAP >> 16:
+ err |= __put_user(from->si_pid, &to->si_pid);
+ err |= __put_user(from->si_uid, &to->si_uid);
+ err |= __put_user(from->si_pt_flags, &to->si_pt_flags);
+ break;
case __SI_TIMER >> 16:
err |= __put_user(from->si_tid, &to->si_tid);
err |= __put_user(from->si_overrun, &to->si_overrun);
diff --git a/arch/sparc/kernel/signal32.c b/arch/sparc/kernel/signal32.c
index 75fad42..c545212 100644
--- a/arch/sparc/kernel/signal32.c
+++ b/arch/sparc/kernel/signal32.c
@@ -102,6 +102,13 @@ typedef struct compat_siginfo{
int _band; /* POLL_IN, POLL_OUT, POLL_MSG */
int _fd;
} _sigpoll;
+
+ /* SIGTRAP */
+ struct {
+ compat_pid_t _pid; /* sender's pid */
+ unsigned int _uid; /* sender's uid */
+ unsigned int _pt_flags;
+ } _sigtrap;
} _sifields;
}compat_siginfo_t;

@@ -165,6 +172,11 @@ int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from)
err |= __put_user(from->si_band, &to->si_band);
err |= __put_user(from->si_fd, &to->si_fd);
break;
+ case __SI_TRAP >> 16:
+ err |= __put_user(from->si_pid, &to->si_pid);
+ err |= __put_user(from->si_uid, &to->si_uid);
+ err |= __put_user(from->si_pt_flags, &to->si_pt_flags);
+ break;
case __SI_RT >> 16: /* This is not generated by the kernel as of now. */
case __SI_MESGQ >> 16:
err |= __put_user(from->si_pid, &to->si_pid);
diff --git a/arch/tile/kernel/compat_signal.c b/arch/tile/kernel/compat_signal.c
index dbb0dfc..0a5d694 100644
--- a/arch/tile/kernel/compat_signal.c
+++ b/arch/tile/kernel/compat_signal.c
@@ -109,6 +109,13 @@ struct compat_siginfo {
int _band; /* POLL_IN, POLL_OUT, POLL_MSG */
int _fd;
} _sigpoll;
+
+ /* SIGTRAP */
+ struct {
+ unsigned int _pid; /* sender's pid */
+ unsigned int _uid; /* sender's uid */
+ unsigned int _pt_flags;
+ } _sigtrap;
} _sifields;
};

@@ -219,6 +226,10 @@ int copy_siginfo_to_user32(struct compat_siginfo __user *to, siginfo_t *from)
case __SI_POLL >> 16:
err |= __put_user(from->si_fd, &to->si_fd);
break;
+ case __SI_TRAP >> 16:
+ err |= __put_user(from->si_uid, &to->si_uid);
+ err |= __put_user(from->si_pt_flags, &to->si_pt_flags);
+ break;
case __SI_TIMER >> 16:
err |= __put_user(from->si_overrun, &to->si_overrun);
err |= __put_user(ptr_to_compat(from->si_ptr),
diff --git a/arch/x86/ia32/ia32_signal.c b/arch/x86/ia32/ia32_signal.c
index 588a7aa..1df88cc 100644
--- a/arch/x86/ia32/ia32_signal.c
+++ b/arch/x86/ia32/ia32_signal.c
@@ -85,6 +85,10 @@ int copy_siginfo_to_user32(compat_siginfo_t __user *to, siginfo_t *from)
case __SI_POLL >> 16:
put_user_ex(from->si_fd, &to->si_fd);
break;
+ case __SI_TRAP:
+ put_user_ex(from->si_uid, &to->si_uid);
+ put_user_ex(from->si_pt_flags, &to->si_pt_flags);
+ break;
case __SI_TIMER >> 16:
put_user_ex(from->si_overrun, &to->si_overrun);
put_user_ex(ptr_to_compat(from->si_ptr),
diff --git a/arch/x86/include/asm/ia32.h b/arch/x86/include/asm/ia32.h
index 1f7e625..7eab27a 100644
--- a/arch/x86/include/asm/ia32.h
+++ b/arch/x86/include/asm/ia32.h
@@ -126,6 +126,13 @@ typedef struct compat_siginfo {
int _band; /* POLL_IN, POLL_OUT, POLL_MSG */
int _fd;
} _sigpoll;
+
+ /* SIGTRAP */
+ struct {
+ unsigned int _pid; /* sender's pid */
+ unsigned int _uid; /* sender's uid */
+ unsigned int _pt_flags;
+ } _sigtrap;
} _sifields;
} compat_siginfo_t;

diff --git a/include/asm-generic/siginfo.h b/include/asm-generic/siginfo.h
index 942d30b..e243927 100644
--- a/include/asm-generic/siginfo.h
+++ b/include/asm-generic/siginfo.h
@@ -90,6 +90,13 @@ typedef struct siginfo {
__ARCH_SI_BAND_T _band; /* POLL_IN, POLL_OUT, POLL_MSG */
int _fd;
} _sigpoll;
+
+ /* SIGTRAP */
+ struct {
+ __kernel_pid_t _pid; /* sender's pid */
+ __ARCH_SI_UID_T _uid; /* sender's uid */
+ unsigned int _pt_flags;
+ } _sigtrap;
} _sifields;
} siginfo_t;

@@ -116,6 +123,7 @@ typedef struct siginfo {
#define si_addr_lsb _sifields._sigfault._addr_lsb
#define si_band _sifields._sigpoll._band
#define si_fd _sifields._sigpoll._fd
+#define si_pt_flags _sifields._sigtrap._pt_flags

#ifdef __KERNEL__
#define __SI_MASK 0xffff0000u
@@ -126,6 +134,7 @@ typedef struct siginfo {
#define __SI_CHLD (4 << 16)
#define __SI_RT (5 << 16)
#define __SI_MESGQ (6 << 16)
+#define __SI_TRAP (7 << 16)
#define __SI_CODE(T,N) ((T) | ((N) & 0xffff))
#else
#define __SI_KILL 0
@@ -135,6 +144,7 @@ typedef struct siginfo {
#define __SI_CHLD 0
#define __SI_RT 0
#define __SI_MESGQ 0
+#define __SI_TRAP 0
#define __SI_CODE(T,N) (N)
#endif

diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h
index 5b6128b..567e189 100644
--- a/include/linux/ptrace.h
+++ b/include/linux/ptrace.h
@@ -73,6 +73,9 @@
#define PTRACE_EVENT_EXIT 6
#define PTRACE_EVENT_INTERRUPT 7

+/* flags in siginfo.si_pt_flags from PTRACE_GETSIGINFO */
+#define PTRACE_SI_STOPPED 0x00000001 /* tracee is job control stopped */
+
#include <asm/ptrace.h>

#ifdef __KERNEL__
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 2a8e930..b18a9b3 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -528,11 +528,13 @@ static int ptrace_setoptions(struct task_struct *child, unsigned long data)

static int ptrace_getsiginfo(struct task_struct *child, siginfo_t *info)
{
+ struct signal_struct *sig;
unsigned long flags;
int error;

if (!lock_task_sighand(child, &flags))
return -ESRCH;
+ sig = child->signal;

error = -EINVAL;
if (!child->last_siginfo)
@@ -540,6 +542,17 @@ static int ptrace_getsiginfo(struct task_struct *child, siginfo_t *info)

error = 0;
*info = *child->last_siginfo;
+
+ /*
+ * If reporting ptrace trap for a seized tracee, enable reporting
+ * of info->si_pt_flags.
+ */
+ if ((child->ptrace & PT_SEIZED) &&
+ (info->si_code & (0x7f | ~0xffff)) == (__SI_TRAP | SIGTRAP)) {
+ /* report whether group stop is in effect w/ SI_STOPPED */
+ if (sig->group_stop_count || (sig->flags & SIGNAL_STOP_STOPPED))
+ info->si_pt_flags |= PTRACE_SI_STOPPED;
+ }
out_unlock:
unlock_task_sighand(child, &flags);
return error;
diff --git a/kernel/signal.c b/kernel/signal.c
index 7add912..9705f5d 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1842,7 +1842,7 @@ static void ptrace_notify_locked(int exit_code)

memset(&info, 0, sizeof info);
info.si_signo = SIGTRAP;
- info.si_code = exit_code;
+ info.si_code = __SI_TRAP | exit_code;
info.si_pid = task_pid_vnr(current);
info.si_uid = current_uid();

@@ -2494,6 +2494,11 @@ int copy_siginfo_to_user(siginfo_t __user *to, siginfo_t *from)
err |= __put_user(from->si_band, &to->si_band);
err |= __put_user(from->si_fd, &to->si_fd);
break;
+ case __SI_TRAP:
+ err |= __put_user(from->si_pid, &to->si_pid);
+ err |= __put_user(from->si_uid, &to->si_uid);
+ err |= __put_user(from->si_pt_flags, &to->si_pt_flags);
+ break;
case __SI_FAULT:
err |= __put_user(from->si_addr, &to->si_addr);
#ifdef __ARCH_SI_TRAPNO
--
1.7.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/