aboutsummaryrefslogtreecommitdiffstats
path: root/kernel
diff options
context:
space:
mode:
authorOleg Nesterov <oleg@redhat.com>2009-04-02 19:57:58 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2009-04-02 22:04:57 -0400
commit90bc8d8b1a38f1ab131a2399a202e1889db95de8 (patch)
tree97001737dc3c1c7fd364ddd995bcbfc1c27b4c3a /kernel
parent6d7b2f5f9e88902b19f91d0c8a7ef58a5455f1a2 (diff)
do_wait: fix waiting for the group stop with the dead leader
do_wait(WSTOPPED) assumes that p->state must be == TASK_STOPPED, this is not true if the leader is already dead. Check SIGNAL_STOP_STOPPED instead and use signal->group_exit_code. Trivial test-case: void *tfunc(void *arg) { pause(); return NULL; } int main(void) { pthread_t thr; pthread_create(&thr, NULL, tfunc, NULL); pthread_exit(NULL); return 0; } It doesn't react to ^Z (and then to ^C or ^\). The task is stopped, but bash can't see this. The bug is very old, and it was reported multiple times. This patch was sent more than a year ago (http://marc.info/?t=119713920000003) but it was ignored. This change also fixes other oddities (but not all) in this area. For example, before this patch: $ sleep 100 ^Z [1]+ Stopped sleep 100 $ strace -p `pidof sleep` Process 11442 attached - interrupt to quit strace hangs in do_wait(), because ->exit_code was already consumed by bash. After this patch, strace happily proceeds: --- SIGTSTP (Stopped) @ 0 (0) --- restart_syscall(<... resuming interrupted call ...> To me, this looks much more "natural" and correct. Another example. Let's suppose we have the main thread M and sub-thread T, the process is stopped, and its parent did wait(WSTOPPED). Now we can ptrace T but not M. This looks at least strange to me. Imho, do_wait() should not confuse the per-thread ptrace stops with the per-process job control stops. Signed-off-by: Oleg Nesterov <oleg@redhat.com> Cc: Denys Vlasenko <dvlasenk@redhat.com> Cc: "Eric W. Biederman" <ebiederm@xmission.com> Cc: Jan Kratochvil <jan.kratochvil@redhat.com> Cc: Kaz Kylheku <kkylheku@gmail.com> Cc: Michael Kerrisk <mtk.manpages@googlemail.com> Cc: Roland McGrath <roland@redhat.com> Cc: Ulrich Drepper <drepper@redhat.com> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'kernel')
-rw-r--r--kernel/exit.c30
1 files changed, 18 insertions, 12 deletions
diff --git a/kernel/exit.c b/kernel/exit.c
index 167e1e3ad7c6..0c06b9efae3b 100644
--- a/kernel/exit.c
+++ b/kernel/exit.c
@@ -1417,6 +1417,18 @@ static int wait_task_zombie(struct task_struct *p, int options,
1417 return retval; 1417 return retval;
1418} 1418}
1419 1419
1420static int *task_stopped_code(struct task_struct *p, bool ptrace)
1421{
1422 if (ptrace) {
1423 if (task_is_stopped_or_traced(p))
1424 return &p->exit_code;
1425 } else {
1426 if (p->signal->flags & SIGNAL_STOP_STOPPED)
1427 return &p->signal->group_exit_code;
1428 }
1429 return NULL;
1430}
1431
1420/* 1432/*
1421 * Handle sys_wait4 work for one task in state TASK_STOPPED. We hold 1433 * Handle sys_wait4 work for one task in state TASK_STOPPED. We hold
1422 * read_lock(&tasklist_lock) on entry. If we return zero, we still hold 1434 * read_lock(&tasklist_lock) on entry. If we return zero, we still hold
@@ -1427,7 +1439,7 @@ static int wait_task_stopped(int ptrace, struct task_struct *p,
1427 int options, struct siginfo __user *infop, 1439 int options, struct siginfo __user *infop,
1428 int __user *stat_addr, struct rusage __user *ru) 1440 int __user *stat_addr, struct rusage __user *ru)
1429{ 1441{
1430 int retval, exit_code, why; 1442 int retval, exit_code, *p_code, why;
1431 uid_t uid = 0; /* unneeded, required by compiler */ 1443 uid_t uid = 0; /* unneeded, required by compiler */
1432 pid_t pid; 1444 pid_t pid;
1433 1445
@@ -1437,22 +1449,16 @@ static int wait_task_stopped(int ptrace, struct task_struct *p,
1437 exit_code = 0; 1449 exit_code = 0;
1438 spin_lock_irq(&p->sighand->siglock); 1450 spin_lock_irq(&p->sighand->siglock);
1439 1451
1440 if (unlikely(!task_is_stopped_or_traced(p))) 1452 p_code = task_stopped_code(p, ptrace);
1441 goto unlock_sig; 1453 if (unlikely(!p_code))
1442
1443 if (!ptrace && p->signal->group_stop_count > 0)
1444 /*
1445 * A group stop is in progress and this is the group leader.
1446 * We won't report until all threads have stopped.
1447 */
1448 goto unlock_sig; 1454 goto unlock_sig;
1449 1455
1450 exit_code = p->exit_code; 1456 exit_code = *p_code;
1451 if (!exit_code) 1457 if (!exit_code)
1452 goto unlock_sig; 1458 goto unlock_sig;
1453 1459
1454 if (!unlikely(options & WNOWAIT)) 1460 if (!unlikely(options & WNOWAIT))
1455 p->exit_code = 0; 1461 *p_code = 0;
1456 1462
1457 /* don't need the RCU readlock here as we're holding a spinlock */ 1463 /* don't need the RCU readlock here as we're holding a spinlock */
1458 uid = __task_cred(p)->uid; 1464 uid = __task_cred(p)->uid;
@@ -1608,7 +1614,7 @@ static int wait_consider_task(struct task_struct *parent, int ptrace,
1608 */ 1614 */
1609 *notask_error = 0; 1615 *notask_error = 0;
1610 1616
1611 if (task_is_stopped_or_traced(p)) 1617 if (task_stopped_code(p, ptrace))
1612 return wait_task_stopped(ptrace, p, options, 1618 return wait_task_stopped(ptrace, p, options,
1613 infop, stat_addr, ru); 1619 infop, stat_addr, ru);
1614 1620