diff mbox

[2/2] qtest: fix crash if SIGABRT during qtest_init()

Message ID 1394703694-3281-3-git-send-email-stefanha@redhat.com
State New
Headers show

Commit Message

Stefan Hajnoczi March 13, 2014, 9:41 a.m. UTC
If an assertion fails during qtest_init() the SIGABRT handler is
invoked.  This is the correct behavior since we need to kill the QEMU
process to avoid leaking it when the test dies.

The global_qtest pointer used by the SIGABRT handler is currently only
assigned after qtest_init() returns.  This results in a segfault if an
assertion failure occurs during qtest_init().

Move global_qtest assignment inside qtest_init().  Not pretty but let's
face it - the signal handler dependeds on global state.

Reported-by: Marcel Apfelbaum <marcel.a@redhat.com>
Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
---
 tests/libqtest.c | 3 ++-
 tests/libqtest.h | 4 +---
 2 files changed, 3 insertions(+), 4 deletions(-)

Comments

Marcel Apfelbaum March 13, 2014, 11:07 a.m. UTC | #1
On Thu, 2014-03-13 at 10:41 +0100, Stefan Hajnoczi wrote:
> If an assertion fails during qtest_init() the SIGABRT handler is
> invoked.  This is the correct behavior since we need to kill the QEMU
> process to avoid leaking it when the test dies.
> 
> The global_qtest pointer used by the SIGABRT handler is currently only
> assigned after qtest_init() returns.  This results in a segfault if an
> assertion failure occurs during qtest_init().
> 
> Move global_qtest assignment inside qtest_init().  Not pretty but let's
> face it - the signal handler dependeds on global state.
Looks OK to me, but it seems that it is symmetrical with my
patch: Mine checked for global_qtest that is not null (not hiding anything :()
and yours increases global_qtest's scope.

I understand why you preferred it this way, to ensure the QEMU instance
is killed, but as I stated before, from my point of view
qtest_init aborted <=> the qemu machine exited because of on error.
(but I might be wrong)

Thanks,
Marcel

> 
> Reported-by: Marcel Apfelbaum <marcel.a@redhat.com>
> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
> ---
>  tests/libqtest.c | 3 ++-
>  tests/libqtest.h | 4 +---
>  2 files changed, 3 insertions(+), 4 deletions(-)
> 
> diff --git a/tests/libqtest.c b/tests/libqtest.c
> index c9e78aa..f387662 100644
> --- a/tests/libqtest.c
> +++ b/tests/libqtest.c
> @@ -120,7 +120,7 @@ QTestState *qtest_init(const char *extra_args)
>      qemu_binary = getenv("QTEST_QEMU_BINARY");
>      g_assert(qemu_binary != NULL);
>  
> -    s = g_malloc(sizeof(*s));
> +    global_qtest = s = g_malloc(sizeof(*s));
>  
>      socket_path = g_strdup_printf("/tmp/qtest-%d.sock", getpid());
>      qmp_socket_path = g_strdup_printf("/tmp/qtest-%d.qmp", getpid());
> @@ -181,6 +181,7 @@ QTestState *qtest_init(const char *extra_args)
>  void qtest_quit(QTestState *s)
>  {
>      sigaction(SIGABRT, &s->sigact_old, NULL);
> +    global_qtest = NULL;
>  
>      kill_qemu(s);
>      close(s->fd);
> diff --git a/tests/libqtest.h b/tests/libqtest.h
> index 9deebdc..7e23a4e 100644
> --- a/tests/libqtest.h
> +++ b/tests/libqtest.h
> @@ -335,8 +335,7 @@ void qtest_add_func(const char *str, void (*fn));
>   */
>  static inline QTestState *qtest_start(const char *args)
>  {
> -    global_qtest = qtest_init(args);
> -    return global_qtest;
> +    return qtest_init(args);
>  }
>  
>  /**
> @@ -347,7 +346,6 @@ static inline QTestState *qtest_start(const char *args)
>  static inline void qtest_end(void)
>  {
>      qtest_quit(global_qtest);
> -    global_qtest = NULL;
>  }
>  
>  /**
Stefan Hajnoczi March 13, 2014, 12:58 p.m. UTC | #2
On Thu, Mar 13, 2014 at 01:07:05PM +0200, Marcel Apfelbaum wrote:
> On Thu, 2014-03-13 at 10:41 +0100, Stefan Hajnoczi wrote:
> > If an assertion fails during qtest_init() the SIGABRT handler is
> > invoked.  This is the correct behavior since we need to kill the QEMU
> > process to avoid leaking it when the test dies.
> > 
> > The global_qtest pointer used by the SIGABRT handler is currently only
> > assigned after qtest_init() returns.  This results in a segfault if an
> > assertion failure occurs during qtest_init().
> > 
> > Move global_qtest assignment inside qtest_init().  Not pretty but let's
> > face it - the signal handler dependeds on global state.
> Looks OK to me, but it seems that it is symmetrical with my
> patch: Mine checked for global_qtest that is not null (not hiding anything :()
> and yours increases global_qtest's scope.
> 
> I understand why you preferred it this way, to ensure the QEMU instance
> is killed, but as I stated before, from my point of view
> qtest_init aborted <=> the qemu machine exited because of on error.
> (but I might be wrong)

Think about this case:

If we hit an assertion failure in qtest_init() because of socket errors
(e.g. QEMU ran for a little bit but closed the socket while we were
negotiating), then we *do* need to kill the QEMU process.

Stefan
Andreas Färber March 13, 2014, 8:10 p.m. UTC | #3
Am 13.03.2014 10:41, schrieb Stefan Hajnoczi:
> If an assertion fails during qtest_init() the SIGABRT handler is
> invoked.  This is the correct behavior since we need to kill the QEMU
> process to avoid leaking it when the test dies.
> 
> The global_qtest pointer used by the SIGABRT handler is currently only
> assigned after qtest_init() returns.  This results in a segfault if an
> assertion failure occurs during qtest_init().
> 
> Move global_qtest assignment inside qtest_init().  Not pretty but let's
> face it - the signal handler dependeds on global state.
> 
> Reported-by: Marcel Apfelbaum <marcel.a@redhat.com>
> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
> ---
>  tests/libqtest.c | 3 ++-
>  tests/libqtest.h | 4 +---
>  2 files changed, 3 insertions(+), 4 deletions(-)

Thanks, applied to qom-next (with typo fix):
https://github.com/afaerber/qemu-cpu/commits/qom-next

Andreas
Markus Armbruster March 27, 2014, 1:09 p.m. UTC | #4
Reply after commit, sorry.

Stefan Hajnoczi <stefanha@redhat.com> writes:

> If an assertion fails during qtest_init() the SIGABRT handler is
> invoked.  This is the correct behavior since we need to kill the QEMU
> process to avoid leaking it when the test dies.
>
> The global_qtest pointer used by the SIGABRT handler is currently only
> assigned after qtest_init() returns.  This results in a segfault if an
> assertion failure occurs during qtest_init().
>
> Move global_qtest assignment inside qtest_init().  Not pretty but let's
> face it - the signal handler dependeds on global state.
>
> Reported-by: Marcel Apfelbaum <marcel.a@redhat.com>
> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
> ---
>  tests/libqtest.c | 3 ++-
>  tests/libqtest.h | 4 +---
>  2 files changed, 3 insertions(+), 4 deletions(-)
>
> diff --git a/tests/libqtest.c b/tests/libqtest.c
> index c9e78aa..f387662 100644
> --- a/tests/libqtest.c
> +++ b/tests/libqtest.c
> @@ -120,7 +120,7 @@ QTestState *qtest_init(const char *extra_args)
>      qemu_binary = getenv("QTEST_QEMU_BINARY");
>      g_assert(qemu_binary != NULL);
>  
> -    s = g_malloc(sizeof(*s));
> +    global_qtest = s = g_malloc(sizeof(*s));
>  
>      socket_path = g_strdup_printf("/tmp/qtest-%d.sock", getpid());
>      qmp_socket_path = g_strdup_printf("/tmp/qtest-%d.qmp", getpid());
> @@ -181,6 +181,7 @@ QTestState *qtest_init(const char *extra_args)
>  void qtest_quit(QTestState *s)
>  {
>      sigaction(SIGABRT, &s->sigact_old, NULL);
> +    global_qtest = NULL;
>  
>      kill_qemu(s);
>      close(s->fd);
> diff --git a/tests/libqtest.h b/tests/libqtest.h
> index 9deebdc..7e23a4e 100644
> --- a/tests/libqtest.h
> +++ b/tests/libqtest.h
> @@ -335,8 +335,7 @@ void qtest_add_func(const char *str, void (*fn));
>   */
>  static inline QTestState *qtest_start(const char *args)
>  {
> -    global_qtest = qtest_init(args);
> -    return global_qtest;
> +    return qtest_init(args);
>  }
>  
>  /**
> @@ -347,7 +346,6 @@ static inline QTestState *qtest_start(const char *args)
>  static inline void qtest_end(void)
>  {
>      qtest_quit(global_qtest);
> -    global_qtest = NULL;
>  }
>  
>  /**

Before this patch, the libqtest API could theoretically support multiple
simultaneous instances of QTestState.  This patch kills that option,
doesn't it?

If yes: fine with me, we don't need it anyway.  But shouldn't we clean
up the API then?  It typically provides a function taking a QTestState
parameter, and a wrapper that passes global_qtest.  If global_qtest is
the only QTestState we can have, having the former function is
pointless.
Andreas Färber March 27, 2014, 1:12 p.m. UTC | #5
Am 27.03.2014 14:09, schrieb Markus Armbruster:
> Reply after commit, sorry.
> 
> Stefan Hajnoczi <stefanha@redhat.com> writes:
> 
>> If an assertion fails during qtest_init() the SIGABRT handler is
>> invoked.  This is the correct behavior since we need to kill the QEMU
>> process to avoid leaking it when the test dies.
>>
>> The global_qtest pointer used by the SIGABRT handler is currently only
>> assigned after qtest_init() returns.  This results in a segfault if an
>> assertion failure occurs during qtest_init().
>>
>> Move global_qtest assignment inside qtest_init().  Not pretty but let's
>> face it - the signal handler dependeds on global state.
>>
>> Reported-by: Marcel Apfelbaum <marcel.a@redhat.com>
>> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
>> ---
>>  tests/libqtest.c | 3 ++-
>>  tests/libqtest.h | 4 +---
>>  2 files changed, 3 insertions(+), 4 deletions(-)
>>
>> diff --git a/tests/libqtest.c b/tests/libqtest.c
>> index c9e78aa..f387662 100644
>> --- a/tests/libqtest.c
>> +++ b/tests/libqtest.c
>> @@ -120,7 +120,7 @@ QTestState *qtest_init(const char *extra_args)
>>      qemu_binary = getenv("QTEST_QEMU_BINARY");
>>      g_assert(qemu_binary != NULL);
>>  
>> -    s = g_malloc(sizeof(*s));
>> +    global_qtest = s = g_malloc(sizeof(*s));
>>  
>>      socket_path = g_strdup_printf("/tmp/qtest-%d.sock", getpid());
>>      qmp_socket_path = g_strdup_printf("/tmp/qtest-%d.qmp", getpid());
>> @@ -181,6 +181,7 @@ QTestState *qtest_init(const char *extra_args)
>>  void qtest_quit(QTestState *s)
>>  {
>>      sigaction(SIGABRT, &s->sigact_old, NULL);
>> +    global_qtest = NULL;
>>  
>>      kill_qemu(s);
>>      close(s->fd);
>> diff --git a/tests/libqtest.h b/tests/libqtest.h
>> index 9deebdc..7e23a4e 100644
>> --- a/tests/libqtest.h
>> +++ b/tests/libqtest.h
>> @@ -335,8 +335,7 @@ void qtest_add_func(const char *str, void (*fn));
>>   */
>>  static inline QTestState *qtest_start(const char *args)
>>  {
>> -    global_qtest = qtest_init(args);
>> -    return global_qtest;
>> +    return qtest_init(args);
>>  }
>>  
>>  /**
>> @@ -347,7 +346,6 @@ static inline QTestState *qtest_start(const char *args)
>>  static inline void qtest_end(void)
>>  {
>>      qtest_quit(global_qtest);
>> -    global_qtest = NULL;
>>  }
>>  
>>  /**
> 
> Before this patch, the libqtest API could theoretically support multiple
> simultaneous instances of QTestState.  This patch kills that option,
> doesn't it?

Ouch, I thought I had looked out for that...

> 
> If yes: fine with me, we don't need it anyway.

We do. Migration and ivshmem are examples that need two machines - might
explain why my ivshmem-test was behaving unexpectedly.

Apart from reverting, what are our options?

Regards,
Andreas

>  But shouldn't we clean
> up the API then?  It typically provides a function taking a QTestState
> parameter, and a wrapper that passes global_qtest.  If global_qtest is
> the only QTestState we can have, having the former function is
> pointless.
>
Marcel Apfelbaum March 27, 2014, 1:34 p.m. UTC | #6
On Thu, 2014-03-27 at 14:12 +0100, Andreas Färber wrote:
> Am 27.03.2014 14:09, schrieb Markus Armbruster:
> > Reply after commit, sorry.
> > 
> > Stefan Hajnoczi <stefanha@redhat.com> writes:
> > 
> >> If an assertion fails during qtest_init() the SIGABRT handler is
> >> invoked.  This is the correct behavior since we need to kill the QEMU
> >> process to avoid leaking it when the test dies.
> >>
> >> The global_qtest pointer used by the SIGABRT handler is currently only
> >> assigned after qtest_init() returns.  This results in a segfault if an
> >> assertion failure occurs during qtest_init().
> >>
> >> Move global_qtest assignment inside qtest_init().  Not pretty but let's
> >> face it - the signal handler dependeds on global state.
> >>
> >> Reported-by: Marcel Apfelbaum <marcel.a@redhat.com>
> >> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
> >> ---
> >>  tests/libqtest.c | 3 ++-
> >>  tests/libqtest.h | 4 +---
> >>  2 files changed, 3 insertions(+), 4 deletions(-)
> >>
> >> diff --git a/tests/libqtest.c b/tests/libqtest.c
> >> index c9e78aa..f387662 100644
> >> --- a/tests/libqtest.c
> >> +++ b/tests/libqtest.c
> >> @@ -120,7 +120,7 @@ QTestState *qtest_init(const char *extra_args)
> >>      qemu_binary = getenv("QTEST_QEMU_BINARY");
> >>      g_assert(qemu_binary != NULL);
> >>  
> >> -    s = g_malloc(sizeof(*s));
> >> +    global_qtest = s = g_malloc(sizeof(*s));
> >>  
> >>      socket_path = g_strdup_printf("/tmp/qtest-%d.sock", getpid());
> >>      qmp_socket_path = g_strdup_printf("/tmp/qtest-%d.qmp", getpid());
> >> @@ -181,6 +181,7 @@ QTestState *qtest_init(const char *extra_args)
> >>  void qtest_quit(QTestState *s)
> >>  {
> >>      sigaction(SIGABRT, &s->sigact_old, NULL);
> >> +    global_qtest = NULL;
> >>  
> >>      kill_qemu(s);
> >>      close(s->fd);
> >> diff --git a/tests/libqtest.h b/tests/libqtest.h
> >> index 9deebdc..7e23a4e 100644
> >> --- a/tests/libqtest.h
> >> +++ b/tests/libqtest.h
> >> @@ -335,8 +335,7 @@ void qtest_add_func(const char *str, void (*fn));
> >>   */
> >>  static inline QTestState *qtest_start(const char *args)
> >>  {
> >> -    global_qtest = qtest_init(args);
> >> -    return global_qtest;
> >> +    return qtest_init(args);
> >>  }
> >>  
> >>  /**
> >> @@ -347,7 +346,6 @@ static inline QTestState *qtest_start(const char *args)
> >>  static inline void qtest_end(void)
> >>  {
> >>      qtest_quit(global_qtest);
> >> -    global_qtest = NULL;
> >>  }
> >>  
> >>  /**
> > 
> > Before this patch, the libqtest API could theoretically support multiple
> > simultaneous instances of QTestState.  This patch kills that option,
> > doesn't it?
> 
> Ouch, I thought I had looked out for that...
> 
> > 
> > If yes: fine with me, we don't need it anyway.
> 
> We do. Migration and ivshmem are examples that need two machines - might
> explain why my ivshmem-test was behaving unexpectedly.
> 
> Apart from reverting, what are our options?
The problem is in 'kill_qemu' function, which is called from
SIGABRT signal handler.  The later needs to know the QTestState
in order to kill the QEMU process.

Without this patch, kill_qemu will cause a segfault because of:
static void kill_qemu(QTestState *s)
{
    if (s->qemu_pid != -1) {
...
s can be NULL if there is an assert in qtest_init.

I suppose we can find a different approach, like keeping
the qemu_pid(s) in another statically defined struct.
 
Thanks,
Marcel

 
> 
> Regards,
> Andreas
> 
> >  But shouldn't we clean
> > up the API then?  It typically provides a function taking a QTestState
> > parameter, and a wrapper that passes global_qtest.  If global_qtest is
> > the only QTestState we can have, having the former function is
> > pointless.
> > 
> 
>
Stefan Hajnoczi March 27, 2014, 1:36 p.m. UTC | #7
On Thu, Mar 27, 2014 at 2:12 PM, Andreas Färber <afaerber@suse.de> wrote:
>> Before this patch, the libqtest API could theoretically support multiple
>> simultaneous instances of QTestState.  This patch kills that option,
>> doesn't it?
>
> Ouch, I thought I had looked out for that...
>
>>
>> If yes: fine with me, we don't need it anyway.
>
> We do. Migration and ivshmem are examples that need two machines - might
> explain why my ivshmem-test was behaving unexpectedly.
>
> Apart from reverting, what are our options?

Argh, I wasn't aware some tests run with two separate instances.

We can implement more elaborate error handling, for example an
atexit(3)-style atabort mechanism.  This way, each instance can get
its callback.

Stefan
Stefan Hajnoczi March 27, 2014, 1:37 p.m. UTC | #8
On Thu, Mar 27, 2014 at 2:34 PM, Marcel Apfelbaum <marcel.a@redhat.com> wrote:
> On Thu, 2014-03-27 at 14:12 +0100, Andreas Färber wrote:
>> Am 27.03.2014 14:09, schrieb Markus Armbruster:
>> > Reply after commit, sorry.
>> >
>> > Stefan Hajnoczi <stefanha@redhat.com> writes:
>> >
>> >> If an assertion fails during qtest_init() the SIGABRT handler is
>> >> invoked.  This is the correct behavior since we need to kill the QEMU
>> >> process to avoid leaking it when the test dies.
>> >>
>> >> The global_qtest pointer used by the SIGABRT handler is currently only
>> >> assigned after qtest_init() returns.  This results in a segfault if an
>> >> assertion failure occurs during qtest_init().
>> >>
>> >> Move global_qtest assignment inside qtest_init().  Not pretty but let's
>> >> face it - the signal handler dependeds on global state.
>> >>
>> >> Reported-by: Marcel Apfelbaum <marcel.a@redhat.com>
>> >> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
>> >> ---
>> >>  tests/libqtest.c | 3 ++-
>> >>  tests/libqtest.h | 4 +---
>> >>  2 files changed, 3 insertions(+), 4 deletions(-)
>> >>
>> >> diff --git a/tests/libqtest.c b/tests/libqtest.c
>> >> index c9e78aa..f387662 100644
>> >> --- a/tests/libqtest.c
>> >> +++ b/tests/libqtest.c
>> >> @@ -120,7 +120,7 @@ QTestState *qtest_init(const char *extra_args)
>> >>      qemu_binary = getenv("QTEST_QEMU_BINARY");
>> >>      g_assert(qemu_binary != NULL);
>> >>
>> >> -    s = g_malloc(sizeof(*s));
>> >> +    global_qtest = s = g_malloc(sizeof(*s));
>> >>
>> >>      socket_path = g_strdup_printf("/tmp/qtest-%d.sock", getpid());
>> >>      qmp_socket_path = g_strdup_printf("/tmp/qtest-%d.qmp", getpid());
>> >> @@ -181,6 +181,7 @@ QTestState *qtest_init(const char *extra_args)
>> >>  void qtest_quit(QTestState *s)
>> >>  {
>> >>      sigaction(SIGABRT, &s->sigact_old, NULL);
>> >> +    global_qtest = NULL;
>> >>
>> >>      kill_qemu(s);
>> >>      close(s->fd);
>> >> diff --git a/tests/libqtest.h b/tests/libqtest.h
>> >> index 9deebdc..7e23a4e 100644
>> >> --- a/tests/libqtest.h
>> >> +++ b/tests/libqtest.h
>> >> @@ -335,8 +335,7 @@ void qtest_add_func(const char *str, void (*fn));
>> >>   */
>> >>  static inline QTestState *qtest_start(const char *args)
>> >>  {
>> >> -    global_qtest = qtest_init(args);
>> >> -    return global_qtest;
>> >> +    return qtest_init(args);
>> >>  }
>> >>
>> >>  /**
>> >> @@ -347,7 +346,6 @@ static inline QTestState *qtest_start(const char *args)
>> >>  static inline void qtest_end(void)
>> >>  {
>> >>      qtest_quit(global_qtest);
>> >> -    global_qtest = NULL;
>> >>  }
>> >>
>> >>  /**
>> >
>> > Before this patch, the libqtest API could theoretically support multiple
>> > simultaneous instances of QTestState.  This patch kills that option,
>> > doesn't it?
>>
>> Ouch, I thought I had looked out for that...
>>
>> >
>> > If yes: fine with me, we don't need it anyway.
>>
>> We do. Migration and ivshmem are examples that need two machines - might
>> explain why my ivshmem-test was behaving unexpectedly.
>>
>> Apart from reverting, what are our options?
> The problem is in 'kill_qemu' function, which is called from
> SIGABRT signal handler.  The later needs to know the QTestState
> in order to kill the QEMU process.
>
> Without this patch, kill_qemu will cause a segfault because of:
> static void kill_qemu(QTestState *s)
> {
>     if (s->qemu_pid != -1) {
> ...
> s can be NULL if there is an assert in qtest_init.
>
> I suppose we can find a different approach, like keeping
> the qemu_pid(s) in another statically defined struct.

We can keep a global linked list of QEMU pids.

Stefan
Markus Armbruster March 27, 2014, 2:02 p.m. UTC | #9
Stefan Hajnoczi <stefanha@gmail.com> writes:

> On Thu, Mar 27, 2014 at 2:34 PM, Marcel Apfelbaum <marcel.a@redhat.com> wrote:
>> On Thu, 2014-03-27 at 14:12 +0100, Andreas Färber wrote:
>>> Am 27.03.2014 14:09, schrieb Markus Armbruster:
>>> > Reply after commit, sorry.
>>> >
>>> > Stefan Hajnoczi <stefanha@redhat.com> writes:
>>> >
>>> >> If an assertion fails during qtest_init() the SIGABRT handler is
>>> >> invoked.  This is the correct behavior since we need to kill the QEMU
>>> >> process to avoid leaking it when the test dies.
>>> >>
>>> >> The global_qtest pointer used by the SIGABRT handler is currently only
>>> >> assigned after qtest_init() returns.  This results in a segfault if an
>>> >> assertion failure occurs during qtest_init().
>>> >>
>>> >> Move global_qtest assignment inside qtest_init().  Not pretty but let's
>>> >> face it - the signal handler dependeds on global state.
>>> >>
>>> >> Reported-by: Marcel Apfelbaum <marcel.a@redhat.com>
>>> >> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
>>> >> ---
>>> >>  tests/libqtest.c | 3 ++-
>>> >>  tests/libqtest.h | 4 +---
>>> >>  2 files changed, 3 insertions(+), 4 deletions(-)
>>> >>
>>> >> diff --git a/tests/libqtest.c b/tests/libqtest.c
>>> >> index c9e78aa..f387662 100644
>>> >> --- a/tests/libqtest.c
>>> >> +++ b/tests/libqtest.c
>>> >> @@ -120,7 +120,7 @@ QTestState *qtest_init(const char *extra_args)
>>> >>      qemu_binary = getenv("QTEST_QEMU_BINARY");
>>> >>      g_assert(qemu_binary != NULL);
>>> >>
>>> >> -    s = g_malloc(sizeof(*s));
>>> >> +    global_qtest = s = g_malloc(sizeof(*s));
>>> >>
>>> >>      socket_path = g_strdup_printf("/tmp/qtest-%d.sock", getpid());
>>> >>      qmp_socket_path = g_strdup_printf("/tmp/qtest-%d.qmp", getpid());
>>> >> @@ -181,6 +181,7 @@ QTestState *qtest_init(const char *extra_args)
>>> >>  void qtest_quit(QTestState *s)
>>> >>  {
>>> >>      sigaction(SIGABRT, &s->sigact_old, NULL);
>>> >> +    global_qtest = NULL;
>>> >>
>>> >>      kill_qemu(s);
>>> >>      close(s->fd);
>>> >> diff --git a/tests/libqtest.h b/tests/libqtest.h
>>> >> index 9deebdc..7e23a4e 100644
>>> >> --- a/tests/libqtest.h
>>> >> +++ b/tests/libqtest.h
>>> >> @@ -335,8 +335,7 @@ void qtest_add_func(const char *str, void (*fn));
>>> >>   */
>>> >>  static inline QTestState *qtest_start(const char *args)
>>> >>  {
>>> >> -    global_qtest = qtest_init(args);
>>> >> -    return global_qtest;
>>> >> +    return qtest_init(args);
>>> >>  }
>>> >>
>>> >>  /**
>>> >> @@ -347,7 +346,6 @@ static inline QTestState *qtest_start(const char *args)
>>> >>  static inline void qtest_end(void)
>>> >>  {
>>> >>      qtest_quit(global_qtest);
>>> >> -    global_qtest = NULL;
>>> >>  }
>>> >>
>>> >>  /**
>>> >
>>> > Before this patch, the libqtest API could theoretically support multiple
>>> > simultaneous instances of QTestState.  This patch kills that option,
>>> > doesn't it?
>>>
>>> Ouch, I thought I had looked out for that...
>>>
>>> >
>>> > If yes: fine with me, we don't need it anyway.
>>>
>>> We do. Migration and ivshmem are examples that need two machines - might
>>> explain why my ivshmem-test was behaving unexpectedly.
>>>
>>> Apart from reverting, what are our options?
>> The problem is in 'kill_qemu' function, which is called from
>> SIGABRT signal handler.  The later needs to know the QTestState
>> in order to kill the QEMU process.
>>
>> Without this patch, kill_qemu will cause a segfault because of:
>> static void kill_qemu(QTestState *s)
>> {
>>     if (s->qemu_pid != -1) {
>> ...
>> s can be NULL if there is an assert in qtest_init.
>>
>> I suppose we can find a different approach, like keeping
>> the qemu_pid(s) in another statically defined struct.
>
> We can keep a global linked list of QEMU pids.

What about a global list of QTestState?
Stefan Hajnoczi March 27, 2014, 2:03 p.m. UTC | #10
On Thu, Mar 27, 2014 at 3:02 PM, Markus Armbruster <armbru@redhat.com> wrote:
> Stefan Hajnoczi <stefanha@gmail.com> writes:
>
>> On Thu, Mar 27, 2014 at 2:34 PM, Marcel Apfelbaum <marcel.a@redhat.com> wrote:
>>> On Thu, 2014-03-27 at 14:12 +0100, Andreas Färber wrote:
>>>> Am 27.03.2014 14:09, schrieb Markus Armbruster:
>>>> > Reply after commit, sorry.
>>>> >
>>>> > Stefan Hajnoczi <stefanha@redhat.com> writes:
>>>> >
>>>> >> If an assertion fails during qtest_init() the SIGABRT handler is
>>>> >> invoked.  This is the correct behavior since we need to kill the QEMU
>>>> >> process to avoid leaking it when the test dies.
>>>> >>
>>>> >> The global_qtest pointer used by the SIGABRT handler is currently only
>>>> >> assigned after qtest_init() returns.  This results in a segfault if an
>>>> >> assertion failure occurs during qtest_init().
>>>> >>
>>>> >> Move global_qtest assignment inside qtest_init().  Not pretty but let's
>>>> >> face it - the signal handler dependeds on global state.
>>>> >>
>>>> >> Reported-by: Marcel Apfelbaum <marcel.a@redhat.com>
>>>> >> Signed-off-by: Stefan Hajnoczi <stefanha@redhat.com>
>>>> >> ---
>>>> >>  tests/libqtest.c | 3 ++-
>>>> >>  tests/libqtest.h | 4 +---
>>>> >>  2 files changed, 3 insertions(+), 4 deletions(-)
>>>> >>
>>>> >> diff --git a/tests/libqtest.c b/tests/libqtest.c
>>>> >> index c9e78aa..f387662 100644
>>>> >> --- a/tests/libqtest.c
>>>> >> +++ b/tests/libqtest.c
>>>> >> @@ -120,7 +120,7 @@ QTestState *qtest_init(const char *extra_args)
>>>> >>      qemu_binary = getenv("QTEST_QEMU_BINARY");
>>>> >>      g_assert(qemu_binary != NULL);
>>>> >>
>>>> >> -    s = g_malloc(sizeof(*s));
>>>> >> +    global_qtest = s = g_malloc(sizeof(*s));
>>>> >>
>>>> >>      socket_path = g_strdup_printf("/tmp/qtest-%d.sock", getpid());
>>>> >>      qmp_socket_path = g_strdup_printf("/tmp/qtest-%d.qmp", getpid());
>>>> >> @@ -181,6 +181,7 @@ QTestState *qtest_init(const char *extra_args)
>>>> >>  void qtest_quit(QTestState *s)
>>>> >>  {
>>>> >>      sigaction(SIGABRT, &s->sigact_old, NULL);
>>>> >> +    global_qtest = NULL;
>>>> >>
>>>> >>      kill_qemu(s);
>>>> >>      close(s->fd);
>>>> >> diff --git a/tests/libqtest.h b/tests/libqtest.h
>>>> >> index 9deebdc..7e23a4e 100644
>>>> >> --- a/tests/libqtest.h
>>>> >> +++ b/tests/libqtest.h
>>>> >> @@ -335,8 +335,7 @@ void qtest_add_func(const char *str, void (*fn));
>>>> >>   */
>>>> >>  static inline QTestState *qtest_start(const char *args)
>>>> >>  {
>>>> >> -    global_qtest = qtest_init(args);
>>>> >> -    return global_qtest;
>>>> >> +    return qtest_init(args);
>>>> >>  }
>>>> >>
>>>> >>  /**
>>>> >> @@ -347,7 +346,6 @@ static inline QTestState *qtest_start(const char *args)
>>>> >>  static inline void qtest_end(void)
>>>> >>  {
>>>> >>      qtest_quit(global_qtest);
>>>> >> -    global_qtest = NULL;
>>>> >>  }
>>>> >>
>>>> >>  /**
>>>> >
>>>> > Before this patch, the libqtest API could theoretically support multiple
>>>> > simultaneous instances of QTestState.  This patch kills that option,
>>>> > doesn't it?
>>>>
>>>> Ouch, I thought I had looked out for that...
>>>>
>>>> >
>>>> > If yes: fine with me, we don't need it anyway.
>>>>
>>>> We do. Migration and ivshmem are examples that need two machines - might
>>>> explain why my ivshmem-test was behaving unexpectedly.
>>>>
>>>> Apart from reverting, what are our options?
>>> The problem is in 'kill_qemu' function, which is called from
>>> SIGABRT signal handler.  The later needs to know the QTestState
>>> in order to kill the QEMU process.
>>>
>>> Without this patch, kill_qemu will cause a segfault because of:
>>> static void kill_qemu(QTestState *s)
>>> {
>>>     if (s->qemu_pid != -1) {
>>> ...
>>> s can be NULL if there is an assert in qtest_init.
>>>
>>> I suppose we can find a different approach, like keeping
>>> the qemu_pid(s) in another statically defined struct.
>>
>> We can keep a global linked list of QEMU pids.
>
> What about a global list of QTestState?

Yes, that's exactly what I ended up implementing.  Sending patch.

Stefan
diff mbox

Patch

diff --git a/tests/libqtest.c b/tests/libqtest.c
index c9e78aa..f387662 100644
--- a/tests/libqtest.c
+++ b/tests/libqtest.c
@@ -120,7 +120,7 @@  QTestState *qtest_init(const char *extra_args)
     qemu_binary = getenv("QTEST_QEMU_BINARY");
     g_assert(qemu_binary != NULL);
 
-    s = g_malloc(sizeof(*s));
+    global_qtest = s = g_malloc(sizeof(*s));
 
     socket_path = g_strdup_printf("/tmp/qtest-%d.sock", getpid());
     qmp_socket_path = g_strdup_printf("/tmp/qtest-%d.qmp", getpid());
@@ -181,6 +181,7 @@  QTestState *qtest_init(const char *extra_args)
 void qtest_quit(QTestState *s)
 {
     sigaction(SIGABRT, &s->sigact_old, NULL);
+    global_qtest = NULL;
 
     kill_qemu(s);
     close(s->fd);
diff --git a/tests/libqtest.h b/tests/libqtest.h
index 9deebdc..7e23a4e 100644
--- a/tests/libqtest.h
+++ b/tests/libqtest.h
@@ -335,8 +335,7 @@  void qtest_add_func(const char *str, void (*fn));
  */
 static inline QTestState *qtest_start(const char *args)
 {
-    global_qtest = qtest_init(args);
-    return global_qtest;
+    return qtest_init(args);
 }
 
 /**
@@ -347,7 +346,6 @@  static inline QTestState *qtest_start(const char *args)
 static inline void qtest_end(void)
 {
     qtest_quit(global_qtest);
-    global_qtest = NULL;
 }
 
 /**