diff mbox series

[v8,13/21] main: keep rcu_atfork callback enabled for qtest

Message ID 20200129053357.27454-14-alxndr@bu.edu
State New
Headers show
Series [v8,01/21] softmmu: split off vl.c:main() into main.c | expand

Commit Message

Alexander Bulekov Jan. 29, 2020, 5:34 a.m. UTC
The qtest-based fuzzer makes use of forking to reset-state between
tests. Keep the callback enabled, so the call_rcu thread gets created
within the child process.

Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
---
 vl.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

Comments

Stefan Hajnoczi Jan. 30, 2020, 2:42 p.m. UTC | #1
On Wed, Jan 29, 2020 at 05:34:22AM +0000, Bulekov, Alexander wrote:
> The qtest-based fuzzer makes use of forking to reset-state between
> tests. Keep the callback enabled, so the call_rcu thread gets created
> within the child process.
> 
> Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
> ---
>  vl.c | 9 ++++++++-
>  1 file changed, 8 insertions(+), 1 deletion(-)
> 
> diff --git a/vl.c b/vl.c
> index bb77935f04..cf8e2d3ebb 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -3794,7 +3794,14 @@ void qemu_init(int argc, char **argv, char **envp)
>      set_memory_options(&ram_slots, &maxram_size, machine_class);
>  
>      os_daemonize();
> -    rcu_disable_atfork();
> +
> +    /*
> +     * If QTest is enabled, keep the rcu_atfork enabled, since system processes
> +     * may be forked testing purposes (e.g. fork-server based fuzzing)
> +     */
> +    if (!qtest_enabled()) {
> +        rcu_disable_atfork();
> +    }

I haven't reviewed the details of whether resources are leaked across
fork but in general it makes sense that we want an RCU thread in the
fork child:

Acked-by: Stefan Hajnoczi <stefanha@redhat.com>
Paolo Bonzini Jan. 30, 2020, 5:24 p.m. UTC | #2
On 30/01/20 15:42, Stefan Hajnoczi wrote:
>> +
>> +    /*
>> +     * If QTest is enabled, keep the rcu_atfork enabled, since system processes
>> +     * may be forked testing purposes (e.g. fork-server based fuzzing)
>> +     */
>> +    if (!qtest_enabled()) {
>> +        rcu_disable_atfork();
>> +    }
> I haven't reviewed the details of whether resources are leaked across
> fork but in general it makes sense that we want an RCU thread in the
> fork child:

Note that there is a possible deadlock between fork and synchronize_rcu
(see commit 73c6e40, "rcu: completely disable pthread_atfork callbacks
as soon as possible", 2016-01-27):

- the CPU thread is inside a RCU critical section and wants to take the
BQL in order to do MMIO

- the I/O thread, which is owning the BQL, forks and calls
rcu_init_lock, which tries to take the rcu_sync_lock

- the call_rcu thread has taken rcu_sync_lock in synchronize_rcu, but
synchronize_rcu needs the CPU thread to end the critical section before
returning.

Therefore it would be best if the fork server could fork before a single
CPU instruction is executed, and then rcu_disable_atfork could be moved
right after the fork server is started (just like right now we do it
right after os_daemonize).  We probably talked about this before, but
how do you ensure that the fork server is started before threads are
created (apart from the RCU thread)?

Paolo
Alexander Bulekov Jan. 30, 2020, 5:42 p.m. UTC | #3
On 200130 1824, Paolo Bonzini wrote:
> On 30/01/20 15:42, Stefan Hajnoczi wrote:
> >> +
> >> +    /*
> >> +     * If QTest is enabled, keep the rcu_atfork enabled, since system processes
> >> +     * may be forked testing purposes (e.g. fork-server based fuzzing)
> >> +     */
> >> +    if (!qtest_enabled()) {
> >> +        rcu_disable_atfork();
> >> +    }
> > I haven't reviewed the details of whether resources are leaked across
> > fork but in general it makes sense that we want an RCU thread in the
> > fork child:
> 
> Note that there is a possible deadlock between fork and synchronize_rcu
> (see commit 73c6e40, "rcu: completely disable pthread_atfork callbacks
> as soon as possible", 2016-01-27):
> 
> - the CPU thread is inside a RCU critical section and wants to take the
> BQL in order to do MMIO
> 
> - the I/O thread, which is owning the BQL, forks and calls
> rcu_init_lock, which tries to take the rcu_sync_lock
> 
> - the call_rcu thread has taken rcu_sync_lock in synchronize_rcu, but
> synchronize_rcu needs the CPU thread to end the critical section before
> returning.
> 
> Therefore it would be best if the fork server could fork before a single
> CPU instruction is executed, and then rcu_disable_atfork could be moved
> right after the fork server is started (just like right now we do it
> right after os_daemonize).  We probably talked about this before, but
> how do you ensure that the fork server is started before threads are
> created (apart from the RCU thread)?

With QTest, is this still a concern, since there are no CPU instructions
involved? Sometimes the fork-server starts after some I/O has already
occured (eg mapping BARs and setting up VQs for virtio-net). I know we
briefly talked about threads at some point, and it seems that iothreads
may be a concern, if any are started before fork. Other than that, since
there is no TCG/CPU thread, are there any other threads that could be
a concern?
-Alex

> Paolo
>
Paolo Bonzini Jan. 30, 2020, 6:14 p.m. UTC | #4
On 30/01/20 18:42, Alexander Bulekov wrote:
> With QTest, is this still a concern, since there are no CPU instructions
> involved? Sometimes the fork-server starts after some I/O has already
> occured (eg mapping BARs and setting up VQs for virtio-net). I know we
> briefly talked about threads at some point, and it seems that iothreads
> may be a concern, if any are started before fork. Other than that, since
> there is no TCG/CPU thread, are there any other threads that could be
> a concern?
> -Alex

There is a CPU thread, it just does not do MMIO.  However, it may still
execute code via run_on_cpu.  It's quite unlikely to have the deadlock,
but if it were possible to force an early start of the fork server (at
the point of os_daemonize() would be ideal) it would be cleaner and it
would allow reverting this patch.

This is not a NACK, just some extra info.

Paolo
Darren Kenny Feb. 5, 2020, 1:58 p.m. UTC | #5
On Wed, Jan 29, 2020 at 05:34:22AM +0000, Bulekov, Alexander wrote:
>The qtest-based fuzzer makes use of forking to reset-state between
>tests. Keep the callback enabled, so the call_rcu thread gets created
>within the child process.
>
>Signed-off-by: Alexander Bulekov <alxndr@bu.edu>

Reviewed-by: Darren Kenny <darren.kenny@oracle.com>

>---
> vl.c | 9 ++++++++-
> 1 file changed, 8 insertions(+), 1 deletion(-)
>
>diff --git a/vl.c b/vl.c
>index bb77935f04..cf8e2d3ebb 100644
>--- a/vl.c
>+++ b/vl.c
>@@ -3794,7 +3794,14 @@ void qemu_init(int argc, char **argv, char **envp)
>     set_memory_options(&ram_slots, &maxram_size, machine_class);
>
>     os_daemonize();
>-    rcu_disable_atfork();
>+
>+    /*
>+     * If QTest is enabled, keep the rcu_atfork enabled, since system processes
>+     * may be forked testing purposes (e.g. fork-server based fuzzing)
>+     */
>+    if (!qtest_enabled()) {
>+        rcu_disable_atfork();
>+    }
>
>     if (pid_file && !qemu_write_pidfile(pid_file, &err)) {
>         error_reportf_err(err, "cannot create PID file: ");
>-- 
>2.23.0
>
>
Thomas Huth June 18, 2020, 7:34 a.m. UTC | #6
On 29/01/2020 06.34, Bulekov, Alexander wrote:
> The qtest-based fuzzer makes use of forking to reset-state between
> tests. Keep the callback enabled, so the call_rcu thread gets created
> within the child process.
> 
> Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
> ---
>  vl.c | 9 ++++++++-
>  1 file changed, 8 insertions(+), 1 deletion(-)
> 
> diff --git a/vl.c b/vl.c
> index bb77935f04..cf8e2d3ebb 100644
> --- a/vl.c
> +++ b/vl.c
> @@ -3794,7 +3794,14 @@ void qemu_init(int argc, char **argv, char **envp)
>      set_memory_options(&ram_slots, &maxram_size, machine_class);
>  
>      os_daemonize();
> -    rcu_disable_atfork();
> +
> +    /*
> +     * If QTest is enabled, keep the rcu_atfork enabled, since system processes
> +     * may be forked testing purposes (e.g. fork-server based fuzzing)
> +     */
> +    if (!qtest_enabled()) {
> +        rcu_disable_atfork();
> +    }
>  
>      if (pid_file && !qemu_write_pidfile(pid_file, &err)) {
>          error_reportf_err(err, "cannot create PID file: ");

 Hi Alexander,

I think this patch might maybe not work as expected: The qtest_enabled()
has been added before configure_accelerators() is called in main(). So
qtest_enabled() should always return "false" and thus
rcu_disabled_fork() is still called in any case... could you please
double-check whether it works for you and I just made a mistake, or
whether this is a bug indeed?

 Thanks,
  Thomas
Alexander Bulekov June 18, 2020, 3:08 p.m. UTC | #7
On 200618 0934, Thomas Huth wrote:
> On 29/01/2020 06.34, Bulekov, Alexander wrote:
> > The qtest-based fuzzer makes use of forking to reset-state between
> > tests. Keep the callback enabled, so the call_rcu thread gets created
> > within the child process.
> > 
> > Signed-off-by: Alexander Bulekov <alxndr@bu.edu>
> > ---
> >  vl.c | 9 ++++++++-
> >  1 file changed, 8 insertions(+), 1 deletion(-)
> > 
> > diff --git a/vl.c b/vl.c
> > index bb77935f04..cf8e2d3ebb 100644
> > --- a/vl.c
> > +++ b/vl.c
> > @@ -3794,7 +3794,14 @@ void qemu_init(int argc, char **argv, char **envp)
> >      set_memory_options(&ram_slots, &maxram_size, machine_class);
> >  
> >      os_daemonize();
> > -    rcu_disable_atfork();
> > +
> > +    /*
> > +     * If QTest is enabled, keep the rcu_atfork enabled, since system processes
> > +     * may be forked testing purposes (e.g. fork-server based fuzzing)
> > +     */
> > +    if (!qtest_enabled()) {
> > +        rcu_disable_atfork();
> > +    }
> >  
> >      if (pid_file && !qemu_write_pidfile(pid_file, &err)) {
> >          error_reportf_err(err, "cannot create PID file: ");
> 
>  Hi Alexander,
> 
> I think this patch might maybe not work as expected: The qtest_enabled()
> has been added before configure_accelerators() is called in main(). So
> qtest_enabled() should always return "false" and thus
> rcu_disabled_fork() is still called in any case... could you please
> double-check whether it works for you and I just made a mistake, or
> whether this is a bug indeed?
Hi,
This is a problem.. I think we can work around this by calling
rcu_enable_atfork from the fuzzer, after qemu_init. I'll send a patch
soon.
Thanks
-Alex

>  Thanks,
>   Thomas
>
diff mbox series

Patch

diff --git a/vl.c b/vl.c
index bb77935f04..cf8e2d3ebb 100644
--- a/vl.c
+++ b/vl.c
@@ -3794,7 +3794,14 @@  void qemu_init(int argc, char **argv, char **envp)
     set_memory_options(&ram_slots, &maxram_size, machine_class);
 
     os_daemonize();
-    rcu_disable_atfork();
+
+    /*
+     * If QTest is enabled, keep the rcu_atfork enabled, since system processes
+     * may be forked testing purposes (e.g. fork-server based fuzzing)
+     */
+    if (!qtest_enabled()) {
+        rcu_disable_atfork();
+    }
 
     if (pid_file && !qemu_write_pidfile(pid_file, &err)) {
         error_reportf_err(err, "cannot create PID file: ");