summaryrefslogtreecommitdiffstats
path: root/arch
diff options
context:
space:
mode:
authorMarkus Metzger <markus.t.metzger@intel.com>2009-02-11 15:10:27 +0100
committerIngo Molnar <mingo@elte.hu>2009-02-11 15:44:20 +0100
commit9f339e7028e2855717af3193c938f9960ad13b38 (patch)
tree76e0e9181f4ee2b324742d517518e837d5c250bf /arch
parent06eb23b1ba39c61ee5d5faeb42a097635693e370 (diff)
downloadkernel-crypto-9f339e7028e2855717af3193c938f9960ad13b38.tar.gz
kernel-crypto-9f339e7028e2855717af3193c938f9960ad13b38.tar.xz
kernel-crypto-9f339e7028e2855717af3193c938f9960ad13b38.zip
x86, ptrace, mm: fix double-free on race
Ptrace_detach() races with __ptrace_unlink() if the traced task is reaped while detaching. This might cause a double-free of the BTS buffer. Change the ptrace_detach() path to only do the memory accounting in ptrace_bts_detach() and leave the buffer free to ptrace_bts_untrace() which will be called from __ptrace_unlink(). The fix follows a proposal from Oleg Nesterov. Reported-by: Oleg Nesterov <oleg@redhat.com> Signed-off-by: Markus Metzger <markus.t.metzger@intel.com> Signed-off-by: Ingo Molnar <mingo@elte.hu>
Diffstat (limited to 'arch')
-rw-r--r--arch/x86/kernel/ptrace.c16
1 files changed, 10 insertions, 6 deletions
diff --git a/arch/x86/kernel/ptrace.c b/arch/x86/kernel/ptrace.c
index 0a5df5f82fb..5a4c23d8989 100644
--- a/arch/x86/kernel/ptrace.c
+++ b/arch/x86/kernel/ptrace.c
@@ -810,12 +810,16 @@ static void ptrace_bts_untrace(struct task_struct *child)
static void ptrace_bts_detach(struct task_struct *child)
{
- if (unlikely(child->bts)) {
- ds_release_bts(child->bts);
- child->bts = NULL;
-
- ptrace_bts_free_buffer(child);
- }
+ /*
+ * Ptrace_detach() races with ptrace_untrace() in case
+ * the child dies and is reaped by another thread.
+ *
+ * We only do the memory accounting at this point and
+ * leave the buffer deallocation and the bts tracer
+ * release to ptrace_bts_untrace() which will be called
+ * later on with tasklist_lock held.
+ */
+ release_locked_buffer(child->bts_buffer, child->bts_size);
}
#else
static inline void ptrace_bts_fork(struct task_struct *tsk) {}