qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Eric Blake <eblake@redhat.com>
To: qemu-devel@nongnu.org
Cc: armbru@redhat.com, Michael Roth <mdroth@linux.vnet.ibm.com>
Subject: [Qemu-devel] [RFC PATCH v2 09/12] qapi: Rework deallocation of partial struct
Date: Thu,  6 Aug 2015 21:52:38 -0600	[thread overview]
Message-ID: <1438919561-27750-10-git-send-email-eblake@redhat.com> (raw)
In-Reply-To: <1438919561-27750-1-git-send-email-eblake@redhat.com>

Commit cee2dedb noticed that if you have a partial flat union
(such as if an input parse failed due to a missing
discriminator), calling the dealloc visitor could result in
trying to dereference the NULL pointer. But the fix it proposed
requires the use of a 'data' member in the union, which may or
may not be the same size as other branches of the union
(consider a 32-bit platform where one of the branches is an
int64), so it feels fairly dirty.  A better, and much shorter,
fix is to tweak all of the generated visit_implicit_FOO()
functions to avoid dereferencing NULL in the first place, by
enhancing the dealloc visitor to not try to descend through an
implicit struct that was not allocated.

The lack of documentation on the visitor interface is appalling,
but I'm not fixing it here. My intended semantics is that if
visit_start_implicit_struct() returns true, then the fields of
the struct will be visited and visit_end_impicit_struct() must
also be called; if it returns false, then everything else about
the struct will be skipped.  Perhaps we should guarantee that
visit_end_implicit_struct() will always be called?  Perhaps we
should pass a bool parameter to visit_end_implicit_struct() that
preserves the value returned by visit_start_implicit_struct()?

Signed-off-by: Eric Blake <eblake@redhat.com>
---
 include/qapi/visitor-impl.h |  4 +---
 include/qapi/visitor.h      |  4 +---
 qapi/qapi-dealloc-visitor.c | 35 +++++++----------------------------
 qapi/qapi-visit-core.c      | 20 +++-----------------
 qapi/qmp-input-visitor.c    |  3 ++-
 scripts/qapi-visit.py       |  9 +--------
 6 files changed, 15 insertions(+), 60 deletions(-)

diff --git a/include/qapi/visitor-impl.h b/include/qapi/visitor-impl.h
index c94e5a1..f991670 100644
--- a/include/qapi/visitor-impl.h
+++ b/include/qapi/visitor-impl.h
@@ -22,7 +22,7 @@ struct Visitor
                          const char *name, size_t size, Error **errp);
     void (*end_struct)(Visitor *v, Error **errp);

-    void (*start_implicit_struct)(Visitor *v, void **obj, size_t size,
+    bool (*start_implicit_struct)(Visitor *v, void **obj, size_t size,
                                   Error **errp);
     void (*end_implicit_struct)(Visitor *v, Error **errp);

@@ -57,8 +57,6 @@ struct Visitor
     void (*type_int64)(Visitor *v, int64_t *obj, const char *name, Error **errp);
     /* visit_type_size() falls back to (*type_uint64)() if type_size is unset */
     void (*type_size)(Visitor *v, uint64_t *obj, const char *name, Error **errp);
-    bool (*start_union)(Visitor *v, bool data_present, Error **errp);
-    void (*end_union)(Visitor *v, bool data_present, Error **errp);
 };

 void input_type_enum(Visitor *v, int *obj, const char * const strings[],
diff --git a/include/qapi/visitor.h b/include/qapi/visitor.h
index 6a93c87..d07655c 100644
--- a/include/qapi/visitor.h
+++ b/include/qapi/visitor.h
@@ -33,7 +33,7 @@ void visit_end_handle(Visitor *v, Error **errp);
 void visit_start_struct(Visitor *v, void **obj, const char *kind,
                         const char *name, size_t size, Error **errp);
 void visit_end_struct(Visitor *v, Error **errp);
-void visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
+bool visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
                                  Error **errp);
 void visit_end_implicit_struct(Visitor *v, Error **errp);
 void visit_start_list(Visitor *v, const char *name, Error **errp);
@@ -59,7 +59,5 @@ void visit_type_bool(Visitor *v, bool *obj, const char *name, Error **errp);
 void visit_type_str(Visitor *v, char **obj, const char *name, Error **errp);
 void visit_type_number(Visitor *v, double *obj, const char *name, Error **errp);
 void visit_type_any(Visitor *v, QObject **obj, const char *name, Error **errp);
-bool visit_start_union(Visitor *v, bool data_present, Error **errp);
-void visit_end_union(Visitor *v, bool data_present, Error **errp);

 #endif
diff --git a/qapi/qapi-dealloc-visitor.c b/qapi/qapi-dealloc-visitor.c
index 737deab..2f0c81e 100644
--- a/qapi/qapi-dealloc-visitor.c
+++ b/qapi/qapi-dealloc-visitor.c
@@ -76,13 +76,17 @@ static void qapi_dealloc_end_struct(Visitor *v, Error **errp)
     }
 }

-static void qapi_dealloc_start_implicit_struct(Visitor *v,
+static bool qapi_dealloc_start_implicit_struct(Visitor *v,
                                                void **obj,
                                                size_t size,
                                                Error **errp)
 {
     QapiDeallocVisitor *qov = to_qov(v);
-    qapi_dealloc_push(qov, obj);
+    if (obj && *obj) {
+        qapi_dealloc_push(qov, obj);
+        return true;
+    }
+    return false;
 }

 static void qapi_dealloc_end_implicit_struct(Visitor *v, Error **errp)
@@ -90,6 +94,7 @@ static void qapi_dealloc_end_implicit_struct(Visitor *v, Error **errp)
     QapiDeallocVisitor *qov = to_qov(v);
     void **obj = qapi_dealloc_pop(qov);
     if (obj) {
+        assert(*obj);
         g_free(*obj);
     }
 }
@@ -171,31 +176,6 @@ static void qapi_dealloc_type_enum(Visitor *v, int *obj,
 {
 }

-/* If there's no data present, the dealloc visitor has nothing to free.
- * Thus, indicate to visitor code that the subsequent union fields can
- * be skipped. This is not an error condition, since the cleanup of the
- * rest of an object can continue unhindered, so leave errp unset in
- * these cases.
- *
- * NOTE: In cases where we're attempting to deallocate an object that
- * may have missing fields, the field indicating the union type may
- * be missing. In such a case, it's possible we don't have enough
- * information to differentiate data_present == false from a case where
- * data *is* present but happens to be a scalar with a value of 0.
- * This is okay, since in the case of the dealloc visitor there's no
- * work that needs to done in either situation.
- *
- * The current inability in QAPI code to more thoroughly verify a union
- * type in such cases will likely need to be addressed if we wish to
- * implement this interface for other types of visitors in the future,
- * however.
- */
-static bool qapi_dealloc_start_union(Visitor *v, bool data_present,
-                                     Error **errp)
-{
-    return data_present;
-}
-
 Visitor *qapi_dealloc_get_visitor(QapiDeallocVisitor *v)
 {
     return &v->visitor;
@@ -226,7 +206,6 @@ QapiDeallocVisitor *qapi_dealloc_visitor_new(void)
     v->visitor.type_number = qapi_dealloc_type_number;
     v->visitor.type_any = qapi_dealloc_type_anything;
     v->visitor.type_size = qapi_dealloc_type_size;
-    v->visitor.start_union = qapi_dealloc_start_union;

     QTAILQ_INIT(&v->stack);

diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 884fe94..1a8d5b2 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -28,12 +28,13 @@ void visit_end_struct(Visitor *v, Error **errp)
     v->end_struct(v, errp);
 }

-void visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
+bool visit_start_implicit_struct(Visitor *v, void **obj, size_t size,
                                  Error **errp)
 {
     if (v->start_implicit_struct) {
-        v->start_implicit_struct(v, obj, size, errp);
+        return v->start_implicit_struct(v, obj, size, errp);
     }
+    return true;
 }

 void visit_end_implicit_struct(Visitor *v, Error **errp)
@@ -58,21 +59,6 @@ void visit_end_list(Visitor *v, Error **errp)
     v->end_list(v, errp);
 }

-bool visit_start_union(Visitor *v, bool data_present, Error **errp)
-{
-    if (v->start_union) {
-        return v->start_union(v, data_present, errp);
-    }
-    return true;
-}
-
-void visit_end_union(Visitor *v, bool data_present, Error **errp)
-{
-    if (v->end_union) {
-        v->end_union(v, data_present, errp);
-    }
-}
-
 void visit_optional(Visitor *v, bool *present, const char *name,
                     Error **errp)
 {
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index 5310db5..0c0a71a 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -145,12 +145,13 @@ static void qmp_input_end_struct(Visitor *v, Error **errp)
     qmp_input_pop(qiv, errp);
 }

-static void qmp_input_start_implicit_struct(Visitor *v, void **obj,
+static bool qmp_input_start_implicit_struct(Visitor *v, void **obj,
                                             size_t size, Error **errp)
 {
     if (obj) {
         *obj = g_malloc0(size);
     }
+    return true;
 }

 static void qmp_input_end_implicit_struct(Visitor *v, Error **errp)
diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
index a3a81c2..4402307 100644
--- a/scripts/qapi-visit.py
+++ b/scripts/qapi-visit.py
@@ -47,8 +47,7 @@ static void visit_type_implicit_%(c_type)s(Visitor *m, %(c_type)s **obj, Error *
 {
     Error *err = NULL;

-    visit_start_implicit_struct(m, (void **)obj, sizeof(%(c_type)s), &err);
-    if (!err) {
+    if (visit_start_implicit_struct(m, (void **)obj, sizeof(%(c_type)s), &err) && !err) {
         visit_type_%(c_type)s_fields(m, obj, errp);
         visit_end_implicit_struct(m, &err);
     }
@@ -288,9 +287,6 @@ void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error
                      c_type=variants.tag_member.type.c_name(),
                      c_name=c_name(tag_key), name=tag_key)
     ret += mcgen('''
-        if (!visit_start_union(m, !!(*obj)->data, &err) || err) {
-            goto out_obj;
-        }
         switch ((*obj)->%(c_name)s) {
 ''',
                  c_name=c_name(tag_key))
@@ -325,9 +321,6 @@ void visit_type_%(c_name)s(Visitor *m, %(c_name)s **obj, const char *name, Error
 out_obj:
         error_propagate(errp, err);
         err = NULL;
-        visit_end_union(m, !!(*obj)->data, &err);
-        error_propagate(errp, err);
-        err = NULL;
     }
     visit_end_struct(m, &err);
 out:
-- 
2.4.3

  parent reply	other threads:[~2015-08-07  3:53 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2015-08-07  3:52 [Qemu-devel] [RFC PATCH v2 00/12] post-introspection qapi cleanups Eric Blake
2015-08-07  3:52 ` [Qemu-devel] [RFC PATCH v2 01/12] qapi: use 'type' in generated C code to match QMP union wire form Eric Blake
2015-08-07  3:52 ` [Qemu-devel] [RFC PATCH v2 02/12] vnc: hoist allocation of VncBasicInfo to callers Eric Blake
2015-08-07  3:52 ` [Qemu-devel] [RFC PATCH v2 03/12] qapi: Unbox base members Eric Blake
2015-08-07  3:52 ` [Qemu-devel] [RFC PATCH v2 04/12] qapi-visit: Remove redundant functions for flat union base Eric Blake
2015-08-07  3:52 ` [Qemu-devel] [RFC PATCH v2 05/12] qapi: Test use of 'number' within alternates Eric Blake
2015-08-07  3:52 ` [Qemu-devel] [RFC PATCH v2 06/12] qapi: Simplify visiting of alternate types Eric Blake
2015-08-07  3:52 ` [Qemu-devel] [RFC PATCH v2 07/12] qapi: Fix alternates that accept 'number' but not 'int' Eric Blake
2015-08-07  3:52 ` [Qemu-devel] [RFC PATCH v2 08/12] qapi: Add tests for empty unions Eric Blake
2015-08-07  3:52 ` Eric Blake [this message]
2015-08-07  3:52 ` [Qemu-devel] [RFC PATCH v2 10/12] qapi: Avoid use of 'data' member of qapi unions Eric Blake
2015-08-07  3:52 ` [Qemu-devel] [RFC PATCH v2 11/12] qapi: Forbid empty unions and useless alternates Eric Blake
2015-08-07  3:52 ` [Qemu-devel] [RFC PATCH v2 12/12] qapi: Drop useless 'data' member of unions Eric Blake
2015-08-07 22:07 ` [Qemu-devel] [RFC PATCH v2 13/12] qapi: Remove dead visitor code Eric Blake
2015-08-07 22:07 ` [Qemu-devel] [RFC PATCH v2 14/12] qapi: Document visitor interfaces Eric Blake

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1438919561-27750-10-git-send-email-eblake@redhat.com \
    --to=eblake@redhat.com \
    --cc=armbru@redhat.com \
    --cc=mdroth@linux.vnet.ibm.com \
    --cc=qemu-devel@nongnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).