Commit 87dc669b authored by Frederic Weisbecker's avatar Frederic Weisbecker

x86, hw_breakpoints: Fix racy access to ptrace breakpoints

While the tracer accesses ptrace breakpoints, the child task may
concurrently exit due to a SIGKILL and thus release its breakpoints
at the same time. We can then dereference some freed pointers.

To fix this, hold a reference on the child breakpoints before
manipulating them.
Reported-by: default avatarOleg Nesterov <oleg@redhat.com>
Signed-off-by: default avatarFrederic Weisbecker <fweisbec@gmail.com>
Cc: Ingo Molnar <mingo@elte.hu>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Will Deacon <will.deacon@arm.com>
Cc: Prasad <prasad@linux.vnet.ibm.com>
Cc: Paul Mundt <lethal@linux-sh.org>
Cc: v2.6.33.. <stable@kernel.org>
Link: http://lkml.kernel.org/r/1302284067-7860-3-git-send-email-fweisbec@gmail.com
parent bf26c018
...@@ -608,6 +608,9 @@ static int ptrace_write_dr7(struct task_struct *tsk, unsigned long data) ...@@ -608,6 +608,9 @@ static int ptrace_write_dr7(struct task_struct *tsk, unsigned long data)
unsigned len, type; unsigned len, type;
struct perf_event *bp; struct perf_event *bp;
if (ptrace_get_breakpoints(tsk) < 0)
return -ESRCH;
data &= ~DR_CONTROL_RESERVED; data &= ~DR_CONTROL_RESERVED;
old_dr7 = ptrace_get_dr7(thread->ptrace_bps); old_dr7 = ptrace_get_dr7(thread->ptrace_bps);
restore: restore:
...@@ -655,6 +658,9 @@ static int ptrace_write_dr7(struct task_struct *tsk, unsigned long data) ...@@ -655,6 +658,9 @@ static int ptrace_write_dr7(struct task_struct *tsk, unsigned long data)
} }
goto restore; goto restore;
} }
ptrace_put_breakpoints(tsk);
return ((orig_ret < 0) ? orig_ret : rc); return ((orig_ret < 0) ? orig_ret : rc);
} }
...@@ -668,10 +674,17 @@ static unsigned long ptrace_get_debugreg(struct task_struct *tsk, int n) ...@@ -668,10 +674,17 @@ static unsigned long ptrace_get_debugreg(struct task_struct *tsk, int n)
if (n < HBP_NUM) { if (n < HBP_NUM) {
struct perf_event *bp; struct perf_event *bp;
if (ptrace_get_breakpoints(tsk) < 0)
return -ESRCH;
bp = thread->ptrace_bps[n]; bp = thread->ptrace_bps[n];
if (!bp) if (!bp)
return 0; val = 0;
val = bp->hw.info.address; else
val = bp->hw.info.address;
ptrace_put_breakpoints(tsk);
} else if (n == 6) { } else if (n == 6) {
val = thread->debugreg6; val = thread->debugreg6;
} else if (n == 7) { } else if (n == 7) {
...@@ -686,6 +699,10 @@ static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr, ...@@ -686,6 +699,10 @@ static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr,
struct perf_event *bp; struct perf_event *bp;
struct thread_struct *t = &tsk->thread; struct thread_struct *t = &tsk->thread;
struct perf_event_attr attr; struct perf_event_attr attr;
int err = 0;
if (ptrace_get_breakpoints(tsk) < 0)
return -ESRCH;
if (!t->ptrace_bps[nr]) { if (!t->ptrace_bps[nr]) {
ptrace_breakpoint_init(&attr); ptrace_breakpoint_init(&attr);
...@@ -709,24 +726,23 @@ static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr, ...@@ -709,24 +726,23 @@ static int ptrace_set_breakpoint_addr(struct task_struct *tsk, int nr,
* writing for the user. And anyway this is the previous * writing for the user. And anyway this is the previous
* behaviour. * behaviour.
*/ */
if (IS_ERR(bp)) if (IS_ERR(bp)) {
return PTR_ERR(bp); err = PTR_ERR(bp);
goto put;
}
t->ptrace_bps[nr] = bp; t->ptrace_bps[nr] = bp;
} else { } else {
int err;
bp = t->ptrace_bps[nr]; bp = t->ptrace_bps[nr];
attr = bp->attr; attr = bp->attr;
attr.bp_addr = addr; attr.bp_addr = addr;
err = modify_user_hw_breakpoint(bp, &attr); err = modify_user_hw_breakpoint(bp, &attr);
if (err)
return err;
} }
put:
return 0; ptrace_put_breakpoints(tsk);
return err;
} }
/* /*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment