summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@g5.osdl.org>2006-05-11 11:08:49 -0700
committerChris Wright <chrisw@sous-sol.org>2006-05-20 15:00:33 -0700
commitf91962f90eb89684ecbfb3f6b8578b3ebd6937df (patch)
tree2c4d2f06aab013d41f6d099a6cc3ea23973fa44a
parent65b01b76265047aa59d6eb741ec61468c8867256 (diff)
downloadkernel-common-f91962f90eb89684ecbfb3f6b8578b3ebd6937df.tar.gz
kernel-common-f91962f90eb89684ecbfb3f6b8578b3ebd6937df.tar.bz2
kernel-common-f91962f90eb89684ecbfb3f6b8578b3ebd6937df.zip
[PATCH] ptrace_attach: fix possible deadlock schenario with irqs
Eric Biederman points out that we can't take the task_lock while holding tasklist_lock for writing, because another CPU that holds the task lock might take an interrupt that then tries to take tasklist_lock for writing. Which would be a nasty deadlock, with one CPU spinning forever in an interrupt handler (although admittedly you need to really work at triggering it ;) Since the ptrace_attach() code is special and very unusual, just make it be extra careful, and use trylock+repeat to avoid the possible deadlock. Cc: Oleg Nesterov <oleg@tv-sign.ru> Cc: Eric W. Biederman <ebiederm@xmission.com> Cc: Roland McGrath <roland@redhat.com> Signed-off-by: Linus Torvalds <torvalds@osdl.org> Signed-off-by: Chris Wright <chrisw@sous-sol.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--kernel/ptrace.c20
1 files changed, 19 insertions, 1 deletions
diff --git a/kernel/ptrace.c b/kernel/ptrace.c
index 99f9fa4550c7..48453c358dfd 100644
--- a/kernel/ptrace.c
+++ b/kernel/ptrace.c
@@ -156,8 +156,26 @@ int ptrace_attach(struct task_struct *task)
if (task->tgid == current->tgid)
goto out;
- write_lock_irq(&tasklist_lock);
+repeat:
+ /*
+ * Nasty, nasty.
+ *
+ * We want to hold both the task-lock and the
+ * tasklist_lock for writing at the same time.
+ * But that's against the rules (tasklist_lock
+ * is taken for reading by interrupts on other
+ * cpu's that may have task_lock).
+ */
task_lock(task);
+ local_irq_disable();
+ if (!write_trylock(&tasklist_lock)) {
+ local_irq_enable();
+ task_unlock(task);
+ do {
+ cpu_relax();
+ } while (!write_can_lock(&tasklist_lock));
+ goto repeat;
+ }
/* the same process cannot be attached many times */
if (task->ptrace & PT_PTRACED)