[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH v5 21/46] qmp: Fix reference-counting of qnull on em
From: |
Eric Blake |
Subject: |
[Qemu-devel] [PATCH v5 21/46] qmp: Fix reference-counting of qnull on empty output visit |
Date: |
Mon, 21 Sep 2015 15:57:37 -0600 |
Commit 6c2f9a15 ensured that we would not return NULL when the
caller used an output visitor but had nothing to visit. But
in doing so, it added a FIXME about a reference count leak
that could abort qemu in the (unlikely) case of SIZE_MAX such
visits (more plausible on 32-bit).
This fixes things by documenting the internal contracts, and
explaining why the internal function can return NULL and only
the public facing interface needs to worry about qnull(),
thus avoiding over-referencing the qnull_ global object.
It does not, however, fix the stupidity of the stack mixing
up two separate pieces of information; add a FIXME to explain
that issue.
Signed-off-by: Eric Blake <address@hidden>
---
"Commit 6c2f9a15" above assumes the commit ids from the pending
pull-qapi-2015-09-21 tag will be preserved
---
qapi/qmp-output-visitor.c | 30 ++++++++++++++++++++++++++++--
tests/test-qmp-output-visitor.c | 2 ++
2 files changed, 30 insertions(+), 2 deletions(-)
diff --git a/qapi/qmp-output-visitor.c b/qapi/qmp-output-visitor.c
index 29899ac..850c83d 100644
--- a/qapi/qmp-output-visitor.c
+++ b/qapi/qmp-output-visitor.c
@@ -29,6 +29,12 @@ typedef QTAILQ_HEAD(QStack, QStackEntry) QStack;
struct QmpOutputVisitor
{
Visitor visitor;
+ /* FIXME: we are abusing stack to hold two separate pieces of
+ * information: the current root object, and the stack of objects
+ * still being built. Worse, our behavior is inconsistent:
+ * visiting two top-level scalars in a row discards the first in
+ * favor of the second, but visiting two top-level objects in a
+ * row tries to append the second object into the first. */
QStack stack;
};
@@ -41,10 +47,12 @@ static QmpOutputVisitor *to_qov(Visitor *v)
return container_of(v, QmpOutputVisitor, visitor);
}
+/* Push @value onto the stack of current QObjects being built */
static void qmp_output_push_obj(QmpOutputVisitor *qov, QObject *value)
{
QStackEntry *e = g_malloc0(sizeof(*e));
+ assert(value);
e->value = value;
if (qobject_type(e->value) == QTYPE_QLIST) {
e->is_list_head = true;
@@ -52,16 +60,20 @@ static void qmp_output_push_obj(QmpOutputVisitor *qov,
QObject *value)
QTAILQ_INSERT_HEAD(&qov->stack, e, node);
}
+/* Grab and remove the most recent QObject from the stack */
static QObject *qmp_output_pop(QmpOutputVisitor *qov)
{
QStackEntry *e = QTAILQ_FIRST(&qov->stack);
QObject *value;
+
+ assert(e);
QTAILQ_REMOVE(&qov->stack, e, node);
value = e->value;
g_free(e);
return value;
}
+/* Grab the root QObject, if any, in preparation to empty the stack */
static QObject *qmp_output_first(QmpOutputVisitor *qov)
{
QStackEntry *e = QTAILQ_LAST(&qov->stack, QStack);
@@ -72,24 +84,32 @@ static QObject *qmp_output_first(QmpOutputVisitor *qov)
* handle null.
*/
if (!e) {
- return qnull();
+ /* No root */
+ return NULL;
}
-
+ assert(e->value);
return e->value;
}
+/* Grab the most recent QObject from the stack, which must exist */
static QObject *qmp_output_last(QmpOutputVisitor *qov)
{
QStackEntry *e = QTAILQ_FIRST(&qov->stack);
+
+ assert(e);
return e->value;
}
+/* Add @value to the current QObject being built.
+ * If the stack is visiting a dictionary or list, @value is now owned
+ * by that container. Otherwise, @value is now the root. */
static void qmp_output_add_obj(QmpOutputVisitor *qov, const char *name,
QObject *value)
{
QObject *cur;
if (QTAILQ_EMPTY(&qov->stack)) {
+ /* Stack was empty, track this object as root */
qmp_output_push_obj(qov, value);
return;
}
@@ -98,13 +118,16 @@ static void qmp_output_add_obj(QmpOutputVisitor *qov,
const char *name,
switch (qobject_type(cur)) {
case QTYPE_QDICT:
+ assert(name);
qdict_put_obj(qobject_to_qdict(cur), name, value);
break;
case QTYPE_QLIST:
qlist_append_obj(qobject_to_qlist(cur), value);
break;
default:
+ /* The previous root was a scalar, replace it with a new root */
qobject_decref(qmp_output_pop(qov));
+ assert(QTAILQ_EMPTY(&qov->stack));
qmp_output_push_obj(qov, value);
break;
}
@@ -198,11 +221,14 @@ static void qmp_output_type_any(Visitor *v, QObject
**obj, const char *name,
qmp_output_add_obj(qov, name, *obj);
}
+/* Finish building, and return the root object. Will not be NULL. */
QObject *qmp_output_get_qobject(QmpOutputVisitor *qov)
{
QObject *obj = qmp_output_first(qov);
if (obj) {
qobject_incref(obj);
+ } else {
+ obj = qnull();
}
return obj;
}
diff --git a/tests/test-qmp-output-visitor.c b/tests/test-qmp-output-visitor.c
index d13e0e0..0f72b5d 100644
--- a/tests/test-qmp-output-visitor.c
+++ b/tests/test-qmp-output-visitor.c
@@ -548,6 +548,8 @@ static void test_visitor_out_empty(TestOutputVisitorData
*data,
arg = qmp_output_get_qobject(data->qov);
g_assert(qobject_type(arg) == QTYPE_QNULL);
+ /* Check that qnull reference counting is sane */
+ g_assert(arg->refcnt == 2);
qobject_decref(arg);
}
--
2.4.3
- [Qemu-devel] [PATCH v5 10/46] qapi: Merge generation of per-member visits, (continued)
- [Qemu-devel] [PATCH v5 10/46] qapi: Merge generation of per-member visits, Eric Blake, 2015/09/21
- [Qemu-devel] [PATCH v5 11/46] qapi: Don't use info as witness of implicit object type, Eric Blake, 2015/09/21
- [Qemu-devel] [PATCH v5 12/46] qapi: Track location that created an implicit type, Eric Blake, 2015/09/21
- [Qemu-devel] [PATCH v5 14/46] qapi: Detect collisions in C member names, Eric Blake, 2015/09/21
- [Qemu-devel] [PATCH v5 16/46] qapi: Detect base class loops, Eric Blake, 2015/09/21
- [Qemu-devel] [PATCH v5 15/46] qapi: Defer duplicate member checks to schema check(), Eric Blake, 2015/09/21
- [Qemu-devel] [PATCH v5 13/46] qapi: Track owner of each object member, Eric Blake, 2015/09/21
- [Qemu-devel] [PATCH v5 17/46] qapi: Provide nicer array names in introspection, Eric Blake, 2015/09/21
- [Qemu-devel] [PATCH v5 18/46] qapi-introspect: Guarantee particular sorting, Eric Blake, 2015/09/21
- [Qemu-devel] [PATCH v5 20/46] qapi: Fix alternates that accept 'number' but not 'int', Eric Blake, 2015/09/21
- [Qemu-devel] [PATCH v5 21/46] qmp: Fix reference-counting of qnull on empty output visit,
Eric Blake <=
- [Qemu-devel] [PATCH v5 19/46] qapi: Simplify visiting of alternate types, Eric Blake, 2015/09/21
- [Qemu-devel] [PATCH v5 22/46] qapi: Don't abuse stack to track qmp-output root, Eric Blake, 2015/09/21
- [Qemu-devel] [PATCH v5 26/46] qapi: Test failure in middle of array parse, Eric Blake, 2015/09/21
- [Qemu-devel] [PATCH v5 23/46] qapi: Remove dead visitor code, Eric Blake, 2015/09/21
- [Qemu-devel] [PATCH v5 27/46] qapi: Simplify visits of optional fields, Eric Blake, 2015/09/21
- [Qemu-devel] [PATCH v5 25/46] qapi: Plug leaks in test-qmp-input-visitor, Eric Blake, 2015/09/21
- [Qemu-devel] [PATCH v5 24/46] qapi: Document visitor interfaces, Eric Blake, 2015/09/21
- [Qemu-devel] [PATCH v5 28/46] qapi: Rework deallocation of partial struct, Eric Blake, 2015/09/21
- [Qemu-devel] [PATCH v5 29/46] qapi: Change visit_type_FOO() to no longer return partial objects, Eric Blake, 2015/09/21
- [Qemu-devel] [PATCH v5 30/46] net: use Netdev instead of NetClientOptions in client init, Eric Blake, 2015/09/21