diff mbox

[v16,02/24] qapi: Guarantee NULL obj on input visitor callback error

Message ID 1461879932-9020-3-git-send-email-eblake@redhat.com
State New
Headers show

Commit Message

Eric Blake April 28, 2016, 9:45 p.m. UTC
Our existing input visitors were not very consistent on errors
in a function taking 'TYPE **obj' (that is, start_struct(),
start_alternate(), type_str(), and type_any(). next_list() is
similar, except that since commit 08f9541, it can't fail).
While all of them set '*obj' to allocated storage on success,
it was not obvious whether '*obj' was guaranteed safe on failure,
or whether it was left uninitialized.  But a future patch wants
to guarantee that visit_type_FOO() does not leak a partially-
constructed obj back to the caller; it is easier to implement
this if we can reliably state that input visitors assign '*obj'
regardless of success or failure, and that on failure *obj is
NULL.  Add assertions to enforce consistency in the final
setting of err vs. *obj.

The opts-visitor start_struct() doesn't set an error, but it
also was doing a weird check for 0 size; all callers pass in
non-zero size if obj is non-NULL.

The testsuite has at least one spot where we no longer need
to pre-initialize a variable prior to a visit; valgrind confirms
that the test is still fine with the cleanup.

A later patch will document the design constraint implemented
here.

Signed-off-by: Eric Blake <eblake@redhat.com>

---
v16: tighten assertions, another commit message tweak
v15: enhance commit message, hoist assertions from later in series
v14: no change
v13: no change
v12: new patch
---
 qapi/qapi-visit-core.c        | 34 ++++++++++++++++++++++++++++++----
 qapi/opts-visitor.c           |  3 ++-
 qapi/qmp-input-visitor.c      |  4 ++++
 qapi/string-input-visitor.c   |  1 +
 tests/test-qmp-input-strict.c |  2 +-
 5 files changed, 38 insertions(+), 6 deletions(-)

Comments

Markus Armbruster April 29, 2016, 8:28 a.m. UTC | #1
Eric Blake <eblake@redhat.com> writes:

> Our existing input visitors were not very consistent on errors
> in a function taking 'TYPE **obj' (that is, start_struct(),
> start_alternate(), type_str(), and type_any(). next_list() is
> similar, except that since commit 08f9541, it can't fail).

Multiple sentences in a parenthesis, ugh :)

    Our existing input visitors were not very consistent on errors in a
    function taking 'TYPE **obj'.  These are start_struct(),
    start_alternate(), type_str(), and type_any().  next_list() is
    similar, but can't fail (see commit since 08f9541).

Can touch up on commit.

> While all of them set '*obj' to allocated storage on success,
> it was not obvious whether '*obj' was guaranteed safe on failure,
> or whether it was left uninitialized.  But a future patch wants
> to guarantee that visit_type_FOO() does not leak a partially-
> constructed obj back to the caller; it is easier to implement
> this if we can reliably state that input visitors assign '*obj'
> regardless of success or failure, and that on failure *obj is
> NULL.  Add assertions to enforce consistency in the final
> setting of err vs. *obj.
>
> The opts-visitor start_struct() doesn't set an error, but it
> also was doing a weird check for 0 size; all callers pass in
> non-zero size if obj is non-NULL.
>
> The testsuite has at least one spot where we no longer need
> to pre-initialize a variable prior to a visit; valgrind confirms
> that the test is still fine with the cleanup.
>
> A later patch will document the design constraint implemented
> here.
>
> Signed-off-by: Eric Blake <eblake@redhat.com>
>
> ---
> v16: tighten assertions, another commit message tweak
> v15: enhance commit message, hoist assertions from later in series
> v14: no change
> v13: no change
> v12: new patch
> ---
>  qapi/qapi-visit-core.c        | 34 ++++++++++++++++++++++++++++++----
>  qapi/opts-visitor.c           |  3 ++-
>  qapi/qmp-input-visitor.c      |  4 ++++
>  qapi/string-input-visitor.c   |  1 +
>  tests/test-qmp-input-strict.c |  2 +-
>  5 files changed, 38 insertions(+), 6 deletions(-)
>
> diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
> index 3cd7edc..7ad5ff4 100644
> --- a/qapi/qapi-visit-core.c
> +++ b/qapi/qapi-visit-core.c
> @@ -23,7 +23,13 @@
>  void visit_start_struct(Visitor *v, const char *name, void **obj,
>                          size_t size, Error **errp)
>  {
> -    v->start_struct(v, name, obj, size, errp);
> +    Error *err = NULL;
> +
> +    v->start_struct(v, name, obj, size, &err);
> +    if (obj && v->type == VISITOR_INPUT) {
> +        assert(!err != !*obj);
> +    }
> +    error_propagate(errp, err);
>  }
>
>  void visit_end_struct(Visitor *v, Error **errp)
> @@ -51,10 +57,16 @@ void visit_start_alternate(Visitor *v, const char *name,
>                             GenericAlternate **obj, size_t size,
>                             bool promote_int, Error **errp)
>  {
> +    Error *err = NULL;
> +
>      assert(obj && size >= sizeof(GenericAlternate));
>      if (v->start_alternate) {
> -        v->start_alternate(v, name, obj, size, promote_int, errp);
> +        v->start_alternate(v, name, obj, size, promote_int, &err);
>      }
> +    if (v->type == VISITOR_INPUT) {
> +        assert(!err != !*obj);

Could assert(v->start_alternate && !err != !*obj), to preempt "what if
!v->start_alternate" worries.  If you like that, I can do it on commit.

> +    }
> +    error_propagate(errp, err);
>  }
>
>  void visit_end_alternate(Visitor *v)
> @@ -188,7 +200,14 @@ void visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp)
>
>  void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp)
>  {
> -    v->type_str(v, name, obj, errp);
> +    Error *err = NULL;
> +
> +    assert(obj);
> +    v->type_str(v, name, obj, &err);
> +    if (v->type == VISITOR_INPUT) {
> +        assert(!err != !*obj);
> +    }
> +    error_propagate(errp, err);
>  }
>
>  void visit_type_number(Visitor *v, const char *name, double *obj,
> @@ -199,7 +218,14 @@ void visit_type_number(Visitor *v, const char *name, double *obj,
>
>  void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp)
>  {
> -    v->type_any(v, name, obj, errp);
> +    Error *err = NULL;
> +
> +    assert(obj);
> +    v->type_any(v, name, obj, &err);
> +    if (v->type == VISITOR_INPUT) {
> +        assert(!err != !*obj);
> +    }
> +    error_propagate(errp, err);
>  }
>
>  static void output_type_enum(Visitor *v, const char *name, int *obj,
> diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
> index 66aeaed..4cb6436 100644
> --- a/qapi/opts-visitor.c
> +++ b/qapi/opts-visitor.c
> @@ -133,7 +133,7 @@ opts_start_struct(Visitor *v, const char *name, void **obj,
>      const QemuOpt *opt;
>
>      if (obj) {
> -        *obj = g_malloc0(size > 0 ? size : 1);
> +        *obj = g_malloc0(size);
>      }
>      if (ov->depth++ > 0) {
>          return;
> @@ -314,6 +314,7 @@ opts_type_str(Visitor *v, const char *name, char **obj, Error **errp)
>
>      opt = lookup_scalar(ov, name, errp);
>      if (!opt) {
> +        *obj = NULL;
>          return;
>      }
>      *obj = g_strdup(opt->str ? opt->str : "");
> diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
> index 02d4233..77cce8b 100644
> --- a/qapi/qmp-input-visitor.c
> +++ b/qapi/qmp-input-visitor.c
> @@ -120,6 +120,9 @@ static void qmp_input_start_struct(Visitor *v, const char *name, void **obj,
>      QObject *qobj = qmp_input_get_object(qiv, name, true);
>      Error *err = NULL;
>
> +    if (obj) {
> +        *obj = NULL;
> +    }
>      if (!qobj || qobject_type(qobj) != QTYPE_QDICT) {
>          error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
>                     "QDict");
> @@ -267,6 +270,7 @@ static void qmp_input_type_str(Visitor *v, const char *name, char **obj,
>      QString *qstr = qobject_to_qstring(qmp_input_get_object(qiv, name, true));
>
>      if (!qstr) {
> +        *obj = NULL;
>          error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
>                     "string");
>          return;
> diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c
> index d604575..797973a 100644
> --- a/qapi/string-input-visitor.c
> +++ b/qapi/string-input-visitor.c
> @@ -293,6 +293,7 @@ static void parse_type_str(Visitor *v, const char *name, char **obj,
>      if (siv->string) {
>          *obj = g_strdup(siv->string);
>      } else {
> +        *obj = NULL;
>          error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
>                     "string");
>      }
> diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c
> index d71727e..d5f80ec 100644
> --- a/tests/test-qmp-input-strict.c
> +++ b/tests/test-qmp-input-strict.c
> @@ -263,7 +263,7 @@ static void test_validate_fail_union_flat_no_discrim(TestInputVisitorData *data,
>  static void test_validate_fail_alternate(TestInputVisitorData *data,
>                                           const void *unused)
>  {
> -    UserDefAlternate *tmp = NULL;
> +    UserDefAlternate *tmp;
>      Visitor *v;
>      Error *err = NULL;
Eric Blake April 29, 2016, 12:10 p.m. UTC | #2
On 04/29/2016 02:28 AM, Markus Armbruster wrote:
> Eric Blake <eblake@redhat.com> writes:
> 
>> Our existing input visitors were not very consistent on errors
>> in a function taking 'TYPE **obj' (that is, start_struct(),
>> start_alternate(), type_str(), and type_any(). next_list() is
>> similar, except that since commit 08f9541, it can't fail).
> 
> Multiple sentences in a parenthesis, ugh :)
> 
>     Our existing input visitors were not very consistent on errors in a
>     function taking 'TYPE **obj'.  These are start_struct(),
>     start_alternate(), type_str(), and type_any().  next_list() is
>     similar, but can't fail (see commit since 08f9541).
> 
> Can touch up on commit.
> 

Yes, sounds better.


>> @@ -51,10 +57,16 @@ void visit_start_alternate(Visitor *v, const char *name,
>>                             GenericAlternate **obj, size_t size,
>>                             bool promote_int, Error **errp)
>>  {
>> +    Error *err = NULL;
>> +
>>      assert(obj && size >= sizeof(GenericAlternate));
>>      if (v->start_alternate) {
>> -        v->start_alternate(v, name, obj, size, promote_int, errp);
>> +        v->start_alternate(v, name, obj, size, promote_int, &err);
>>      }
>> +    if (v->type == VISITOR_INPUT) {
>> +        assert(!err != !*obj);
> 
> Could assert(v->start_alternate && !err != !*obj), to preempt "what if
> !v->start_alternate" worries.  If you like that, I can do it on commit.

Can't hurt :)
Eric Blake April 29, 2016, 12:17 p.m. UTC | #3
On 04/29/2016 06:10 AM, Eric Blake wrote:
> On 04/29/2016 02:28 AM, Markus Armbruster wrote:
>> Eric Blake <eblake@redhat.com> writes:
>>
>>> Our existing input visitors were not very consistent on errors
>>> in a function taking 'TYPE **obj' (that is, start_struct(),
>>> start_alternate(), type_str(), and type_any(). next_list() is
>>> similar, except that since commit 08f9541, it can't fail).
>>
>> Multiple sentences in a parenthesis, ugh :)
>>
>>     Our existing input visitors were not very consistent on errors in a
>>     function taking 'TYPE **obj'.  These are start_struct(),
>>     start_alternate(), type_str(), and type_any().  next_list() is
>>     similar, but can't fail (see commit since 08f9541).
>>
>> Can touch up on commit.
>>
> 
> Yes, sounds better.

Spoke too soon: "see commit since 08f9541" is too wordy, should be
either "see commit 08f9541" or "since commit 08f9541"

> 
> 
>>> @@ -51,10 +57,16 @@ void visit_start_alternate(Visitor *v, const char *name,
>>>                             GenericAlternate **obj, size_t size,
>>>                             bool promote_int, Error **errp)
>>>  {
>>> +    Error *err = NULL;
>>> +
>>>      assert(obj && size >= sizeof(GenericAlternate));
>>>      if (v->start_alternate) {
>>> -        v->start_alternate(v, name, obj, size, promote_int, errp);
>>> +        v->start_alternate(v, name, obj, size, promote_int, &err);
>>>      }
>>> +    if (v->type == VISITOR_INPUT) {
>>> +        assert(!err != !*obj);
>>
>> Could assert(v->start_alternate && !err != !*obj), to preempt "what if
>> !v->start_alternate" worries.  If you like that, I can do it on commit.
> 
> Can't hurt :)
>
Markus Armbruster April 29, 2016, 12:59 p.m. UTC | #4
Eric Blake <eblake@redhat.com> writes:

> On 04/29/2016 06:10 AM, Eric Blake wrote:
>> On 04/29/2016 02:28 AM, Markus Armbruster wrote:
>>> Eric Blake <eblake@redhat.com> writes:
>>>
>>>> Our existing input visitors were not very consistent on errors
>>>> in a function taking 'TYPE **obj' (that is, start_struct(),
>>>> start_alternate(), type_str(), and type_any(). next_list() is
>>>> similar, except that since commit 08f9541, it can't fail).
>>>
>>> Multiple sentences in a parenthesis, ugh :)
>>>
>>>     Our existing input visitors were not very consistent on errors in a
>>>     function taking 'TYPE **obj'.  These are start_struct(),
>>>     start_alternate(), type_str(), and type_any().  next_list() is
>>>     similar, but can't fail (see commit since 08f9541).
>>>
>>> Can touch up on commit.
>>>
>> 
>> Yes, sounds better.
>
> Spoke too soon: "see commit since 08f9541" is too wordy, should be
> either "see commit 08f9541" or "since commit 08f9541"

I'm terribly prone to leaving unwanted words behind when tinkering with
a sentence...  Thanks!

>>>> @@ -51,10 +57,16 @@ void visit_start_alternate(Visitor *v, const char *name,
>>>>                             GenericAlternate **obj, size_t size,
>>>>                             bool promote_int, Error **errp)
>>>>  {
>>>> +    Error *err = NULL;
>>>> +
>>>>      assert(obj && size >= sizeof(GenericAlternate));
>>>>      if (v->start_alternate) {
>>>> -        v->start_alternate(v, name, obj, size, promote_int, errp);
>>>> +        v->start_alternate(v, name, obj, size, promote_int, &err);
>>>>      }
>>>> +    if (v->type == VISITOR_INPUT) {
>>>> +        assert(!err != !*obj);
>>>
>>> Could assert(v->start_alternate && !err != !*obj), to preempt "what if
>>> !v->start_alternate" worries.  If you like that, I can do it on commit.
>> 
>> Can't hurt :)

Done.
diff mbox

Patch

diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
index 3cd7edc..7ad5ff4 100644
--- a/qapi/qapi-visit-core.c
+++ b/qapi/qapi-visit-core.c
@@ -23,7 +23,13 @@ 
 void visit_start_struct(Visitor *v, const char *name, void **obj,
                         size_t size, Error **errp)
 {
-    v->start_struct(v, name, obj, size, errp);
+    Error *err = NULL;
+
+    v->start_struct(v, name, obj, size, &err);
+    if (obj && v->type == VISITOR_INPUT) {
+        assert(!err != !*obj);
+    }
+    error_propagate(errp, err);
 }

 void visit_end_struct(Visitor *v, Error **errp)
@@ -51,10 +57,16 @@  void visit_start_alternate(Visitor *v, const char *name,
                            GenericAlternate **obj, size_t size,
                            bool promote_int, Error **errp)
 {
+    Error *err = NULL;
+
     assert(obj && size >= sizeof(GenericAlternate));
     if (v->start_alternate) {
-        v->start_alternate(v, name, obj, size, promote_int, errp);
+        v->start_alternate(v, name, obj, size, promote_int, &err);
     }
+    if (v->type == VISITOR_INPUT) {
+        assert(!err != !*obj);
+    }
+    error_propagate(errp, err);
 }

 void visit_end_alternate(Visitor *v)
@@ -188,7 +200,14 @@  void visit_type_bool(Visitor *v, const char *name, bool *obj, Error **errp)

 void visit_type_str(Visitor *v, const char *name, char **obj, Error **errp)
 {
-    v->type_str(v, name, obj, errp);
+    Error *err = NULL;
+
+    assert(obj);
+    v->type_str(v, name, obj, &err);
+    if (v->type == VISITOR_INPUT) {
+        assert(!err != !*obj);
+    }
+    error_propagate(errp, err);
 }

 void visit_type_number(Visitor *v, const char *name, double *obj,
@@ -199,7 +218,14 @@  void visit_type_number(Visitor *v, const char *name, double *obj,

 void visit_type_any(Visitor *v, const char *name, QObject **obj, Error **errp)
 {
-    v->type_any(v, name, obj, errp);
+    Error *err = NULL;
+
+    assert(obj);
+    v->type_any(v, name, obj, &err);
+    if (v->type == VISITOR_INPUT) {
+        assert(!err != !*obj);
+    }
+    error_propagate(errp, err);
 }

 static void output_type_enum(Visitor *v, const char *name, int *obj,
diff --git a/qapi/opts-visitor.c b/qapi/opts-visitor.c
index 66aeaed..4cb6436 100644
--- a/qapi/opts-visitor.c
+++ b/qapi/opts-visitor.c
@@ -133,7 +133,7 @@  opts_start_struct(Visitor *v, const char *name, void **obj,
     const QemuOpt *opt;

     if (obj) {
-        *obj = g_malloc0(size > 0 ? size : 1);
+        *obj = g_malloc0(size);
     }
     if (ov->depth++ > 0) {
         return;
@@ -314,6 +314,7 @@  opts_type_str(Visitor *v, const char *name, char **obj, Error **errp)

     opt = lookup_scalar(ov, name, errp);
     if (!opt) {
+        *obj = NULL;
         return;
     }
     *obj = g_strdup(opt->str ? opt->str : "");
diff --git a/qapi/qmp-input-visitor.c b/qapi/qmp-input-visitor.c
index 02d4233..77cce8b 100644
--- a/qapi/qmp-input-visitor.c
+++ b/qapi/qmp-input-visitor.c
@@ -120,6 +120,9 @@  static void qmp_input_start_struct(Visitor *v, const char *name, void **obj,
     QObject *qobj = qmp_input_get_object(qiv, name, true);
     Error *err = NULL;

+    if (obj) {
+        *obj = NULL;
+    }
     if (!qobj || qobject_type(qobj) != QTYPE_QDICT) {
         error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
                    "QDict");
@@ -267,6 +270,7 @@  static void qmp_input_type_str(Visitor *v, const char *name, char **obj,
     QString *qstr = qobject_to_qstring(qmp_input_get_object(qiv, name, true));

     if (!qstr) {
+        *obj = NULL;
         error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
                    "string");
         return;
diff --git a/qapi/string-input-visitor.c b/qapi/string-input-visitor.c
index d604575..797973a 100644
--- a/qapi/string-input-visitor.c
+++ b/qapi/string-input-visitor.c
@@ -293,6 +293,7 @@  static void parse_type_str(Visitor *v, const char *name, char **obj,
     if (siv->string) {
         *obj = g_strdup(siv->string);
     } else {
+        *obj = NULL;
         error_setg(errp, QERR_INVALID_PARAMETER_TYPE, name ? name : "null",
                    "string");
     }
diff --git a/tests/test-qmp-input-strict.c b/tests/test-qmp-input-strict.c
index d71727e..d5f80ec 100644
--- a/tests/test-qmp-input-strict.c
+++ b/tests/test-qmp-input-strict.c
@@ -263,7 +263,7 @@  static void test_validate_fail_union_flat_no_discrim(TestInputVisitorData *data,
 static void test_validate_fail_alternate(TestInputVisitorData *data,
                                          const void *unused)
 {
-    UserDefAlternate *tmp = NULL;
+    UserDefAlternate *tmp;
     Visitor *v;
     Error *err = NULL;