qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Qemu-devel] [PATCH v2 01/17] qapi: fix error propagation


From: Luiz Capitulino
Subject: Re: [Qemu-devel] [PATCH v2 01/17] qapi: fix error propagation
Date: Fri, 13 Jul 2012 13:38:52 -0300

On Wed, 13 Jun 2012 10:22:32 +0200
Laszlo Ersek <address@hidden> wrote:

> From: Paolo Bonzini <address@hidden>
> 
> Don't overwrite / leak previously set errors.

Can you elaborate a bit more? It's not clear to me where the bug is.

More comments below.

> Don't try to end a container that could not be started.
> 
> Signed-off-by: Paolo Bonzini <address@hidden>
> Signed-off-by: Laszlo Ersek <address@hidden>
> ---
>  error.h                        |    4 +-
>  error.c                        |    4 +-
>  qapi/qapi-visit-core.c         |   10 +--
>  tests/test-qmp-input-visitor.c |   24 +++++---
>  docs/qapi-code-gen.txt         |    2 +
>  scripts/qapi-visit.py          |  129 +++++++++++++++++++++++----------------
>  6 files changed, 102 insertions(+), 71 deletions(-)
> 
> diff --git a/error.h b/error.h
> index 45ff6c1..6898f84 100644
> --- a/error.h
> +++ b/error.h
> @@ -24,7 +24,7 @@ typedef struct Error Error;
>  /**
>   * Set an indirect pointer to an error given a printf-style format parameter.
>   * Currently, qerror.h defines these error formats.  This function is not
> - * meant to be used outside of QEMU.
> + * meant to be used outside of QEMU.  Errors after the first are discarded.
>   */
>  void error_set(Error **err, const char *fmt, ...) GCC_FMT_ATTR(2, 3);
>  
> @@ -57,7 +57,7 @@ void error_set_field(Error *err, const char *field, const 
> char *value);
>  /**
>   * Propagate an error to an indirect pointer to an error.  This function will
>   * always transfer ownership of the error reference and handles the case 
> where
> - * dst_err is NULL correctly.
> + * dst_err is NULL correctly.  Errors after the first are discarded.
>   */
>  void error_propagate(Error **dst_err, Error *local_err);
>  
> diff --git a/error.c b/error.c
> index a52b771..0177972 100644
> --- a/error.c
> +++ b/error.c
> @@ -29,7 +29,7 @@ void error_set(Error **errp, const char *fmt, ...)
>      Error *err;
>      va_list ap;
>  
> -    if (errp == NULL) {
> +    if (errp == NULL || *errp != NULL) {

I think we should use assert() here.

If the error is already set, that most probably indicates a bug in the caller, 
as
it's the caller's responsibility to decide which error to return.

>          return;
>      }
>  
> @@ -132,7 +132,7 @@ bool error_is_type(Error *err, const char *fmt)
>  
>  void error_propagate(Error **dst_err, Error *local_err)
>  {
> -    if (dst_err) {
> +    if (dst_err && !*dst_err) {
>          *dst_err = local_err;
>      } else if (local_err) {
>          error_free(local_err);
> diff --git a/qapi/qapi-visit-core.c b/qapi/qapi-visit-core.c
> index ffffbf7..0a513d2 100644
> --- a/qapi/qapi-visit-core.c
> +++ b/qapi/qapi-visit-core.c
> @@ -39,9 +39,8 @@ void visit_start_struct(Visitor *v, void **obj, const char 
> *kind,
>  
>  void visit_end_struct(Visitor *v, Error **errp)
>  {
> -    if (!error_is_set(errp)) {
> -        v->end_struct(v, errp);
> -    }

Is this the ending of a container that could not be started? But if it couldn't
be started, then errp be will be set and we won't try to end it, no?

> +    assert(!error_is_set(errp));
> +    v->end_struct(v, errp);
>  }
>  
>  void visit_start_list(Visitor *v, const char *name, Error **errp)
> @@ -62,9 +61,8 @@ GenericList *visit_next_list(Visitor *v, GenericList 
> **list, Error **errp)
>  
>  void visit_end_list(Visitor *v, Error **errp)
>  {
> -    if (!error_is_set(errp)) {
> -        v->end_list(v, errp);
> -    }
> +    assert(!error_is_set(errp));
> +    v->end_list(v, errp);
>  }
>  
>  void visit_start_optional(Visitor *v, bool *present, const char *name,
> diff --git a/tests/test-qmp-input-visitor.c b/tests/test-qmp-input-visitor.c
> index c30fdc4..8f5a509 100644
> --- a/tests/test-qmp-input-visitor.c
> +++ b/tests/test-qmp-input-visitor.c
> @@ -151,14 +151,22 @@ typedef struct TestStruct
>  static void visit_type_TestStruct(Visitor *v, TestStruct **obj,
>                                    const char *name, Error **errp)
>  {
> -    visit_start_struct(v, (void **)obj, "TestStruct", name, 
> sizeof(TestStruct),
> -                       errp);
> -
> -    visit_type_int(v, &(*obj)->integer, "integer", errp);
> -    visit_type_bool(v, &(*obj)->boolean, "boolean", errp);
> -    visit_type_str(v, &(*obj)->string, "string", errp);
> -
> -    visit_end_struct(v, errp);
> +    Error *err = NULL;
> +    if (!error_is_set(errp)) {
> +        visit_start_struct(v, (void **)obj, "TestStruct", name, 
> sizeof(TestStruct),
> +                           &err);
> +        if (!err) {
> +            visit_type_int(v, &(*obj)->integer, "integer", &err);
> +            visit_type_bool(v, &(*obj)->boolean, "boolean", &err);
> +            visit_type_str(v, &(*obj)->string, "string", &err);
> +
> +            /* Always call end_struct if start_struct succeeded.  */
> +            error_propagate(errp, err);
> +            err = NULL;
> +            visit_end_struct(v, &err);
> +        }
> +        error_propagate(errp, err);
> +    }
>  }
>  
>  static void test_visitor_in_struct(TestInputVisitorData *data,
> diff --git a/docs/qapi-code-gen.txt b/docs/qapi-code-gen.txt
> index ad11767..cccb11e 100644
> --- a/docs/qapi-code-gen.txt
> +++ b/docs/qapi-code-gen.txt
> @@ -220,6 +220,8 @@ Example:
>      #endif
>      address@hidden:~/w/qemu2.git$
>  
> +(The actual structure of the visit_type_* functions is a bit more complex
> +in order to propagate errors correctly and avoid leaking memory).
>  
>  === scripts/qapi-commands.py ===
>  
> diff --git a/scripts/qapi-visit.py b/scripts/qapi-visit.py
> index 8d4e94a..61cf586 100644
> --- a/scripts/qapi-visit.py
> +++ b/scripts/qapi-visit.py
> @@ -17,14 +17,37 @@ import os
>  import getopt
>  import errno
>  
> -def generate_visit_struct_body(field_prefix, members):
> -    ret = ""
> +def generate_visit_struct_body(field_prefix, name, members):
> +    ret = mcgen('''
> +if (!error_is_set(errp)) {
> +''')
> +    push_indent()
> +
>      if len(field_prefix):
>          field_prefix = field_prefix + "."
> +        ret += mcgen('''
> +Error **errp = &err; /* from outer scope */
> +Error *err = NULL;
> +visit_start_struct(m, NULL, "", "%(name)s", 0, &err);
> +''',
> +                name=name)
> +    else:
> +        ret += mcgen('''
> +Error *err = NULL;
> +visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), 
> &err);
> +''',
> +                name=name)
> +
> +    ret += mcgen('''
> +if (!err) {
> +    assert(!obj || *obj);
> +''')
> +
> +    push_indent()
>      for argname, argentry, optional, structured in parse_args(members):
>          if optional:
>              ret += mcgen('''
> -visit_start_optional(m, (obj && *obj) ? &(*obj)->%(c_prefix)shas_%(c_name)s 
> : NULL, "%(name)s", errp);
> +visit_start_optional(m, obj ? &(*obj)->%(c_prefix)shas_%(c_name)s : NULL, 
> "%(name)s", &err);
>  if ((*obj)->%(prefix)shas_%(c_name)s) {
>  ''',
>                           c_prefix=c_var(field_prefix), prefix=field_prefix,
> @@ -32,17 +55,10 @@ if ((*obj)->%(prefix)shas_%(c_name)s) {
>              push_indent()
>  
>          if structured:
> -            ret += mcgen('''
> -visit_start_struct(m, NULL, "", "%(name)s", 0, errp);
> -''',
> -                         name=argname)
> -            ret += generate_visit_struct_body(field_prefix + argname, 
> argentry)
> -            ret += mcgen('''
> -visit_end_struct(m, errp);
> -''')
> +            ret += generate_visit_struct_body(field_prefix + argname, 
> argname, argentry)
>          else:
>              ret += mcgen('''
> -visit_type_%(type)s(m, (obj && *obj) ? &(*obj)->%(c_prefix)s%(c_name)s : 
> NULL, "%(name)s", errp);
> +visit_type_%(type)s(m, obj ? &(*obj)->%(c_prefix)s%(c_name)s : NULL, 
> "%(name)s", &err);
>  ''',
>                           c_prefix=c_var(field_prefix), prefix=field_prefix,
>                           type=type_name(argentry), c_name=c_var(argname),
> @@ -52,7 +68,19 @@ visit_type_%(type)s(m, (obj && *obj) ? 
> &(*obj)->%(c_prefix)s%(c_name)s : NULL, "
>              pop_indent()
>              ret += mcgen('''
>  }
> -visit_end_optional(m, errp);
> +visit_end_optional(m, &err);
> +''')
> +
> +    pop_indent()
> +    pop_indent()
> +    ret += mcgen('''
> +        /* Always call end_struct if start_struct succeeded.  */
> +        error_propagate(errp, err);
> +        err = NULL;
> +        visit_end_struct(m, &err);
> +    }
> +    error_propagate(errp, err);
> +}
>  ''')
>      return ret
>  
> @@ -61,22 +89,14 @@ def generate_visit_struct(name, members):
>  
>  void visit_type_%(name)s(Visitor *m, %(name)s ** obj, const char *name, 
> Error **errp)
>  {
> -    if (error_is_set(errp)) {
> -        return;
> -    }
> -    visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), 
> errp);
> -    if (obj && !*obj) {
> -        goto end;
> -    }
>  ''',
>                  name=name)
> +
>      push_indent()
> -    ret += generate_visit_struct_body("", members)
> +    ret += generate_visit_struct_body("", name, members)
>      pop_indent()
>  
>      ret += mcgen('''
> -end:
> -    visit_end_struct(m, errp);
>  }
>  ''')
>      return ret
> @@ -87,18 +107,22 @@ def generate_visit_list(name, members):
>  void visit_type_%(name)sList(Visitor *m, %(name)sList ** obj, const char 
> *name, Error **errp)
>  {
>      GenericList *i, **prev = (GenericList **)obj;
> +    Error *err = NULL;
>  
> -    if (error_is_set(errp)) {
> -        return;
> -    }
> -    visit_start_list(m, name, errp);
> -
> -    for (; (i = visit_next_list(m, prev, errp)) != NULL; prev = &i) {
> -        %(name)sList *native_i = (%(name)sList *)i;
> -        visit_type_%(name)s(m, &native_i->value, NULL, errp);
> +    if (!error_is_set(errp)) {
> +        visit_start_list(m, name, &err);
> +        if (!err) {
> +            for (; (i = visit_next_list(m, prev, &err)) != NULL; prev = &i) {
> +                %(name)sList *native_i = (%(name)sList *)i;
> +                visit_type_%(name)s(m, &native_i->value, NULL, &err);
> +            }
> +            /* Always call end_list if start_list succeeded.  */
> +            error_propagate(errp, err);
> +            err = NULL;
> +            visit_end_list(m, &err);
> +        }
> +        error_propagate(errp, err);
>      }
> -
> -    visit_end_list(m, errp);
>  }
>  ''',
>                  name=name)
> @@ -122,27 +146,21 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, 
> const char *name, Error **
>  {
>      Error *err = NULL;
>  
> -    if (error_is_set(errp)) {
> -        return;
> -    }
> -    visit_start_struct(m, (void **)obj, "%(name)s", name, sizeof(%(name)s), 
> &err);
> -    if (obj && !*obj) {
> -        goto end;
> -    }
> -    visit_type_%(name)sKind(m, &(*obj)->kind, "type", &err);
> -    if (err) {
> -        error_propagate(errp, err);
> -        goto end;
> -    }
> -    switch ((*obj)->kind) {
> +    if (!error_is_set(errp)) {
> +        visit_start_struct(m, (void **)obj, "%(name)s", name, 
> sizeof(%(name)s), &err);
> +        if (!err) {
> +            visit_type_%(name)sKind(m, &(*obj)->kind, "type", &err);
> +        }
> +        if (!err) {
> +            switch ((*obj)->kind) {
>  ''',
>                   name=name)
>  
>      for key in members:
>          ret += mcgen('''
> -    case %(abbrev)s_KIND_%(enum)s:
> -        visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "data", errp);
> -        break;
> +            case %(abbrev)s_KIND_%(enum)s:
> +                visit_type_%(c_type)s(m, &(*obj)->%(c_name)s, "data", &err);
> +                break;
>  ''',
>                  abbrev = de_camel_case(name).upper(),
>                  enum = c_fun(de_camel_case(key)).upper(),
> @@ -150,11 +168,16 @@ void visit_type_%(name)s(Visitor *m, %(name)s ** obj, 
> const char *name, Error **
>                  c_name=c_fun(key))
>  
>      ret += mcgen('''
> -    default:
> -        abort();
> +            default:
> +                abort();
> +            }
> +        }
> +        /* Always call end_struct if start_struct succeeded.  */
> +        error_propagate(errp, err);
> +        err = NULL;
> +        visit_end_struct(m, &err);
>      }
> -end:
> -    visit_end_struct(m, errp);
> +    error_propagate(errp, err);
>  }
>  ''')
>  




reply via email to

[Prev in Thread] Current Thread [Next in Thread]