Skip to content

Commit

Permalink
x86, ptrace, mm: fix double-free on race
Browse files Browse the repository at this point in the history
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 <[email protected]>
Signed-off-by: Markus Metzger <[email protected]>
Signed-off-by: Ingo Molnar <[email protected]>
  • Loading branch information
markus-metzger authored and Ingo Molnar committed Feb 11, 2009
1 parent 06eb23b commit 9f339e7
Show file tree
Hide file tree
Showing 3 changed files with 17 additions and 7 deletions.
16 changes: 10 additions & 6 deletions arch/x86/kernel/ptrace.c
Original file line number Diff line number Diff line change
Expand Up @@ -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) {}
Expand Down
1 change: 1 addition & 0 deletions include/linux/mm.h
Original file line number Diff line number Diff line change
Expand Up @@ -1305,5 +1305,6 @@ void vmemmap_populate_print_last(void);

extern void *alloc_locked_buffer(size_t size);
extern void free_locked_buffer(void *buffer, size_t size);
extern void release_locked_buffer(void *buffer, size_t size);
#endif /* __KERNEL__ */
#endif /* _LINUX_MM_H */
7 changes: 6 additions & 1 deletion mm/mlock.c
Original file line number Diff line number Diff line change
Expand Up @@ -657,7 +657,7 @@ void *alloc_locked_buffer(size_t size)
return buffer;
}

void free_locked_buffer(void *buffer, size_t size)
void release_locked_buffer(void *buffer, size_t size)
{
unsigned long pgsz = PAGE_ALIGN(size) >> PAGE_SHIFT;

Expand All @@ -667,6 +667,11 @@ void free_locked_buffer(void *buffer, size_t size)
current->mm->locked_vm -= pgsz;

up_write(&current->mm->mmap_sem);
}

void free_locked_buffer(void *buffer, size_t size)
{
release_locked_buffer(buffer, size);

kfree(buffer);
}

0 comments on commit 9f339e7

Please sign in to comment.