uprobes: misc cleanups/simplifications
Subsystems (9)
Oleg Nesterov
maintainerv2 cover uprobes: misc cleanups/simplifications
Peter, I don't think these changes can really complicate your ongoing work. But again, if you are going to send the next version "soon" I can rebase these cleanups on top of it.
Andrii, I dared to preserve your acks, all the changes are simple.
Changes since v1:
- update the comment in register_for_each_vma()
- remove the now unused "struct path *" arg from bpf_uprobe_unregister()
- forward-declare struct uprobe in include/linux/uprobes.h
- kernel-doc fixes/updates
- fix use-after free in uprobe_unregister(). See "TODO:" in 5/5.
Oleg.
---
include/linux/uprobes.h | 22 +++++------ kernel/events/uprobes.c | 96 +++++++++++++++++---------------------------- kernel/trace/bpf_trace.c | 25 ++++++------ kernel/trace/trace_uprobe.c | 31 ++++++--------- 4 files changed, 69 insertions(+), 105 deletions(-)
Oleg Nesterov
maintainerPATCH v2 1/5 uprobes: document the usage of mm->mmap_lock
The comment above uprobe_write_opcode() is wrong, unapply_uprobe() calls it under mmap_read_lock() and this is correct.
And it is completely unclear why register_for_each_vma() takes mmap_lock for writing, add a comment to explain that mmap_write_lock() is needed to avoid the following race:
- A task T hits the bp installed by uprobe and calls find_active_uprobe()
- uprobe_unregister() removes this uprobe/bp
- T calls find_uprobe() which returns NULL
- another uprobe_register() installs the bp at the same address
- T calls is_trap_at_addr() which returns true
- T returns to handle_swbp() and gets SIGTRAP.
--- kernel/events/uprobes.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-)
kernel/events/uprobes.c +8 -2
@@ -453,7 +453,7 @@ static int update_ref_ctr(struct uprobe *uprobe, struct mm_struct *mm,* @vaddr: the virtual address to store the opcode. * @opcode: opcode to be written at @vaddr. * -* Called with mm->mmap_lock held for write. +* Called with mm->mmap_lock held for read or write. * Return 0 (success) or a negative errno. */ int uprobe_write_opcode(struct arch_uprobe *auprobe, struct mm_struct *mm,
@@ -1046,7 +1046,13 @@ register_for_each_vma(struct uprobe *uprobe, struct uprobe_consumer *new)if (err && is_register) goto free; - +/* +* We take mmap_lock for writing to avoid the race with +* find_active_uprobe() which takes mmap_lock for reading. +* Thus this install_breakpoint() can not make +* is_trap_at_addr() true right after find_uprobe() +* returns NULL in find_active_uprobe(). +*/ mmap_write_lock (mm); vma =find_vma (mm, info->vaddr); if (!vma || !valid_vma(vma, is_register) ||
Signature · 1 line
2.25.1.362.g51ebf55
Oleg Nesterov
maintainerPATCH v2 2/5 uprobes: is_trap_at_addr: don't use get_user_pages_remote()
In reply to Oleg Nesterovget_user_pages_remote() and the comment above it make no sense.
There is no task_struct passed into get_user_pages_remote() anymore, and nowadays mm_account_fault() increments the current->min/maj_flt counters regardless of FAULT_FLAG_REMOTE.
--- kernel/events/uprobes.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-)
kernel/events/uprobes.c +1 -7
@@ -2035,13 +2035,7 @@ static int is_trap_at_addr(struct mm_struct *mm, unsigned long vaddr)if (likely (result ==0 )) goto out; -/* -* The NULL 'tsk' here ensures that any faults that occur here -* will not be accounted to the task. 'mm' *is* current->mm, -* but we treat this as a 'remote' access since it is -* essentially a kernel access to the memory. -*/ - result =get_user_pages_remote (mm, vaddr,1 ,FOLL_FORCE , &page,NULL ); + result =get_user_pages (vaddr,1 ,FOLL_FORCE , &page);if (result <0 )return result;
Signature · 1 line
2.25.1.362.g51ebf55
Oleg Nesterov
maintainerPATCH v2 3/5 uprobes: simplify error handling for alloc_uprobe()
In reply to Oleg NesterovFrom: Andrii Nakryiko <[email protected]>
Return -ENOMEM instead of NULL, which makes caller's error handling just a touch simpler.
--- kernel/events/uprobes.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-)
kernel/events/uprobes.c +1 -3
@@ -725,7 +725,7 @@ static struct uprobe *alloc_uprobe(struct inode *inode, loff_t offset, uprobe =kzalloc (sizeof (struct uprobe ),GFP_KERNEL );if (!uprobe) -return NULL ; +return ERR_PTR (-ENOMEM ); uprobe->inode = inode; uprobe->offset = offset;
@@ -1167,8 +1167,6 @@ static int __uprobe_register(struct inode *inode, loff_t offset, retry: uprobe =alloc_uprobe (inode, offset, ref_ctr_offset); -if (!uprobe) -return -ENOMEM ;if (IS_ERR (uprobe))return PTR_ERR (uprobe);
Signature · 1 line
2.25.1.362.g51ebf55
Oleg Nesterov
maintainerPATCH v2 4/5 uprobes: kill uprobe_register_refctr()
In reply to Oleg NesterovIt doesn't make any sense to have 2 versions of _register(). Note that trace_uprobe_enable(), the only user of uprobe_register(), doesn't need to check tu->ref_ctr_offset to decide which one should be used, it could safely pass ref_ctr_offset == 0 to uprobe_register_refctr().
Add this argument to uprobe_register(), update the callers, and kill uprobe_register_refctr().
--- include/linux/uprobes.h | 9 ++------- kernel/events/uprobes.c | 24 ++++++------------------ kernel/trace/bpf_trace.c | 8 ++++---- kernel/trace/trace_uprobe.c | 7 +------ 4 files changed, 13 insertions(+), 35 deletions(-)
include/linux/uprobes.h +2 -7
@@ -110,8 +110,7 @@ extern bool is_trap_insn(uprobe_opcode_t *insn);extern unsigned long uprobe_get_swbp_addr (struct pt_regs *regs);extern unsigned long uprobe_get_trap_addr (struct pt_regs *regs);extern int uprobe_write_opcode (struct arch_uprobe *auprobe,struct mm_struct *mm,unsigned long vaddr,uprobe_opcode_t ); -extern int uprobe_register (struct inode *inode,loff_t offset,struct uprobe_consumer *uc); -extern int uprobe_register_refctr (struct inode *inode,loff_t offset,loff_t ref_ctr_offset,struct uprobe_consumer *uc); +extern int uprobe_register (struct inode *inode,loff_t offset,loff_t ref_ctr_offset,struct uprobe_consumer *uc);extern int uprobe_apply (struct inode *inode,loff_t offset,struct uprobe_consumer *uc,bool );extern void uprobe_unregister (struct inode *inode,loff_t offset,struct uprobe_consumer *uc);extern int uprobe_mmap (struct vm_area_struct *vma);
@@ -152,11 +151,7 @@ static inline void uprobes_init(void)#define uprobe_get_trap_addr (regs) instruction_pointer(regs)static inline int -uprobe_register (struct inode *inode,loff_t offset,struct uprobe_consumer *uc) -{ -return -ENOSYS ; -} -static inline int uprobe_register_refctr (struct inode *inode,loff_t offset,loff_t ref_ctr_offset,struct uprobe_consumer *uc) +uprobe_register (struct inode *inode,loff_t offset,loff_t ref_ctr_offset,struct uprobe_consumer *uc) {return -ENOSYS ; }
kernel/events/uprobes.c +6 -18
@@ -1121,25 +1121,26 @@ void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumeEXPORT_SYMBOL_GPL (uprobe_unregister);/* -* __uprobe_register - register a probe +* uprobe_register - register a probe * @inode: the file in which the probe has to be placed. * @offset: offset from the start of the file. +* @ref_ctr_offset: offset of SDT marker / reference counter * @uc: information on howto handle the probe.. * -* Apart from the access refcount, __uprobe_register() takes a creation +* Apart from the access refcount, uprobe_register() takes a creation * refcount (thro alloc_uprobe) if and only if this @uprobe is getting * inserted into the rbtree (i.e first consumer for a @inode:@offset * tuple). Creation refcount stops uprobe_unregister from freeing the * @uprobe even before the register operation is complete. Creation * refcount is released when the last @uc for the @uprobe -* unregisters. Caller of __uprobe_register() is required to keep @inode +* unregisters. Caller of uprobe_register() is required to keep @inode * (and the containing mount) referenced. * * Return errno if it cannot successully install probes * else return 0 (success) */ -static int __uprobe_register (struct inode *inode,loff_t offset, -loff_t ref_ctr_offset,struct uprobe_consumer *uc) +int uprobe_register (struct inode *inode,loff_t offset,loff_t ref_ctr_offset, +struct uprobe_consumer *uc) {struct uprobe *uprobe;int ret;
@@ -1189,21 +1190,8 @@ static int __uprobe_register(struct inode *inode, loff_t offset, goto retry;return ret; } - -int uprobe_register (struct inode *inode,loff_t offset, -struct uprobe_consumer *uc) -{ -return __uprobe_register (inode, offset,0 , uc); -}EXPORT_SYMBOL_GPL (uprobe_register); -int uprobe_register_refctr (struct inode *inode,loff_t offset, -loff_t ref_ctr_offset,struct uprobe_consumer *uc) -{ -return __uprobe_register (inode, offset, ref_ctr_offset, uc); -} -EXPORT_SYMBOL_GPL (uprobe_register_refctr); -/* * uprobe_apply - unregister an already registered probe. * @inode: the file in which the probe has to be removed.
kernel/trace/bpf_trace.c +4 -4
@@ -3480,10 +3480,10 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr &bpf_uprobe_multi_link_lops, prog);for (i =0 ; i < cnt; i++) { - err =uprobe_register_refctr (d_real_inode (link->path.dentry), - uprobes[i].offset, - uprobes[i].ref_ctr_offset, - &uprobes[i].consumer); + err =uprobe_register (d_real_inode (link->path.dentry), + uprobes[i].offset, + uprobes[i].ref_ctr_offset, + &uprobes[i].consumer);if (err) {bpf_uprobe_unregister (&path, uprobes, i); goto error_free;
kernel/trace/trace_uprobe.c +1 -6
@@ -1089,12 +1089,7 @@ static int trace_uprobe_enable(struct trace_uprobe *tu, filter_func_t filter) tu->consumer.filter = filter; tu->inode =d_real_inode (tu->path.dentry); -if (tu->ref_ctr_offset) - ret =uprobe_register_refctr (tu->inode, tu->offset, - tu->ref_ctr_offset, &tu->consumer); -else - ret =uprobe_register (tu->inode, tu->offset, &tu->consumer); - + ret =uprobe_register (tu->inode, tu->offset, tu->ref_ctr_offset, &tu->consumer);if (ret) tu->inode =NULL ;
Signature · 1 line
2.25.1.362.g51ebf55
Masami Hiramatsu (Google)
maintainerRe: [PATCH v2 4/5] uprobes: kill uprobe_register_refctr()
Quoted 8 lines · On Mon, 29 Jul 2024 15:45:30 +0200 Oleg Nesterov <[email protected]> wrote:
It doesn't make any sense to have 2 versions of
_register(). Note thattrace_uprobe_enable(), the only user ofuprobe_register(), doesn't need to checktu->ref_ctr_offsetto decide which one should be used, it could safely passref_ctr_offset== 0 touprobe_register_refctr().Add this argument to
uprobe_register(), update the callers, and killuprobe_register_refctr().
Quoted 8 lines
Signed-off-by Oleg Nesterov <[email protected]>Acked-by Andrii Nakryiko <[email protected]>--- include/linux/uprobes.h | 9 ++------- kernel/events/uprobes.c | 24 ++++++------------------ kernel/trace/bpf_trace.c | 8 ++++---- kernel/trace/trace_uprobe.c | 7 +------ 4 files changed, 13 insertions(+), 35 deletions(-)
OK, but it seems
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c
is still using uprobe_register_refctr().
That should be updated too.
Thanks,
Quoted 126 lines
include/linux/uprobes.h +2
-7@@ -110,8 +110,7 @@ extern bool is_trap_insn(uprobe_opcode_t *insn);extern unsigned long uprobe_get_swbp_addr (struct pt_regs *regs);extern unsigned long uprobe_get_trap_addr (struct pt_regs *regs);extern int uprobe_write_opcode (struct arch_uprobe *auprobe,struct mm_struct *mm,unsigned long vaddr,uprobe_opcode_t ); -extern int uprobe_register (struct inode *inode,loff_t offset,struct uprobe_consumer *uc); -extern int uprobe_register_refctr (struct inode *inode,loff_t offset,loff_t ref_ctr_offset,struct uprobe_consumer *uc); +extern int uprobe_register (struct inode *inode,loff_t offset,loff_t ref_ctr_offset,struct uprobe_consumer *uc);extern int uprobe_apply (struct inode *inode,loff_t offset,struct uprobe_consumer *uc,bool );extern void uprobe_unregister (struct inode *inode,loff_t offset,struct uprobe_consumer *uc);extern int uprobe_mmap (struct vm_area_struct *vma);@@ -152,11 +151,7 @@ static inline void uprobes_init(void)#define uprobe_get_trap_addr (regs) instruction_pointer(regs)static inline int -uprobe_register (struct inode *inode,loff_t offset,struct uprobe_consumer *uc) -{ -return -ENOSYS ; -} -static inline int uprobe_register_refctr (struct inode *inode,loff_t offset,loff_t ref_ctr_offset,struct uprobe_consumer *uc) +uprobe_register (struct inode *inode,loff_t offset,loff_t ref_ctr_offset,struct uprobe_consumer *uc) {return -ENOSYS ; }kernel/events/uprobes.c +6
-18@@ -1121,25 +1121,26 @@ void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumeEXPORT_SYMBOL_GPL (uprobe_unregister);/* -* __uprobe_register - register a probe +* uprobe_register - register a probe * @inode: the file in which the probe has to be placed. * @offset: offset from the start of the file. +* @ref_ctr_offset: offset of SDT marker / reference counter * @uc: information on howto handle the probe.. * -* Apart from the access refcount, __uprobe_register() takes a creation +* Apart from the access refcount, uprobe_register() takes a creation * refcount (thro alloc_uprobe) if and only if this @uprobe is getting * inserted into the rbtree (i.e first consumer for a @inode:@offset * tuple). Creation refcount stops uprobe_unregister from freeing the * @uprobe even before the register operation is complete. Creation * refcount is released when the last @uc for the @uprobe -* unregisters. Caller of __uprobe_register() is required to keep @inode +* unregisters. Caller of uprobe_register() is required to keep @inode * (and the containing mount) referenced. * * Return errno if it cannot successully install probes * else return 0 (success) */ -static int __uprobe_register (struct inode *inode,loff_t offset, -loff_t ref_ctr_offset,struct uprobe_consumer *uc) +int uprobe_register (struct inode *inode,loff_t offset,loff_t ref_ctr_offset, +struct uprobe_consumer *uc) {struct uprobe *uprobe;int ret;@@ -1189,21 +1190,8 @@ static int __uprobe_register(struct inode *inode, loff_t offset, goto retry;return ret; } - -int uprobe_register (struct inode *inode,loff_t offset, -struct uprobe_consumer *uc) -{ -return __uprobe_register (inode, offset,0 , uc); -}EXPORT_SYMBOL_GPL (uprobe_register); -int uprobe_register_refctr (struct inode *inode,loff_t offset, -loff_t ref_ctr_offset,struct uprobe_consumer *uc) -{ -return __uprobe_register (inode, offset, ref_ctr_offset, uc); -} -EXPORT_SYMBOL_GPL (uprobe_register_refctr); -/* * uprobe_apply - unregister an already registered probe. * @inode: the file in which the probe has to be removed. kernel/trace/bpf_trace.c +4
-4@@ -3480,10 +3480,10 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr &bpf_uprobe_multi_link_lops, prog);for (i =0 ; i < cnt; i++) { - err =uprobe_register_refctr (d_real_inode (link->path.dentry), - uprobes[i].offset, - uprobes[i].ref_ctr_offset, - &uprobes[i].consumer); + err =uprobe_register (d_real_inode (link->path.dentry), + uprobes[i].offset, + uprobes[i].ref_ctr_offset, + &uprobes[i].consumer);if (err) {bpf_uprobe_unregister (&path, uprobes, i); goto error_free;kernel/trace/trace_uprobe.c +1
-6@@ -1089,12 +1089,7 @@ static int trace_uprobe_enable(struct trace_uprobe *tu, filter_func_t filter) tu->consumer.filter = filter; tu->inode =d_real_inode (tu->path.dentry); -if (tu->ref_ctr_offset) - ret =uprobe_register_refctr (tu->inode, tu->offset, - tu->ref_ctr_offset, &tu->consumer); -else - ret =uprobe_register (tu->inode, tu->offset, &tu->consumer); - + ret =uprobe_register (tu->inode, tu->offset, tu->ref_ctr_offset, &tu->consumer);if (ret) tu->inode =NULL ;Signature · 1 line
2.25.1.362.g51ebf55
Signature · 1 line
Masami Hiramatsu (Google) <[email protected]>
Oleg Nesterov
maintainerRe: [PATCH v2 4/5] uprobes: kill uprobe_register_refctr()
Quoted 8 lines · Masami Hiramatsu
OK, but it seems
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.cis still using
uprobe_register_refctr().That should be updated too.
OOPS, thanks a lot :/
I'll send v3 with the additional change below in reply to 4/5 in a minute.
Masami, Peter, please let me know if you want me to resend the whole series.
Oleg.
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c +2 -2
@@ -458,8 +458,8 @@ static int testmod_register_uprobe(loff_t offset)if (err) goto out; - err =uprobe_register_refctr (d_real_inode (uprobe.path.dentry), - offset,0 , &uprobe.consumer); + err =uprobe_register (d_real_inode (uprobe.path.dentry), + offset,0 , &uprobe.consumer);if (err)path_put (&uprobe.path);else
Oleg Nesterov
maintainerPATCH v3 4/5 uprobes: kill uprobe_register_refctr()
In reply to Oleg NesterovIt doesn't make any sense to have 2 versions of _register(). Note that trace_uprobe_enable(), the only user of uprobe_register(), doesn't need to check tu->ref_ctr_offset to decide which one should be used, it could safely pass ref_ctr_offset == 0 to uprobe_register_refctr().
Add this argument to uprobe_register(), update the callers, and kill uprobe_register_refctr().
--- include/linux/uprobes.h | 9 ++----- kernel/events/uprobes.c | 24 +++++-------------- kernel/trace/bpf_trace.c | 8 +++---- kernel/trace/trace_uprobe.c | 7 +----- .../selftests/bpf/bpf_testmod/bpf_testmod.c | 4 ++-- 5 files changed, 15 insertions(+), 37 deletions(-)
include/linux/uprobes.h +2 -7
@@ -110,8 +110,7 @@ extern bool is_trap_insn(uprobe_opcode_t *insn);extern unsigned long uprobe_get_swbp_addr (struct pt_regs *regs);extern unsigned long uprobe_get_trap_addr (struct pt_regs *regs);extern int uprobe_write_opcode (struct arch_uprobe *auprobe,struct mm_struct *mm,unsigned long vaddr,uprobe_opcode_t ); -extern int uprobe_register (struct inode *inode,loff_t offset,struct uprobe_consumer *uc); -extern int uprobe_register_refctr (struct inode *inode,loff_t offset,loff_t ref_ctr_offset,struct uprobe_consumer *uc); +extern int uprobe_register (struct inode *inode,loff_t offset,loff_t ref_ctr_offset,struct uprobe_consumer *uc);extern int uprobe_apply (struct inode *inode,loff_t offset,struct uprobe_consumer *uc,bool );extern void uprobe_unregister (struct inode *inode,loff_t offset,struct uprobe_consumer *uc);extern int uprobe_mmap (struct vm_area_struct *vma);
@@ -152,11 +151,7 @@ static inline void uprobes_init(void)#define uprobe_get_trap_addr (regs) instruction_pointer(regs)static inline int -uprobe_register (struct inode *inode,loff_t offset,struct uprobe_consumer *uc) -{ -return -ENOSYS ; -} -static inline int uprobe_register_refctr (struct inode *inode,loff_t offset,loff_t ref_ctr_offset,struct uprobe_consumer *uc) +uprobe_register (struct inode *inode,loff_t offset,loff_t ref_ctr_offset,struct uprobe_consumer *uc) {return -ENOSYS ; }
kernel/events/uprobes.c +6 -18
@@ -1121,25 +1121,26 @@ void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consumeEXPORT_SYMBOL_GPL (uprobe_unregister);/* -* __uprobe_register - register a probe +* uprobe_register - register a probe * @inode: the file in which the probe has to be placed. * @offset: offset from the start of the file. +* @ref_ctr_offset: offset of SDT marker / reference counter * @uc: information on howto handle the probe.. * -* Apart from the access refcount, __uprobe_register() takes a creation +* Apart from the access refcount, uprobe_register() takes a creation * refcount (thro alloc_uprobe) if and only if this @uprobe is getting * inserted into the rbtree (i.e first consumer for a @inode:@offset * tuple). Creation refcount stops uprobe_unregister from freeing the * @uprobe even before the register operation is complete. Creation * refcount is released when the last @uc for the @uprobe -* unregisters. Caller of __uprobe_register() is required to keep @inode +* unregisters. Caller of uprobe_register() is required to keep @inode * (and the containing mount) referenced. * * Return errno if it cannot successully install probes * else return 0 (success) */ -static int __uprobe_register (struct inode *inode,loff_t offset, -loff_t ref_ctr_offset,struct uprobe_consumer *uc) +int uprobe_register (struct inode *inode,loff_t offset,loff_t ref_ctr_offset, +struct uprobe_consumer *uc) {struct uprobe *uprobe;int ret;
@@ -1189,21 +1190,8 @@ static int __uprobe_register(struct inode *inode, loff_t offset, goto retry;return ret; } - -int uprobe_register (struct inode *inode,loff_t offset, -struct uprobe_consumer *uc) -{ -return __uprobe_register (inode, offset,0 , uc); -}EXPORT_SYMBOL_GPL (uprobe_register); -int uprobe_register_refctr (struct inode *inode,loff_t offset, -loff_t ref_ctr_offset,struct uprobe_consumer *uc) -{ -return __uprobe_register (inode, offset, ref_ctr_offset, uc); -} -EXPORT_SYMBOL_GPL (uprobe_register_refctr); -/* * uprobe_apply - unregister an already registered probe. * @inode: the file in which the probe has to be removed.
kernel/trace/bpf_trace.c +4 -4
@@ -3480,10 +3480,10 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr &bpf_uprobe_multi_link_lops, prog);for (i =0 ; i < cnt; i++) { - err =uprobe_register_refctr (d_real_inode (link->path.dentry), - uprobes[i].offset, - uprobes[i].ref_ctr_offset, - &uprobes[i].consumer); + err =uprobe_register (d_real_inode (link->path.dentry), + uprobes[i].offset, + uprobes[i].ref_ctr_offset, + &uprobes[i].consumer);if (err) {bpf_uprobe_unregister (&path, uprobes, i); goto error_free;
kernel/trace/trace_uprobe.c +1 -6
@@ -1089,12 +1089,7 @@ static int trace_uprobe_enable(struct trace_uprobe *tu, filter_func_t filter) tu->consumer.filter = filter; tu->inode =d_real_inode (tu->path.dentry); -if (tu->ref_ctr_offset) - ret =uprobe_register_refctr (tu->inode, tu->offset, - tu->ref_ctr_offset, &tu->consumer); -else - ret =uprobe_register (tu->inode, tu->offset, &tu->consumer); - + ret =uprobe_register (tu->inode, tu->offset, tu->ref_ctr_offset, &tu->consumer);if (ret) tu->inode =NULL ;
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c +2 -2
@@ -458,8 +458,8 @@ static int testmod_register_uprobe(loff_t offset)if (err) goto out; - err =uprobe_register_refctr (d_real_inode (uprobe.path.dentry), - offset,0 , &uprobe.consumer); + err =uprobe_register (d_real_inode (uprobe.path.dentry), + offset,0 , &uprobe.consumer);if (err)path_put (&uprobe.path);else
Signature · 1 line
2.25.1.362.g51ebf55
Oleg Nesterov
maintainerPATCH v2 5/5 uprobes: make uprobe_register() return struct uprobe *
In reply to Oleg NesterovThis way uprobe_unregister() and uprobe_apply() can use "struct uprobe *" rather than inode + offset. This simplifies the code and allows to avoid the unnecessary find_uprobe() + put_uprobe() in these functions.
TODO: uprobe_unregister() still needs get_uprobe/put_uprobe to ensure that this uprobe can't be freed before up_write(&uprobe->register_rwsem).
--- include/linux/uprobes.h | 15 +++++----- kernel/events/uprobes.c | 56 +++++++++++++++---------------------- kernel/trace/bpf_trace.c | 25 ++++++++--------- kernel/trace/trace_uprobe.c | 26 ++++++++--------- 4 files changed, 55 insertions(+), 67 deletions(-)
include/linux/uprobes.h +8 -7
@@ -16,6 +16,7 @@#include <linux/types.h> #include <linux/wait.h> +struct uprobe ;struct vm_area_struct ;struct mm_struct ;struct inode ;
@@ -110,9 +111,9 @@ extern bool is_trap_insn(uprobe_opcode_t *insn);extern unsigned long uprobe_get_swbp_addr (struct pt_regs *regs);extern unsigned long uprobe_get_trap_addr (struct pt_regs *regs);extern int uprobe_write_opcode (struct arch_uprobe *auprobe,struct mm_struct *mm,unsigned long vaddr,uprobe_opcode_t ); -extern int uprobe_register (struct inode *inode,loff_t offset,loff_t ref_ctr_offset,struct uprobe_consumer *uc); -extern int uprobe_apply (struct inode *inode,loff_t offset,struct uprobe_consumer *uc,bool ); -extern void uprobe_unregister (struct inode *inode,loff_t offset,struct uprobe_consumer *uc); +extern struct uprobe *uprobe_register (struct inode *inode,loff_t offset,loff_t ref_ctr_offset,struct uprobe_consumer *uc); +extern int uprobe_apply (struct uprobe *uprobe,struct uprobe_consumer *uc,bool ); +extern void uprobe_unregister (struct uprobe *uprobe,struct uprobe_consumer *uc);extern int uprobe_mmap (struct vm_area_struct *vma);extern void uprobe_munmap (struct vm_area_struct *vma,unsigned long start,unsigned long end);extern void uprobe_start_dup_mmap (void );
@@ -150,18 +151,18 @@ static inline void uprobes_init(void)#define uprobe_get_trap_addr (regs) instruction_pointer(regs) -static inline int +static inline struct uprobe *uprobe_register (struct inode *inode,loff_t offset,loff_t ref_ctr_offset,struct uprobe_consumer *uc) { -return -ENOSYS ; +return ERR_PTR (-ENOSYS ); }static inline int -uprobe_apply (struct inode *inode,loff_t offset,struct uprobe_consumer *uc,bool add) +uprobe_apply (struct uprobe * uprobe,struct uprobe_consumer *uc,bool add) {return -ENOSYS ; }static inline void -uprobe_unregister (struct inode *inode,loff_t offset,struct uprobe_consumer *uc) +uprobe_unregister (struct uprobe *uprobe,struct uprobe_consumer *uc) { }static inline int uprobe_mmap (struct vm_area_struct *vma)
kernel/events/uprobes.c +22 -34
124 lines not on this page · raw message
kernel/trace/bpf_trace.c +12 -13
@@ -3160,6 +3160,7 @@ struct bpf_uprobe {loff_t offset;unsigned long ref_ctr_offset;u64 cookie; +struct uprobe *uprobe;struct uprobe_consumer consumer; };
@@ -3178,15 +3179,12 @@ struct bpf_uprobe_multi_run_ctx {struct bpf_uprobe *uprobe; }; -static void bpf_uprobe_unregister (struct path *path,struct bpf_uprobe *uprobes, -u32 cnt) +static void bpf_uprobe_unregister (struct bpf_uprobe *uprobes,u32 cnt) {u32 i; -for (i =0 ; i < cnt; i++) { -uprobe_unregister (d_real_inode (path->dentry), uprobes[i].offset, - &uprobes[i].consumer); - } +for (i =0 ; i < cnt; i++) +uprobe_unregister (uprobes[i].uprobe, &uprobes[i].consumer); }static void bpf_uprobe_multi_link_release (struct bpf_link *link)
@@ -3194,7 +3192,7 @@ static void bpf_uprobe_multi_link_release(struct bpf_link *link)struct bpf_uprobe_multi_link *umulti_link; umulti_link = container_of(link, struct bpf_uprobe_multi_link, link); -bpf_uprobe_unregister (&umulti_link->path, umulti_link->uprobes, umulti_link->cnt); +bpf_uprobe_unregister (umulti_link->uprobes, umulti_link->cnt);if (umulti_link->task)put_task_struct (umulti_link->task);path_put (&umulti_link->path);
@@ -3480,12 +3478,13 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr &bpf_uprobe_multi_link_lops, prog);for (i =0 ; i < cnt; i++) { - err =uprobe_register (d_real_inode (link->path.dentry), - uprobes[i].offset, - uprobes[i].ref_ctr_offset, - &uprobes[i].consumer); -if (err) { -bpf_uprobe_unregister (&path, uprobes, i); + uprobes[i].uprobe =uprobe_register (d_real_inode (link->path.dentry), + uprobes[i].offset, + uprobes[i].ref_ctr_offset, + &uprobes[i].consumer); +if (IS_ERR (uprobes[i].uprobe)) { + err =PTR_ERR (uprobes[i].uprobe); +bpf_uprobe_unregister (uprobes, i); goto error_free; } }
kernel/trace/trace_uprobe.c +13 -13
@@ -58,8 +58,8 @@ struct trace_uprobe {struct dyn_event devent;struct uprobe_consumer consumer;struct path path; -struct inode *inode;char *filename; +struct uprobe *uprobe;unsigned long offset;unsigned long ref_ctr_offset;unsigned long nhit;
@@ -1084,16 +1084,16 @@ typedef bool (*filter_func_t)(struct uprobe_consumer *self,static int trace_uprobe_enable (struct trace_uprobe *tu,filter_func_t filter) { -int ret; +struct inode *inode =d_real_inode (tu->path.dentry); +struct uprobe *uprobe; tu->consumer.filter = filter; - tu->inode =d_real_inode (tu->path.dentry); - - ret =uprobe_register (tu->inode, tu->offset, tu->ref_ctr_offset, &tu->consumer); -if (ret) - tu->inode =NULL ; + uprobe =uprobe_register (inode, tu->offset, tu->ref_ctr_offset, &tu->consumer); +if (IS_ERR (uprobe)) +return PTR_ERR (uprobe); -return ret; + tu->uprobe = uprobe; +return 0 ; }static void __probe_event_disable (struct trace_probe *tp)
@@ -1104,11 +1104,11 @@ static void __probe_event_disable(struct trace_probe *tp)WARN_ON (!uprobe_filter_is_empty (tu->tp.event->filter));list_for_each_entry (tu,trace_probe_probe_list (tp), tp.list) { -if (!tu->inode) +if (!tu->uprobe)continue ; -uprobe_unregister (tu->inode, tu->offset, &tu->consumer); - tu->inode =NULL ; +uprobe_unregister (tu->uprobe, &tu->consumer); + tu->uprobe =NULL ; } }
@@ -1305,7 +1305,7 @@ static int uprobe_perf_close(struct trace_event_call *call,return 0 ;list_for_each_entry (tu,trace_probe_probe_list (tp), tp.list) { - ret =uprobe_apply (tu->inode, tu->offset, &tu->consumer, false); + ret =uprobe_apply (tu->uprobe, &tu->consumer, false);if (ret)break ; }
@@ -1329,7 +1329,7 @@ static int uprobe_perf_open(struct trace_event_call *call,return 0 ;list_for_each_entry (tu,trace_probe_probe_list (tp), tp.list) { - err =uprobe_apply (tu->inode, tu->offset, &tu->consumer, true); + err =uprobe_apply (tu->uprobe, &tu->consumer, true);if (err) {uprobe_perf_close (call, event);break ;
Signature · 1 line
2.25.1.362.g51ebf55
Masami Hiramatsu (Google)
maintainerRe: [PATCH v2 5/5] uprobes: make uprobe_register() return struct uprobe *
Quoted 6 lines · On Mon, 29 Jul 2024 15:45:35 +0200 Oleg Nesterov <[email protected]> wrote:
This way
uprobe_unregister()anduprobe_apply()can use "struct uprobe *" rather than inode + offset. This simplifies the code and allows to avoid the unnecessaryfind_uprobe()+put_uprobe()in these functions.TODO:
uprobe_unregister()still needs get_uprobe/put_uprobe to ensure that this uprobe can't be freed beforeup_write(&uprobe->register_rwsem).
Is this TODO item, or just a note? At this moment, this is natural to use get_uprobe() to protect uprobe itself.
Thank you,
Quoted 325 lines
Signed-off-by Oleg Nesterov <[email protected]>Acked-by Andrii Nakryiko <[email protected]>--- include/linux/uprobes.h | 15 +++++----- kernel/events/uprobes.c | 56 +++++++++++++++---------------------- kernel/trace/bpf_trace.c | 25 ++++++++--------- kernel/trace/trace_uprobe.c | 26 ++++++++--------- 4 files changed, 55 insertions(+), 67 deletions(-)include/linux/uprobes.h +8
-7@@ -16,6 +16,7 @@#include <linux/types.h> #include <linux/wait.h> +struct uprobe ;struct vm_area_struct ;struct mm_struct ;struct inode ;@@ -110,9 +111,9 @@ extern bool is_trap_insn(uprobe_opcode_t *insn);extern unsigned long uprobe_get_swbp_addr (struct pt_regs *regs);extern unsigned long uprobe_get_trap_addr (struct pt_regs *regs);extern int uprobe_write_opcode (struct arch_uprobe *auprobe,struct mm_struct *mm,unsigned long vaddr,uprobe_opcode_t ); -extern int uprobe_register (struct inode *inode,loff_t offset,loff_t ref_ctr_offset,struct uprobe_consumer *uc); -extern int uprobe_apply (struct inode *inode,loff_t offset,struct uprobe_consumer *uc,bool ); -extern void uprobe_unregister (struct inode *inode,loff_t offset,struct uprobe_consumer *uc); +extern struct uprobe *uprobe_register (struct inode *inode,loff_t offset,loff_t ref_ctr_offset,struct uprobe_consumer *uc); +extern int uprobe_apply (struct uprobe *uprobe,struct uprobe_consumer *uc,bool ); +extern void uprobe_unregister (struct uprobe *uprobe,struct uprobe_consumer *uc);extern int uprobe_mmap (struct vm_area_struct *vma);extern void uprobe_munmap (struct vm_area_struct *vma,unsigned long start,unsigned long end);extern void uprobe_start_dup_mmap (void );@@ -150,18 +151,18 @@ static inline void uprobes_init(void)#define uprobe_get_trap_addr (regs) instruction_pointer(regs) -static inline int +static inline struct uprobe *uprobe_register (struct inode *inode,loff_t offset,loff_t ref_ctr_offset,struct uprobe_consumer *uc) { -return -ENOSYS ; +return ERR_PTR (-ENOSYS ); }static inline int -uprobe_apply (struct inode *inode,loff_t offset,struct uprobe_consumer *uc,bool add) +uprobe_apply (struct uprobe * uprobe,struct uprobe_consumer *uc,bool add) {return -ENOSYS ; }static inline void -uprobe_unregister (struct inode *inode,loff_t offset,struct uprobe_consumer *uc) +uprobe_unregister (struct uprobe *uprobe,struct uprobe_consumer *uc) { }static inline int uprobe_mmap (struct vm_area_struct *vma)kernel/events/uprobes.c +22
-34@@ -1099,20 +1099,14 @@ __uprobe_unregister(struct uprobe *uprobe, struct uprobe_consumer *uc)delete_uprobe (uprobe); } -/* +/** * uprobe_unregister - unregister an already registered probe. -* @inode: the file in which the probe has to be removed. -* @offset: offset from the start of the file. +* @uprobe: uprobe to remove * @uc: identify which probe if multiple probes are colocated. */ -void uprobe_unregister (struct inode *inode,loff_t offset,struct uprobe_consumer *uc) +void uprobe_unregister (struct uprobe *uprobe,struct uprobe_consumer *uc) { -struct uprobe *uprobe; - - uprobe =find_uprobe (inode, offset); -if (WARN_ON (!uprobe)) -return ; - +get_uprobe (uprobe);down_write (&uprobe->register_rwsem);__uprobe_unregister (uprobe, uc);up_write (&uprobe->register_rwsem);@@ -1120,7 +1114,7 @@ void uprobe_unregister(struct inode *inode, loff_t offset, struct uprobe_consume }EXPORT_SYMBOL_GPL (uprobe_unregister); -/* +/** * uprobe_register - register a probe * @inode: the file in which the probe has to be placed. * @offset: offset from the start of the file. @@ -1136,40 +1130,40 @@ EXPORT_SYMBOL_GPL(uprobe_unregister);* unregisters. Caller of uprobe_register() is required to keep @inode * (and the containing mount) referenced. * -* Return errno if it cannot successully install probes -* else return 0 (success) +* Return: pointer to the new uprobe on success or an ERR_PTR on failure. */ -int uprobe_register (struct inode *inode,loff_t offset,loff_t ref_ctr_offset, -struct uprobe_consumer *uc) +struct uprobe *uprobe_register (struct inode *inode, +loff_t offset,loff_t ref_ctr_offset, +struct uprobe_consumer *uc) {struct uprobe *uprobe;int ret;/* Uprobe must have at least one set consumer */ if (!uc->handler && !uc->ret_handler) -return -EINVAL ; +return ERR_PTR (-EINVAL );/* copy_insn() uses read_mapping_page() or shmem_read_mapping_page() */ if (!inode->i_mapping->a_ops->read_folio && !shmem_mapping (inode->i_mapping)) -return -EIO ; +return ERR_PTR (-EIO );/* Racy, just to catch the obvious mistakes */ if (offset >i_size_read (inode)) -return -EINVAL ; +return ERR_PTR (-EINVAL );/* * This ensures that copy_from_page(), copy_to_page() and * __update_ref_ctr() can't cross page boundary. */ if (!IS_ALIGNED (offset,UPROBE_SWBP_INSN_SIZE )) -return -EINVAL ; +return ERR_PTR (-EINVAL );if (!IS_ALIGNED (ref_ctr_offset,sizeof (short ))) -return -EINVAL ; +return ERR_PTR (-EINVAL ); retry: uprobe =alloc_uprobe (inode, offset, ref_ctr_offset);if (IS_ERR (uprobe)) -return PTR_ERR (uprobe); +return uprobe;/* * We can race with uprobe_unregister()->delete_uprobe(). @@ -1188,35 +1182,29 @@ int uprobe_register(struct inode *inode, loff_t offset, loff_t ref_ctr_offset,if (unlikely (ret == -EAGAIN )) goto retry; -return ret; + +return ret ?ERR_PTR (ret) : uprobe; }EXPORT_SYMBOL_GPL (uprobe_register); -/* -* uprobe_apply - unregister an already registered probe. -* @inode: the file in which the probe has to be removed. -* @offset: offset from the start of the file. +/** +* uprobe_apply - add or remove the breakpoints according to @uc->filter +* @uprobe: uprobe which "owns" the breakpoint * @uc: consumer which wants to add more or remove some breakpoints * @add: add or remove the breakpoints +* Return: 0 on success or negative error code. */ -int uprobe_apply (struct inode *inode,loff_t offset, -struct uprobe_consumer *uc,bool add) +int uprobe_apply (struct uprobe *uprobe,struct uprobe_consumer *uc,bool add) { -struct uprobe *uprobe;struct uprobe_consumer *con;int ret = -ENOENT ; - uprobe =find_uprobe (inode, offset); -if (WARN_ON (!uprobe)) -return ret; -down_write (&uprobe->register_rwsem);for (con = uprobe->consumers; con && con != uc ; con = con->next) ;if (con) ret =register_for_each_vma (uprobe, add ? uc :NULL );up_write (&uprobe->register_rwsem); -put_uprobe (uprobe);return ret; }kernel/trace/bpf_trace.c +12
-13@@ -3160,6 +3160,7 @@ struct bpf_uprobe {loff_t offset;unsigned long ref_ctr_offset;u64 cookie; +struct uprobe *uprobe;struct uprobe_consumer consumer; };@@ -3178,15 +3179,12 @@ struct bpf_uprobe_multi_run_ctx {struct bpf_uprobe *uprobe; }; -static void bpf_uprobe_unregister (struct path *path,struct bpf_uprobe *uprobes, -u32 cnt) +static void bpf_uprobe_unregister (struct bpf_uprobe *uprobes,u32 cnt) {u32 i; -for (i =0 ; i < cnt; i++) { -uprobe_unregister (d_real_inode (path->dentry), uprobes[i].offset, - &uprobes[i].consumer); - } +for (i =0 ; i < cnt; i++) +uprobe_unregister (uprobes[i].uprobe, &uprobes[i].consumer); }static void bpf_uprobe_multi_link_release (struct bpf_link *link)@@ -3194,7 +3192,7 @@ static void bpf_uprobe_multi_link_release(struct bpf_link *link)struct bpf_uprobe_multi_link *umulti_link; umulti_link = container_of(link, struct bpf_uprobe_multi_link, link); -bpf_uprobe_unregister (&umulti_link->path, umulti_link->uprobes, umulti_link->cnt); +bpf_uprobe_unregister (umulti_link->uprobes, umulti_link->cnt);if (umulti_link->task)put_task_struct (umulti_link->task);path_put (&umulti_link->path);@@ -3480,12 +3478,13 @@ int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *pr &bpf_uprobe_multi_link_lops, prog);for (i =0 ; i < cnt; i++) { - err =uprobe_register (d_real_inode (link->path.dentry), - uprobes[i].offset, - uprobes[i].ref_ctr_offset, - &uprobes[i].consumer); -if (err) { -bpf_uprobe_unregister (&path, uprobes, i); + uprobes[i].uprobe =uprobe_register (d_real_inode (link->path.dentry), + uprobes[i].offset, + uprobes[i].ref_ctr_offset, + &uprobes[i].consumer); +if (IS_ERR (uprobes[i].uprobe)) { + err =PTR_ERR (uprobes[i].uprobe); +bpf_uprobe_unregister (uprobes, i); goto error_free; } }kernel/trace/trace_uprobe.c +13
-13@@ -58,8 +58,8 @@ struct trace_uprobe {struct dyn_event devent;struct uprobe_consumer consumer;struct path path; -struct inode *inode;char *filename; +struct uprobe *uprobe;unsigned long offset;unsigned long ref_ctr_offset;unsigned long nhit;@@ -1084,16 +1084,16 @@ typedef bool (*filter_func_t)(struct uprobe_consumer *self,static int trace_uprobe_enable (struct trace_uprobe *tu,filter_func_t filter) { -int ret; +struct inode *inode =d_real_inode (tu->path.dentry); +struct uprobe *uprobe; tu->consumer.filter = filter; - tu->inode =d_real_inode (tu->path.dentry); - - ret =uprobe_register (tu->inode, tu->offset, tu->ref_ctr_offset, &tu->consumer); -if (ret) - tu->inode =NULL ; + uprobe =uprobe_register (inode, tu->offset, tu->ref_ctr_offset, &tu->consumer); +if (IS_ERR (uprobe)) +return PTR_ERR (uprobe); -return ret; + tu->uprobe = uprobe; +return 0 ; }static void __probe_event_disable (struct trace_probe *tp)@@ -1104,11 +1104,11 @@ static void __probe_event_disable(struct trace_probe *tp)WARN_ON (!uprobe_filter_is_empty (tu->tp.event->filter));list_for_each_entry (tu,trace_probe_probe_list (tp), tp.list) { -if (!tu->inode) +if (!tu->uprobe)continue ; -uprobe_unregister (tu->inode, tu->offset, &tu->consumer); - tu->inode =NULL ; +uprobe_unregister (tu->uprobe, &tu->consumer); + tu->uprobe =NULL ; } }@@ -1305,7 +1305,7 @@ static int uprobe_perf_close(struct trace_event_call *call,return 0 ;list_for_each_entry (tu,trace_probe_probe_list (tp), tp.list) { - ret =uprobe_apply (tu->inode, tu->offset, &tu->consumer, false); + ret =uprobe_apply (tu->uprobe, &tu->consumer, false);if (ret)break ; }@@ -1329,7 +1329,7 @@ static int uprobe_perf_open(struct trace_event_call *call,return 0 ;list_for_each_entry (tu,trace_probe_probe_list (tp), tp.list) { - err =uprobe_apply (tu->inode, tu->offset, &tu->consumer, true); + err =uprobe_apply (tu->uprobe, &tu->consumer, true);if (err) {uprobe_perf_close (call, event);break ;Signature · 1 line
2.25.1.362.g51ebf55
Signature · 1 line
Masami Hiramatsu (Google) <[email protected]>
Oleg Nesterov
maintainerRe: [PATCH v2 5/5] uprobes: make uprobe_register() return struct uprobe *
Quoted 8 lines · Masami Hiramatsu
Quoted 4 lines · On Mon, 29 Jul 2024 15:45:35 +0200 Oleg Nesterov <[email protected]> wrote:
This way
uprobe_unregister()anduprobe_apply()can use "struct uprobe *" rather than inode + offset. This simplifies the code and allows to avoid the unnecessaryfind_uprobe()+put_uprobe()in these functions.
Quoted 5 lines
Quoted 2 lines
TODO:
uprobe_unregister()still needs get_uprobe/put_uprobe to ensure that this uprobe can't be freed beforeup_write(&uprobe->register_rwsem).Is this TODO item, or just a note? At this moment, this is natural to use
get_uprobe()to protect uprobe itself.
3/3 from the next series removes the extra get_uprobe() + put_uprobe().
Initially the change said something like
This patch adds the additional get_uprobe/put_uprobe into _register, the next patch will remove this.
But then decided to split this "next" patch and send it in another series.
Thanks,
Oleg.
Re: [PATCH v2 5/5] uprobes: make uprobe_register() return struct uprobe *
In reply to Oleg NesterovQuoted 8 lines · Oleg Nesterov
This way
uprobe_unregister()anduprobe_apply()can use "struct uprobe *" rather than inode + offset. This simplifies the code and allows to avoid the unnecessaryfind_uprobe()+put_uprobe()in these functions.TODO:
uprobe_unregister()still needs get_uprobe/put_uprobe to ensure that this uprobe can't be freed beforeup_write(&uprobe->register_rwsem).
Quoted 9 lines
Signed-off-by Oleg Nesterov <[email protected]>Acked-by Andrii Nakryiko <[email protected]>--- include/linux/uprobes.h | 15 +++++----- kernel/events/uprobes.c | 56 +++++++++++++++---------------------- kernel/trace/bpf_trace.c | 25 ++++++++--------- kernel/trace/trace_uprobe.c | 26 ++++++++--------- 4 files changed, 55 insertions(+), 67 deletions(-)
You'll need something like below to not break our bpf_testmod. And please send pull patch sets, not individually updated patches, it's a PITA to deal with. Thanks!
commit 9f739a9997ab833394196459fa7e6dd4d13dd48b (HEAD -> uprobes-oleg-cleanups)
Author: Andrii Nakryiko <[email protected]> Date: Wed Jul 31 09:15:46 2024 -0700
uprobes: fix bpf_testmod after uprobe_register/uprobe_unregister API change
(quoted diff) +0 -0
b/tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c index 5f152afdec2f..73a6b041bcce 100644
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c +0 -0
@@ -431,6 +431,7 @@ uprobe_ret_handler(struct uprobe_consumer *self,
unsigned long func,
}
struct testmod_uprobe {
+ struct uprobe *uprobe;
struct path path;
loff_t offset;
struct uprobe_consumer consumer;
@@ -458,12 +459,14 @@ static int testmod_register_uprobe(loff_t offset)
if (err)
goto out;- err = uprobe_register(d_real_inode(uprobe.path.dentry),
- offset, 0, &uprobe.consumer);
- if (err)
+ uprobe.uprobe = uprobe_register(d_real_inode(uprobe.path.dentry),
+ offset, 0, &uprobe.consumer);
+ if (IS_ERR(uprobe.uprobe)) {
path_put(&uprobe.path);
- else
+ uprobe.uprobe = NULL;
+ } else {
uprobe.offset = offset;+ }
out:
mutex_unlock(&testmod_uprobe_mutex);
@@ -474,10 +477,10 @@ static void testmod_unregister_uprobe(void)
{
mutex_lock(&testmod_uprobe_mutex);- if (uprobe.offset) {
- uprobe_unregister(d_real_inode(uprobe.path.dentry),
- uprobe.offset, &uprobe.consumer);
+ if (uprobe.uprobe) {
+ uprobe_unregister(uprobe.uprobe, &uprobe.consumer);
uprobe.offset = 0;
+ uprobe.uprobe = NULL;
}
mutex_unlock(&testmod_uprobe_mutex);[...]
Peter Zijlstra
maintainerRe: [PATCH v2 5/5] uprobes: make uprobe_register() return struct uprobe *
Quoted 19 lines · Andrii Nakryiko
Quoted 8 lines · Oleg Nesterov
This way
uprobe_unregister()anduprobe_apply()can use "struct uprobe *" rather than inode + offset. This simplifies the code and allows to avoid the unnecessaryfind_uprobe()+put_uprobe()in these functions.TODO:
uprobe_unregister()still needs get_uprobe/put_uprobe to ensure that this uprobe can't be freed beforeup_write(&uprobe->register_rwsem).Quoted 9 lines
Signed-off-by Oleg Nesterov <[email protected]>Acked-by Andrii Nakryiko <[email protected]>--- include/linux/uprobes.h | 15 +++++----- kernel/events/uprobes.c | 56 +++++++++++++++---------------------- kernel/trace/bpf_trace.c | 25 ++++++++--------- kernel/trace/trace_uprobe.c | 26 ++++++++--------- 4 files changed, 55 insertions(+), 67 deletions(-)
Quoted 3 lines
You'll need something like below to not break our
bpf_testmod. And please send pull patch sets, not individually updated patches, it's a PITA to deal with. Thanks!
Do I stuff this on top of Oleg's patch or do you want me to fold it in one of them?
Quoted 58 lines
commit
9f739a9997ab833394196459fa7e6dd4d13dd48b(HEAD -> uprobes-oleg-cleanups)Author: Andrii Nakryiko <[email protected]> Date: Wed Jul 31 09:15:46 2024 -0700uprobes: fix
bpf_testmodafter uprobe_register/uprobe_unregister API changeSigned-off-by Andrii Nakryiko <[email protected]>tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c +1
-0@@ -431,6 +431,7 @@ uprobe_ret_handler(struct uprobe_consumer *self, unsigned long func, }struct testmod_uprobe { +struct uprobe *uprobe;struct path path;loff_t offset;struct uprobe_consumer consumer;tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c +9
-7@@ -458,12 +459,14 @@ static int testmod_register_uprobe(loff_t offset)if (err) goto out; - err =uprobe_register (d_real_inode (uprobe.path.dentry), - offset,0 , &uprobe.consumer); -if (err) + uprobe.uprobe =uprobe_register (d_real_inode (uprobe.path.dentry), + offset,0 , &uprobe.consumer); +if (IS_ERR (uprobe.uprobe)) {path_put (&uprobe.path); -else + uprobe.uprobe =NULL ; + }else { uprobe.offset = offset; + } out:mutex_unlock (&testmod_uprobe_mutex);@@ -474,10 +477,10 @@ static void testmod_unregister_uprobe(void) {mutex_lock (&testmod_uprobe_mutex); -if (uprobe.offset) { -uprobe_unregister (d_real_inode (uprobe.path.dentry), - uprobe.offset, &uprobe.consumer); +if (uprobe.uprobe) { +uprobe_unregister (uprobe.uprobe, &uprobe.consumer); uprobe.offset =0 ; + uprobe.uprobe =NULL ; }mutex_unlock (&testmod_uprobe_mutex);
Re: [PATCH v2 5/5] uprobes: make uprobe_register() return struct uprobe *
Quoted 21 lines · Peter Zijlstra
Quoted 19 lines · Andrii Nakryiko
Quoted 17 lines · Oleg Nesterov
This way
uprobe_unregister()anduprobe_apply()can use "struct uprobe *" rather than inode + offset. This simplifies the code and allows to avoid the unnecessaryfind_uprobe()+put_uprobe()in these functions.TODO:
uprobe_unregister()still needs get_uprobe/put_uprobe to ensure that this uprobe can't be freed beforeup_write(&uprobe->register_rwsem).Signed-off-by Oleg Nesterov <[email protected]>Acked-by Andrii Nakryiko <[email protected]>--- include/linux/uprobes.h | 15 +++++----- kernel/events/uprobes.c | 56 +++++++++++++++---------------------- kernel/trace/bpf_trace.c | 25 ++++++++--------- kernel/trace/trace_uprobe.c | 26 ++++++++--------- 4 files changed, 55 insertions(+), 67 deletions(-)
Quoted 6 lines
Quoted 3 lines
You'll need something like below to not break our
bpf_testmod. And please send pull patch sets, not individually updated patches, it's a PITA to deal with. Thanks!Do I stuff this on top of Oleg's patch or do you want me to fold it in one of them?
Please fold so we have better (potential) bisectability of BPF selftests, thanks!
Quoted 59 lines
Quoted 59 lines
commit
9f739a9997ab833394196459fa7e6dd4d13dd48b(HEAD -> uprobes-oleg-cleanups)Author: Andrii Nakryiko <[email protected]> Date: Wed Jul 31 09:15:46 2024 -0700uprobes: fix
bpf_testmodafter uprobe_register/uprobe_unregister API changeSigned-off-by Andrii Nakryiko <[email protected]>tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c +1
-0@@ -431,6 +431,7 @@ uprobe_ret_handler(struct uprobe_consumer *self, unsigned long func, }struct testmod_uprobe { +struct uprobe *uprobe;struct path path;loff_t offset;struct uprobe_consumer consumer;tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c +9
-7@@ -458,12 +459,14 @@ static int testmod_register_uprobe(loff_t offset)if (err) goto out; - err =uprobe_register (d_real_inode (uprobe.path.dentry), - offset,0 , &uprobe.consumer); -if (err) + uprobe.uprobe =uprobe_register (d_real_inode (uprobe.path.dentry), + offset,0 , &uprobe.consumer); +if (IS_ERR (uprobe.uprobe)) {path_put (&uprobe.path); -else + uprobe.uprobe =NULL ; + }else { uprobe.offset = offset; + } out:mutex_unlock (&testmod_uprobe_mutex);@@ -474,10 +477,10 @@ static void testmod_unregister_uprobe(void) {mutex_lock (&testmod_uprobe_mutex); -if (uprobe.offset) { -uprobe_unregister (d_real_inode (uprobe.path.dentry), - uprobe.offset, &uprobe.consumer); +if (uprobe.uprobe) { +uprobe_unregister (uprobe.uprobe, &uprobe.consumer); uprobe.offset =0 ; + uprobe.uprobe =NULL ; }mutex_unlock (&testmod_uprobe_mutex);
Peter Zijlstra
maintainerRe: [PATCH v2 5/5] uprobes: make uprobe_register() return struct uprobe *
Quoted 26 lines · Andrii Nakryiko
Quoted 25 lines · Peter Zijlstra
Quoted 22 lines · Andrii Nakryiko
Quoted 8 lines · Oleg Nesterov
This way
uprobe_unregister()anduprobe_apply()can use "struct uprobe *" rather than inode + offset. This simplifies the code and allows to avoid the unnecessaryfind_uprobe()+put_uprobe()in these functions.TODO:
uprobe_unregister()still needs get_uprobe/put_uprobe to ensure that this uprobe can't be freed beforeup_write(&uprobe->register_rwsem).Quoted 9 lines
Signed-off-by Oleg Nesterov <[email protected]>Acked-by Andrii Nakryiko <[email protected]>--- include/linux/uprobes.h | 15 +++++----- kernel/events/uprobes.c | 56 +++++++++++++++---------------------- kernel/trace/bpf_trace.c | 25 ++++++++--------- kernel/trace/trace_uprobe.c | 26 ++++++++--------- 4 files changed, 55 insertions(+), 67 deletions(-)You'll need something like below to not break our
bpf_testmod. And please send pull patch sets, not individually updated patches, it's a PITA to deal with. Thanks!
Quoted 5 lines
Quoted 2 lines
Do I stuff this on top of Oleg's patch or do you want me to fold it in one of them?
Please fold so we have better (potential) bisectability of BPF selftests, thanks!
Fold where, patch 5?
Re: [PATCH v2 5/5] uprobes: make uprobe_register() return struct uprobe *
Quoted 28 lines · Peter Zijlstra
Quoted 26 lines · Andrii Nakryiko
Quoted 25 lines · Peter Zijlstra
Quoted 22 lines · Andrii Nakryiko
Quoted 8 lines · Oleg Nesterov
This way
uprobe_unregister()anduprobe_apply()can use "struct uprobe *" rather than inode + offset. This simplifies the code and allows to avoid the unnecessaryfind_uprobe()+put_uprobe()in these functions.TODO:
uprobe_unregister()still needs get_uprobe/put_uprobe to ensure that this uprobe can't be freed beforeup_write(&uprobe->register_rwsem).Quoted 9 lines
Signed-off-by Oleg Nesterov <[email protected]>Acked-by Andrii Nakryiko <[email protected]>--- include/linux/uprobes.h | 15 +++++----- kernel/events/uprobes.c | 56 +++++++++++++++---------------------- kernel/trace/bpf_trace.c | 25 ++++++++--------- kernel/trace/trace_uprobe.c | 26 ++++++++--------- 4 files changed, 55 insertions(+), 67 deletions(-)You'll need something like below to not break our
bpf_testmod. And please send pull patch sets, not individually updated patches, it's a PITA to deal with. Thanks!
Quoted 7 lines
Quoted 5 lines
Quoted 2 lines
Do I stuff this on top of Oleg's patch or do you want me to fold it in one of them?
Please fold so we have better (potential) bisectability of BPF selftests, thanks!
Fold where, patch 5?
Yep, this one, where Oleg changed uprobe_register/uprobe_unregister API.
But note, I did the lazy thing and just copy/pasted git show output into Gmail. So all the whitespaces are butchered and unlikely you'll be able to apply that patch as is. My expectation was that Oleg will just incorporate that by hand and will send the final v4 patch set.
Oleg Nesterov
maintainerRe: [PATCH v2 5/5] uprobes: make uprobe_register() return struct uprobe *
Quoted 3 lines · Andrii Nakryiko
My expectation was that Oleg will
just incorporate that by hand and will send the final v4 patch set.
Will do tomorrow, thanks.
Oleg.
Oleg Nesterov
maintainerRe: [PATCH v2 5/5] uprobes: make uprobe_register() return struct uprobe *
In reply to Peter ZijlstraQuoted 9 lines · Peter Zijlstra
Quoted 5 lines · Andrii Nakryiko
Quoted 2 lines
Do I stuff this on top of Oleg's patch or do you want me to fold it in one of them?
Please fold so we have better (potential) bisectability of BPF selftests, thanks!
Fold where, patch 5?
Yes...
Or I can resend these 2 series as v3 1-8 with 5/5 updated (thanks Andrii !!!), whatever is more convenient for you.
Oleg.
Peter Zijlstra
maintainerRe: [PATCH v2 5/5] uprobes: make uprobe_register() return struct uprobe *
Quoted 9 lines · Oleg Nesterov
Quoted 8 lines · Peter Zijlstra
Quoted 5 lines · Andrii Nakryiko
Quoted 2 lines
Do I stuff this on top of Oleg's patch or do you want me to fold it in one of them?
Please fold so we have better (potential) bisectability of BPF selftests, thanks!
Quoted 6 lines
Quoted 1 line
Fold where, patch 5?
Yes...
Or I can resend these 2 series as v3 1-8 with 5/5 updated (thanks Andrii !!!), whatever is more convenient for you.
I think I've lost the plot a little, so if you could resent a whole series that'd probably be easiest.
Re: [PATCH v2 5/5] uprobes: make uprobe_register() return struct uprobe *
In reply to Andrii NakryikoOn Wed, Jul 31, 2024 at 09:18:00AM -0700, Andrii Nakryiko wrote:
SNIP
Quoted 34 lines
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c +1
-0@@ -431,6 +431,7 @@ uprobe_ret_handler(struct uprobe_consumer *self, unsigned long func, }struct testmod_uprobe { +struct uprobe *uprobe;struct path path;loff_t offset;struct uprobe_consumer consumer;tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c +6
-4@@ -458,12 +459,14 @@ static int testmod_register_uprobe(loff_t offset)if (err) goto out; - err =uprobe_register (d_real_inode (uprobe.path.dentry), - offset,0 , &uprobe.consumer); -if (err) + uprobe.uprobe =uprobe_register (d_real_inode (uprobe.path.dentry), + offset,0 , &uprobe.consumer); +if (IS_ERR (uprobe.uprobe)) {path_put (&uprobe.path); -else + uprobe.uprobe =NULL ; + }else { uprobe.offset = offset; + } out:mutex_unlock (&testmod_uprobe_mutex);
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c static void testmod_unregister_uprobe(void) · line 483
Quoted 10 lines
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c +3
-3@@ -474,10 +477,10 @@ static void testmod_unregister_uprobe(void) {mutex_lock (&testmod_uprobe_mutex); -if (uprobe.offset) { -uprobe_unregister (d_real_inode (uprobe.path.dentry), - uprobe.offset, &uprobe.consumer); +if (uprobe.uprobe) { +uprobe_unregister (uprobe.uprobe, &uprobe.consumer); uprobe.offset =0 ; + uprobe.uprobe =NULL ;
ugh, I think we leak &uprobe.path.. I can send follow up fix if needed
jirka
Quoted 6 lines
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c +0
-0}mutex_unlock (&testmod_uprobe_mutex);
Oleg Nesterov
maintainerRe: [PATCH v2 5/5] uprobes: make uprobe_register() return struct uprobe *
Quoted 5 lines · Jiri Olsa
Quoted 5 lines
(quoted diff) +0
-0@@ -474,10 +477,10 @@ static void testmod_unregister_uprobe(void) { mutex_lock(&testmod_uprobe_mutex);
Quoted 9 lines
Review static void testmod_unregister_uprobe(void) · line 483
Quoted 7 lines
(quoted diff) +3
-3- if (uprobe.offset) { - uprobe_unregister(d_real_inode(uprobe.path.dentry), - uprobe.offset, &uprobe.consumer); + if (uprobe.uprobe) { + uprobe_unregister(uprobe.uprobe, &uprobe.consumer); uprobe.offset = 0; + uprobe.uprobe = NULL;ugh, I think we leak &uprobe.path.. I can send follow up fix if needed
Yeah, with or without this change. And with this change we do not need uprobe.offset.
Please see the patch below, this is what I've added to 5/5.
Do you see any problems?
Note the additional path_put() in testmod_unregister_uprobe(). Does it need a separate patch or can it come with 5/5 ?
Oleg.
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c +13 -13
@@ -432,7 +432,7 @@ uprobe_ret_handler(struct uprobe_consumer *self, unsigned long func,struct testmod_uprobe {struct path path; -loff_t offset; +struct uprobe *uprobe;struct uprobe_consumer consumer; };
@@ -446,25 +446,25 @@ static int testmod_register_uprobe(loff_t offset) {int err = -EBUSY ; -if (uprobe.offset) +if (uprobe.uprobe)return -EBUSY ;mutex_lock (&testmod_uprobe_mutex); -if (uprobe.offset) +if (uprobe.uprobe) goto out; err =kern_path ("/proc/self/exe" ,LOOKUP_FOLLOW , &uprobe.path);if (err) goto out; - err =uprobe_register (d_real_inode (uprobe.path.dentry), - offset,0 , &uprobe.consumer); -if (err) + uprobe.uprobe =uprobe_register (d_real_inode (uprobe.path.dentry), + offset,0 , &uprobe.consumer); +if (IS_ERR (uprobe.uprobe)) { + err =PTR_ERR (uprobe.uprobe);path_put (&uprobe.path); -else - uprobe.offset = offset; - + uprobe.uprobe =NULL ; + } out:mutex_unlock (&testmod_uprobe_mutex);return err;
@@ -474,10 +474,10 @@ static void testmod_unregister_uprobe(void) {mutex_lock (&testmod_uprobe_mutex); -if (uprobe.offset) { -uprobe_unregister (d_real_inode (uprobe.path.dentry), - uprobe.offset, &uprobe.consumer); - uprobe.offset =0 ; +if (uprobe.uprobe) { +uprobe_unregister (uprobe.uprobe, &uprobe.consumer); +path_put (&uprobe.path); + uprobe.uprobe =NULL ; }mutex_unlock (&testmod_uprobe_mutex);
Re: [PATCH v2 5/5] uprobes: make uprobe_register() return struct uprobe *
Quoted 16 lines · Oleg Nesterov
Quoted 5 lines · Jiri Olsa
Quoted 5 lines
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c +0
-0@@ -474,10 +477,10 @@ static void testmod_unregister_uprobe(void) {mutex_lock (&testmod_uprobe_mutex);Quoted 9 lines
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c static void testmod_unregister_uprobe(void) · line 480
Quoted 7 lines
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c +3
-3-if (uprobe.offset) { -uprobe_unregister (d_real_inode (uprobe.path.dentry), - uprobe.offset, &uprobe.consumer); +if (uprobe.uprobe) { +uprobe_unregister (uprobe.uprobe, &uprobe.consumer); uprobe.offset =0 ; + uprobe.uprobe =NULL ;ugh, I think we leak &uprobe.path.. I can send follow up fix if needed
Quoted 5 lines
Yeah, with or without this change. And with this change we do not need uprobe.offset.
Please see the patch below, this is what I've added to 5/5.
Do you see any problems?
looks good, thanks!
Quoted 3 lines
Note the additional
path_put()intestmod_unregister_uprobe(). Does it need a separate patch or can it come with 5/5 ?
I think it'd be better to have it separately, the test is already released.. so people might want to backport just the fix
thanks,
jirka
Quoted 64 lines
Oleg.
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c +13
-13@@ -432,7 +432,7 @@ uprobe_ret_handler(struct uprobe_consumer *self, unsigned long func,struct testmod_uprobe {struct path path; -loff_t offset; +struct uprobe *uprobe;struct uprobe_consumer consumer; };@@ -446,25 +446,25 @@ static int testmod_register_uprobe(loff_t offset) {int err = -EBUSY ; -if (uprobe.offset) +if (uprobe.uprobe)return -EBUSY ;mutex_lock (&testmod_uprobe_mutex); -if (uprobe.offset) +if (uprobe.uprobe) goto out; err =kern_path ("/proc/self/exe" ,LOOKUP_FOLLOW , &uprobe.path);if (err) goto out; - err =uprobe_register (d_real_inode (uprobe.path.dentry), - offset,0 , &uprobe.consumer); -if (err) + uprobe.uprobe =uprobe_register (d_real_inode (uprobe.path.dentry), + offset,0 , &uprobe.consumer); +if (IS_ERR (uprobe.uprobe)) { + err =PTR_ERR (uprobe.uprobe);path_put (&uprobe.path); -else - uprobe.offset = offset; - + uprobe.uprobe =NULL ; + } out:mutex_unlock (&testmod_uprobe_mutex);return err;@@ -474,10 +474,10 @@ static void testmod_unregister_uprobe(void) {mutex_lock (&testmod_uprobe_mutex); -if (uprobe.offset) { -uprobe_unregister (d_real_inode (uprobe.path.dentry), - uprobe.offset, &uprobe.consumer); - uprobe.offset =0 ; +if (uprobe.uprobe) { +uprobe_unregister (uprobe.uprobe, &uprobe.consumer); +path_put (&uprobe.path); + uprobe.uprobe =NULL ; }mutex_unlock (&testmod_uprobe_mutex);
Oleg Nesterov
maintainerRe: [PATCH v2 5/5] uprobes: make uprobe_register() return struct uprobe *
Quoted 6 lines · Jiri Olsa
Quoted 3 lines
Note the additional
path_put()intestmod_unregister_uprobe(). Does it need a separate patch or can it come with 5/5 ?I think it'd be better to have it separately, the test is already released.. so people might want to backport just the fix
OK, I'll rebase and add the patch below to v4. OK?
Oleg.
---
From f6bf42015048938d826880e3bf4a318bb64a05b4 Mon Sep 17 00:00:00 2001
From: Oleg Nesterov <[email protected]>
Date: Thu, 1 Aug 2024 14:21:47 +0200
Subject: [PATCH] selftests/bpf: fix uprobe.path leak in bpf_testmod
From: Jiri Olsa <[email protected]>
testmod_unregister_uprobe() forgets to path_put(&uprobe.path).
--- tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c | 1 + 1 file changed, 1 insertion(+)
tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c +1 -0
@@ -477,6 +477,7 @@ static void testmod_unregister_uprobe(void)if (uprobe.offset) {uprobe_unregister (d_real_inode (uprobe.path.dentry), uprobe.offset, &uprobe.consumer); +path_put (&uprobe.path); uprobe.offset =0 ; }
Signature · 1 line
2.25.1.362.g51ebf55
Re: [PATCH v2 5/5] uprobes: make uprobe_register() return struct uprobe *
Quoted 9 lines · Oleg Nesterov
Quoted 6 lines · Jiri Olsa
Quoted 3 lines
Note the additional
path_put()intestmod_unregister_uprobe(). Does it need a separate patch or can it come with 5/5 ?I think it'd be better to have it separately, the test is already released.. so people might want to backport just the fix
OK, I'll rebase and add the patch below to v4. OK?
heh, suggested-by would have been fine, but sure ;-)
thanks,
jirka
Quoted 35 lines
Oleg.
---From
f6bf42015048938d826880e3bf4a318bb64a05b4Mon Sep 17 00:00:00 2001
From: Oleg Nesterov <[email protected]>
Date: Thu, 1 Aug 2024 14:21:47 +0200
Subject: [PATCH] selftests/bpf: fix uprobe.path leak inbpf_testmodFrom: Jiri Olsa <[email protected]>
testmod_unregister_uprobe()forgets topath_put(&uprobe.path).Signed-off-by Jiri Olsa <[email protected]>Signed-off-by Oleg Nesterov <[email protected]>--- tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c | 1 + 1 file changed, 1 insertion(+)tools/testing/selftests/bpf/bpf_testmod/bpf_testmod.c +1
-0@@ -477,6 +477,7 @@ static void testmod_unregister_uprobe(void)if (uprobe.offset) {uprobe_unregister (d_real_inode (uprobe.path.dentry), uprobe.offset, &uprobe.consumer); +path_put (&uprobe.path); uprobe.offset =0 ; }Signature · 1 line
2.25.1.362.g51ebf55
Quoted 6 lines · Oleg Nesterov
Peter, I don't think these changes can really complicate your ongoing work. But again, if you are going to send the next version "soon" I can rebase these cleanups on top of it.
Andrii, I dared to preserve your acks, all the changes are simple.
Quoted 7 lines
Changes since v1:
- update the comment in
register_for_each_vma()
- remove the now unused "struct path *" arg frombpf_uprobe_unregister()
- forward-declare struct uprobe ininclude/linux/uprobes.h
- kernel-doc fixes/updates
- fix use-after free inuprobe_unregister(). See "TODO:" in 5/5.
lgtm
thanks,
jirka
Quoted 10 lines
Oleg.
---include/linux/uprobes.h | 22 +++++------ kernel/events/uprobes.c | 96 +++++++++++++++++---------------------------- kernel/trace/bpf_trace.c | 25 ++++++------ kernel/trace/trace_uprobe.c | 31 ++++++--------- 4 files changed, 69 insertions(+), 105 deletions(-)