* [PATCH mm-stable] uprobes: Revert ref_ctr_offset in uprobe_unregister error path
@ 2025-05-13 12:21 Jiri Olsa
2025-05-13 13:16 ` David Hildenbrand
2025-05-13 15:46 ` Oleg Nesterov
0 siblings, 2 replies; 6+ messages in thread
From: Jiri Olsa @ 2025-05-13 12:21 UTC (permalink / raw)
To: Masami Hiramatsu, Oleg Nesterov, Peter Zijlstra, Andrii Nakryiko
Cc: linux-kernel, linux-trace-kernel, David Hildenbrand
From: Jiri Olsa <olsajiri@gmail.com>
There's error path that could lead to inactive uprobe:
1) uprobe_register succeeds - updates instruction to int3 and
changes ref_ctr from 0 to 1
2) uprobe_unregister fails - int3 stays in place, but ref_ctr
is changed to 0 (it's not restored to 1 in the fail path)
uprobe is leaked
3) another uprobe_register comes and re-uses the leaked uprobe
and succeds - but int3 is already in place, so ref_ctr update
is skipped and it stays 0 - uprobe CAN NOT be triggered now
4) uprobe_unregister fails because ref_ctr value is unexpected
Fixing this by reverting the updated ref_ctr value back to 1 in step 2),
which is the case when uprobe_unregister fails (int3 stays in place),
but we have already updated refctr.
The new scenario will go as follows:
1) uprobe_register succeeds - updates instruction to int3 and
changes ref_ctr from 0 to 1
2) uprobe_unregister fails - int3 stays in place and ref_ctr
is reverted to 1.. uprobe is leaked
3) another uprobe_register comes and re-uses the leaked uprobe
and succeds - but int3 is already in place, so ref_ctr update
is skipped and it stays 1 - uprobe CAN be triggered now
4) uprobe_unregister succeeds
Suggested-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Jiri Olsa <jolsa@kernel.org>
---
Please note it's based on mm-stable branch, because it has the
latest uprobe_write_opcode rewrite changes.
kernel/events/uprobes.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c
index 4c965ba77f9f..84ee7b590861 100644
--- a/kernel/events/uprobes.c
+++ b/kernel/events/uprobes.c
@@ -581,8 +581,8 @@ int uprobe_write_opcode(struct arch_uprobe *auprobe, struct vm_area_struct *vma,
out:
/* Revert back reference counter if instruction update failed. */
- if (ret < 0 && is_register && ref_ctr_updated)
- update_ref_ctr(uprobe, mm, -1);
+ if (ret < 0 && ref_ctr_updated)
+ update_ref_ctr(uprobe, mm, is_register ? -1 : 1);
/* try collapse pmd for compound page */
if (ret > 0)
--
2.49.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* Re: [PATCH mm-stable] uprobes: Revert ref_ctr_offset in uprobe_unregister error path 2025-05-13 12:21 [PATCH mm-stable] uprobes: Revert ref_ctr_offset in uprobe_unregister error path Jiri Olsa @ 2025-05-13 13:16 ` David Hildenbrand 2025-05-13 13:17 ` David Hildenbrand 2025-05-13 15:46 ` Oleg Nesterov 1 sibling, 1 reply; 6+ messages in thread From: David Hildenbrand @ 2025-05-13 13:16 UTC (permalink / raw) To: Jiri Olsa, Masami Hiramatsu, Oleg Nesterov, Peter Zijlstra, Andrii Nakryiko Cc: linux-kernel, linux-trace-kernel On 13.05.25 14:21, Jiri Olsa wrote: > From: Jiri Olsa <olsajiri@gmail.com> > Thanks for debugging. > There's error path that could lead to inactive uprobe: > > 1) uprobe_register succeeds - updates instruction to int3 and > changes ref_ctr from 0 to 1 > 2) uprobe_unregister fails - int3 stays in place, but ref_ctr > is changed to 0 (it's not restored to 1 in the fail path) > uprobe is leaked > 3) another uprobe_register comes and re-uses the leaked uprobe > and succeds - but int3 is already in place, so ref_ctr update > is skipped and it stays 0 - uprobe CAN NOT be triggered now > 4) uprobe_unregister fails because ref_ctr value is unexpected > > Fixing this by reverting the updated ref_ctr value back to 1 in step 2), > which is the case when uprobe_unregister fails (int3 stays in place), > but we have already updated refctr. > > The new scenario will go as follows: > > 1) uprobe_register succeeds - updates instruction to int3 and > changes ref_ctr from 0 to 1 > 2) uprobe_unregister fails - int3 stays in place and ref_ctr > is reverted to 1.. uprobe is leaked > 3) another uprobe_register comes and re-uses the leaked uprobe > and succeds - but int3 is already in place, so ref_ctr update > is skipped and it stays 1 - uprobe CAN be triggered now > 4) uprobe_unregister succeeds > > Suggested-by: Oleg Nesterov <oleg@redhat.com> If it's in mm-stable, we should have Fixes: ... here > Signed-off-by: Jiri Olsa <jolsa@kernel.org> > --- > Please note it's based on mm-stable branch, because it has the > latest uprobe_write_opcode rewrite changes. > > kernel/events/uprobes.c | 4 ++-- > 1 file changed, 2 insertions(+), 2 deletions(-) > > diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c > index 4c965ba77f9f..84ee7b590861 100644 > --- a/kernel/events/uprobes.c > +++ b/kernel/events/uprobes.c > @@ -581,8 +581,8 @@ int uprobe_write_opcode(struct arch_uprobe *auprobe, struct vm_area_struct *vma, > > out: > /* Revert back reference counter if instruction update failed. */ > - if (ret < 0 && is_register && ref_ctr_updated) > - update_ref_ctr(uprobe, mm, -1); > + if (ret < 0 && ref_ctr_updated) > + update_ref_ctr(uprobe, mm, is_register ? -1 : 1); Hm, but my patch essentially did here /* Revert back reference counter if instruction update failed. */ - if (ret && is_register && ref_ctr_updated) + if (ret < 0 && is_register && ref_ctr_updated) update_ref_ctr(uprobe, mm, -1); So how come this wasn't a problem before? -- Cheers, David / dhildenb ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH mm-stable] uprobes: Revert ref_ctr_offset in uprobe_unregister error path 2025-05-13 13:16 ` David Hildenbrand @ 2025-05-13 13:17 ` David Hildenbrand 2025-05-13 13:48 ` Jiri Olsa 0 siblings, 1 reply; 6+ messages in thread From: David Hildenbrand @ 2025-05-13 13:17 UTC (permalink / raw) To: Jiri Olsa, Masami Hiramatsu, Oleg Nesterov, Peter Zijlstra, Andrii Nakryiko Cc: linux-kernel, linux-trace-kernel On 13.05.25 15:16, David Hildenbrand wrote: > On 13.05.25 14:21, Jiri Olsa wrote: >> From: Jiri Olsa <olsajiri@gmail.com> >> > > Thanks for debugging. > >> There's error path that could lead to inactive uprobe: >> >> 1) uprobe_register succeeds - updates instruction to int3 and >> changes ref_ctr from 0 to 1 >> 2) uprobe_unregister fails - int3 stays in place, but ref_ctr >> is changed to 0 (it's not restored to 1 in the fail path) >> uprobe is leaked >> 3) another uprobe_register comes and re-uses the leaked uprobe >> and succeds - but int3 is already in place, so ref_ctr update >> is skipped and it stays 0 - uprobe CAN NOT be triggered now >> 4) uprobe_unregister fails because ref_ctr value is unexpected >> >> Fixing this by reverting the updated ref_ctr value back to 1 in step 2), >> which is the case when uprobe_unregister fails (int3 stays in place), >> but we have already updated refctr. >> >> The new scenario will go as follows: >> >> 1) uprobe_register succeeds - updates instruction to int3 and >> changes ref_ctr from 0 to 1 >> 2) uprobe_unregister fails - int3 stays in place and ref_ctr >> is reverted to 1.. uprobe is leaked >> 3) another uprobe_register comes and re-uses the leaked uprobe >> and succeds - but int3 is already in place, so ref_ctr update >> is skipped and it stays 1 - uprobe CAN be triggered now >> 4) uprobe_unregister succeeds >> >> Suggested-by: Oleg Nesterov <oleg@redhat.com> > > If it's in mm-stable, we should have > > Fixes: ... > > here > >> Signed-off-by: Jiri Olsa <jolsa@kernel.org> >> --- >> Please note it's based on mm-stable branch, because it has the >> latest uprobe_write_opcode rewrite changes. >> >> kernel/events/uprobes.c | 4 ++-- >> 1 file changed, 2 insertions(+), 2 deletions(-) >> >> diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c >> index 4c965ba77f9f..84ee7b590861 100644 >> --- a/kernel/events/uprobes.c >> +++ b/kernel/events/uprobes.c >> @@ -581,8 +581,8 @@ int uprobe_write_opcode(struct arch_uprobe *auprobe, struct vm_area_struct *vma, >> >> out: >> /* Revert back reference counter if instruction update failed. */ >> - if (ret < 0 && is_register && ref_ctr_updated) >> - update_ref_ctr(uprobe, mm, -1); >> + if (ret < 0 && ref_ctr_updated) >> + update_ref_ctr(uprobe, mm, is_register ? -1 : 1); > > > Hm, but my patch essentially did here > > /* Revert back reference counter if instruction update failed. */ > - if (ret && is_register && ref_ctr_updated) > + if (ret < 0 && is_register && ref_ctr_updated) > update_ref_ctr(uprobe, mm, -1); > > So how come this wasn't a problem before? Oh, or was this a problem before? Then we should find the corresponding commit that needs fixing. -- Cheers, David / dhildenb ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH mm-stable] uprobes: Revert ref_ctr_offset in uprobe_unregister error path 2025-05-13 13:17 ` David Hildenbrand @ 2025-05-13 13:48 ` Jiri Olsa 0 siblings, 0 replies; 6+ messages in thread From: Jiri Olsa @ 2025-05-13 13:48 UTC (permalink / raw) To: David Hildenbrand Cc: Masami Hiramatsu, Oleg Nesterov, Peter Zijlstra, Andrii Nakryiko, linux-kernel, linux-trace-kernel On Tue, May 13, 2025 at 03:17:46PM +0200, David Hildenbrand wrote: > On 13.05.25 15:16, David Hildenbrand wrote: > > On 13.05.25 14:21, Jiri Olsa wrote: > > > From: Jiri Olsa <olsajiri@gmail.com> > > > > > > > Thanks for debugging. > > > > > There's error path that could lead to inactive uprobe: > > > > > > 1) uprobe_register succeeds - updates instruction to int3 and > > > changes ref_ctr from 0 to 1 > > > 2) uprobe_unregister fails - int3 stays in place, but ref_ctr > > > is changed to 0 (it's not restored to 1 in the fail path) > > > uprobe is leaked > > > 3) another uprobe_register comes and re-uses the leaked uprobe > > > and succeds - but int3 is already in place, so ref_ctr update > > > is skipped and it stays 0 - uprobe CAN NOT be triggered now > > > 4) uprobe_unregister fails because ref_ctr value is unexpected > > > > > > Fixing this by reverting the updated ref_ctr value back to 1 in step 2), > > > which is the case when uprobe_unregister fails (int3 stays in place), > > > but we have already updated refctr. > > > > > > The new scenario will go as follows: > > > > > > 1) uprobe_register succeeds - updates instruction to int3 and > > > changes ref_ctr from 0 to 1 > > > 2) uprobe_unregister fails - int3 stays in place and ref_ctr > > > is reverted to 1.. uprobe is leaked > > > 3) another uprobe_register comes and re-uses the leaked uprobe > > > and succeds - but int3 is already in place, so ref_ctr update > > > is skipped and it stays 1 - uprobe CAN be triggered now > > > 4) uprobe_unregister succeeds > > > > > > Suggested-by: Oleg Nesterov <oleg@redhat.com> > > > > If it's in mm-stable, we should have > > > > Fixes: ... ok > > > > here > > > > > Signed-off-by: Jiri Olsa <jolsa@kernel.org> > > > --- > > > Please note it's based on mm-stable branch, because it has the > > > latest uprobe_write_opcode rewrite changes. > > > > > > kernel/events/uprobes.c | 4 ++-- > > > 1 file changed, 2 insertions(+), 2 deletions(-) > > > > > > diff --git a/kernel/events/uprobes.c b/kernel/events/uprobes.c > > > index 4c965ba77f9f..84ee7b590861 100644 > > > --- a/kernel/events/uprobes.c > > > +++ b/kernel/events/uprobes.c > > > @@ -581,8 +581,8 @@ int uprobe_write_opcode(struct arch_uprobe *auprobe, struct vm_area_struct *vma, > > > out: > > > /* Revert back reference counter if instruction update failed. */ > > > - if (ret < 0 && is_register && ref_ctr_updated) > > > - update_ref_ctr(uprobe, mm, -1); > > > + if (ret < 0 && ref_ctr_updated) > > > + update_ref_ctr(uprobe, mm, is_register ? -1 : 1); > > > > > > Hm, but my patch essentially did here > > > > /* Revert back reference counter if instruction update failed. */ > > - if (ret && is_register && ref_ctr_updated) > > + if (ret < 0 && is_register && ref_ctr_updated) > > update_ref_ctr(uprobe, mm, -1); > > > > So how come this wasn't a problem before? > > Oh, or was this a problem before? Then we should find the corresponding > commit that needs fixing. yes, I think it was a problem before, introduced early on by: 1cc33161a83d uprobes: Support SDT markers having reference count (semaphore) it seems the scenario described in changelog will hit the same issue even without your patch, I'll re-run the test to be sure thanks, jirka ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH mm-stable] uprobes: Revert ref_ctr_offset in uprobe_unregister error path 2025-05-13 12:21 [PATCH mm-stable] uprobes: Revert ref_ctr_offset in uprobe_unregister error path Jiri Olsa 2025-05-13 13:16 ` David Hildenbrand @ 2025-05-13 15:46 ` Oleg Nesterov 2025-05-13 16:56 ` David Hildenbrand 1 sibling, 1 reply; 6+ messages in thread From: Oleg Nesterov @ 2025-05-13 15:46 UTC (permalink / raw) To: Jiri Olsa Cc: Masami Hiramatsu, Peter Zijlstra, Andrii Nakryiko, linux-kernel, linux-trace-kernel, David Hildenbrand On 05/13, Jiri Olsa wrote: > > --- a/kernel/events/uprobes.c > +++ b/kernel/events/uprobes.c > @@ -581,8 +581,8 @@ int uprobe_write_opcode(struct arch_uprobe *auprobe, struct vm_area_struct *vma, > > out: > /* Revert back reference counter if instruction update failed. */ > - if (ret < 0 && is_register && ref_ctr_updated) > - update_ref_ctr(uprobe, mm, -1); > + if (ret < 0 && ref_ctr_updated) > + update_ref_ctr(uprobe, mm, is_register ? -1 : 1); Acked-by: Oleg Nesterov <oleg@redhat.com> And just in case, I agree this has nothing to do with the recent changes from David. Oleg. ^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH mm-stable] uprobes: Revert ref_ctr_offset in uprobe_unregister error path 2025-05-13 15:46 ` Oleg Nesterov @ 2025-05-13 16:56 ` David Hildenbrand 0 siblings, 0 replies; 6+ messages in thread From: David Hildenbrand @ 2025-05-13 16:56 UTC (permalink / raw) To: Oleg Nesterov, Jiri Olsa Cc: Masami Hiramatsu, Peter Zijlstra, Andrii Nakryiko, linux-kernel, linux-trace-kernel On 13.05.25 17:46, Oleg Nesterov wrote: > On 05/13, Jiri Olsa wrote: >> >> --- a/kernel/events/uprobes.c >> +++ b/kernel/events/uprobes.c >> @@ -581,8 +581,8 @@ int uprobe_write_opcode(struct arch_uprobe *auprobe, struct vm_area_struct *vma, >> >> out: >> /* Revert back reference counter if instruction update failed. */ >> - if (ret < 0 && is_register && ref_ctr_updated) >> - update_ref_ctr(uprobe, mm, -1); >> + if (ret < 0 && ref_ctr_updated) >> + update_ref_ctr(uprobe, mm, is_register ? -1 : 1); > > Acked-by: Oleg Nesterov <oleg@redhat.com> > > And just in case, I agree this has nothing to do with the recent changes from David. BTW, I stumbled over this when doing the rework. Back then, I was wondering if this is to handle the case where un-registering effectively fails because someone MADV_DONTNEED'ed the page. But, we only perform the update_ref_ctr() after verify_opcode(), so that does not apply. With proper Fixes: Acked-by: David Hildenbrand <david@redhat.com> -- Cheers, David / dhildenb ^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2025-05-13 16:57 UTC | newest] Thread overview: 6+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2025-05-13 12:21 [PATCH mm-stable] uprobes: Revert ref_ctr_offset in uprobe_unregister error path Jiri Olsa 2025-05-13 13:16 ` David Hildenbrand 2025-05-13 13:17 ` David Hildenbrand 2025-05-13 13:48 ` Jiri Olsa 2025-05-13 15:46 ` Oleg Nesterov 2025-05-13 16:56 ` David Hildenbrand
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox