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: Oleg Nesterov <oleg@redhat.com> Signed-off-by: Frederic 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
This commit is contained in:
parent
bf26c01849
commit
87dc669ba2
@ -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 @@ restore:
|
|||||||
}
|
}
|
||||||
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)
|
|||||||
|
|
||||||
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,
|
|||||||
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,
|
|||||||
* 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
Loading…
x
Reference in New Issue
Block a user