All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Lorenzo Stoakes (ARM)" <ljs@kernel.org>
To: Alexei Starovoitov <alexei.starovoitov@gmail.com>
Cc: bpf@vger.kernel.org, daniel@iogearbox.net, andrii@kernel.org,
	 eddyz87@gmail.com, memxor@gmail.com, oleg@redhat.com,
	peterz@infradead.org,  mhiramat@kernel.org, jolsa@kernel.org,
	akpm@linux-foundation.org, liam@infradead.org,
	 surenb@google.com, david@kernel.org, vbabka@kernel.org,
	jannh@google.com,  hughd@google.com, kees@kernel.org,
	mingo@redhat.com, tglx@kernel.org,  x86@kernel.org,
	linux-trace-kernel@vger.kernel.org, linux-mm@kvack.org,
	 linux-perf-users@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH bpf-next 7/9] fork: Let the new task point to its mm before uprobes can find it
Date: Tue, 27 Oct 2026 16:08:17 +0000	[thread overview]
Message-ID: <auDGKwWWNuhuB3IE@gremlin> (raw)
In-Reply-To: <20261022015712.1297331-8-alexei.starovoitov@gmail.com>

On Thu, Oct 22, 2026 at 01:57:10AM +0000, Alexei Starovoitov wrote:
> From: Alexei Starovoitov <ast@kernel.org>
>
> uprobe_perf_filter() compares event->hw.target->mm with mm. The target of
> an inherited event is the new task. copy_mm() sets tsk->mm after dup_mm()
> returned, but register_for_each_vma() can find the new mm as soon as
> dup_mmap() releases dup_mmap_sem. No task points to this mm yet, so
> the filter says that nobody wants it.
>
> The walk that removes the breakpoint, because another consumer of the same
> uprobe goes away, sees !filter_chain() and removes it from the child.
> Nobody installs it again, and the inherited event doesn't see this child.
>
> It's an old bug with a short window. The previous patch releases
> dup_mmap_sem before dup_userfaultfd_complete(), so with UFFD_EVENT_FORK
> the window is as long as the monitor likes.
>
> Set tsk->mm at the end of dup_mmap() before it releases dup_mmap_sem.
> Not earlier. The new task can be reached via its perf events and from bpf
> progs. They shouldn't find mm that is half copied. Clear tsk->mm when
> dup_mm() fails after dup_mmap(). Do both under task_lock() like exec_mmap()
> and exit_mm().
>
> The tasks with such event fork, while another consumer of the same uprobe
> comes and goes. 5 cpu VM, 90 sec and 7000 children per run. The number of
> children that the inherited event didn't see:
>
>   without this series          11 to 24, 8 runs
>   patches 1, 2, 5, 6 only      23, 12, 21
>   this series                  0, 0, 0
>
> With UFFD_EVENT_FORK monitor that reads the event after 5 sec the child
> lost the breakpoint in 5 of 5 runs without this patch, and in 0 of 5 with.
>
> Signed-off-by: Alexei Starovoitov <ast@kernel.org>

This feels like a 'fix in the wrong place' thing again really.

What would a fix in uprobe code look like?

I don't love that core mm fork code is being changed to address uprobe
problems in general.

> ---
>  kernel/fork.c |  9 ++++++++-
>  mm/internal.h |  3 ++-
>  mm/mmap.c     | 14 +++++++++++++-
>  mm/nommu.c    |  3 ++-
>  4 files changed, 25 insertions(+), 4 deletions(-)
>
> diff --git a/kernel/fork.c b/kernel/fork.c
> index 8fd6be932afe..5a2f571b64b7 100644
> --- a/kernel/fork.c
> +++ b/kernel/fork.c
> @@ -1539,7 +1539,7 @@ static struct mm_struct *dup_mm(struct task_struct *tsk,
>  	if (!mm_init(mm, tsk))
>  		goto fail_nomem;
>
> -	err = dup_mmap(mm, oldmm);
> +	err = dup_mmap(tsk, mm, oldmm);
>  	if (err)
>  		goto free_pt;
>
> @@ -1552,6 +1552,13 @@ static struct mm_struct *dup_mm(struct task_struct *tsk,
>  	return mm;
>
>  free_pt:
> +	/*
> +	 * dup_mmap() has set it if it succeeded. get_task_mm() must not find
> +	 * it after the mmput() below.
> +	 */
> +	task_lock(tsk);
> +	tsk->mm = NULL;
> +	task_unlock(tsk);

It's not great to straddle some task stuff here, and some in
dup_mmap(). Could it all just live in copy_mm(), where other task stuff is
done?

It seems like changes you did in the previous patch dictate a move here,
but the fix that David mentions moves the mmap lock back to dup_mm() anyway
so a rework should hopefully avoid all that :)

>  	/* don't put binfmt in mmput, we haven't got module yet */
>  	mm->binfmt = NULL;
>  	mm_init_owner(mm, NULL);
> diff --git a/mm/internal.h b/mm/internal.h
> index 9e3be236f18c..70e40306d881 100644
> --- a/mm/internal.h
> +++ b/mm/internal.h
> @@ -1520,7 +1520,8 @@ int walk_page_range_debug(struct mm_struct *mm, unsigned long start,
>  			  pgd_t *pgd, void *private);
>
>  void dup_mm_exe_file(struct mm_struct *mm, struct mm_struct *oldmm);
> -int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm);
> +int dup_mmap(struct task_struct *tsk, struct mm_struct *mm,
> +	     struct mm_struct *oldmm);
>
>  int remap_pfn_range_prepare(struct vm_area_desc *desc);
>  int remap_pfn_range_complete(struct vm_area_struct *vma,
> diff --git a/mm/mmap.c b/mm/mmap.c
> index 6ce654e33e3b..3167272a77ad 100644
> --- a/mm/mmap.c
> +++ b/mm/mmap.c
> @@ -46,6 +46,7 @@
>  #include <linux/pkeys.h>
>  #include <linux/oom.h>
>  #include <linux/sched/mm.h>
> +#include <linux/sched/task.h>
>  #include <linux/ksm.h>
>  #include <linux/memfd.h>
>
> @@ -1705,7 +1706,8 @@ bool mmap_read_lock_maybe_expand(struct mm_struct *mm,
>  	return true;
>  }
>
> -__latent_entropy int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
> +__latent_entropy int dup_mmap(struct task_struct *tsk, struct mm_struct *mm,
> +			      struct mm_struct *oldmm)
>  {
>  	struct vm_area_struct *mpnt, *tmp;
>  	int retval;
> @@ -1897,6 +1899,16 @@ __latent_entropy int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
>  	mmap_write_unlock(mm);
>  	flush_tlb_mm(oldmm);
>  	mmap_write_unlock(oldmm);
> +	if (!retval) {
> +		/*
> +		 * As soon as dup_mmap_sem is released uprobes can find mm via
> +		 * the rmap tree and ask the consumers if they want it. The
> +		 * filter of an inherited perf event looks for mm in tsk->mm.
> +		 */
> +		task_lock(tsk);
> +		tsk->mm = mm;
> +		task_unlock(tsk);
> +	}

Yeah this isn't lovely, it feels like you're having mm logic handle
uprobe's problems. I don't like the idea of changing the signature here and
having core mm deal with the task stuff like this, and now it's being
fiddled with over 3 separate functions.

You should be doing this in the more generic fork code.

Or, ideally, if you could find a solution that doesn't involve changing
core fork code but rather modified uprobe to stop making what are,
currently, seemingly broken or at least sub-optimal assumptions that'd be a
lot better!

>  	uprobe_end_dup_mmap();
>  	if (!retval)
>  		dup_userfaultfd_complete(&uf);
> diff --git a/mm/nommu.c b/mm/nommu.c
> index 68b05565e15a..7529e03d3f2c 100644
> --- a/mm/nommu.c
> +++ b/mm/nommu.c
> @@ -1885,7 +1885,8 @@ static int __meminit init_admin_reserve(void)
>  }
>  subsys_initcall(init_admin_reserve);
>
> -int dup_mmap(struct mm_struct *mm, struct mm_struct *oldmm)
> +int dup_mmap(struct task_struct *tsk, struct mm_struct *mm,
> +	     struct mm_struct *oldmm)
>  {
>  	mmap_write_lock(oldmm);
>  	dup_mm_exe_file(mm, oldmm);
> --
> 2.55.0
>

--
Cheers, Lorenzo

  parent reply	other threads:[~2026-10-27 16:08 UTC|newest]

Thread overview: 34+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-22  1:57 [PATCH bpf-next 0/9] uprobes: Don't stall fork() when mmap_lock is busy Alexei Starovoitov
2026-10-22  1:57 ` [PATCH bpf-next 1/9] mm: Bring back mmap_write_trylock() Alexei Starovoitov
2026-10-22  2:02   ` sashiko-bot
2026-10-22  1:57 ` [PATCH bpf-next 2/9] uprobes: Don't run exit_mmap() under dup_mmap_sem Alexei Starovoitov
2026-10-22  2:04   ` sashiko-bot
2026-10-22  1:57 ` [PATCH bpf-next 3/9] uprobes/x86: Finish an unoptimization which failed half-way Alexei Starovoitov
2026-10-22  2:06   ` sashiko-bot
2026-10-22  2:57   ` Gregory Price (Meta)
2026-10-23  7:13   ` Jiri Olsa
2026-10-22  1:57 ` [PATCH bpf-next 4/9] uprobes/x86: Don't WARN if an optimized probe can't be written Alexei Starovoitov
2026-10-22  2:03   ` sashiko-bot
2026-10-22  2:37   ` bot+bpf-ci
2026-10-22 17:56   ` Jann Horn
2026-10-22 19:33     ` Liam R. Howlett
2026-10-23 14:06       ` Lorenzo Stoakes (ARM)
2026-10-23  7:13   ` Jiri Olsa
2026-10-22  1:57 ` [PATCH bpf-next 5/9] uprobes: Don't sleep on mmap_lock under dup_mmap_sem Alexei Starovoitov
2026-10-22  2:16   ` sashiko-bot
2026-10-22  2:37   ` bot+bpf-ci
2026-10-22 23:11   ` Andrii Nakryiko
2026-10-22  1:57 ` [PATCH bpf-next 6/9] uprobes: Don't sleep on mmap_lock with dup_mmap_sem held in dup_mmap() Alexei Starovoitov
2026-10-22  2:15   ` sashiko-bot
2026-10-22 18:42   ` Jann Horn
2026-10-23 20:51   ` David Hildenbrand (Arm)
2026-10-27 15:38   ` Lorenzo Stoakes (ARM)
2026-10-22  1:57 ` [PATCH bpf-next 7/9] fork: Let the new task point to its mm before uprobes can find it Alexei Starovoitov
2026-10-22  2:23   ` sashiko-bot
2026-10-27 16:08   ` Lorenzo Stoakes (ARM) [this message]
2026-10-22  1:57 ` [PATCH bpf-next 8/9] uprobes: Don't leak the breakpoints if the unregistering task is killed Alexei Starovoitov
2026-10-22  2:20   ` sashiko-bot
2026-10-22  1:57 ` [PATCH bpf-next 9/9] selftests/bpf: Test uprobes and fork() with busy mmap_lock Alexei Starovoitov
2026-10-22  2:10   ` sashiko-bot
2026-10-22  2:22     ` Alexei Starovoitov
2026-10-22  2:37   ` bot+bpf-ci

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=auDGKwWWNuhuB3IE@gremlin \
    --to=ljs@kernel.org \
    --cc=akpm@linux-foundation.org \
    --cc=alexei.starovoitov@gmail.com \
    --cc=andrii@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=david@kernel.org \
    --cc=eddyz87@gmail.com \
    --cc=hughd@google.com \
    --cc=jannh@google.com \
    --cc=jolsa@kernel.org \
    --cc=kees@kernel.org \
    --cc=liam@infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=linux-perf-users@vger.kernel.org \
    --cc=linux-trace-kernel@vger.kernel.org \
    --cc=memxor@gmail.com \
    --cc=mhiramat@kernel.org \
    --cc=mingo@redhat.com \
    --cc=oleg@redhat.com \
    --cc=peterz@infradead.org \
    --cc=surenb@google.com \
    --cc=tglx@kernel.org \
    --cc=vbabka@kernel.org \
    --cc=x86@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.