Skip to content

Commit

Permalink
signals: check ->group_stop_count after tracehook_get_signal()
Browse files Browse the repository at this point in the history
Move the call to do_signal_stop() down, after tracehook call.
This makes ->group_stop_count condition visible to tracers before
do_signal_stop() will participate in this group-stop.

Currently the patch has no effect, tracehook_get_signal() always
returns 0.

Signed-off-by: Oleg Nesterov <[email protected]>
Signed-off-by: Roland McGrath <[email protected]>
  • Loading branch information
oleg-nesterov authored and Pierre Carrier committed Jul 12, 2012
1 parent d84c4ca commit b47305e
Show file tree
Hide file tree
Showing 10 changed files with 79 additions and 46 deletions.
2 changes: 2 additions & 0 deletions arch/powerpc/include/asm/ptrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ extern void user_enable_single_step(struct task_struct *);
extern void user_enable_block_step(struct task_struct *);
extern void user_disable_single_step(struct task_struct *);

#define ARCH_HAS_USER_SINGLE_STEP_INFO

#endif /* __ASSEMBLY__ */

#endif /* __KERNEL__ */
Expand Down
9 changes: 9 additions & 0 deletions arch/powerpc/kernel/traps.c
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,15 @@ int die(const char *str, struct pt_regs *regs, long err)
return 0;
}

void user_single_step_siginfo(struct task_struct *tsk,
struct pt_regs *regs, siginfo_t *info)
{
memset(info, 0, sizeof(*info));
info->si_signo = SIGTRAP;
info->si_code = TRAP_TRACE;
info->si_addr = (void __user *)regs->nip;
}

void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr)
{
siginfo_t info;
Expand Down
6 changes: 3 additions & 3 deletions arch/s390/kernel/traps.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/errno.h>
#include <linux/ptrace.h>
#include <linux/tracehook.h>
#include <linux/timer.h>
#include <linux/mm.h>
#include <linux/smp.h>
Expand Down Expand Up @@ -345,7 +345,7 @@ void __kprobes do_single_step(struct pt_regs *regs)
SIGTRAP) == NOTIFY_STOP){
return;
}
if ((current->ptrace & PT_PTRACED) != 0)
if (tracehook_consider_fatal_signal(current, SIGTRAP))
force_sig(SIGTRAP, current);
}

Expand Down Expand Up @@ -446,7 +446,7 @@ static void illegal_op(struct pt_regs * regs, long interruption_code)
if (get_user(*((__u16 *) opcode), (__u16 __user *) location))
return;
if (*((__u16 *) opcode) == S390_BREAKPOINT_U16) {
if (current->ptrace & PT_PTRACED)
if (tracehook_consider_fatal_signal(current, SIGTRAP))
force_sig(SIGTRAP, current);
else
signal = SIGILL;
Expand Down
2 changes: 2 additions & 0 deletions arch/x86/include/asm/ptrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,8 @@ extern void user_enable_block_step(struct task_struct *);
#define arch_has_block_step() (boot_cpu_data.x86 >= 6)
#endif

#define ARCH_HAS_USER_SINGLE_STEP_INFO

struct user_desc;
extern int do_get_thread_area(struct task_struct *p, int idx,
struct user_desc __user *info);
Expand Down
51 changes: 28 additions & 23 deletions arch/x86/kernel/ptrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -1437,21 +1437,33 @@ const struct user_regset_view *task_user_regset_view(struct task_struct *task)
#endif
}

void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs,
int error_code, int si_code)
static void fill_sigtrap_info(struct task_struct *tsk,
struct pt_regs *regs,
int error_code, int si_code,
struct siginfo *info)
{
struct siginfo info;

tsk->thread.trap_no = 1;
tsk->thread.error_code = error_code;

memset(&info, 0, sizeof(info));
info.si_signo = SIGTRAP;
info.si_code = si_code;
memset(info, 0, sizeof(*info));
info->si_signo = SIGTRAP;
info->si_code = si_code;
info->si_addr = user_mode_vm(regs) ? (void __user *)regs->ip : NULL;
}

/* User-mode ip? */
info.si_addr = user_mode_vm(regs) ? (void __user *) regs->ip : NULL;
void user_single_step_siginfo(struct task_struct *tsk,
struct pt_regs *regs,
struct siginfo *info)
{
fill_sigtrap_info(tsk, regs, 0, TRAP_BRKPT, info);
}

void send_sigtrap(struct task_struct *tsk, struct pt_regs *regs,
int error_code, int si_code)
{
struct siginfo info;

fill_sigtrap_info(tsk, regs, error_code, si_code, &info);
/* Send us the fake SIGTRAP */
force_sig_info(SIGTRAP, &info, tsk);
}
Expand Down Expand Up @@ -1516,29 +1528,22 @@ asmregparm long syscall_trace_enter(struct pt_regs *regs)

asmregparm void syscall_trace_leave(struct pt_regs *regs)
{
bool step;

if (unlikely(current->audit_context))
audit_syscall_exit(AUDITSC_RESULT(regs->ax), regs->ax);

if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
trace_sys_exit(regs, regs->ax);

if (test_thread_flag(TIF_SYSCALL_TRACE))
tracehook_report_syscall_exit(regs, 0);

/*
* If TIF_SYSCALL_EMU is set, we only get here because of
* TIF_SINGLESTEP (i.e. this is PTRACE_SYSEMU_SINGLESTEP).
* We already reported this syscall instruction in
* syscall_trace_enter(), so don't do any more now.
*/
if (unlikely(test_thread_flag(TIF_SYSCALL_EMU)))
return;

/*
* If we are single-stepping, synthesize a trap to follow the
* system call instruction.
* syscall_trace_enter().
*/
if (test_thread_flag(TIF_SINGLESTEP) &&
tracehook_consider_fatal_signal(current, SIGTRAP))
send_sigtrap(current, regs, 0, TRAP_BRKPT);
step = unlikely(test_thread_flag(TIF_SINGLESTEP)) &&
!test_thread_flag(TIF_SYSCALL_EMU);
if (step || test_thread_flag(TIF_SYSCALL_TRACE))
tracehook_report_syscall_exit(regs, step);
}
24 changes: 16 additions & 8 deletions include/linux/ptrace.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ extern int ptrace_traceme(void);
extern int ptrace_readdata(struct task_struct *tsk, unsigned long src, char __user *dst, int len);
extern int ptrace_writedata(struct task_struct *tsk, char __user *src, unsigned long dst, int len);
extern int ptrace_attach(struct task_struct *tsk);
extern bool __ptrace_detach(struct task_struct *tracer, struct task_struct *tracee);
extern int ptrace_detach(struct task_struct *, unsigned int);
extern void ptrace_disable(struct task_struct *);
extern int ptrace_check_attach(struct task_struct *task, int kill);
Expand All @@ -105,12 +106,7 @@ static inline int ptrace_reparented(struct task_struct *child)
{
return child->real_parent != child->parent;
}
static inline void ptrace_link(struct task_struct *child,
struct task_struct *new_parent)
{
if (unlikely(child->ptrace))
__ptrace_link(child, new_parent);
}

static inline void ptrace_unlink(struct task_struct *child)
{
if (unlikely(child->ptrace))
Expand Down Expand Up @@ -169,9 +165,9 @@ static inline void ptrace_init_task(struct task_struct *child, bool ptrace)
INIT_LIST_HEAD(&child->ptraced);
child->parent = child->real_parent;
child->ptrace = 0;
if (unlikely(ptrace)) {
if (unlikely(ptrace) && (current->ptrace & PT_PTRACED)) {
child->ptrace = current->ptrace;
ptrace_link(child, current->parent);
__ptrace_link(child, current->parent);
}
}

Expand Down Expand Up @@ -278,6 +274,18 @@ static inline void user_enable_block_step(struct task_struct *task)
}
#endif /* arch_has_block_step */

#ifdef ARCH_HAS_USER_SINGLE_STEP_INFO
extern void user_single_step_siginfo(struct task_struct *tsk,
struct pt_regs *regs, siginfo_t *info);
#else
static inline void user_single_step_siginfo(struct task_struct *tsk,
struct pt_regs *regs, siginfo_t *info)
{
memset(info, 0, sizeof(*info));
info->si_signo = SIGTRAP;
}
#endif

#ifndef arch_ptrace_stop_needed
/**
* arch_ptrace_stop_needed - Decide whether arch_ptrace_stop() should be called
Expand Down
1 change: 1 addition & 0 deletions include/linux/sched.h
Original file line number Diff line number Diff line change
Expand Up @@ -2130,6 +2130,7 @@ extern int kill_pgrp(struct pid *pid, int sig, int priv);
extern int kill_pid(struct pid *pid, int sig, int priv);
extern int kill_proc_info(int, struct siginfo *, pid_t);
extern int do_notify_parent(struct task_struct *, int);
extern void do_notify_parent_cldstop(struct task_struct *, int);
extern void __wake_up_parent(struct task_struct *p, struct task_struct *parent);
extern void force_sig(int, struct task_struct *);
extern void force_sig_specific(int, struct task_struct *);
Expand Down
15 changes: 11 additions & 4 deletions include/linux/tracehook.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,13 @@ static inline __must_check int tracehook_report_syscall_entry(
*/
static inline void tracehook_report_syscall_exit(struct pt_regs *regs, int step)
{
if (step && (task_ptrace(current) & PT_PTRACED)) {
siginfo_t info;
user_single_step_siginfo(current, regs, &info);
force_sig_info(SIGTRAP, &info, current);
return;
}

ptrace_report_syscall(regs);
}

Expand All @@ -149,7 +156,7 @@ static inline int tracehook_unsafe_exec(struct task_struct *task)
{
int unsafe = 0;
int ptrace = task_ptrace(task);
if (ptrace & PT_PTRACED) {
if (ptrace) {
if (ptrace & PT_PTRACE_CAP)
unsafe |= LSM_UNSAFE_PTRACE_CAP;
else
Expand All @@ -171,7 +178,7 @@ static inline int tracehook_unsafe_exec(struct task_struct *task)
*/
static inline struct task_struct *tracehook_tracer_task(struct task_struct *tsk)
{
if (task_ptrace(tsk) & PT_PTRACED)
if (task_ptrace(tsk))
return rcu_dereference(tsk->parent);
return NULL;
}
Expand Down Expand Up @@ -379,7 +386,7 @@ static inline void tracehook_signal_handler(int sig, siginfo_t *info,
const struct k_sigaction *ka,
struct pt_regs *regs, int stepping)
{
if (stepping)
if (stepping && (task_ptrace(current) & PT_PTRACED))
ptrace_notify(SIGTRAP);
}

Expand Down Expand Up @@ -485,7 +492,7 @@ static inline int tracehook_get_signal(struct task_struct *task,
*/
static inline int tracehook_notify_jctl(int notify, int why)
{
return notify ?: (current->ptrace & PT_PTRACED) ? why : 0;
return notify ?: task_ptrace(current) ? why : 0;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion kernel/ptrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ static int ignoring_children(struct sighand_struct *sigh)
* reap it now, in that case we must also wake up sub-threads sleeping in
* do_wait().
*/
static bool __ptrace_detach(struct task_struct *tracer, struct task_struct *p)
bool __ptrace_detach(struct task_struct *tracer, struct task_struct *p)
{
__ptrace_unlink(p);

Expand Down
13 changes: 6 additions & 7 deletions kernel/signal.c
Original file line number Diff line number Diff line change
Expand Up @@ -1464,7 +1464,7 @@ int do_notify_parent(struct task_struct *tsk, int sig)
return ret;
}

static void do_notify_parent_cldstop(struct task_struct *tsk, int why)
void do_notify_parent_cldstop(struct task_struct *tsk, int why)
{
struct siginfo info;
unsigned long flags;
Expand Down Expand Up @@ -1734,7 +1734,7 @@ static int do_signal_stop(int signr)
static int ptrace_signal(int signr, siginfo_t *info,
struct pt_regs *regs, void *cookie)
{
if (!task_ptrace(current))
if (!(task_ptrace(current) & PT_PTRACED))
return signr;

ptrace_signal_deliver(regs, cookie);
Expand Down Expand Up @@ -1810,11 +1810,6 @@ int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka,

for (;;) {
struct k_sigaction *ka;

if (unlikely(signal->group_stop_count > 0) &&
do_signal_stop(0))
goto relock;

/*
* Tracing can induce an artifical signal and choose sigaction.
* The return value in @signr determines the default action,
Expand All @@ -1826,6 +1821,10 @@ int get_signal_to_deliver(siginfo_t *info, struct k_sigaction *return_ka,
if (unlikely(signr != 0))
ka = return_ka;
else {
if (unlikely(signal->group_stop_count > 0) &&
do_signal_stop(0))
goto relock;

signr = dequeue_signal(current, &current->blocked,
info);

Expand Down

0 comments on commit b47305e

Please sign in to comment.