uprobes: misc
Subsystems (2)
Oleg Nesterov
maintainercover uprobes: misc
Hello,
Resend of the already discussed patches rebased against -tip.
The only change is the empty line in 2/15 requested by Srikar.
Re-ordered, bugfixes come first. Perhaps 1-3 makes sense for 3.5.
Oleg.
Oleg Nesterov
maintainerPATCH 1/15 uprobes: valid_vma() should reject VM_HUGETLB
__replace_page() obviously can't work with the hugetlbfs mappings, uprobe_register() will likely crash the kernel. Change valid_vma() to check VM_HUGETLB as well.
As for PageTransHuge() no need to worry, vma->vm_file != NULL.
--- kernel/events/uprobes.c | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-)
kernel/events/uprobes.c +2 -1
@@ -99,7 +99,8 @@ static bool valid_vma(struct vm_area_struct *vma, bool is_register)if (!is_register)return true; -if ((vma->vm_flags & (VM_READ |VM_WRITE |VM_EXEC |VM_SHARED )) == (VM_READ |VM_EXEC )) +if ((vma->vm_flags & (VM_HUGETLB |VM_READ |VM_WRITE |VM_EXEC |VM_SHARED )) + == (VM_READ |VM_EXEC ))return true;return false;
Signature · 1 line
1.5.5.1
tip-bot for Oleg Nesterov
maintainer[tip:perf/core] uprobes: Valid_vma() should reject VM_HUGETLB
Commit-ID: ea131377148cdfe90641b42ae9aa5a6b3a4fa327 Gitweb: http://git.kernel.org/tip/ea131377148cdfe90641b42ae9aa5a6b3a4fa327 Author: Oleg Nesterov <[email protected]> AuthorDate: Fri, 15 Jun 2012 17:43:22 +0200 Committer: Ingo Molnar <[email protected]> CommitDate: Sat, 16 Jun 2012 09:10:41 +0200
uprobes: Valid_vma() should reject VM_HUGETLB
__replace_page() obviously can't work with the hugetlbfs mappings, uprobe_register() will likely crash the kernel. Change valid_vma() to check VM_HUGETLB as well.
As for PageTransHuge() no need to worry, vma->vm_file != NULL.
--- kernel/events/uprobes.c | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-)
kernel/events/uprobes.c +2 -1
@@ -99,7 +99,8 @@ static bool valid_vma(struct vm_area_struct *vma, bool is_register)if (!is_register)return true; -if ((vma->vm_flags & (VM_READ |VM_WRITE |VM_EXEC |VM_SHARED )) == (VM_READ |VM_EXEC )) +if ((vma->vm_flags & (VM_HUGETLB |VM_READ |VM_WRITE |VM_EXEC |VM_SHARED )) + == (VM_READ |VM_EXEC ))return true;return false;
Oleg Nesterov
maintainerPATCH 2/15 uprobes: __copy_insn() should ensure a_ops->readpage != NULL
In reply to Oleg Nesterov__copy_insn() blindly calls read_mapping_page(), this will crash the kernel if ->readpage == NULL, add the necessary check. For example, hugetlbfs_aops->readpage is NULL. Perhaps we should change read_mapping_page() instead.
--- kernel/events/uprobes.c | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-)
kernel/events/uprobes.c +3 -0
@@ -610,6 +610,9 @@ __copy_insn(struct address_space *mapping, struct vm_area_struct *vma, char *insif (!filp)return -EINVAL ; +if (!mapping->a_ops->readpage) +return -EIO ; + idx = (unsigned long )(offset >>PAGE_CACHE_SHIFT ); off1 = offset &= ~PAGE_MASK ;
Signature · 1 line
1.5.5.1
tip-bot for Oleg Nesterov
maintainer[tip:perf/core] uprobes: __copy_insn() should ensure a_ops-> readpage != NULL
Commit-ID: cc359d180fa9c25a4c1819f17e07a422d788353d Gitweb: http://git.kernel.org/tip/cc359d180fa9c25a4c1819f17e07a422d788353d Author: Oleg Nesterov <[email protected]> AuthorDate: Fri, 15 Jun 2012 17:43:25 +0200 Committer: Ingo Molnar <[email protected]> CommitDate: Sat, 16 Jun 2012 09:10:42 +0200
uprobes: __copy_insn() should ensure a_ops->readpage != NULL
__copy_insn() blindly calls read_mapping_page(), this will crash the kernel if ->readpage == NULL, add the necessary check. For example, hugetlbfs_aops->readpage is NULL. Perhaps we should change read_mapping_page() instead.
--- kernel/events/uprobes.c | 3 +++ 1 files changed, 3 insertions(+), 0 deletions(-)
kernel/events/uprobes.c +3 -0
@@ -610,6 +610,9 @@ __copy_insn(struct address_space *mapping, struct vm_area_struct *vma, char *insif (!filp)return -EINVAL ; +if (!mapping->a_ops->readpage) +return -EIO ; + idx = (unsigned long )(offset >>PAGE_CACHE_SHIFT ); off1 = offset &= ~PAGE_MASK ;
Oleg Nesterov
maintainerPATCH 3/15 uprobes: write_opcode()->__replace_page() can race with try_to_unmap()
In reply to Oleg Nesterovwrite_opcode() gets old_page via get_user_pages() and then calls __replace_page() which assumes that this old_page is still mapped after pte_offset_map_lock().
This is not true if this old_page was already try_to_unmap()'ed, and in this case everything __replace_page() does with old_page is wrong. Just for example, put_page() is not balanced.
I think it is possible to teach __replace_page() to handle this unlikely case correctly, but this patch simply changes it to use page_check_address() and return -EAGAIN if it fails. The caller should notice this error code and retry.
Note: write_opcode() asks for the cleanups, I'll try to do this in a separate patch.
--- kernel/events/uprobes.c | 41 +++++++++++++---------------------------- 1 files changed, 13 insertions(+), 28 deletions(-)
kernel/events/uprobes.c +13 -28
@@ -129,33 +129,17 @@ static loff_t vma_address(struct vm_area_struct *vma, loff_t offset)static int __replace_page (struct vm_area_struct *vma,struct page *page,struct page *kpage) {struct mm_struct *mm = vma->vm_mm; -pgd_t *pgd; -pud_t *pud; -pmd_t *pmd; -pte_t *ptep; -spinlock_t *ptl;unsigned long addr; -int err = -EFAULT ; +spinlock_t *ptl; +pte_t *ptep; addr =page_address_in_vma (page, vma);if (addr == -EFAULT ) - goto out; - - pgd =pgd_offset (mm, addr); -if (!pgd_present (*pgd)) - goto out; - - pud =pud_offset (pgd, addr); -if (!pud_present (*pud)) - goto out; +return -EFAULT ; - pmd =pmd_offset (pud, addr); -if (!pmd_present (*pmd)) - goto out; - - ptep =pte_offset_map_lock (mm, pmd, addr, &ptl); + ptep =page_check_address (page, mm, addr, &ptl,0 );if (!ptep) - goto out; +return -EAGAIN ;get_page (kpage);page_add_new_anon_rmap (kpage, vma, addr);
@@ -174,10 +158,8 @@ static int __replace_page(struct vm_area_struct *vma, struct page *page, structtry_to_free_swap (page);put_page (page);pte_unmap_unlock (ptep, ptl); - err =0 ; -out: -return err; +return 0 ; }/**
@@ -222,9 +204,10 @@ static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm,void *vaddr_old, *vaddr_new;struct vm_area_struct *vma;struct uprobe *uprobe; +unsigned long pgoff;loff_t addr;int ret; - +retry:/* Read the page with vaddr into memory */ ret =get_user_pages (NULL , mm, vaddr,1 ,0 ,0 , &old_page, &vma);if (ret <=0 )
@@ -269,9 +252,9 @@ static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm,memcpy (vaddr_new, vaddr_old,PAGE_SIZE );/* poke the new insn in, ASSUMES we don't cross page boundary */ - vaddr &= ~PAGE_MASK ; -BUG_ON (vaddr +UPROBE_SWBP_INSN_SIZE >PAGE_SIZE ); -memcpy (vaddr_new + vaddr, &opcode,UPROBE_SWBP_INSN_SIZE ); + pgoff = (vaddr & ~PAGE_MASK ); +BUG_ON (pgoff +UPROBE_SWBP_INSN_SIZE >PAGE_SIZE ); +memcpy (vaddr_new + pgoff, &opcode,UPROBE_SWBP_INSN_SIZE );kunmap_atomic (vaddr_new);kunmap_atomic (vaddr_old);
@@ -291,6 +274,8 @@ unlock_out: put_out:put_page (old_page); +if (unlikely (ret == -EAGAIN )) + goto retry;return ret; }
Signature · 1 line
1.5.5.1
tip-bot for Oleg Nesterov
maintainer[tip:perf/core] uprobes: Write_opcode()->__replace_page() can race with try_to_unmap()
Commit-ID: 5323ce71e4b4e1f188ebbc0cc7776885ea6c75fb Gitweb: http://git.kernel.org/tip/5323ce71e4b4e1f188ebbc0cc7776885ea6c75fb Author: Oleg Nesterov <[email protected]> AuthorDate: Fri, 15 Jun 2012 17:43:28 +0200 Committer: Ingo Molnar <[email protected]> CommitDate: Sat, 16 Jun 2012 09:10:42 +0200
uprobes: Write_opcode()->__replace_page() can race with try_to_unmap()
write_opcode() gets old_page via get_user_pages() and then calls __replace_page() which assumes that this old_page is still mapped after pte_offset_map_lock().
This is not true if this old_page was already try_to_unmap()'ed, and in this case everything __replace_page() does with old_page is wrong. Just for example, put_page() is not balanced.
I think it is possible to teach __replace_page() to handle this unlikely case correctly, but this patch simply changes it to use page_check_address() and return -EAGAIN if it fails. The caller should notice this error code and retry.
Note: write_opcode() asks for the cleanups, I'll try to do this in a separate patch.
--- kernel/events/uprobes.c | 41 +++++++++++++---------------------------- 1 files changed, 13 insertions(+), 28 deletions(-)
kernel/events/uprobes.c +13 -28
@@ -129,33 +129,17 @@ static loff_t vma_address(struct vm_area_struct *vma, loff_t offset)static int __replace_page (struct vm_area_struct *vma,struct page *page,struct page *kpage) {struct mm_struct *mm = vma->vm_mm; -pgd_t *pgd; -pud_t *pud; -pmd_t *pmd; -pte_t *ptep; -spinlock_t *ptl;unsigned long addr; -int err = -EFAULT ; +spinlock_t *ptl; +pte_t *ptep; addr =page_address_in_vma (page, vma);if (addr == -EFAULT ) - goto out; - - pgd =pgd_offset (mm, addr); -if (!pgd_present (*pgd)) - goto out; - - pud =pud_offset (pgd, addr); -if (!pud_present (*pud)) - goto out; +return -EFAULT ; - pmd =pmd_offset (pud, addr); -if (!pmd_present (*pmd)) - goto out; - - ptep =pte_offset_map_lock (mm, pmd, addr, &ptl); + ptep =page_check_address (page, mm, addr, &ptl,0 );if (!ptep) - goto out; +return -EAGAIN ;get_page (kpage);page_add_new_anon_rmap (kpage, vma, addr);
@@ -174,10 +158,8 @@ static int __replace_page(struct vm_area_struct *vma, struct page *page, structtry_to_free_swap (page);put_page (page);pte_unmap_unlock (ptep, ptl); - err =0 ; -out: -return err; +return 0 ; }/**
@@ -222,9 +204,10 @@ static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm,void *vaddr_old, *vaddr_new;struct vm_area_struct *vma;struct uprobe *uprobe; +unsigned long pgoff;loff_t addr;int ret; - +retry:/* Read the page with vaddr into memory */ ret =get_user_pages (NULL , mm, vaddr,1 ,0 ,0 , &old_page, &vma);if (ret <=0 )
@@ -269,9 +252,9 @@ static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm,memcpy (vaddr_new, vaddr_old,PAGE_SIZE );/* poke the new insn in, ASSUMES we don't cross page boundary */ - vaddr &= ~PAGE_MASK ; -BUG_ON (vaddr +UPROBE_SWBP_INSN_SIZE >PAGE_SIZE ); -memcpy (vaddr_new + vaddr, &opcode,UPROBE_SWBP_INSN_SIZE ); + pgoff = (vaddr & ~PAGE_MASK ); +BUG_ON (pgoff +UPROBE_SWBP_INSN_SIZE >PAGE_SIZE ); +memcpy (vaddr_new + pgoff, &opcode,UPROBE_SWBP_INSN_SIZE );kunmap_atomic (vaddr_new);kunmap_atomic (vaddr_old);
@@ -291,6 +274,8 @@ unlock_out: put_out:put_page (old_page); +if (unlikely (ret == -EAGAIN )) + goto retry;return ret; }
Oleg Nesterov
maintainerPATCH 4/15 uprobes: install_breakpoint() should fail if is_swbp_insn() == T
In reply to Oleg Nesterovinstall_breakpoint() returns -EEXIST if is_swbp_insn(orig_insn) == T, the caller treats this code as success.
This is doubly wrong. The successful return should set UPROBE_COPY_INSN, but the real problem is that it shouldn't succeed. If the probed insn is int3 the application should get SIGTRAP, this won't happen with uprobe.
Probably we can fix this, we can add the UPROBE_SHARED_BP flag and teach handle_swbp/set_orig_insn to handle this case correctly. But this needs some complications and we have other insns which can't be probed, lets make a simple fix for now.
I think this needs a cleanup. UPROBE_COPY_INSN should die, copy_insn() should be called by alloc_uprobe(). arch_uprobe_analyze_insn() depends on ->mm (ia32_compat) but it is called only once.
--- kernel/events/uprobes.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
kernel/events/uprobes.c +1 -1
@@ -693,7 +693,7 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm,return ret;if (is_swbp_insn ((uprobe_opcode_t *)uprobe->arch.insn)) -return -EEXIST ; +return -ENOTSUPP ; ret =arch_uprobe_analyze_insn (&uprobe->arch, mm, addr);if (ret)
Signature · 1 line
1.5.5.1
tip-bot for Oleg Nesterov
maintainer[tip:perf/core] uprobes: Install_breakpoint() should fail if is_swbp_insn() == T
Commit-ID: c1914a0936f79ed0236f670122e06e36e4d332ee Gitweb: http://git.kernel.org/tip/c1914a0936f79ed0236f670122e06e36e4d332ee Author: Oleg Nesterov <[email protected]> AuthorDate: Fri, 15 Jun 2012 17:43:31 +0200 Committer: Ingo Molnar <[email protected]> CommitDate: Sat, 16 Jun 2012 09:10:43 +0200
uprobes: Install_breakpoint() should fail if is_swbp_insn() == T
install_breakpoint() returns -EEXIST if is_swbp_insn(orig_insn) == T, the caller treats this code as success.
This is doubly wrong. The successful return should set UPROBE_COPY_INSN, but the real problem is that it shouldn't succeed. If the probed insn is int3 the application should get SIGTRAP, this won't happen with uprobe.
Probably we can fix this, we can add the UPROBE_SHARED_BP flag and teach handle_swbp/set_orig_insn to handle this case correctly. But this needs some complications and we have other insns which can't be probed, lets make a simple fix for now.
I think this needs a cleanup. UPROBE_COPY_INSN should die, copy_insn() should be called by alloc_uprobe(). arch_uprobe_analyze_insn() depends on ->mm (ia32_compat) but it is called only once.
--- kernel/events/uprobes.c | 2 +- 1 files changed, 1 insertions(+), 1 deletions(-)
kernel/events/uprobes.c +1 -1
@@ -693,7 +693,7 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm,return ret;if (is_swbp_insn ((uprobe_opcode_t *)uprobe->arch.insn)) -return -EEXIST ; +return -ENOTSUPP ; ret =arch_uprobe_analyze_insn (&uprobe->arch, mm, addr);if (ret)
Oleg Nesterov
maintainerPATCH 5/15 uprobes: rework register_for_each_vma() to make it O(n)
In reply to Oleg NesterovCurrently register_for_each_vma() is O(n ** 2) + O(n ** 3), every time find_next_vma_info() "restarts" the vma_prio_tree_foreach() loop and each iteration rechecks the whole try_list. This also means that try_list can grow "indefinitely" if register/unregister races with munmap/mmap activity even if the number of mapping is bounded at any time.
With this patch register_for_each_vma() builds the list of mm/vaddr structures only once and does install_breakpoint() for each entry.
We do not care about the new mappings which can be created after build_map_info() drops mapping->i_mmap_mutex, uprobe_mmap() should do its work.
Note that we do not allocate map_info under i_mmap_mutex, this can deadlock with page reclaim (but see the next patch). So we use 2 lists, "curr" which we are going to return, and "prev" which holds the already allocated memory. The main loop deques the entry from "prev" (initially it is empty), and if "prev" becomes empty again it counts the number of entries we need to pre-allocate outside of i_mmap_mutex.
--- kernel/events/uprobes.c | 199 ++++++++++++++++++++--------------------------- 1 files changed, 86 insertions(+), 113 deletions(-)
kernel/events/uprobes.c +86 -113
@@ -60,17 +60,6 @@ static struct mutex uprobes_mmap_mutex[UPROBES_HASH_SZ];*/ static atomic_t uprobe_events =ATOMIC_INIT (0 ); -/* -* Maintain a temporary per vma info that can be used to search if a vma -* has already been handled. This structure is introduced since extending -* vm_area_struct wasnt recommended. -*/ -struct vma_info { -struct list_head probe_list; -struct mm_struct *mm; -loff_t vaddr; -}; -struct uprobe {struct rb_node rb_node;/* node in the rb tree */ atomic_t ref;
@@ -742,139 +731,123 @@ static void delete_uprobe(struct uprobe *uprobe)atomic_dec (&uprobe_events); } -static struct vma_info * -__find_next_vma_info (struct address_space *mapping,struct list_head *head, -struct vma_info *vi,loff_t offset,bool is_register) +struct map_info { +struct map_info *next; +struct mm_struct *mm; +loff_t vaddr; +}; + +static inline struct map_info *free_map_info (struct map_info *info) { +struct map_info *next = info->next; +kfree (info); +return next; +} + +static struct map_info * +build_map_info (struct address_space *mapping,loff_t offset,bool is_register) +{ +unsigned long pgoff = offset >>PAGE_SHIFT ;struct prio_tree_iter iter;struct vm_area_struct *vma; -struct vma_info *tmpvi; -unsigned long pgoff; -int existing_vma; -loff_t vaddr; - - pgoff = offset >>PAGE_SHIFT ; +struct map_info *curr =NULL ; +struct map_info *prev =NULL ; +struct map_info *info; +int more =0 ; + again: +mutex_lock (&mapping->i_mmap_mutex); vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) {if (!valid_vma (vma, is_register))continue ; - existing_vma =0 ; - vaddr =vma_address (vma, offset); - -list_for_each_entry (tmpvi, head, probe_list) { -if (tmpvi->mm == vma->vm_mm && tmpvi->vaddr == vaddr) { - existing_vma =1 ; -break ; - } - } - -/* -* Another vma needs a probe to be installed. However skip -* installing the probe if the vma is about to be unlinked. -*/ -if (!existing_vma &&atomic_inc_not_zero (&vma->vm_mm->mm_users)) { - vi->mm = vma->vm_mm; - vi->vaddr = vaddr; -list_add (&vi->probe_list, head); - -return vi; +if (!prev) { + more++; +continue ; } - } - -return NULL ; -} -/* -* Iterate in the rmap prio tree and find a vma where a probe has not -* yet been inserted. -*/ -static struct vma_info * -find_next_vma_info (struct address_space *mapping,struct list_head *head, -loff_t offset,bool is_register) -{ -struct vma_info *vi, *retvi; +if (!atomic_inc_not_zero (&vma->vm_mm->mm_users)) +continue ; - vi =kzalloc (sizeof (struct vma_info ),GFP_KERNEL ); -if (!vi) -return ERR_PTR (-ENOMEM ); + info = prev; + prev = prev->next; + info->next = curr; + curr = info; -mutex_lock (&mapping->i_mmap_mutex); - retvi =__find_next_vma_info (mapping, head, vi, offset, is_register); + info->mm = vma->vm_mm; + info->vaddr =vma_address (vma, offset); + }mutex_unlock (&mapping->i_mmap_mutex); -if (!retvi) -kfree (vi); +if (!more) + goto out; + + prev = curr; +while (curr) { +mmput (curr->mm); + curr = curr->next; + } -return retvi; +do { + info =kmalloc (sizeof (struct map_info ),GFP_KERNEL ); +if (!info) { + curr =ERR_PTR (-ENOMEM ); + goto out; + } + info->next = prev; + prev = info; + }while (--more); + + goto again; + out: +while (prev) + prev =free_map_info (prev); +return curr; }static int register_for_each_vma (struct uprobe *uprobe,bool is_register) { -struct list_head try_list; -struct vm_area_struct *vma; -struct address_space *mapping; -struct vma_info *vi, *tmpvi; -struct mm_struct *mm; -loff_t vaddr; -int ret; +struct map_info *info; +int err =0 ; - mapping = uprobe->inode->i_mapping; -INIT_LIST_HEAD (&try_list); - - ret =0 ; + info =build_map_info (uprobe->inode->i_mapping, + uprobe->offset, is_register); +if (IS_ERR (info)) +return PTR_ERR (info); -for (;;) { - vi =find_next_vma_info (mapping, &try_list, uprobe->offset, is_register); -if (!vi) -break ; +while (info) { +struct mm_struct *mm = info->mm; +struct vm_area_struct *vma; +loff_t vaddr; -if (IS_ERR (vi)) { - ret =PTR_ERR (vi); -break ; - } +if (err) + goto free; - mm = vi->mm;down_write (&mm->mmap_sem); - vma =find_vma (mm, (unsigned long )vi->vaddr); -if (!vma || !valid_vma (vma, is_register)) { -list_del (&vi->probe_list); -kfree (vi); -up_write (&mm->mmap_sem); -mmput (mm); -continue ; - } + vma =find_vma (mm, (unsigned long )info->vaddr); +if (!vma || !valid_vma (vma, is_register)) + goto unlock; + vaddr =vma_address (vma, uprobe->offset);if (vma->vm_file->f_mapping->host != uprobe->inode || - vaddr != vi->vaddr) { -list_del (&vi->probe_list); -kfree (vi); -up_write (&mm->mmap_sem); -mmput (mm); -continue ; - } + vaddr != info->vaddr) + goto unlock; -if (is_register) - ret =install_breakpoint (uprobe, mm, vma, vi->vaddr); -else -remove_breakpoint (uprobe, mm, vi->vaddr); - -up_write (&mm->mmap_sem); -mmput (mm);if (is_register) { -if (ret && ret == -EEXIST ) - ret =0 ; -if (ret) -break ; + err =install_breakpoint (uprobe, mm, vma, info->vaddr); +if (err == -EEXIST ) + err =0 ; + }else { +remove_breakpoint (uprobe, mm, info->vaddr); } + unlock: +up_write (&mm->mmap_sem); + free: +mmput (mm); + info =free_map_info (info); } -list_for_each_entry_safe (vi, tmpvi, &try_list, probe_list) { -list_del (&vi->probe_list); -kfree (vi); - } - -return ret; +return err; }static int __uprobe_register (struct uprobe *uprobe)
Signature · 1 line
1.5.5.1
tip-bot for Oleg Nesterov
maintainer[tip:perf/core] uprobes: Rework register_for_each_vma() to make it O(n)
Commit-ID: 268720903f87e0b84b161626c4447b81671b5d18 Gitweb: http://git.kernel.org/tip/268720903f87e0b84b161626c4447b81671b5d18 Author: Oleg Nesterov <[email protected]> AuthorDate: Fri, 15 Jun 2012 17:43:33 +0200 Committer: Ingo Molnar <[email protected]> CommitDate: Sat, 16 Jun 2012 09:10:43 +0200
uprobes: Rework register_for_each_vma() to make it O(n)
Currently register_for_each_vma() is O(n ** 2) + O(n ** 3), every time find_next_vma_info() "restarts" the vma_prio_tree_foreach() loop and each iteration rechecks the whole try_list. This also means that try_list can grow "indefinitely" if register/unregister races with munmap/mmap activity even if the number of mapping is bounded at any time.
With this patch register_for_each_vma() builds the list of mm/vaddr structures only once and does install_breakpoint() for each entry.
We do not care about the new mappings which can be created after build_map_info() drops mapping->i_mmap_mutex, uprobe_mmap() should do its work.
Note that we do not allocate map_info under i_mmap_mutex, this can deadlock with page reclaim (but see the next patch). So we use 2 lists, "curr" which we are going to return, and "prev" which holds the already allocated memory. The main loop deques the entry from "prev" (initially it is empty), and if "prev" becomes empty again it counts the number of entries we need to pre-allocate outside of i_mmap_mutex.
--- kernel/events/uprobes.c | 199 ++++++++++++++++++++--------------------------- 1 files changed, 86 insertions(+), 113 deletions(-)
kernel/events/uprobes.c +86 -113
@@ -60,17 +60,6 @@ static struct mutex uprobes_mmap_mutex[UPROBES_HASH_SZ];*/ static atomic_t uprobe_events =ATOMIC_INIT (0 ); -/* -* Maintain a temporary per vma info that can be used to search if a vma -* has already been handled. This structure is introduced since extending -* vm_area_struct wasnt recommended. -*/ -struct vma_info { -struct list_head probe_list; -struct mm_struct *mm; -loff_t vaddr; -}; -struct uprobe {struct rb_node rb_node;/* node in the rb tree */ atomic_t ref;
@@ -742,139 +731,123 @@ static void delete_uprobe(struct uprobe *uprobe)atomic_dec (&uprobe_events); } -static struct vma_info * -__find_next_vma_info (struct address_space *mapping,struct list_head *head, -struct vma_info *vi,loff_t offset,bool is_register) +struct map_info { +struct map_info *next; +struct mm_struct *mm; +loff_t vaddr; +}; + +static inline struct map_info *free_map_info (struct map_info *info) { +struct map_info *next = info->next; +kfree (info); +return next; +} + +static struct map_info * +build_map_info (struct address_space *mapping,loff_t offset,bool is_register) +{ +unsigned long pgoff = offset >>PAGE_SHIFT ;struct prio_tree_iter iter;struct vm_area_struct *vma; -struct vma_info *tmpvi; -unsigned long pgoff; -int existing_vma; -loff_t vaddr; - - pgoff = offset >>PAGE_SHIFT ; +struct map_info *curr =NULL ; +struct map_info *prev =NULL ; +struct map_info *info; +int more =0 ; + again: +mutex_lock (&mapping->i_mmap_mutex); vma_prio_tree_foreach(vma, &iter, &mapping->i_mmap, pgoff, pgoff) {if (!valid_vma (vma, is_register))continue ; - existing_vma =0 ; - vaddr =vma_address (vma, offset); - -list_for_each_entry (tmpvi, head, probe_list) { -if (tmpvi->mm == vma->vm_mm && tmpvi->vaddr == vaddr) { - existing_vma =1 ; -break ; - } - } - -/* -* Another vma needs a probe to be installed. However skip -* installing the probe if the vma is about to be unlinked. -*/ -if (!existing_vma &&atomic_inc_not_zero (&vma->vm_mm->mm_users)) { - vi->mm = vma->vm_mm; - vi->vaddr = vaddr; -list_add (&vi->probe_list, head); - -return vi; +if (!prev) { + more++; +continue ; } - } - -return NULL ; -} -/* -* Iterate in the rmap prio tree and find a vma where a probe has not -* yet been inserted. -*/ -static struct vma_info * -find_next_vma_info (struct address_space *mapping,struct list_head *head, -loff_t offset,bool is_register) -{ -struct vma_info *vi, *retvi; +if (!atomic_inc_not_zero (&vma->vm_mm->mm_users)) +continue ; - vi =kzalloc (sizeof (struct vma_info ),GFP_KERNEL ); -if (!vi) -return ERR_PTR (-ENOMEM ); + info = prev; + prev = prev->next; + info->next = curr; + curr = info; -mutex_lock (&mapping->i_mmap_mutex); - retvi =__find_next_vma_info (mapping, head, vi, offset, is_register); + info->mm = vma->vm_mm; + info->vaddr =vma_address (vma, offset); + }mutex_unlock (&mapping->i_mmap_mutex); -if (!retvi) -kfree (vi); +if (!more) + goto out; + + prev = curr; +while (curr) { +mmput (curr->mm); + curr = curr->next; + } -return retvi; +do { + info =kmalloc (sizeof (struct map_info ),GFP_KERNEL ); +if (!info) { + curr =ERR_PTR (-ENOMEM ); + goto out; + } + info->next = prev; + prev = info; + }while (--more); + + goto again; + out: +while (prev) + prev =free_map_info (prev); +return curr; }static int register_for_each_vma (struct uprobe *uprobe,bool is_register) { -struct list_head try_list; -struct vm_area_struct *vma; -struct address_space *mapping; -struct vma_info *vi, *tmpvi; -struct mm_struct *mm; -loff_t vaddr; -int ret; +struct map_info *info; +int err =0 ; - mapping = uprobe->inode->i_mapping; -INIT_LIST_HEAD (&try_list); - - ret =0 ; + info =build_map_info (uprobe->inode->i_mapping, + uprobe->offset, is_register); +if (IS_ERR (info)) +return PTR_ERR (info); -for (;;) { - vi =find_next_vma_info (mapping, &try_list, uprobe->offset, is_register); -if (!vi) -break ; +while (info) { +struct mm_struct *mm = info->mm; +struct vm_area_struct *vma; +loff_t vaddr; -if (IS_ERR (vi)) { - ret =PTR_ERR (vi); -break ; - } +if (err) + goto free; - mm = vi->mm;down_write (&mm->mmap_sem); - vma =find_vma (mm, (unsigned long )vi->vaddr); -if (!vma || !valid_vma (vma, is_register)) { -list_del (&vi->probe_list); -kfree (vi); -up_write (&mm->mmap_sem); -mmput (mm); -continue ; - } + vma =find_vma (mm, (unsigned long )info->vaddr); +if (!vma || !valid_vma (vma, is_register)) + goto unlock; + vaddr =vma_address (vma, uprobe->offset);if (vma->vm_file->f_mapping->host != uprobe->inode || - vaddr != vi->vaddr) { -list_del (&vi->probe_list); -kfree (vi); -up_write (&mm->mmap_sem); -mmput (mm); -continue ; - } + vaddr != info->vaddr) + goto unlock; -if (is_register) - ret =install_breakpoint (uprobe, mm, vma, vi->vaddr); -else -remove_breakpoint (uprobe, mm, vi->vaddr); - -up_write (&mm->mmap_sem); -mmput (mm);if (is_register) { -if (ret && ret == -EEXIST ) - ret =0 ; -if (ret) -break ; + err =install_breakpoint (uprobe, mm, vma, info->vaddr); +if (err == -EEXIST ) + err =0 ; + }else { +remove_breakpoint (uprobe, mm, info->vaddr); } + unlock: +up_write (&mm->mmap_sem); + free: +mmput (mm); + info =free_map_info (info); } -list_for_each_entry_safe (vi, tmpvi, &try_list, probe_list) { -list_del (&vi->probe_list); -kfree (vi); - } - -return ret; +return err; }static int __uprobe_register (struct uprobe *uprobe)
Oleg Nesterov
maintainerPATCH 6/15 uprobes: change build_map_info() to try kmalloc(GFP_NOWAIT) first
In reply to Oleg Nesterovbuild_map_info() doesn't allocate the memory under i_mmap_mutex to avoid the deadlock with page reclaim. But it can try GFP_NOWAIT first, it should work in the likely case and thus we almost never need the pre-alloc-and-retry path.
--- kernel/events/uprobes.c | 10 ++++++++++ 1 files changed, 10 insertions(+), 0 deletions(-)
kernel/events/uprobes.c +10 -0
@@ -761,6 +761,16 @@ build_map_info(struct address_space *mapping, loff_t offset, bool is_register)if (!valid_vma (vma, is_register))continue ; +if (!prev && !more) { +/* +* Needs GFP_NOWAIT to avoid i_mmap_mutex recursion through +* reclaim. This is optimistic, no harm done if it fails. +*/ + prev =kmalloc (sizeof (struct map_info ), +GFP_NOWAIT | __GFP_NOMEMALLOC | __GFP_NOWARN); +if (prev) + prev->next =NULL ; + }if (!prev) { more++;continue ;
Signature · 1 line
1.5.5.1
tip-bot for Oleg Nesterov
maintainer[tip:perf/core] uprobes: Change build_map_info() to try kmalloc( GFP_NOWAIT) first
Commit-ID: 7a5bfb66b07f22d2429db776da7bb8b57bfb5cff Gitweb: http://git.kernel.org/tip/7a5bfb66b07f22d2429db776da7bb8b57bfb5cff Author: Oleg Nesterov <[email protected]> AuthorDate: Fri, 15 Jun 2012 17:43:36 +0200 Committer: Ingo Molnar <[email protected]> CommitDate: Sat, 16 Jun 2012 09:10:44 +0200
uprobes: Change build_map_info() to try kmalloc(GFP_NOWAIT) first
build_map_info() doesn't allocate the memory under i_mmap_mutex to avoid the deadlock with page reclaim. But it can try GFP_NOWAIT first, it should work in the likely case and thus we almost never need the pre-alloc-and-retry path.
--- kernel/events/uprobes.c | 10 ++++++++++ 1 files changed, 10 insertions(+), 0 deletions(-)
kernel/events/uprobes.c +10 -0
@@ -761,6 +761,16 @@ build_map_info(struct address_space *mapping, loff_t offset, bool is_register)if (!valid_vma (vma, is_register))continue ; +if (!prev && !more) { +/* +* Needs GFP_NOWAIT to avoid i_mmap_mutex recursion through +* reclaim. This is optimistic, no harm done if it fails. +*/ + prev =kmalloc (sizeof (struct map_info ), +GFP_NOWAIT | __GFP_NOMEMALLOC | __GFP_NOWARN); +if (prev) + prev->next =NULL ; + }if (!prev) { more++;continue ;
Oleg Nesterov
maintainerPATCH 7/15 uprobes: document uprobe_register() vs uprobe_mmap() race
In reply to Oleg NesterovFrom: Peter Zijlstra <[email protected]>
Because the mind is treacherous and makes us forget we need to write stuff down.
--- kernel/events/uprobes.c | 31 ++++++++++++++++++++++++++++--- 1 files changed, 28 insertions(+), 3 deletions(-)
kernel/events/uprobes.c +28 -3
@@ -44,6 +44,23 @@ static DEFINE_SPINLOCK(uprobes_treelock); /* serialize rbtree access */#define UPROBES_HASH_SZ 13 +/* +* We need separate register/unregister and mmap/munmap lock hashes because +* of mmap_sem nesting. +* +* uprobe_register() needs to install probes on (potentially) all processes +* and thus needs to acquire multiple mmap_sems (consequtively, not +* concurrently), whereas uprobe_mmap() is called while holding mmap_sem +* for the particular process doing the mmap. +* +* uprobe_register()->register_for_each_vma() needs to drop/acquire mmap_sem +* because of lock order against i_mmap_mutex. This means there's a hole in +* the register vma iteration where a mmap() can happen. +* +* Thus uprobe_register() can race with uprobe_mmap() and we can try and +* install a probe where one is already installed. +*/ +/* serialize (un)register */ static struct mutex uprobes_mutex[UPROBES_HASH_SZ ];
@@ -339,7 +356,9 @@ out:int __weak set_swbp (struct arch_uprobe *auprobe,struct mm_struct *mm,unsigned long vaddr) {int result; - +/* +* See the comment near uprobes_hash(). +*/ result =is_swbp_at_addr (mm, vaddr);if (result ==1 )return -EEXIST ;
@@ -845,6 +864,10 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register)if (is_register) { err =install_breakpoint (uprobe, mm, vma, info->vaddr); +/* +* We can race against uprobe_mmap(), see the +* comment near uprobe_hash(). +*/ if (err == -EEXIST ) err =0 ; }else {
@@ -1054,8 +1077,10 @@ int uprobe_mmap(struct vm_area_struct *vma) } ret =install_breakpoint (uprobe, vma->vm_mm, vma, vaddr); - -/* Ignore double add: */ +/* +* We can race against uprobe_register(), see the +* comment near uprobe_hash(). +*/ if (ret == -EEXIST ) { ret =0 ;
Signature · 1 line
1.5.5.1
[tip:perf/core] uprobes: Document uprobe_register() vs uprobe_mmap () race
Commit-ID: c5784de2b351fe871bb57487878f7fc7ec5b075c Gitweb: http://git.kernel.org/tip/c5784de2b351fe871bb57487878f7fc7ec5b075c Author: Peter Zijlstra <[email protected]> AuthorDate: Fri, 15 Jun 2012 17:43:39 +0200 Committer: Ingo Molnar <[email protected]> CommitDate: Sat, 16 Jun 2012 09:10:45 +0200
uprobes: Document uprobe_register() vs uprobe_mmap() race
Because the mind is treacherous and makes us forget we need to write stuff down.
--- kernel/events/uprobes.c | 31 ++++++++++++++++++++++++++++--- 1 files changed, 28 insertions(+), 3 deletions(-)
kernel/events/uprobes.c +28 -3
@@ -44,6 +44,23 @@ static DEFINE_SPINLOCK(uprobes_treelock); /* serialize rbtree access */#define UPROBES_HASH_SZ 13 +/* +* We need separate register/unregister and mmap/munmap lock hashes because +* of mmap_sem nesting. +* +* uprobe_register() needs to install probes on (potentially) all processes +* and thus needs to acquire multiple mmap_sems (consequtively, not +* concurrently), whereas uprobe_mmap() is called while holding mmap_sem +* for the particular process doing the mmap. +* +* uprobe_register()->register_for_each_vma() needs to drop/acquire mmap_sem +* because of lock order against i_mmap_mutex. This means there's a hole in +* the register vma iteration where a mmap() can happen. +* +* Thus uprobe_register() can race with uprobe_mmap() and we can try and +* install a probe where one is already installed. +*/ +/* serialize (un)register */ static struct mutex uprobes_mutex[UPROBES_HASH_SZ ];
@@ -339,7 +356,9 @@ out:int __weak set_swbp (struct arch_uprobe *auprobe,struct mm_struct *mm,unsigned long vaddr) {int result; - +/* +* See the comment near uprobes_hash(). +*/ result =is_swbp_at_addr (mm, vaddr);if (result ==1 )return -EEXIST ;
@@ -845,6 +864,10 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register)if (is_register) { err =install_breakpoint (uprobe, mm, vma, info->vaddr); +/* +* We can race against uprobe_mmap(), see the +* comment near uprobe_hash(). +*/ if (err == -EEXIST ) err =0 ; }else {
@@ -1054,8 +1077,10 @@ int uprobe_mmap(struct vm_area_struct *vma) } ret =install_breakpoint (uprobe, vma->vm_mm, vma, vaddr); - -/* Ignore double add: */ +/* +* We can race against uprobe_register(), see the +* comment near uprobe_hash(). +*/ if (ret == -EEXIST ) { ret =0 ;
Oleg Nesterov
maintainerPATCH 8/15 uprobes: copy_insn() shouldn't depend on mm/vma/vaddr
In reply to Oleg Nesterov1. copy_insn() doesn't need "addr", it can use uprobe->offset. Remove this argument.
2. Change copy_insn/__copy_insn to accept "struct file*" instead of vma.
copy_insn() is called only once and mm/vma/vaddr are random, it shouldn't depend on them.
--- kernel/events/uprobes.c | 15 ++++++--------- 1 files changed, 6 insertions(+), 9 deletions(-)
kernel/events/uprobes.c +6 -9
@@ -591,10 +591,9 @@ static bool consumer_del(struct uprobe *uprobe, struct uprobe_consumer *uc) }static int -__copy_insn (struct address_space *mapping,struct vm_area_struct *vma,char *insn, +__copy_insn (struct address_space *mapping,struct file *filp,char *insn,unsigned long nbytes,unsigned long offset) { -struct file *filp = vma->vm_file;struct page *page;void *vaddr;unsigned long off1;
@@ -625,15 +624,13 @@ __copy_insn(struct address_space *mapping, struct vm_area_struct *vma, char *insreturn 0 ; } -static int -copy_insn (struct uprobe *uprobe,struct vm_area_struct *vma,unsigned long addr) +static int copy_insn (struct uprobe *uprobe,struct file *filp) {struct address_space *mapping;unsigned long nbytes;int bytes; - addr &= ~PAGE_MASK ; - nbytes =PAGE_SIZE - addr; + nbytes =PAGE_SIZE - (uprobe->offset & ~PAGE_MASK ); mapping = uprobe->inode->i_mapping;/* Instruction at end of binary; copy only available bytes */
@@ -644,13 +641,13 @@ copy_insn(struct uprobe *uprobe, struct vm_area_struct *vma, unsigned long addr)/* Instruction at the page-boundary; copy bytes in second page */ if (nbytes < bytes) { -if (__copy_insn (mapping, vma, uprobe->arch.insn + nbytes, +if (__copy_insn (mapping, filp, uprobe->arch.insn + nbytes, bytes - nbytes, uprobe->offset + nbytes))return -ENOMEM ; bytes = nbytes; } -return __copy_insn (mapping, vma, uprobe->arch.insn, bytes, uprobe->offset); +return __copy_insn (mapping, filp, uprobe->arch.insn, bytes, uprobe->offset); }/*
@@ -696,7 +693,7 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, addr = (unsigned long )vaddr;if (!(uprobe->flags &UPROBE_COPY_INSN )) { - ret =copy_insn (uprobe, vma, addr); + ret =copy_insn (uprobe, vma->vm_file);if (ret)return ret;
Signature · 1 line
1.5.5.1
tip-bot for Oleg Nesterov
maintainer[tip:perf/core] uprobes: Copy_insn() shouldn't depend on mm/vma/ vaddr
Commit-ID: d436615e60c386095dac4a9bf72b08868d2a7564 Gitweb: http://git.kernel.org/tip/d436615e60c386095dac4a9bf72b08868d2a7564 Author: Oleg Nesterov <[email protected]> AuthorDate: Fri, 15 Jun 2012 17:43:42 +0200 Committer: Ingo Molnar <[email protected]> CommitDate: Sat, 16 Jun 2012 09:10:45 +0200
uprobes: Copy_insn() shouldn't depend on mm/vma/vaddr
1. copy_insn() doesn't need "addr", it can use uprobe->offset. Remove this argument.
2. Change copy_insn/__copy_insn to accept "struct file*" instead of vma.
copy_insn() is called only once and mm/vma/vaddr are random, it shouldn't depend on them.
--- kernel/events/uprobes.c | 15 ++++++--------- 1 files changed, 6 insertions(+), 9 deletions(-)
kernel/events/uprobes.c +6 -9
@@ -591,10 +591,9 @@ static bool consumer_del(struct uprobe *uprobe, struct uprobe_consumer *uc) }static int -__copy_insn (struct address_space *mapping,struct vm_area_struct *vma,char *insn, +__copy_insn (struct address_space *mapping,struct file *filp,char *insn,unsigned long nbytes,unsigned long offset) { -struct file *filp = vma->vm_file;struct page *page;void *vaddr;unsigned long off1;
@@ -625,15 +624,13 @@ __copy_insn(struct address_space *mapping, struct vm_area_struct *vma, char *insreturn 0 ; } -static int -copy_insn (struct uprobe *uprobe,struct vm_area_struct *vma,unsigned long addr) +static int copy_insn (struct uprobe *uprobe,struct file *filp) {struct address_space *mapping;unsigned long nbytes;int bytes; - addr &= ~PAGE_MASK ; - nbytes =PAGE_SIZE - addr; + nbytes =PAGE_SIZE - (uprobe->offset & ~PAGE_MASK ); mapping = uprobe->inode->i_mapping;/* Instruction at end of binary; copy only available bytes */
@@ -644,13 +641,13 @@ copy_insn(struct uprobe *uprobe, struct vm_area_struct *vma, unsigned long addr)/* Instruction at the page-boundary; copy bytes in second page */ if (nbytes < bytes) { -if (__copy_insn (mapping, vma, uprobe->arch.insn + nbytes, +if (__copy_insn (mapping, filp, uprobe->arch.insn + nbytes, bytes - nbytes, uprobe->offset + nbytes))return -ENOMEM ; bytes = nbytes; } -return __copy_insn (mapping, vma, uprobe->arch.insn, bytes, uprobe->offset); +return __copy_insn (mapping, filp, uprobe->arch.insn, bytes, uprobe->offset); }/*
@@ -696,7 +693,7 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, addr = (unsigned long )vaddr;if (!(uprobe->flags &UPROBE_COPY_INSN )) { - ret =copy_insn (uprobe, vma, addr); + ret =copy_insn (uprobe, vma->vm_file);if (ret)return ret;
Oleg Nesterov
maintainerPATCH 9/15 uprobes: copy_insn() should not return -ENOMEM if __copy_insn() fails
In reply to Oleg Nesterovcopy_insn() returns -ENOMEM if the first __copy_insn() fails, it should return the correct error code.
--- kernel/events/uprobes.c | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-)
kernel/events/uprobes.c +4 -4
@@ -641,10 +641,10 @@ static int copy_insn(struct uprobe *uprobe, struct file *filp)/* Instruction at the page-boundary; copy bytes in second page */ if (nbytes < bytes) { -if (__copy_insn (mapping, filp, uprobe->arch.insn + nbytes, - bytes - nbytes, uprobe->offset + nbytes)) -return -ENOMEM ; - +int err =__copy_insn (mapping, filp, uprobe->arch.insn + nbytes, + bytes - nbytes, uprobe->offset + nbytes); +if (err) +return err; bytes = nbytes; }return __copy_insn (mapping, filp, uprobe->arch.insn, bytes, uprobe->offset);
Signature · 1 line
1.5.5.1
tip-bot for Oleg Nesterov
maintainer[tip:perf/core] uprobes: Copy_insn() should not return -ENOMEM if __copy_insn() fails
Commit-ID: fc36f59565861af2e897225bc3782479a26c5d5a Gitweb: http://git.kernel.org/tip/fc36f59565861af2e897225bc3782479a26c5d5a Author: Oleg Nesterov <[email protected]> AuthorDate: Fri, 15 Jun 2012 17:43:44 +0200 Committer: Ingo Molnar <[email protected]> CommitDate: Sat, 16 Jun 2012 09:10:46 +0200
uprobes: Copy_insn() should not return -ENOMEM if __copy_insn() fails
copy_insn() returns -ENOMEM if the first __copy_insn() fails, it should return the correct error code.
--- kernel/events/uprobes.c | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-)
kernel/events/uprobes.c +4 -4
@@ -641,10 +641,10 @@ static int copy_insn(struct uprobe *uprobe, struct file *filp)/* Instruction at the page-boundary; copy bytes in second page */ if (nbytes < bytes) { -if (__copy_insn (mapping, filp, uprobe->arch.insn + nbytes, - bytes - nbytes, uprobe->offset + nbytes)) -return -ENOMEM ; - +int err =__copy_insn (mapping, filp, uprobe->arch.insn + nbytes, + bytes - nbytes, uprobe->offset + nbytes); +if (err) +return err; bytes = nbytes; }return __copy_insn (mapping, filp, uprobe->arch.insn, bytes, uprobe->offset);
Oleg Nesterov
maintainerPATCH 10/15 uprobes: no need to re-check vma_address() in write_opcode()
In reply to Oleg Nesterovwrite_opcode() is called by register_for_each_vma() and uprobe_mmap() paths. In both cases the caller has already verified this vaddr under mmap_sem, no need to re-check.
Note also that this check is wrong anyway, we should not truncate loff_t returned by vma_address() if we do not trust this mapping.
--- kernel/events/uprobes.c | 5 ----- 1 files changed, 0 insertions(+), 5 deletions(-)
kernel/events/uprobes.c +0 -5
@@ -211,7 +211,6 @@ static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm,struct vm_area_struct *vma;struct uprobe *uprobe;unsigned long pgoff; -loff_t addr;int ret; retry:/* Read the page with vaddr into memory */
@@ -235,10 +234,6 @@ retry:if (mapping != vma->vm_file->f_mapping) goto put_out; - addr =vma_address (vma, uprobe->offset); -if (vaddr != (unsigned long )addr) - goto put_out; - ret = -ENOMEM ; new_page =alloc_page_vma (GFP_HIGHUSER_MOVABLE , vma, vaddr);if (!new_page)
Signature · 1 line
1.5.5.1
tip-bot for Oleg Nesterov
maintainer[tip:perf/core] uprobes: No need to re-check vma_address() in write_opcode()
Commit-ID: eb2bf57bee42c7565032f93adaa211e2c9fcc52c Gitweb: http://git.kernel.org/tip/eb2bf57bee42c7565032f93adaa211e2c9fcc52c Author: Oleg Nesterov <[email protected]> AuthorDate: Fri, 15 Jun 2012 17:43:47 +0200 Committer: Ingo Molnar <[email protected]> CommitDate: Sat, 16 Jun 2012 09:10:47 +0200
uprobes: No need to re-check vma_address() in write_opcode()
write_opcode() is called by register_for_each_vma() and uprobe_mmap() paths. In both cases the caller has already verified this vaddr under mmap_sem, no need to re-check.
Note also that this check is wrong anyway, we should not truncate loff_t returned by vma_address() if we do not trust this mapping.
--- kernel/events/uprobes.c | 5 ----- 1 files changed, 0 insertions(+), 5 deletions(-)
kernel/events/uprobes.c +0 -5
@@ -211,7 +211,6 @@ static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm,struct vm_area_struct *vma;struct uprobe *uprobe;unsigned long pgoff; -loff_t addr;int ret; retry:/* Read the page with vaddr into memory */
@@ -235,10 +234,6 @@ retry:if (mapping != vma->vm_file->f_mapping) goto put_out; - addr =vma_address (vma, uprobe->offset); -if (vaddr != (unsigned long )addr) - goto put_out; - ret = -ENOMEM ; new_page =alloc_page_vma (GFP_HIGHUSER_MOVABLE , vma, vaddr);if (!new_page)
Oleg Nesterov
maintainerPATCH 11/15 uprobes: move BUG_ON(UPROBE_SWBP_INSN_SIZE) from write_opcode() to install_breakpoint()
In reply to Oleg Nesterovwrite_opcode() ensures that UPROBE_SWBP_INSN doesn't cross the page boundary. This looks a bit confusing, the check does not depend on vaddr and it is enough to do it only once right after install_breakpoint()->arch_uprobe_analyze_insn().
--- kernel/events/uprobes.c | 11 +++++------ 1 files changed, 5 insertions(+), 6 deletions(-)
kernel/events/uprobes.c +5 -6
@@ -210,7 +210,6 @@ static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm,void *vaddr_old, *vaddr_new;struct vm_area_struct *vma;struct uprobe *uprobe; -unsigned long pgoff;int ret; retry:/* Read the page with vaddr into memory */
@@ -251,11 +250,7 @@ retry: vaddr_new =kmap_atomic (new_page);memcpy (vaddr_new, vaddr_old,PAGE_SIZE ); - -/* poke the new insn in, ASSUMES we don't cross page boundary */ - pgoff = (vaddr & ~PAGE_MASK ); -BUG_ON (pgoff +UPROBE_SWBP_INSN_SIZE >PAGE_SIZE ); -memcpy (vaddr_new + pgoff, &opcode,UPROBE_SWBP_INSN_SIZE ); +memcpy (vaddr_new + (vaddr & ~PAGE_MASK ), &opcode,UPROBE_SWBP_INSN_SIZE );kunmap_atomic (vaddr_new);kunmap_atomic (vaddr_old);
@@ -699,6 +694,10 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm,if (ret)return ret; +/* write_opcode() assumes we don't cross page boundary */ +BUG_ON ((uprobe->offset & ~PAGE_MASK ) + +UPROBE_SWBP_INSN_SIZE >PAGE_SIZE ); + uprobe->flags |=UPROBE_COPY_INSN ; }
Signature · 1 line
1.5.5.1
Re: [PATCH 11/15] uprobes: move BUG_ON(UPROBE_SWBP_INSN_SIZE) from write_opcode() to install_breakpoint()
Quoted 28 lines
kernel/events/uprobes.c +1
-6@@ -210,7 +210,6 @@ static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm,void *vaddr_old, *vaddr_new;struct vm_area_struct *vma;struct uprobe *uprobe; -unsigned long pgoff;int ret; retry:/* Read the page with vaddr into memory */ @@ -251,11 +250,7 @@ retry: vaddr_new =kmap_atomic (new_page);memcpy (vaddr_new, vaddr_old,PAGE_SIZE ); - -/* poke the new insn in, ASSUMES we don't cross page boundary */ - pgoff = (vaddr & ~PAGE_MASK ); -BUG_ON (pgoff +UPROBE_SWBP_INSN_SIZE >PAGE_SIZE ); -memcpy (vaddr_new + pgoff, &opcode,UPROBE_SWBP_INSN_SIZE ); +memcpy (vaddr_new + (vaddr & ~PAGE_MASK ), &opcode,UPROBE_SWBP_INSN_SIZE );kunmap_atomic (vaddr_new);kunmap_atomic (vaddr_old);
kernel/events/uprobes.c install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, · line 702
Quoted 9 lines
kernel/events/uprobes.c +4
-0@@ -699,6 +694,10 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm,if (ret)return ret; +/* write_opcode() assumes we don't cross page boundary */ +BUG_ON ((uprobe->offset & ~PAGE_MASK ) + +UPROBE_SWBP_INSN_SIZE >PAGE_SIZE ); + uprobe->flags |=UPROBE_COPY_INSN ; }
I am now thinking if we really need a BUG_ON? I am now thinking I should have had a check at the start in uprobe_register() and failed the request.
Something like
if ((offset & ~PAGE_MASK) + UPROBE_SWBP_INSN_SIZE > PAGE_SIZE) return -EINVAL;
Signature · 2 lines
Thanks and Regards Srikar
Oleg Nesterov
maintainerRe: [PATCH 11/15] uprobes: move BUG_ON(UPROBE_SWBP_INSN_SIZE) from write_opcode() to install_breakpoint()
Quoted 5 lines · Srikar Dronamraju
Quoted 5 lines
kernel/events/uprobes.c +0
-0@@ -699,6 +694,10 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm,if (ret)return ret;
Quoted 8 lines
kernel/events/uprobes.c install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, · line 702
Quoted 6 lines
kernel/events/uprobes.c +4
-0+/* write_opcode() assumes we don't cross page boundary */ +BUG_ON ((uprobe->offset & ~PAGE_MASK ) + +UPROBE_SWBP_INSN_SIZE >PAGE_SIZE ); + uprobe->flags |=UPROBE_COPY_INSN ; }I am now thinking if we really need a
BUG_ON?
I was thinking about this too.
Quoted 6 lines
I am now thinking I should
have had a check at the start inuprobe_register()and failed the request.Something like
if ((offset & ~PAGE_MASK) + UPROBE_SWBP_INSN_SIZE > PAGE_SIZE) return -EINVAL;
Perhaps. Or we can simply remove it. arch_uprobe_analyze_insn() should be careful anyway, and all this validation should be moved into uprobe_register/alloc_uprobe.
I do not really mind, I only wanted to simplify write_opcode() which does a lot of unnecessary things (say, lock_page, I am going to kill it).
So. Do you want me to redo this patch? Or do you think we can keep this "must not happen after arch_uprobe_analyze_insn" check?
Oleg.
Re: [PATCH 11/15] uprobes: move BUG_ON(UPROBE_SWBP_INSN_SIZE) from write_opcode() to install_breakpoint()
Quoted 28 lines · Oleg Nesterov
Quoted 5 lines · Srikar Dronamraju
Quoted 5 lines
kernel/events/uprobes.c +0
-0@@ -699,6 +694,10 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm,if (ret)return ret;Quoted 8 lines
kernel/events/uprobes.c install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, · line 702
Quoted 6 lines
kernel/events/uprobes.c +4
-0+/* write_opcode() assumes we don't cross page boundary */ +BUG_ON ((uprobe->offset & ~PAGE_MASK ) + +UPROBE_SWBP_INSN_SIZE >PAGE_SIZE ); + uprobe->flags |=UPROBE_COPY_INSN ; }I am now thinking if we really need a
BUG_ON?I was thinking about this too.
Quoted 6 lines
I am now thinking I should
have had a check at the start inuprobe_register()and failed the request.Something like
if ((offset & ~PAGE_MASK) + UPROBE_SWBP_INSN_SIZE > PAGE_SIZE) return -EINVAL;Perhaps. Or we can simply remove it.
arch_uprobe_analyze_insn()should be careful anyway, and all this validation should be moved into uprobe_register/alloc_uprobe.
Quoted 7 lines
I do not really mind, I only wanted to simplify
write_opcode()which does a lot of unnecessary things (say,lock_page, I am going to kill it).So. Do you want me to redo this patch? Or do you think we can keep this "must not happen after
arch_uprobe_analyze_insn" check?
No, I will just fix it up later.
Signature · 2 lines
Thanks and Regards Srikar
tip-bot for Oleg Nesterov
maintainer[tip:perf/core] uprobes: Move BUG_ON(UPROBE_SWBP_INSN_SIZE) from write_opcode() to install_breakpoint()
In reply to Oleg NesterovCommit-ID: d9c4a30e82614d43b55893a73f31e7284007ce82 Gitweb: http://git.kernel.org/tip/d9c4a30e82614d43b55893a73f31e7284007ce82 Author: Oleg Nesterov <[email protected]> AuthorDate: Fri, 15 Jun 2012 17:43:50 +0200 Committer: Ingo Molnar <[email protected]> CommitDate: Sat, 16 Jun 2012 09:10:47 +0200
uprobes: Move BUG_ON(UPROBE_SWBP_INSN_SIZE) from write_opcode() to install_breakpoint()
write_opcode() ensures that UPROBE_SWBP_INSN doesn't cross the page boundary. This looks a bit confusing, the check does not depend on vaddr and it is enough to do it only once right after install_breakpoint()->arch_uprobe_analyze_insn().
--- kernel/events/uprobes.c | 11 +++++------ 1 files changed, 5 insertions(+), 6 deletions(-)
kernel/events/uprobes.c +5 -6
@@ -210,7 +210,6 @@ static int write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm,void *vaddr_old, *vaddr_new;struct vm_area_struct *vma;struct uprobe *uprobe; -unsigned long pgoff;int ret; retry:/* Read the page with vaddr into memory */
@@ -251,11 +250,7 @@ retry: vaddr_new =kmap_atomic (new_page);memcpy (vaddr_new, vaddr_old,PAGE_SIZE ); - -/* poke the new insn in, ASSUMES we don't cross page boundary */ - pgoff = (vaddr & ~PAGE_MASK ); -BUG_ON (pgoff +UPROBE_SWBP_INSN_SIZE >PAGE_SIZE ); -memcpy (vaddr_new + pgoff, &opcode,UPROBE_SWBP_INSN_SIZE ); +memcpy (vaddr_new + (vaddr & ~PAGE_MASK ), &opcode,UPROBE_SWBP_INSN_SIZE );kunmap_atomic (vaddr_new);kunmap_atomic (vaddr_old);
@@ -699,6 +694,10 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm,if (ret)return ret; +/* write_opcode() assumes we don't cross page boundary */ +BUG_ON ((uprobe->offset & ~PAGE_MASK ) + +UPROBE_SWBP_INSN_SIZE >PAGE_SIZE ); + uprobe->flags |=UPROBE_COPY_INSN ; }
Oleg Nesterov
maintainerPATCH 12/15 uprobes: simplify the usage of uprobe->pending_list
In reply to Oleg Nesterovuprobe->pending_list is only used to create the temporary list, it has no meaning after we drop uprobes_mmap_hash(inode).
No need to initialize this node or remove it from tmp_list, and we can use list_for_each_entry().
--- kernel/events/uprobes.c | 12 ++++-------- 1 files changed, 4 insertions(+), 8 deletions(-)
kernel/events/uprobes.c +4 -8
@@ -513,7 +513,6 @@ static struct uprobe *alloc_uprobe(struct inode *inode, loff_t offset) uprobe->inode =igrab (inode); uprobe->offset = offset;init_rwsem (&uprobe->consumer_rwsem); -INIT_LIST_HEAD (&uprobe->pending_list);/* add to uprobes_tree, sorted on inode:offset */ cur_uprobe =insert_uprobe (uprobe);
@@ -1037,7 +1036,7 @@ static void build_probe_list(struct inode *inode, struct list_head *head)int uprobe_mmap (struct vm_area_struct *vma) {struct list_head tmp_list; -struct uprobe *uprobe, *u; +struct uprobe *uprobe;struct inode *inode;int ret, count;
@@ -1055,10 +1054,9 @@ int uprobe_mmap(struct vm_area_struct *vma) ret =0 ; count =0 ; -list_for_each_entry_safe (uprobe, u, &tmp_list, pending_list) { +list_for_each_entry (uprobe, &tmp_list, pending_list) {loff_t vaddr; -list_del (&uprobe->pending_list);if (!ret) { vaddr =vma_address (vma, uprobe->offset);
@@ -1106,7 +1104,7 @@ int uprobe_mmap(struct vm_area_struct *vma)void uprobe_munmap (struct vm_area_struct *vma,unsigned long start,unsigned long end) {struct list_head tmp_list; -struct uprobe *uprobe, *u; +struct uprobe *uprobe;struct inode *inode;if (!atomic_read (&uprobe_events) || !valid_vma (vma, false))
@@ -1123,12 +1121,10 @@ void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned lonmutex_lock (uprobes_mmap_hash (inode));build_probe_list (inode, &tmp_list); -list_for_each_entry_safe (uprobe, u, &tmp_list, pending_list) { +list_for_each_entry (uprobe, &tmp_list, pending_list) {loff_t vaddr; -list_del (&uprobe->pending_list); vaddr =vma_address (vma, uprobe->offset); -if (vaddr >= start && vaddr < end) {/* * An unregister could have removed the probe before
Signature · 1 line
1.5.5.1
tip-bot for Oleg Nesterov
maintainer[tip:perf/core] uprobes: Simplify the usage of uprobe-> pending_list
Commit-ID: 449d0d7c9fb87277175db34c009c70cb348004a8 Gitweb: http://git.kernel.org/tip/449d0d7c9fb87277175db34c009c70cb348004a8 Author: Oleg Nesterov <[email protected]> AuthorDate: Fri, 15 Jun 2012 17:43:53 +0200 Committer: Ingo Molnar <[email protected]> CommitDate: Sat, 16 Jun 2012 09:10:48 +0200
uprobes: Simplify the usage of uprobe->pending_list
uprobe->pending_list is only used to create the temporary list, it has no meaning after we drop uprobes_mmap_hash(inode).
No need to initialize this node or remove it from tmp_list, and we can use list_for_each_entry().
--- kernel/events/uprobes.c | 12 ++++-------- 1 files changed, 4 insertions(+), 8 deletions(-)
kernel/events/uprobes.c +4 -8
@@ -513,7 +513,6 @@ static struct uprobe *alloc_uprobe(struct inode *inode, loff_t offset) uprobe->inode =igrab (inode); uprobe->offset = offset;init_rwsem (&uprobe->consumer_rwsem); -INIT_LIST_HEAD (&uprobe->pending_list);/* add to uprobes_tree, sorted on inode:offset */ cur_uprobe =insert_uprobe (uprobe);
@@ -1037,7 +1036,7 @@ static void build_probe_list(struct inode *inode, struct list_head *head)int uprobe_mmap (struct vm_area_struct *vma) {struct list_head tmp_list; -struct uprobe *uprobe, *u; +struct uprobe *uprobe;struct inode *inode;int ret, count;
@@ -1055,10 +1054,9 @@ int uprobe_mmap(struct vm_area_struct *vma) ret =0 ; count =0 ; -list_for_each_entry_safe (uprobe, u, &tmp_list, pending_list) { +list_for_each_entry (uprobe, &tmp_list, pending_list) {loff_t vaddr; -list_del (&uprobe->pending_list);if (!ret) { vaddr =vma_address (vma, uprobe->offset);
@@ -1106,7 +1104,7 @@ int uprobe_mmap(struct vm_area_struct *vma)void uprobe_munmap (struct vm_area_struct *vma,unsigned long start,unsigned long end) {struct list_head tmp_list; -struct uprobe *uprobe, *u; +struct uprobe *uprobe;struct inode *inode;if (!atomic_read (&uprobe_events) || !valid_vma (vma, false))
@@ -1123,12 +1121,10 @@ void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned lonmutex_lock (uprobes_mmap_hash (inode));build_probe_list (inode, &tmp_list); -list_for_each_entry_safe (uprobe, u, &tmp_list, pending_list) { +list_for_each_entry (uprobe, &tmp_list, pending_list) {loff_t vaddr; -list_del (&uprobe->pending_list); vaddr =vma_address (vma, uprobe->offset); -if (vaddr >= start && vaddr < end) {/* * An unregister could have removed the probe before
Oleg Nesterov
maintainerPATCH 13/15 uprobes: don't use loff_t for the valid virtual address
In reply to Oleg Nesterovloff_t looks confusing when it is used for the virtual address. Change map_info and install_breakpoint/remove_breakpoint paths to use "unsigned long".
The patch doesn't change vma_address(), it can't return "long" because it is used to verify the mapping. But probably this needs some cleanups too.
--- kernel/events/uprobes.c | 26 +++++++++----------------- 1 files changed, 9 insertions(+), 17 deletions(-)
kernel/events/uprobes.c +9 -17
@@ -664,9 +664,8 @@ static int copy_insn(struct uprobe *uprobe, struct file *filp)*/ static int install_breakpoint (struct uprobe *uprobe,struct mm_struct *mm, -struct vm_area_struct *vma,loff_t vaddr) +struct vm_area_struct *vma,unsigned long vaddr) { -unsigned long addr;int ret;/*
@@ -679,8 +678,6 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm,if (!uprobe->consumers)return -EEXIST ; - addr = (unsigned long )vaddr; -if (!(uprobe->flags &UPROBE_COPY_INSN )) { ret =copy_insn (uprobe, vma->vm_file);if (ret)
@@ -689,7 +686,7 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm,if (is_swbp_insn ((uprobe_opcode_t *)uprobe->arch.insn))return -ENOTSUPP ; - ret =arch_uprobe_analyze_insn (&uprobe->arch, mm, addr); + ret =arch_uprobe_analyze_insn (&uprobe->arch, mm, vaddr);if (ret)return ret;
@@ -709,7 +706,7 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm,* Hence increment before and decrement on failure. */ atomic_inc (&mm->uprobes_state.count); - ret =set_swbp (&uprobe->arch, mm, addr); + ret =set_swbp (&uprobe->arch, mm, vaddr);if (ret)atomic_dec (&mm->uprobes_state.count);
@@ -717,9 +714,9 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, }static void -remove_breakpoint (struct uprobe *uprobe,struct mm_struct *mm,loff_t vaddr) +remove_breakpoint (struct uprobe *uprobe,struct mm_struct *mm,unsigned long vaddr) { -if (!set_orig_insn (&uprobe->arch, mm, (unsigned long )vaddr, true)) +if (!set_orig_insn (&uprobe->arch, mm, vaddr, true))atomic_dec (&mm->uprobes_state.count); }
@@ -743,7 +740,7 @@ static void delete_uprobe(struct uprobe *uprobe)struct map_info {struct map_info *next;struct mm_struct *mm; -loff_t vaddr; +unsigned long vaddr; };static inline struct map_info *free_map_info (struct map_info *info)
@@ -837,7 +834,6 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register)while (info) {struct mm_struct *mm = info->mm;struct vm_area_struct *vma; -loff_t vaddr;if (err) goto free;
@@ -847,9 +843,8 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register)if (!vma || !valid_vma (vma, is_register)) goto unlock; - vaddr =vma_address (vma, uprobe->offset);if (vma->vm_file->f_mapping->host != uprobe->inode || - vaddr != info->vaddr) +vma_address (vma, uprobe->offset) != info->vaddr) goto unlock;if (is_register) {
@@ -1055,10 +1050,8 @@ int uprobe_mmap(struct vm_area_struct *vma) count =0 ;list_for_each_entry (uprobe, &tmp_list, pending_list) { -loff_t vaddr; -if (!ret) { - vaddr =vma_address (vma, uprobe->offset); +loff_t vaddr =vma_address (vma, uprobe->offset);if (vaddr < vma->vm_start || vaddr >= vma->vm_end) {put_uprobe (uprobe);
@@ -1122,9 +1115,8 @@ void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned lonbuild_probe_list (inode, &tmp_list);list_for_each_entry (uprobe, &tmp_list, pending_list) { -loff_t vaddr; +loff_t vaddr =vma_address (vma, uprobe->offset); - vaddr =vma_address (vma, uprobe->offset);if (vaddr >= start && vaddr < end) {/* * An unregister could have removed the probe before
Signature · 1 line
1.5.5.1
tip-bot for Oleg Nesterov
maintainer[tip:perf/core] uprobes: Don' t use loff_t for the valid virtual address
Commit-ID: 816c03fbabe64fa09f66fbb64e932081af381415 Gitweb: http://git.kernel.org/tip/816c03fbabe64fa09f66fbb64e932081af381415 Author: Oleg Nesterov <[email protected]> AuthorDate: Fri, 15 Jun 2012 17:43:55 +0200 Committer: Ingo Molnar <[email protected]> CommitDate: Sat, 16 Jun 2012 09:10:48 +0200
uprobes: Don't use loff_t for the valid virtual address
loff_t looks confusing when it is used for the virtual address. Change map_info and install_breakpoint/remove_breakpoint paths to use "unsigned long".
The patch doesn't change vma_address(), it can't return "long" because it is used to verify the mapping. But probably this needs some cleanups too.
--- kernel/events/uprobes.c | 26 +++++++++----------------- 1 files changed, 9 insertions(+), 17 deletions(-)
kernel/events/uprobes.c +9 -17
@@ -664,9 +664,8 @@ static int copy_insn(struct uprobe *uprobe, struct file *filp)*/ static int install_breakpoint (struct uprobe *uprobe,struct mm_struct *mm, -struct vm_area_struct *vma,loff_t vaddr) +struct vm_area_struct *vma,unsigned long vaddr) { -unsigned long addr;int ret;/*
@@ -679,8 +678,6 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm,if (!uprobe->consumers)return -EEXIST ; - addr = (unsigned long )vaddr; -if (!(uprobe->flags &UPROBE_COPY_INSN )) { ret =copy_insn (uprobe, vma->vm_file);if (ret)
@@ -689,7 +686,7 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm,if (is_swbp_insn ((uprobe_opcode_t *)uprobe->arch.insn))return -ENOTSUPP ; - ret =arch_uprobe_analyze_insn (&uprobe->arch, mm, addr); + ret =arch_uprobe_analyze_insn (&uprobe->arch, mm, vaddr);if (ret)return ret;
@@ -709,7 +706,7 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm,* Hence increment before and decrement on failure. */ atomic_inc (&mm->uprobes_state.count); - ret =set_swbp (&uprobe->arch, mm, addr); + ret =set_swbp (&uprobe->arch, mm, vaddr);if (ret)atomic_dec (&mm->uprobes_state.count);
@@ -717,9 +714,9 @@ install_breakpoint(struct uprobe *uprobe, struct mm_struct *mm, }static void -remove_breakpoint (struct uprobe *uprobe,struct mm_struct *mm,loff_t vaddr) +remove_breakpoint (struct uprobe *uprobe,struct mm_struct *mm,unsigned long vaddr) { -if (!set_orig_insn (&uprobe->arch, mm, (unsigned long )vaddr, true)) +if (!set_orig_insn (&uprobe->arch, mm, vaddr, true))atomic_dec (&mm->uprobes_state.count); }
@@ -743,7 +740,7 @@ static void delete_uprobe(struct uprobe *uprobe)struct map_info {struct map_info *next;struct mm_struct *mm; -loff_t vaddr; +unsigned long vaddr; };static inline struct map_info *free_map_info (struct map_info *info)
@@ -837,7 +834,6 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register)while (info) {struct mm_struct *mm = info->mm;struct vm_area_struct *vma; -loff_t vaddr;if (err) goto free;
@@ -847,9 +843,8 @@ static int register_for_each_vma(struct uprobe *uprobe, bool is_register)if (!vma || !valid_vma (vma, is_register)) goto unlock; - vaddr =vma_address (vma, uprobe->offset);if (vma->vm_file->f_mapping->host != uprobe->inode || - vaddr != info->vaddr) +vma_address (vma, uprobe->offset) != info->vaddr) goto unlock;if (is_register) {
@@ -1055,10 +1050,8 @@ int uprobe_mmap(struct vm_area_struct *vma) count =0 ;list_for_each_entry (uprobe, &tmp_list, pending_list) { -loff_t vaddr; -if (!ret) { - vaddr =vma_address (vma, uprobe->offset); +loff_t vaddr =vma_address (vma, uprobe->offset);if (vaddr < vma->vm_start || vaddr >= vma->vm_end) {put_uprobe (uprobe);
@@ -1122,9 +1115,8 @@ void uprobe_munmap(struct vm_area_struct *vma, unsigned long start, unsigned lonbuild_probe_list (inode, &tmp_list);list_for_each_entry (uprobe, &tmp_list, pending_list) { -loff_t vaddr; +loff_t vaddr =vma_address (vma, uprobe->offset); - vaddr =vma_address (vma, uprobe->offset);if (vaddr >= start && vaddr < end) {/* * An unregister could have removed the probe before
Oleg Nesterov
maintainerPATCH 14/15 uprobes: __copy_insn() needs "loff_t offset"
In reply to Oleg Nesterov1. __copy_insn() needs "loff_t offset", not "unsigned long", to read the file.
2. use pgoff_t for "idx" and remove the unnecessary typecast.
3. fix the typo, "&=" is not what we want
4. can't resist, rename off1 to off.
--- kernel/events/uprobes.c | 12 ++++++------ 1 files changed, 6 insertions(+), 6 deletions(-)
kernel/events/uprobes.c +6 -6
@@ -581,12 +581,12 @@ static bool consumer_del(struct uprobe *uprobe, struct uprobe_consumer *uc)static int __copy_insn (struct address_space *mapping,struct file *filp,char *insn, -unsigned long nbytes,unsigned long offset) +unsigned long nbytes,loff_t offset) {struct page *page;void *vaddr; -unsigned long off1; -unsigned long idx; +unsigned long off; +pgoff_t idx;if (!filp)return -EINVAL ;
@@ -594,8 +594,8 @@ __copy_insn(struct address_space *mapping, struct file *filp, char *insn,if (!mapping->a_ops->readpage)return -EIO ; - idx = (unsigned long )(offset >>PAGE_CACHE_SHIFT ); - off1 = offset &= ~PAGE_MASK ; + idx = offset >>PAGE_CACHE_SHIFT ; + off = offset & ~PAGE_MASK ;/* * Ensure that the page that has the original instruction is
@@ -606,7 +606,7 @@ __copy_insn(struct address_space *mapping, struct file *filp, char *insn,return PTR_ERR (page); vaddr =kmap_atomic (page); -memcpy (insn, vaddr + off1, nbytes); +memcpy (insn, vaddr + off, nbytes);kunmap_atomic (vaddr);page_cache_release (page);
Signature · 1 line
1.5.5.1
tip-bot for Oleg Nesterov
maintainer[tip:perf/core] uprobes: __copy_insn() needs "loff_t offset"
Commit-ID: 593609a59600c8377f311b300f14deacb155b9a4 Gitweb: http://git.kernel.org/tip/593609a59600c8377f311b300f14deacb155b9a4 Author: Oleg Nesterov <[email protected]> AuthorDate: Fri, 15 Jun 2012 17:43:59 +0200 Committer: Ingo Molnar <[email protected]> CommitDate: Sat, 16 Jun 2012 09:10:49 +0200
uprobes: __copy_insn() needs "loff_t offset"
1. __copy_insn() needs "loff_t offset", not "unsigned long", to read the file.
2. use pgoff_t for "idx" and remove the unnecessary typecast.
3. fix the typo, "&=" is not what we want
4. can't resist, rename off1 to off.
--- kernel/events/uprobes.c | 12 ++++++------ 1 files changed, 6 insertions(+), 6 deletions(-)
kernel/events/uprobes.c +6 -6
@@ -581,12 +581,12 @@ static bool consumer_del(struct uprobe *uprobe, struct uprobe_consumer *uc)static int __copy_insn (struct address_space *mapping,struct file *filp,char *insn, -unsigned long nbytes,unsigned long offset) +unsigned long nbytes,loff_t offset) {struct page *page;void *vaddr; -unsigned long off1; -unsigned long idx; +unsigned long off; +pgoff_t idx;if (!filp)return -EINVAL ;
@@ -594,8 +594,8 @@ __copy_insn(struct address_space *mapping, struct file *filp, char *insn,if (!mapping->a_ops->readpage)return -EIO ; - idx = (unsigned long )(offset >>PAGE_CACHE_SHIFT ); - off1 = offset &= ~PAGE_MASK ; + idx = offset >>PAGE_CACHE_SHIFT ; + off = offset & ~PAGE_MASK ;/* * Ensure that the page that has the original instruction is
@@ -606,7 +606,7 @@ __copy_insn(struct address_space *mapping, struct file *filp, char *insn,return PTR_ERR (page); vaddr =kmap_atomic (page); -memcpy (insn, vaddr + off1, nbytes); +memcpy (insn, vaddr + off, nbytes);kunmap_atomic (vaddr);page_cache_release (page);
Oleg Nesterov
maintainerPATCH 15/15 uprobes: remove the unnecessary initialization in add_utask()
In reply to Oleg NesterovTrivial cleanup. No need to nullify ->active_uprobe after kzalloc().
--- kernel/events/uprobes.c | 1 - 1 files changed, 0 insertions(+), 1 deletions(-)
kernel/events/uprobes.c +0 -1
@@ -1392,7 +1392,6 @@ static struct uprobe_task *add_utask(void)if (unlikely (!utask))return NULL ; - utask->active_uprobe =NULL ; current->utask = utask;return utask; }
Signature · 1 line
1.5.5.1
tip-bot for Oleg Nesterov
maintainer[tip:perf/core] uprobes: Remove the unnecessary initialization in add_utask()
Commit-ID: e227051b13956b8f71c0abecc41ad351e64671c8 Gitweb: http://git.kernel.org/tip/e227051b13956b8f71c0abecc41ad351e64671c8 Author: Oleg Nesterov <[email protected]> AuthorDate: Fri, 15 Jun 2012 17:44:01 +0200 Committer: Ingo Molnar <[email protected]> CommitDate: Sat, 16 Jun 2012 09:10:52 +0200
uprobes: Remove the unnecessary initialization in add_utask()
Trivial cleanup. No need to nullify ->active_uprobe after kzalloc().
--- kernel/events/uprobes.c | 1 - 1 files changed, 0 insertions(+), 1 deletions(-)
kernel/events/uprobes.c +0 -1
@@ -1392,7 +1392,6 @@ static struct uprobe_task *add_utask(void)if (unlikely (!utask))return NULL ; - utask->active_uprobe =NULL ; current->utask = utask;return utask; }