diff mbox

[2/4] qapi: output visitor crashes qemu if it encounters a NULL value

Message ID 1399473780-20374-3-git-send-email-marcel.a@redhat.com
State New
Headers show

Commit Message

Marcel Apfelbaum May 7, 2014, 2:42 p.m. UTC
A NULL value is not added to visitor's stack, but there
is no check for that when the visitor tries to return
that value, leading to Qemu crash.

Reviewed-by: Eric Blake <eblake@redhat.com>
Signed-off-by: Marcel Apfelbaum <marcel.a@redhat.com>
---
 qapi/qmp-output-visitor.c | 5 +++++
 1 file changed, 5 insertions(+)

Comments

Andreas Färber May 13, 2014, 5:36 p.m. UTC | #1
Am 07.05.2014 16:42, schrieb Marcel Apfelbaum:
> A NULL value is not added to visitor's stack, but there
> is no check for that when the visitor tries to return
> that value, leading to Qemu crash.
> 
> Reviewed-by: Eric Blake <eblake@redhat.com>
> Signed-off-by: Marcel Apfelbaum <marcel.a@redhat.com>

Where does the Rb come from on this v1? Is it in any tree already?

Regards,
Andreas
Eric Blake May 13, 2014, 7:08 p.m. UTC | #2
On 05/13/2014 11:36 AM, Andreas Färber wrote:
> Am 07.05.2014 16:42, schrieb Marcel Apfelbaum:
>> A NULL value is not added to visitor's stack, but there
>> is no check for that when the visitor tries to return
>> that value, leading to Qemu crash.
>>
>> Reviewed-by: Eric Blake <eblake@redhat.com>
>> Signed-off-by: Marcel Apfelbaum <marcel.a@redhat.com>
> 
> Where does the Rb come from on this v1? Is it in any tree already?
> 

The (weak) R-b was here:
https://lists.gnu.org/archive/html/qemu-devel/2014-02/msg02861.html
Andreas Färber May 14, 2014, 5 p.m. UTC | #3
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Am 13.05.2014 21:08, schrieb Eric Blake:
> On 05/13/2014 11:36 AM, Andreas Färber wrote:
>> Am 07.05.2014 16:42, schrieb Marcel Apfelbaum:
>>> A NULL value is not added to visitor's stack, but there is no
>>> check for that when the visitor tries to return that value,
>>> leading to Qemu crash.
>>> 
>>> Reviewed-by: Eric Blake <eblake@redhat.com> Signed-off-by:
>>> Marcel Apfelbaum <marcel.a@redhat.com>
>> 
>> Where does the Rb come from on this v1? Is it in any tree
>> already?
>> 
> 
> The (weak) R-b was here: 
> https://lists.gnu.org/archive/html/qemu-devel/2014-02/msg02861.html

Thanks.
> 
So Luiz was okay with it too, but his last message seems to be
indicating this needs to be fixed somewhere else, too:

https://lists.gnu.org/archive/html/qemu-devel/2014-02/msg05228.html
https://lists.gnu.org/archive/html/qemu-devel/2014-03/msg00217.html

Can/should that be addressed as a follow-up? Or is there a test case
that breaks?

Regards,
Andreas

- -- 
SUSE LINUX Products GmbH, Maxfeldstr. 5, 90409 Nürnberg, Germany
GF: Jeff Hawn, Jennifer Guild, Felix Imendörffer; HRB 16746 AG Nürnberg
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v2.0.22 (GNU/Linux)

iQIcBAEBAgAGBQJTc6EeAAoJEPou0S0+fgE/EZEP/0Demtm8RD/bDXkgSZYq8H7C
6FuPxjrkx2F/5Cot36VWeyGWB9A4GX3xH8+OJAsWdPyiq2ChOVXub/knlfedvBKP
et8Y2uub5FoLV3l8fPOy8CCO97erF4ZpZ28L1Br7jeUO+gR/b0/a0c2fj3V5SeMO
vgY6or46nBteVTyYtkAVx4Ne3VddNjiJJrgTASJkIFCFicG1VSQn6+YT89qmEx1i
JL8bbh5UNQ/hSFgQzl88mge9Udv3Zjp1zmbochhdazmoG6dHy7g0A7TuDkhIqnLX
p4uEzerjJo+24z6rX50CRj4zr6jnZtMdSWJF25+hvRGOz0UsHQ8D5OTZfs7qwBN3
Nkzqx/tMGENcNOnivJ3CGu7GhlBBEK3moK885dlZQ8p7w0+agNQDrR0CSMJlI4ck
8LgxcDNfGgnK948ZZNPTAlqOR7ZJV1dNE1fHulPVNS1xSdk2h48xOn8NNncRAwyq
bs+45pxwTjp+L5WLF7XLKsVF1hYy4wi5pBhT0w+xTymYoP5aHKRmIhogvJY1b6Wf
5isZQUHUeGiepZ26smYROrrQhO4Q2ZqHKjp3yUNfhHaP7bdZYh9ykPQQZra+9+K/
lXNmzFIj2L+kdl5R6/pn9h3nZDjrWxGd+IreVASseW0RuLZXDivOtBKZJ6ZBAqQd
Dx5lbtVnihJl9KRRxQw2
=+B1b
-----END PGP SIGNATURE-----
Marcel Apfelbaum May 14, 2014, 5:29 p.m. UTC | #4
On Wed, 2014-05-14 at 19:00 +0200, Andreas Färber wrote:
> Am 13.05.2014 21:08, schrieb Eric Blake:
> > On 05/13/2014 11:36 AM, Andreas Färber wrote:
> >> Am 07.05.2014 16:42, schrieb Marcel Apfelbaum:
> >>> A NULL value is not added to visitor's stack, but there is no
> >>> check for that when the visitor tries to return that value,
> >>> leading to Qemu crash.
> >>> 
> >>> Reviewed-by: Eric Blake <eblake@redhat.com> Signed-off-by:
> >>> Marcel Apfelbaum <marcel.a@redhat.com>
> >> 
> >> Where does the Rb come from on this v1? Is it in any tree
> >> already?
> >> 
> > 
> > The (weak) R-b was here: 
> > https://lists.gnu.org/archive/html/qemu-devel/2014-02/msg02861.html
> 
> Thanks.
> > 
> So Luiz was okay with it too, but his last message seems to be
> indicating this needs to be fixed somewhere else, too:
> 
> https://lists.gnu.org/archive/html/qemu-devel/2014-02/msg05228.html
> https://lists.gnu.org/archive/html/qemu-devel/2014-03/msg00217.html
> 
> Can/should that be addressed as a follow-up? Or is there a test case
> that breaks?
Simple and "popular" test case: the user does not use the -kernel-cmdline parameter.
The patch is needed because otherwise the main function will fail
if no value is passed by the user to string parameters. 

Regarding Luiz's concern, it can be a follow-up as I am not aware of
any problem with that.

Thanks,
Marcel






> 
> Regards,
> Andreas
>
Luiz Capitulino May 14, 2014, 6:25 p.m. UTC | #5
On Wed, 14 May 2014 20:29:37 +0300
Marcel Apfelbaum <marcel.a@redhat.com> wrote:

> On Wed, 2014-05-14 at 19:00 +0200, Andreas Färber wrote:
> > Am 13.05.2014 21:08, schrieb Eric Blake:
> > > On 05/13/2014 11:36 AM, Andreas Färber wrote:
> > >> Am 07.05.2014 16:42, schrieb Marcel Apfelbaum:
> > >>> A NULL value is not added to visitor's stack, but there is no
> > >>> check for that when the visitor tries to return that value,
> > >>> leading to Qemu crash.
> > >>> 
> > >>> Reviewed-by: Eric Blake <eblake@redhat.com> Signed-off-by:
> > >>> Marcel Apfelbaum <marcel.a@redhat.com>
> > >> 
> > >> Where does the Rb come from on this v1? Is it in any tree
> > >> already?
> > >> 
> > > 
> > > The (weak) R-b was here: 
> > > https://lists.gnu.org/archive/html/qemu-devel/2014-02/msg02861.html
> > 
> > Thanks.
> > > 
> > So Luiz was okay with it too, but his last message seems to be
> > indicating this needs to be fixed somewhere else, too:
> > 
> > https://lists.gnu.org/archive/html/qemu-devel/2014-02/msg05228.html
> > https://lists.gnu.org/archive/html/qemu-devel/2014-03/msg00217.html
> > 
> > Can/should that be addressed as a follow-up? Or is there a test case
> > that breaks?
> Simple and "popular" test case: the user does not use the -kernel-cmdline parameter.
> The patch is needed because otherwise the main function will fail
> if no value is passed by the user to string parameters. 
> 
> Regarding Luiz's concern, it can be a follow-up as I am not aware of
> any problem with that.

My concern was that I wasn't sure if this is the right fix for the issue
or if it's papering over the real bug. I quickly checked the code and it
seemed to make sense, but I didn't have time to study it deeper.

We could ask Michael Roth or Anthony, but I wouldn't hold this series
because of that. Here's my ACK if you need it:

Acked-by: Luiz Capitulino <lcapitulino@redhat.com>
Markus Armbruster May 14, 2014, 7:51 p.m. UTC | #6
Luiz Capitulino <lcapitulino@redhat.com> writes:

> On Wed, 14 May 2014 20:29:37 +0300
> Marcel Apfelbaum <marcel.a@redhat.com> wrote:
>
>> On Wed, 2014-05-14 at 19:00 +0200, Andreas Färber wrote:
>> > Am 13.05.2014 21:08, schrieb Eric Blake:
>> > > On 05/13/2014 11:36 AM, Andreas Färber wrote:
>> > >> Am 07.05.2014 16:42, schrieb Marcel Apfelbaum:
>> > >>> A NULL value is not added to visitor's stack, but there is no
>> > >>> check for that when the visitor tries to return that value,
>> > >>> leading to Qemu crash.
>> > >>> 
>> > >>> Reviewed-by: Eric Blake <eblake@redhat.com> Signed-off-by:
>> > >>> Marcel Apfelbaum <marcel.a@redhat.com>
>> > >> 
>> > >> Where does the Rb come from on this v1? Is it in any tree
>> > >> already?
>> > >> 
>> > > 
>> > > The (weak) R-b was here: 
>> > > https://lists.gnu.org/archive/html/qemu-devel/2014-02/msg02861.html
>> > 
>> > Thanks.
>> > > 
>> > So Luiz was okay with it too, but his last message seems to be
>> > indicating this needs to be fixed somewhere else, too:
>> > 
>> > https://lists.gnu.org/archive/html/qemu-devel/2014-02/msg05228.html
>> > https://lists.gnu.org/archive/html/qemu-devel/2014-03/msg00217.html
>> > 
>> > Can/should that be addressed as a follow-up? Or is there a test case
>> > that breaks?
>> Simple and "popular" test case: the user does not use the
>> -kernel-cmdline parameter.
>> The patch is needed because otherwise the main function will fail
>> if no value is passed by the user to string parameters. 
>> 
>> Regarding Luiz's concern, it can be a follow-up as I am not aware of
>> any problem with that.
>
> My concern was that I wasn't sure if this is the right fix for the issue
> or if it's papering over the real bug. I quickly checked the code and it
> seemed to make sense, but I didn't have time to study it deeper.

I can have a look tomorrow.

> We could ask Michael Roth or Anthony, but I wouldn't hold this series
> because of that. Here's my ACK if you need it:
>
> Acked-by: Luiz Capitulino <lcapitulino@redhat.com>
Andreas Färber May 14, 2014, 8:26 p.m. UTC | #7
Am 14.05.2014 19:29, schrieb Marcel Apfelbaum:
> On Wed, 2014-05-14 at 19:00 +0200, Andreas Färber wrote:
>> Am 13.05.2014 21:08, schrieb Eric Blake:
>>> On 05/13/2014 11:36 AM, Andreas Färber wrote:
>>>> Am 07.05.2014 16:42, schrieb Marcel Apfelbaum:
>>>>> A NULL value is not added to visitor's stack, but there is no
>>>>> check for that when the visitor tries to return that value,
>>>>> leading to Qemu crash.
>>>>>
>>>>> Reviewed-by: Eric Blake <eblake@redhat.com> Signed-off-by:
>>>>> Marcel Apfelbaum <marcel.a@redhat.com>
>>>>
>>>> Where does the Rb come from on this v1? Is it in any tree
>>>> already?
>>>>
>>>
>>> The (weak) R-b was here: 
>>> https://lists.gnu.org/archive/html/qemu-devel/2014-02/msg02861.html
>>
>> Thanks.
>>>
>> So Luiz was okay with it too, but his last message seems to be
>> indicating this needs to be fixed somewhere else, too:
>>
>> https://lists.gnu.org/archive/html/qemu-devel/2014-02/msg05228.html
>> https://lists.gnu.org/archive/html/qemu-devel/2014-03/msg00217.html
>>
>> Can/should that be addressed as a follow-up? Or is there a test case
>> that breaks?
> Simple and "popular" test case: the user does not use the -kernel-cmdline parameter.

You had already mentioned a NULL -kernel. I was asking what test case
Luiz' qmp_output_last() would be about. :)

Cheers,
Andreas
Markus Armbruster May 15, 2014, 4:13 p.m. UTC | #8
Marcel Apfelbaum <marcel.a@redhat.com> writes:

> A NULL value is not added to visitor's stack, but there
> is no check for that when the visitor tries to return
> that value, leading to Qemu crash.
>
> Reviewed-by: Eric Blake <eblake@redhat.com>
> Signed-off-by: Marcel Apfelbaum <marcel.a@redhat.com>
> ---
>  qapi/qmp-output-visitor.c | 5 +++++
>  1 file changed, 5 insertions(+)
>
> diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
> index 74a5684..0562f49 100644
> --- a/qapi/qmp-output-visitor.c
> +++ b/qapi/qmp-output-visitor.c
> @@ -66,6 +66,11 @@ static QObject *qmp_output_pop(QmpOutputVisitor *qov)
>  static QObject *qmp_output_first(QmpOutputVisitor *qov)
>  {
>      QStackEntry *e = QTAILQ_LAST(&qov->stack, QStack);
> +
> +    if (!e) {
> +        return NULL;
> +    }
> +
>      return e->value;
>  }

Let's see how this thing works.

The visitor's mutable state is a QStack, which is stack of (QObject,
bool).  We can ignore the bool; it's just for qmp_output_next_list().

Visits start with an empty stack.  See qmp_output_visitor_new().

qmp_output_first() returns the object on the bottom of the stack.
qmp_output_last() returns the object on the top of the stack.

<rant>
When you implement a stack with a double-ended queue, you're totally
free to pick either end of the queue for top of stack.  You're also free
to name your functions accessing top and the bottom of the stack however
you like.  "Of course" the author picked queue end and function names
for maximum confusion:

    static QObject *qmp_output_first(QmpOutputVisitor *qov)
    {
        QStackEntry *e = QTAILQ_LAST(&qov->stack, QStack);
        return e->value;
    }

    static QObject *qmp_output_last(QmpOutputVisitor *qov)
    {
        QStackEntry *e = QTAILQ_FIRST(&qov->stack);
        return e->value;
    }

I hate you.
</rant>

The result of the visit sits at the bottom of the stack.  Empty stack,
null result.  See qmp_output_get_qobject().

Visiting a scalar type creates the appropriate scalar QObject, and
"adds" it.  We'll find out what "adding" means shortly.  See
qmp_output_type_{int,bool,str,number}().

Special case: null strings get converted to empty strings.  See
qmp_output_type_str().

Starting a struct visit creates a QDict, adds it, and pushes it onto the
stack.  Ending it pops it from the stack.  See
qmp_output_{start,end}_struct().

Starting a list visit creates a QList, adds it, and pushes it onto the
stack.  Ending it pops it from the stack.  See
qmp_output_{start,end}_list().

Visiting a list member does nothing interesting; see
qmp_output_next_list().  Aside: I suspect the GenericList traversal
stuff now done in every next_list() method should be done in the visitor
core instead.

Now let's figure out what it means to "add" an object.  This is
qmp_output_add_obj().

If the stack is still empty, the object is the root object, and it gets
pushed.

Else, if the object on top of the stack is a QDict, we're visiting a
struct.  Enter the object into the QDict.

Else, if the object on top of the stack is a QList, we're visiting a
list.  Append the object to the QList.

Else, the object on top of the stack must be scalar, and I think it must
be the root object.  We replace it by the object being added.  WTF?

This feels more complicated than it could be.  Anyway, how could a null
object end up at the bottom of the stack, so that qmp_output_first()
chokes on it?  I can't see that.

If it can get added, then why can it be seen only by qmp_output_first(),
but not by qmp_output_last() and qmp_output_pop()?
Michael Roth May 15, 2014, 4:27 p.m. UTC | #9
Quoting Markus Armbruster (2014-05-15 11:13:09)
> Marcel Apfelbaum <marcel.a@redhat.com> writes:
> 
> > A NULL value is not added to visitor's stack, but there
> > is no check for that when the visitor tries to return
> > that value, leading to Qemu crash.
> >
> > Reviewed-by: Eric Blake <eblake@redhat.com>
> > Signed-off-by: Marcel Apfelbaum <marcel.a@redhat.com>
> > ---
> >  qapi/qmp-output-visitor.c | 5 +++++
> >  1 file changed, 5 insertions(+)
> >
> > diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
> > index 74a5684..0562f49 100644
> > --- a/qapi/qmp-output-visitor.c
> > +++ b/qapi/qmp-output-visitor.c
> > @@ -66,6 +66,11 @@ static QObject *qmp_output_pop(QmpOutputVisitor *qov)
> >  static QObject *qmp_output_first(QmpOutputVisitor *qov)
> >  {
> >      QStackEntry *e = QTAILQ_LAST(&qov->stack, QStack);
> > +
> > +    if (!e) {
> > +        return NULL;
> > +    }
> > +
> >      return e->value;
> >  }
> 
> Let's see how this thing works.
> 
> The visitor's mutable state is a QStack, which is stack of (QObject,
> bool).  We can ignore the bool; it's just for qmp_output_next_list().
> 
> Visits start with an empty stack.  See qmp_output_visitor_new().
> 
> qmp_output_first() returns the object on the bottom of the stack.
> qmp_output_last() returns the object on the top of the stack.
> 
> <rant>
> When you implement a stack with a double-ended queue, you're totally
> free to pick either end of the queue for top of stack.  You're also free
> to name your functions accessing top and the bottom of the stack however
> you like.  "Of course" the author picked queue end and function names
> for maximum confusion:
> 
>     static QObject *qmp_output_first(QmpOutputVisitor *qov)
>     {
>         QStackEntry *e = QTAILQ_LAST(&qov->stack, QStack);
>         return e->value;
>     }
> 
>     static QObject *qmp_output_last(QmpOutputVisitor *qov)
>     {
>         QStackEntry *e = QTAILQ_FIRST(&qov->stack);
>         return e->value;
>     }
> 
> I hate you.
> </rant>
> 
> The result of the visit sits at the bottom of the stack.  Empty stack,
> null result.  See qmp_output_get_qobject().
> 
> Visiting a scalar type creates the appropriate scalar QObject, and
> "adds" it.  We'll find out what "adding" means shortly.  See
> qmp_output_type_{int,bool,str,number}().
> 
> Special case: null strings get converted to empty strings.  See
> qmp_output_type_str().
> 
> Starting a struct visit creates a QDict, adds it, and pushes it onto the
> stack.  Ending it pops it from the stack.  See
> qmp_output_{start,end}_struct().
> 
> Starting a list visit creates a QList, adds it, and pushes it onto the
> stack.  Ending it pops it from the stack.  See
> qmp_output_{start,end}_list().
> 
> Visiting a list member does nothing interesting; see
> qmp_output_next_list().  Aside: I suspect the GenericList traversal
> stuff now done in every next_list() method should be done in the visitor
> core instead.
> 
> Now let's figure out what it means to "add" an object.  This is
> qmp_output_add_obj().
> 
> If the stack is still empty, the object is the root object, and it gets
> pushed.
> 
> Else, if the object on top of the stack is a QDict, we're visiting a
> struct.  Enter the object into the QDict.
> 
> Else, if the object on top of the stack is a QList, we're visiting a
> list.  Append the object to the QList.
> 
> Else, the object on top of the stack must be scalar, and I think it must
> be the root object.  We replace it by the object being added.  WTF?
> 
> This feels more complicated than it could be.  Anyway, how could a null
> object end up at the bottom of the stack, so that qmp_output_first()
> chokes on it?  I can't see that.
> 
> If it can get added, then why can it be seen only by qmp_output_first(),
> but not by qmp_output_last() and qmp_output_pop()?

See my note above, the corner case we're hitting seems to be when there's
nothing in the stack at all: generating a QObject from an empty
QmpOutputVisitor.

This occurs with object_property_get_str skips visit_type_str if the
property-specific accessor returns NULL, but we still covert the
visitor to a QObject to pull the string out later.
Markus Armbruster May 15, 2014, 5:19 p.m. UTC | #10
Michael Roth <mdroth@linux.vnet.ibm.com> writes:

> Quoting Markus Armbruster (2014-05-15 11:13:09)
>> Marcel Apfelbaum <marcel.a@redhat.com> writes:
>> 
>> > A NULL value is not added to visitor's stack, but there
>> > is no check for that when the visitor tries to return
>> > that value, leading to Qemu crash.
>> >
>> > Reviewed-by: Eric Blake <eblake@redhat.com>
>> > Signed-off-by: Marcel Apfelbaum <marcel.a@redhat.com>
>> > ---
>> >  qapi/qmp-output-visitor.c | 5 +++++
>> >  1 file changed, 5 insertions(+)
>> >
>> > diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
>> > index 74a5684..0562f49 100644
>> > --- a/qapi/qmp-output-visitor.c
>> > +++ b/qapi/qmp-output-visitor.c
>> > @@ -66,6 +66,11 @@ static QObject *qmp_output_pop(QmpOutputVisitor *qov)
>> >  static QObject *qmp_output_first(QmpOutputVisitor *qov)
>> >  {
>> >      QStackEntry *e = QTAILQ_LAST(&qov->stack, QStack);
>> > +
>> > +    if (!e) {
>> > +        return NULL;
>> > +    }
>> > +
>> >      return e->value;
>> >  }
>> 
>> Let's see how this thing works.
>> 
>> The visitor's mutable state is a QStack, which is stack of (QObject,
>> bool).  We can ignore the bool; it's just for qmp_output_next_list().
>> 
>> Visits start with an empty stack.  See qmp_output_visitor_new().
>> 
>> qmp_output_first() returns the object on the bottom of the stack.
>> qmp_output_last() returns the object on the top of the stack.
>> 
>> <rant>
>> When you implement a stack with a double-ended queue, you're totally
>> free to pick either end of the queue for top of stack.  You're also free
>> to name your functions accessing top and the bottom of the stack however
>> you like.  "Of course" the author picked queue end and function names
>> for maximum confusion:
>> 
>>     static QObject *qmp_output_first(QmpOutputVisitor *qov)
>>     {
>>         QStackEntry *e = QTAILQ_LAST(&qov->stack, QStack);
>>         return e->value;
>>     }
>> 
>>     static QObject *qmp_output_last(QmpOutputVisitor *qov)
>>     {
>>         QStackEntry *e = QTAILQ_FIRST(&qov->stack);
>>         return e->value;
>>     }
>> 
>> I hate you.
>> </rant>
>> 
>> The result of the visit sits at the bottom of the stack.  Empty stack,
>> null result.  See qmp_output_get_qobject().
>> 
>> Visiting a scalar type creates the appropriate scalar QObject, and
>> "adds" it.  We'll find out what "adding" means shortly.  See
>> qmp_output_type_{int,bool,str,number}().
>> 
>> Special case: null strings get converted to empty strings.  See
>> qmp_output_type_str().
>> 
>> Starting a struct visit creates a QDict, adds it, and pushes it onto the
>> stack.  Ending it pops it from the stack.  See
>> qmp_output_{start,end}_struct().
>> 
>> Starting a list visit creates a QList, adds it, and pushes it onto the
>> stack.  Ending it pops it from the stack.  See
>> qmp_output_{start,end}_list().
>> 
>> Visiting a list member does nothing interesting; see
>> qmp_output_next_list().  Aside: I suspect the GenericList traversal
>> stuff now done in every next_list() method should be done in the visitor
>> core instead.
>> 
>> Now let's figure out what it means to "add" an object.  This is
>> qmp_output_add_obj().
>> 
>> If the stack is still empty, the object is the root object, and it gets
>> pushed.
>> 
>> Else, if the object on top of the stack is a QDict, we're visiting a
>> struct.  Enter the object into the QDict.
>> 
>> Else, if the object on top of the stack is a QList, we're visiting a
>> list.  Append the object to the QList.
>> 
>> Else, the object on top of the stack must be scalar, and I think it must
>> be the root object.  We replace it by the object being added.  WTF?
>> 
>> This feels more complicated than it could be.  Anyway, how could a null
>> object end up at the bottom of the stack, so that qmp_output_first()
>> chokes on it?  I can't see that.
>> 
>> If it can get added, then why can it be seen only by qmp_output_first(),
>> but not by qmp_output_last() and qmp_output_pop()?
>
> See my note above, the corner case we're hitting seems to be when there's
> nothing in the stack at all: generating a QObject from an empty
> QmpOutputVisitor.

The other user of qmp_output_first() calls it like this:

    QObject *root = QTAILQ_EMPTY(&v->stack) ? NULL : qmp_output_first(v);

Patching qmp_output_first() makes this check redundant.

I suspect we should change both callers to test QTAILQ_EMPTY() instead.

> This occurs with object_property_get_str skips visit_type_str if the
> property-specific accessor returns NULL, but we still covert the
> visitor to a QObject to pull the string out later.

Can't see visit_type_str() being called from object_property_get_str().
Do you mean property_get_str()?

static void property_get_str(Object *obj, Visitor *v, void *opaque,
                             const char *name, Error **errp)
{
    StringProperty *prop = opaque;
    char *value;

    value = prop->get(obj, errp);
    if (value) {
        visit_type_str(v, &value, name, errp);
        g_free(value);
    }
}

Why do we skip visit_type_str() when value is null?
Michael Roth May 15, 2014, 5:55 p.m. UTC | #11
Quoting Markus Armbruster (2014-05-15 12:19:08)
> Michael Roth <mdroth@linux.vnet.ibm.com> writes:
> 
> > Quoting Markus Armbruster (2014-05-15 11:13:09)
> >> Marcel Apfelbaum <marcel.a@redhat.com> writes:
> >> 
> >> > A NULL value is not added to visitor's stack, but there
> >> > is no check for that when the visitor tries to return
> >> > that value, leading to Qemu crash.
> >> >
> >> > Reviewed-by: Eric Blake <eblake@redhat.com>
> >> > Signed-off-by: Marcel Apfelbaum <marcel.a@redhat.com>
> >> > ---
> >> >  qapi/qmp-output-visitor.c | 5 +++++
> >> >  1 file changed, 5 insertions(+)
> >> >
> >> > diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
> >> > index 74a5684..0562f49 100644
> >> > --- a/qapi/qmp-output-visitor.c
> >> > +++ b/qapi/qmp-output-visitor.c
> >> > @@ -66,6 +66,11 @@ static QObject *qmp_output_pop(QmpOutputVisitor *qov)
> >> >  static QObject *qmp_output_first(QmpOutputVisitor *qov)
> >> >  {
> >> >      QStackEntry *e = QTAILQ_LAST(&qov->stack, QStack);
> >> > +
> >> > +    if (!e) {
> >> > +        return NULL;
> >> > +    }
> >> > +
> >> >      return e->value;
> >> >  }
> >> 
> >> Let's see how this thing works.
> >> 
> >> The visitor's mutable state is a QStack, which is stack of (QObject,
> >> bool).  We can ignore the bool; it's just for qmp_output_next_list().
> >> 
> >> Visits start with an empty stack.  See qmp_output_visitor_new().
> >> 
> >> qmp_output_first() returns the object on the bottom of the stack.
> >> qmp_output_last() returns the object on the top of the stack.
> >> 
> >> <rant>
> >> When you implement a stack with a double-ended queue, you're totally
> >> free to pick either end of the queue for top of stack.  You're also free
> >> to name your functions accessing top and the bottom of the stack however
> >> you like.  "Of course" the author picked queue end and function names
> >> for maximum confusion:
> >> 
> >>     static QObject *qmp_output_first(QmpOutputVisitor *qov)
> >>     {
> >>         QStackEntry *e = QTAILQ_LAST(&qov->stack, QStack);
> >>         return e->value;
> >>     }
> >> 
> >>     static QObject *qmp_output_last(QmpOutputVisitor *qov)
> >>     {
> >>         QStackEntry *e = QTAILQ_FIRST(&qov->stack);
> >>         return e->value;
> >>     }
> >> 
> >> I hate you.
> >> </rant>
> >> 
> >> The result of the visit sits at the bottom of the stack.  Empty stack,
> >> null result.  See qmp_output_get_qobject().
> >> 
> >> Visiting a scalar type creates the appropriate scalar QObject, and
> >> "adds" it.  We'll find out what "adding" means shortly.  See
> >> qmp_output_type_{int,bool,str,number}().
> >> 
> >> Special case: null strings get converted to empty strings.  See
> >> qmp_output_type_str().
> >> 
> >> Starting a struct visit creates a QDict, adds it, and pushes it onto the
> >> stack.  Ending it pops it from the stack.  See
> >> qmp_output_{start,end}_struct().
> >> 
> >> Starting a list visit creates a QList, adds it, and pushes it onto the
> >> stack.  Ending it pops it from the stack.  See
> >> qmp_output_{start,end}_list().
> >> 
> >> Visiting a list member does nothing interesting; see
> >> qmp_output_next_list().  Aside: I suspect the GenericList traversal
> >> stuff now done in every next_list() method should be done in the visitor
> >> core instead.
> >> 
> >> Now let's figure out what it means to "add" an object.  This is
> >> qmp_output_add_obj().
> >> 
> >> If the stack is still empty, the object is the root object, and it gets
> >> pushed.
> >> 
> >> Else, if the object on top of the stack is a QDict, we're visiting a
> >> struct.  Enter the object into the QDict.
> >> 
> >> Else, if the object on top of the stack is a QList, we're visiting a
> >> list.  Append the object to the QList.
> >> 
> >> Else, the object on top of the stack must be scalar, and I think it must
> >> be the root object.  We replace it by the object being added.  WTF?
> >> 
> >> This feels more complicated than it could be.  Anyway, how could a null
> >> object end up at the bottom of the stack, so that qmp_output_first()
> >> chokes on it?  I can't see that.
> >> 
> >> If it can get added, then why can it be seen only by qmp_output_first(),
> >> but not by qmp_output_last() and qmp_output_pop()?
> >
> > See my note above, the corner case we're hitting seems to be when there's
> > nothing in the stack at all: generating a QObject from an empty
> > QmpOutputVisitor.
> 
> The other user of qmp_output_first() calls it like this:
> 
>     QObject *root = QTAILQ_EMPTY(&v->stack) ? NULL : qmp_output_first(v);
> 
> Patching qmp_output_first() makes this check redundant.
> 
> I suspect we should change both callers to test QTAILQ_EMPTY() instead.

Or remove the redundant check, don't have a strong preference either way,
though personally I think qmp_output_first should handle it internally
and just give us the (possibly NULL) QObject, since it exposes less
internals to callers.

> 
> > This occurs with object_property_get_str skips visit_type_str if the
> > property-specific accessor returns NULL, but we still covert the
> > visitor to a QObject to pull the string out later.
> 
> Can't see visit_type_str() being called from object_property_get_str().
> Do you mean property_get_str()?

Yah, I think it's something like:
 object_property_get_str->object_property_get->prop.get->property_get_str

> 
> static void property_get_str(Object *obj, Visitor *v, void *opaque,
>                              const char *name, Error **errp)
> {
>     StringProperty *prop = opaque;
>     char *value;
> 
>     value = prop->get(obj, errp);
>     if (value) {
>         visit_type_str(v, &value, name, errp);
>         g_free(value);
>     }
> }
> 
> Why do we skip visit_type_str() when value is null?

Not sure..., seems like an explicit fall through to the
QERR_INVALID_PARAMETER_TYPE error in object_property_get_str that
wasn't reachable prior to Marcel's patch (due to segfault), so
I'm not sure this code path was in play until now.
diff mbox

Patch

diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
index 74a5684..0562f49 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -66,6 +66,11 @@  static QObject *qmp_output_pop(QmpOutputVisitor *qov)
 static QObject *qmp_output_first(QmpOutputVisitor *qov)
 {
     QStackEntry *e = QTAILQ_LAST(&qov->stack, QStack);
+
+    if (!e) {
+        return NULL;
+    }
+
     return e->value;
 }