All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Alex Bennée" <alex.bennee@linaro.org>
To: Aleksandr Sergeev <sergeev0xef@gmail.com>
Cc: qemu-devel@nongnu.org,
	Richard Henderson <richard.henderson@linaro.org>,
	Paolo Bonzini <pbonzini@redhat.com>
Subject: Re: [PATCH] linux-user/syscall.c: Prevent acquiring clone_lock while fork()
Date: Mon, 26 Jan 2026 15:57:57 +0000	[thread overview]
Message-ID: <87a4y0e58a.fsf@draig.linaro.org> (raw)
In-Reply-To: <20260126151612.2176451-1-sergeev0xef@gmail.com> (Aleksandr Sergeev's message of "Mon, 26 Jan 2026 18:16:12 +0300")

Aleksandr Sergeev <sergeev0xef@gmail.com> writes:

(Adding Paolo/Richard to Cc for exclusive discussion)

> By the spec, fork() copies only the thread which executes it.
> So it may happen, what while one thread is doing a fork,
> another thread is holding `clone_lock` mutex
> (e.g. doing a `fork()` or `exit()`).
> So the child process is born with the mutex being held,
> and there are nobody to release it.
>
> As the thread executing do_syscall() is not considered running,
> start_exclusive() does not protect us from the case.

I see the logic of what you say as:

    /* Write pending_cpus before reading other_cpu->running.  */
    smp_mb();
    running_cpus = 0;
    CPU_FOREACH(other_cpu) {
        if (qatomic_read(&other_cpu->running)) {
            other_cpu->has_waiter = true;
            running_cpus++;
            qemu_cpu_kick(other_cpu);
        }
    }

but...

>
> Resolves: https://gitlab.com/qemu-project/qemu/-/issues/3226
> Signed-off-by: Aleksandr Sergeev <sergeev0xef@gmail.com>
> ---
>  linux-user/main.c           |  2 ++
>  linux-user/syscall.c        | 14 ++++++++++++++
>  linux-user/user-internals.h |  2 ++
>  3 files changed, 18 insertions(+)
>
> diff --git a/linux-user/main.c b/linux-user/main.c
> index db751c07576..c49d1e91d22 100644
> --- a/linux-user/main.c
> +++ b/linux-user/main.c
> @@ -145,6 +145,7 @@ unsigned long guest_stack_size = TARGET_DEFAULT_STACK_SIZE;
>  void fork_start(void)
>  {
>      start_exclusive();
> +    clone_fork_start();
>      mmap_fork_start();
>      cpu_list_lock();
>      qemu_plugin_user_prefork_lock();
> @@ -174,6 +175,7 @@ void fork_end(pid_t pid)
>          cpu_list_unlock();
>      }
>      gdbserver_fork_end(thread_cpu, pid);
> +    clone_fork_end(child);
>      /*
>       * qemu_init_cpu_list() reinitialized the child exclusive state, but we
>       * also need to keep current_cpu consistent, so call end_exclusive() for
> diff --git a/linux-user/syscall.c b/linux-user/syscall.c
> index 3944004568f..e64e5db1139 100644
> --- a/linux-user/syscall.c
> +++ b/linux-user/syscall.c
> @@ -6856,6 +6856,20 @@ static void *clone_func(void *arg)
>      return NULL;
>  }
>  
> +void clone_fork_start(void)
> +{
> +    pthread_mutex_lock(&clone_lock);
> +}
> +
> +void clone_fork_end(bool child)
> +{
> +    if (child) {
> +        pthread_mutex_init(&clone_lock, NULL);
> +    } else {
> +        pthread_mutex_unlock(&clone_lock);
> +    }
> +}
> +

This looks like it should just be open-coded in the:

  if (flags & CLONE_VM) {

  }

leg of do_fork()?

Or maybe I'm missing the subtly of the race condition?


>  /* do_fork() Must return host values and target errnos (unlike most
>     do_*() functions). */
>  static int do_fork(CPUArchState *env, unsigned int flags, abi_ulong newsp,
> diff --git a/linux-user/user-internals.h b/linux-user/user-internals.h
> index 067c02bb93e..24d35998f07 100644
> --- a/linux-user/user-internals.h
> +++ b/linux-user/user-internals.h
> @@ -69,6 +69,8 @@ abi_long get_errno(abi_long ret);
>  const char *target_strerror(int err);
>  int get_osversion(void);
>  void init_qemu_uname_release(void);
> +void clone_fork_start(void);
> +void clone_fork_end(bool child);
>  void fork_start(void);
>  void fork_end(pid_t pid);

-- 
Alex Bennée
Virtualisation Tech Lead @ Linaro


  reply	other threads:[~2026-01-26 16:02 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-01-26 15:16 [PATCH] linux-user/syscall.c: Prevent acquiring clone_lock while fork() Aleksandr Sergeev
2026-01-26 15:57 ` Alex Bennée [this message]
2026-01-26 16:30   ` Peter Maydell
2026-02-03  2:42 ` Richard Henderson
2026-02-03  4:26   ` Richard Henderson
2026-02-10  9:43 ` Michael Tokarev

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=87a4y0e58a.fsf@draig.linaro.org \
    --to=alex.bennee@linaro.org \
    --cc=pbonzini@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=richard.henderson@linaro.org \
    --cc=sergeev0xef@gmail.com \
    /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.