Commit 9f339e7028e2855717af3193c938f9960ad13b38
Committed by
Ingo Molnar
1 parent
06eb23b1ba
Exists in
master
and in
20 other branches
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>
Showing 3 changed files with 17 additions and 7 deletions Side-by-side Diff
arch/x86/kernel/ptrace.c
... | ... | @@ -810,12 +810,16 @@ |
810 | 810 | |
811 | 811 | static void ptrace_bts_detach(struct task_struct *child) |
812 | 812 | { |
813 | - if (unlikely(child->bts)) { | |
814 | - ds_release_bts(child->bts); | |
815 | - child->bts = NULL; | |
816 | - | |
817 | - ptrace_bts_free_buffer(child); | |
818 | - } | |
813 | + /* | |
814 | + * Ptrace_detach() races with ptrace_untrace() in case | |
815 | + * the child dies and is reaped by another thread. | |
816 | + * | |
817 | + * We only do the memory accounting at this point and | |
818 | + * leave the buffer deallocation and the bts tracer | |
819 | + * release to ptrace_bts_untrace() which will be called | |
820 | + * later on with tasklist_lock held. | |
821 | + */ | |
822 | + release_locked_buffer(child->bts_buffer, child->bts_size); | |
819 | 823 | } |
820 | 824 | #else |
821 | 825 | static inline void ptrace_bts_fork(struct task_struct *tsk) {} |
include/linux/mm.h
... | ... | @@ -1305,6 +1305,7 @@ |
1305 | 1305 | |
1306 | 1306 | extern void *alloc_locked_buffer(size_t size); |
1307 | 1307 | extern void free_locked_buffer(void *buffer, size_t size); |
1308 | +extern void release_locked_buffer(void *buffer, size_t size); | |
1308 | 1309 | #endif /* __KERNEL__ */ |
1309 | 1310 | #endif /* _LINUX_MM_H */ |
mm/mlock.c
... | ... | @@ -657,7 +657,7 @@ |
657 | 657 | return buffer; |
658 | 658 | } |
659 | 659 | |
660 | -void free_locked_buffer(void *buffer, size_t size) | |
660 | +void release_locked_buffer(void *buffer, size_t size) | |
661 | 661 | { |
662 | 662 | unsigned long pgsz = PAGE_ALIGN(size) >> PAGE_SHIFT; |
663 | 663 | |
... | ... | @@ -667,6 +667,11 @@ |
667 | 667 | current->mm->locked_vm -= pgsz; |
668 | 668 | |
669 | 669 | up_write(¤t->mm->mmap_sem); |
670 | +} | |
671 | + | |
672 | +void free_locked_buffer(void *buffer, size_t size) | |
673 | +{ | |
674 | + release_locked_buffer(buffer, size); | |
670 | 675 | |
671 | 676 | kfree(buffer); |
672 | 677 | } |