uprobes: minor fixes + name the xol vma
Subsystems (2)
Oleg Nesterov
maintainerHello,
The bugs are theoretical, but still should be fixed imo. And it would be nice to name the special vma installed by uprobes. As a side effect this vma will be core-dump'ed, I think this is good.
This doesn't depend on the pending longjmp fixes.
Oleg.
kernel/events/uprobes.c | 43 +++++++++++++++++++++++++++---------------- 1 files changed, 27 insertions(+), 16 deletions(-)
Oleg Nesterov
maintainerPATCH v2 1/3 uprobes: fix the usage of install_special_mapping()
install_special_mapping(pages) expects that "pages" is the zero- terminated array while xol_add_vma() passes &area->page, this means that special_mapping_fault() can wrongly use the next member in xol_area (vaddr) as "struct page *".
Fortunately, this area is not expandable so pgoff != 0 isn't possible (modulo bugs in special_mapping_vmops), but still this does not look good.
--- kernel/events/uprobes.c | 16 ++++++++-------- 1 files changed, 8 insertions(+), 8 deletions(-)
kernel/events/uprobes.c +8 -8
@@ -99,7 +99,7 @@ struct xol_area {wait_queue_head_t wq;/* if all slots are busy */ atomic_t slot_count;/* number of in-use slots */ unsigned long *bitmap;/* 0 = free slot */ -struct page *page; +struct page *pages[2 ];/* * We keep the vma's vm_start rather than a pointer to the vma
@@ -1142,7 +1142,7 @@ static int xol_add_vma(struct mm_struct *mm, struct xol_area *area) } ret =install_special_mapping (mm, area->vaddr,PAGE_SIZE , -VM_EXEC |VM_MAYEXEC |VM_DONTCOPY |VM_IO , &area->page); +VM_EXEC |VM_MAYEXEC |VM_DONTCOPY |VM_IO , area->pages);if (ret) goto fail;
@@ -1168,8 +1168,8 @@ static struct xol_area *__create_xol_area(unsigned long vaddr)if (!area->bitmap) goto free_area; - area->page =alloc_page (GFP_HIGHUSER ); -if (!area->page) + area->pages[0 ] =alloc_page (GFP_HIGHUSER ); +if (!area->pages[0 ]) goto free_bitmap; area->vaddr = vaddr;
@@ -1177,12 +1177,12 @@ static struct xol_area *__create_xol_area(unsigned long vaddr)/* Reserve the 1st slot for get_trampoline_vaddr() */ set_bit (0 , area->bitmap);atomic_set (&area->slot_count,1 ); -copy_to_page (area->page,0 , &insn,UPROBE_SWBP_INSN_SIZE ); +copy_to_page (area->pages[0 ],0 , &insn,UPROBE_SWBP_INSN_SIZE );if (!xol_add_vma (mm, area))return area; -__free_page (area->page); +__free_page (area->pages[0 ]); free_bitmap:kfree (area->bitmap); free_area:
@@ -1220,7 +1220,7 @@ void uprobe_clear_state(struct mm_struct *mm)if (!area)return ; -put_page (area->page); +put_page (area->pages[0 ]);kfree (area->bitmap);kfree (area); }
@@ -1289,7 +1289,7 @@ static unsigned long xol_get_insn_slot(struct uprobe *uprobe)if (unlikely (!xol_vaddr))return 0 ; -arch_uprobe_copy_ixol (area->page, xol_vaddr, +arch_uprobe_copy_ixol (area->pages[0 ], xol_vaddr, &uprobe->arch.ixol,sizeof (uprobe->arch.ixol));return xol_vaddr;
Signature · 1 line
1.5.5.1
Oleg Nesterov
maintainerPATCH v2 2/3 uprobes: use vm_special_mapping to name the xol vma
In reply to Oleg NesterovChange xol_add_vma() to use _install_special_mapping(), this way we can name the vma installed by uprobes. Currently it looks like private anonymous mapping, this is confusing and complicates the debugging. With this change /proc/$pid/maps reports "[uprobes]".
--- kernel/events/uprobes.c | 30 ++++++++++++++++++++---------- 1 files changed, 20 insertions(+), 10 deletions(-)
kernel/events/uprobes.c +20 -10
@@ -96,17 +96,18 @@ struct uprobe {* allocated. */ struct xol_area { -wait_queue_head_t wq;/* if all slots are busy */ -atomic_t slot_count;/* number of in-use slots */ -unsigned long *bitmap;/* 0 = free slot */ -struct page *pages[2 ]; +wait_queue_head_t wq;/* if all slots are busy */ +atomic_t slot_count;/* number of in-use slots */ +unsigned long *bitmap;/* 0 = free slot */ +struct vm_special_mapping xol_mapping; +struct page *pages[2 ];/* * We keep the vma's vm_start rather than a pointer to the vma * itself. The probed process or a naughty kernel module could make * the vma go away, and we must handle that reasonably gracefully. */ -unsigned long vaddr;/* Page(s) of instruction slots */ +unsigned long vaddr;/* Page(s) of instruction slots */ };/*
@@ -1125,11 +1126,14 @@ void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned lon/* Slot allocation for XOL */ static int xol_add_vma (struct mm_struct *mm,struct xol_area *area) { -int ret = -EALREADY ; +struct vm_area_struct *vma; +int ret;down_write (&mm->mmap_sem); -if (mm->uprobes_state.xol_area) +if (mm->uprobes_state.xol_area) { + ret = -EALREADY ; goto fail; + }if (!area->vaddr) {/* Try to map as high as possible, this is only a hint. */
@@ -1141,11 +1145,15 @@ static int xol_add_vma(struct mm_struct *mm, struct xol_area *area) } } - ret =install_special_mapping (mm, area->vaddr,PAGE_SIZE , -VM_EXEC |VM_MAYEXEC |VM_DONTCOPY |VM_IO , area->pages); -if (ret) + vma =_install_special_mapping (mm, area->vaddr,PAGE_SIZE , +VM_EXEC |VM_MAYEXEC |VM_DONTCOPY |VM_IO , + &area->xol_mapping); +if (IS_ERR (vma)) { + ret =PTR_ERR (vma); goto fail; + } + ret =0 ;smp_wmb ();/* pairs with get_xol_area() */ mm->uprobes_state.xol_area = area; fail:
@@ -1168,6 +1176,8 @@ static struct xol_area *__create_xol_area(unsigned long vaddr)if (!area->bitmap) goto free_area; + area->xol_mapping.name ="[uprobes]" ; + area->xol_mapping.pages = area->pages; area->pages[0 ] =alloc_page (GFP_HIGHUSER );if (!area->pages[0 ]) goto free_bitmap;
Signature · 1 line
1.5.5.1
Oleg Nesterov
maintainerPATCH v2 3/3 uprobes: fix the waitqueue_active() check in xol_free_insn_slot()
In reply to Oleg NesterovThe xol_free_insn_slot()->waitqueue_active() check is buggy. We need mb() after we set the conditon for wait_event(), or xol_take_insn_slot() can miss the wakeup.
--- kernel/events/uprobes.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
kernel/events/uprobes.c +1 -0
@@ -1336,6 +1336,7 @@ static void xol_free_insn_slot(struct task_struct *tsk)clear_bit (slot_nr, area->bitmap);atomic_dec (&area->slot_count); +smp_mb__after_atomic ();/* pairs with prepare_to_wait() */ if (waitqueue_active (&area->wq))wake_up (&area->wq);
Signature · 1 line
1.5.5.1
Re: [PATCH 1/3] uprobes: fix the usage of install_special_mapping()
In reply to Oleg NesterovQuoted 9 lines · Oleg Nesterov
install_special_mapping(pages)expects that "pages" is the zero- terminated array whilexol_add_vma()passes&area->page, this means thatspecial_mapping_fault()can wrongly use the next member inxol_area(vaddr) as "struct page *".Fortunately, this area is not expandable so pgoff != 0 isn't possible (modulo bugs in
special_mapping_vmops), but still this does not look good.
I fell for that awhile back, too, causing a bizarre HPET bug.
What zeroes pages[1]?
--Andy
Re: [PATCH 2/3] uprobes: use vm_special_mapping to name the xol vma
In reply to Oleg NesterovQuoted 4 lines · Oleg Nesterov
Change
xol_add_vma()to use_install_special_mapping(), this way we can name the vma installed by uprobes. Currently it looks like private anonymous mapping, this is confusing and complicates the debugging. With this change/proc/$pid/mapsreports "[uprobes]".
I think this will cause core dumps to include it as a side effect. Is that okay?
--Andy
Oleg Nesterov
maintainerRe: [PATCH 1/3] uprobes: fix the usage of install_special_mapping()
In reply to Andy LutomirskiQuoted 7 lines · Andy Lutomirski
Quoted 5 lines · Oleg Nesterov
install_special_mapping(pages)expects that "pages" is the zero- terminated array whilexol_add_vma()passes&area->page, this means thatspecial_mapping_fault()can wrongly use the next member inxol_area(vaddr) as "struct page *".
Quoted 6 lines
Quoted 4 lines
Fortunately, this area is not expandable so pgoff != 0 isn't possible (modulo bugs in
special_mapping_vmops), but still this does not look good.I fell for that awhile back, too, causing a bizarre HPET bug.
I guess you mean no_pages[] = {NULL} in map_vdso() ?
uprobes differs, I think pgoff != 0 is not actually possible (assuming we fix special_mapping_fault). But this doesn't matter, this is wrong anyway.
Quoted 1 line
What zeroes pages[1]?
Heh ;) Thanks. I'll send v2.
Oleg.
Oleg Nesterov
maintainerRe: [PATCH 2/3] uprobes: use vm_special_mapping to name the xol vma
In reply to Andy LutomirskiQuoted 9 lines · Andy Lutomirski
Quoted 4 lines · Oleg Nesterov
Change
xol_add_vma()to use_install_special_mapping(), this way we can name the vma installed by uprobes. Currently it looks like private anonymous mapping, this is confusing and complicates the debugging. With this change/proc/$pid/mapsreports "[uprobes]".I think this will cause core dumps to include it as a side effect. Is that okay?
Yes, I even mentioned this in 0/3... I'll move thhis note into the changelog in v2.
Thanks!
Oleg.
Re: [PATCH 1/3] uprobes: fix the usage of install_special_mapping()
In reply to Oleg NesterovQuoted 8 lines · Oleg Nesterov
Quoted 7 lines · Andy Lutomirski
Quoted 5 lines · Oleg Nesterov
install_special_mapping(pages)expects that "pages" is the zero- terminated array whilexol_add_vma()passes&area->page, this means thatspecial_mapping_fault()can wrongly use the next member inxol_area(vaddr) as "struct page *".
Quoted 8 lines
Quoted 6 lines
Quoted 4 lines
Fortunately, this area is not expandable so pgoff != 0 isn't possible (modulo bugs in
special_mapping_vmops), but still this does not look good.I fell for that awhile back, too, causing a bizarre HPET bug.
I guess you mean no_pages[] = {NULL} in
map_vdso()?
Yes.
--Andy
Oleg Nesterov
maintainerv2 cover uprobes: minor fixes + name the xol vma
In reply to Oleg NesterovQuoted 8 lines · Oleg Nesterov
Hello,
The bugs are theoretical, but still should be fixed imo. And it would be nice to name the special vma installed by uprobes. As a side effect this vma will be core-dump'ed, I think this is good.
This doesn't depend on the pending longjmp fixes.
v2:
1/3 - initialize pages[1] (thanks Andy!)
2/3 - update the changelog
Oleg.
kernel/events/uprobes.c | 44 ++++++++++++++++++++++++++++---------------- 1 files changed, 28 insertions(+), 16 deletions(-)
Oleg Nesterov
maintainerPATCH v2 1/3 uprobes: fix the usage of install_special_mapping()
install_special_mapping(pages) expects that "pages" is the zero- terminated array while xol_add_vma() passes &area->page, this means that special_mapping_fault() can wrongly use the next member in xol_area (vaddr) as "struct page *".
Fortunately, this area is not expandable so pgoff != 0 isn't possible (modulo bugs in special_mapping_vmops), but still this does not look good.
--- kernel/events/uprobes.c | 17 +++++++++-------- 1 files changed, 9 insertions(+), 8 deletions(-)
kernel/events/uprobes.c +9 -8
@@ -99,7 +99,7 @@ struct xol_area {wait_queue_head_t wq;/* if all slots are busy */ atomic_t slot_count;/* number of in-use slots */ unsigned long *bitmap;/* 0 = free slot */ -struct page *page; +struct page *pages[2 ];/* * We keep the vma's vm_start rather than a pointer to the vma
@@ -1142,7 +1142,7 @@ static int xol_add_vma(struct mm_struct *mm, struct xol_area *area) } ret =install_special_mapping (mm, area->vaddr,PAGE_SIZE , -VM_EXEC |VM_MAYEXEC |VM_DONTCOPY |VM_IO , &area->page); +VM_EXEC |VM_MAYEXEC |VM_DONTCOPY |VM_IO , area->pages);if (ret) goto fail;
@@ -1168,21 +1168,22 @@ static struct xol_area *__create_xol_area(unsigned long vaddr)if (!area->bitmap) goto free_area; - area->page =alloc_page (GFP_HIGHUSER ); -if (!area->page) + area->pages[0 ] =alloc_page (GFP_HIGHUSER ); +if (!area->pages[0 ]) goto free_bitmap; + area->pages[1 ] =NULL ; area->vaddr = vaddr;init_waitqueue_head (&area->wq);/* Reserve the 1st slot for get_trampoline_vaddr() */ set_bit (0 , area->bitmap);atomic_set (&area->slot_count,1 ); -copy_to_page (area->page,0 , &insn,UPROBE_SWBP_INSN_SIZE ); +copy_to_page (area->pages[0 ],0 , &insn,UPROBE_SWBP_INSN_SIZE );if (!xol_add_vma (mm, area))return area; -__free_page (area->page); +__free_page (area->pages[0 ]); free_bitmap:kfree (area->bitmap); free_area:
@@ -1220,7 +1221,7 @@ void uprobe_clear_state(struct mm_struct *mm)if (!area)return ; -put_page (area->page); +put_page (area->pages[0 ]);kfree (area->bitmap);kfree (area); }
@@ -1289,7 +1290,7 @@ static unsigned long xol_get_insn_slot(struct uprobe *uprobe)if (unlikely (!xol_vaddr))return 0 ; -arch_uprobe_copy_ixol (area->page, xol_vaddr, +arch_uprobe_copy_ixol (area->pages[0 ], xol_vaddr, &uprobe->arch.ixol,sizeof (uprobe->arch.ixol));return xol_vaddr;
Signature · 1 line
1.5.5.1
Oleg Nesterov
maintainerPATCH v2 3/3 uprobes: fix the waitqueue_active() check in xol_free_insn_slot()
In reply to Oleg NesterovThe xol_free_insn_slot()->waitqueue_active() check is buggy. We need mb() after we set the conditon for wait_event(), or xol_take_insn_slot() can miss the wakeup.
--- kernel/events/uprobes.c | 1 + 1 files changed, 1 insertions(+), 0 deletions(-)
kernel/events/uprobes.c +1 -0
@@ -1337,6 +1337,7 @@ static void xol_free_insn_slot(struct task_struct *tsk)clear_bit (slot_nr, area->bitmap);atomic_dec (&area->slot_count); +smp_mb__after_atomic ();/* pairs with prepare_to_wait() */ if (waitqueue_active (&area->wq))wake_up (&area->wq);
Signature · 1 line
1.5.5.1
Oleg Nesterov
maintainerPATCH v2 2/3 uprobes: use vm_special_mapping to name the xol vma
In reply to Oleg NesterovChange xol_add_vma() to use _install_special_mapping(), this way we can name the vma installed by uprobes. Currently it looks like private anonymous mapping, this is confusing and complicates the debugging. With this change /proc/$pid/maps reports "[uprobes]".
As a side effect this will cause core dumps to include xol vma and I think this is good; this can help to debug the problem if the app crashed because it was probed.
--- kernel/events/uprobes.c | 30 ++++++++++++++++++++---------- 1 files changed, 20 insertions(+), 10 deletions(-)
kernel/events/uprobes.c +20 -10
@@ -96,17 +96,18 @@ struct uprobe {* allocated. */ struct xol_area { -wait_queue_head_t wq;/* if all slots are busy */ -atomic_t slot_count;/* number of in-use slots */ -unsigned long *bitmap;/* 0 = free slot */ -struct page *pages[2 ]; +wait_queue_head_t wq;/* if all slots are busy */ +atomic_t slot_count;/* number of in-use slots */ +unsigned long *bitmap;/* 0 = free slot */ +struct vm_special_mapping xol_mapping; +struct page *pages[2 ];/* * We keep the vma's vm_start rather than a pointer to the vma * itself. The probed process or a naughty kernel module could make * the vma go away, and we must handle that reasonably gracefully. */ -unsigned long vaddr;/* Page(s) of instruction slots */ +unsigned long vaddr;/* Page(s) of instruction slots */ };/*
@@ -1125,11 +1126,14 @@ void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned lon/* Slot allocation for XOL */ static int xol_add_vma (struct mm_struct *mm,struct xol_area *area) { -int ret = -EALREADY ; +struct vm_area_struct *vma; +int ret;down_write (&mm->mmap_sem); -if (mm->uprobes_state.xol_area) +if (mm->uprobes_state.xol_area) { + ret = -EALREADY ; goto fail; + }if (!area->vaddr) {/* Try to map as high as possible, this is only a hint. */
@@ -1141,11 +1145,15 @@ static int xol_add_vma(struct mm_struct *mm, struct xol_area *area) } } - ret =install_special_mapping (mm, area->vaddr,PAGE_SIZE , -VM_EXEC |VM_MAYEXEC |VM_DONTCOPY |VM_IO , area->pages); -if (ret) + vma =_install_special_mapping (mm, area->vaddr,PAGE_SIZE , +VM_EXEC |VM_MAYEXEC |VM_DONTCOPY |VM_IO , + &area->xol_mapping); +if (IS_ERR (vma)) { + ret =PTR_ERR (vma); goto fail; + } + ret =0 ;smp_wmb ();/* pairs with get_xol_area() */ mm->uprobes_state.xol_area = area; fail:
@@ -1168,6 +1176,8 @@ static struct xol_area *__create_xol_area(unsigned long vaddr)if (!area->bitmap) goto free_area; + area->xol_mapping.name ="[uprobes]" ; + area->xol_mapping.pages = area->pages; area->pages[0 ] =alloc_page (GFP_HIGHUSER );if (!area->pages[0 ]) goto free_bitmap;
Signature · 1 line
1.5.5.1