diff mbox

qmp_output_visitor_cleanup(): fix double free

Message ID 1331925372-5429-1-git-send-email-lersek@redhat.com
State New
Headers show

Commit Message

Laszlo Ersek March 16, 2012, 7:16 p.m. UTC
Stack entries in QmpOutputVisitor are navigation links (weak references),
except the bottom (ie. least recently added) entry, which owns the root
QObject [1]. Make qmp_output_visitor_cleanup() drop the stack entries,
then release the QObject tree by the root.

Attempting to serialize an invalid enum inside a dictionary is an example
for triggering the double free.

[1] http://lists.nongnu.org/archive/html/qemu-devel/2012-03/msg03276.html

Signed-off-by: Laszlo Ersek <lersek@redhat.com>
---
 qapi/qmp-output-visitor.c |    8 +++++---
 1 files changed, 5 insertions(+), 3 deletions(-)

Comments

Michael Roth March 20, 2012, 12:43 a.m. UTC | #1
On Fri, Mar 16, 2012 at 08:16:12PM +0100, Laszlo Ersek wrote:
> Stack entries in QmpOutputVisitor are navigation links (weak references),
> except the bottom (ie. least recently added) entry, which owns the root
> QObject [1]. Make qmp_output_visitor_cleanup() drop the stack entries,
> then release the QObject tree by the root.
> 
> Attempting to serialize an invalid enum inside a dictionary is an example
> for triggering the double free.
> 
> [1] http://lists.nongnu.org/archive/html/qemu-devel/2012-03/msg03276.html
> 
> Signed-off-by: Laszlo Ersek <lersek@redhat.com>

Tested-by: Michael Roth <mdroth@linux.vnet.ibm.com>

Looks good. Did you or Paolo want to re-send that test case with a SoB?
I have a test case that triggers this by manually calling error_set(),
but I think yours is better.

> ---
>  qapi/qmp-output-visitor.c |    8 +++++---
>  1 files changed, 5 insertions(+), 3 deletions(-)
> 
> diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
> index e0697b0..2bce9d5 100644
> --- a/qapi/qmp-output-visitor.c
> +++ b/qapi/qmp-output-visitor.c
> @@ -199,14 +199,16 @@ void qmp_output_visitor_cleanup(QmpOutputVisitor *v)
>  {
>      QStackEntry *e, *tmp;
> 
> +    /* The bottom QStackEntry, if any, owns the root QObject. See the
> +     * qmp_output_push_obj() invocations in qmp_output_add_obj(). */
> +    QObject *root = QTAILQ_EMPTY(&v->stack) ? NULL : qmp_output_first(v);
> +
>      QTAILQ_FOREACH_SAFE(e, &v->stack, node, tmp) {
>          QTAILQ_REMOVE(&v->stack, e, node);
> -        if (e->value) {
> -            qobject_decref(e->value);
> -        }
>          g_free(e);
>      }
> 
> +    qobject_decref(root);
>      g_free(v);
>  }
> 
> -- 
> 1.7.1
>
Laszlo Ersek March 20, 2012, 10:22 a.m. UTC | #2
v1->v2: added Paolo's test case as second patch in the series. Also tried
to come up with a better subject for 1/2.

Laszlo Ersek (1):
  qapi: fix double free in qmp_output_visitor_cleanup()

Paolo Bonzini (1):
  qapi: add struct-errors test case to test-qmp-output-visitor

 qapi-schema-test.json     |    2 +-
 qapi/qmp-output-visitor.c |    8 +++++---
 test-qmp-output-visitor.c |   20 ++++++++++++++++++++
 3 files changed, 26 insertions(+), 4 deletions(-)
Luiz Capitulino March 20, 2012, 1:51 p.m. UTC | #3
On Tue, 20 Mar 2012 11:22:47 +0100
Laszlo Ersek <lersek@redhat.com> wrote:

> v1->v2: added Paolo's test case as second patch in the series. Also tried
> to come up with a better subject for 1/2.

Applied to the qmp branch, thanks.

> 
> Laszlo Ersek (1):
>   qapi: fix double free in qmp_output_visitor_cleanup()
> 
> Paolo Bonzini (1):
>   qapi: add struct-errors test case to test-qmp-output-visitor
> 
>  qapi-schema-test.json     |    2 +-
>  qapi/qmp-output-visitor.c |    8 +++++---
>  test-qmp-output-visitor.c |   20 ++++++++++++++++++++
>  3 files changed, 26 insertions(+), 4 deletions(-)
>
diff mbox

Patch

diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
index e0697b0..2bce9d5 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -199,14 +199,16 @@  void qmp_output_visitor_cleanup(QmpOutputVisitor *v)
 {
     QStackEntry *e, *tmp;
 
+    /* The bottom QStackEntry, if any, owns the root QObject. See the
+     * qmp_output_push_obj() invocations in qmp_output_add_obj(). */
+    QObject *root = QTAILQ_EMPTY(&v->stack) ? NULL : qmp_output_first(v);
+
     QTAILQ_FOREACH_SAFE(e, &v->stack, node, tmp) {
         QTAILQ_REMOVE(&v->stack, e, node);
-        if (e->value) {
-            qobject_decref(e->value);
-        }
         g_free(e);
     }
 
+    qobject_decref(root);
     g_free(v);
 }