qemu-devel
[Top][All Lists]
Advanced

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

Re: [Qemu-devel] [PATCH v5 21/28] qapi: Require valid names


From: Markus Armbruster
Subject: Re: [Qemu-devel] [PATCH v5 21/28] qapi: Require valid names
Date: Fri, 27 Mar 2015 09:48:05 +0100
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/24.3 (gnu/linux)

Eric Blake <address@hidden> writes:

> Previous commits demonstrated that the generator overlooked various
> bad naming situations:
> - types, commands, and events need a valid name
> - union and alternate branches cannot be marked optional
>
> The set of valid names includes [a-zA-Z0-9._-] (where '.' is
> useful only in downstream extensions).
>
> Signed-off-by: Eric Blake <address@hidden>
> ---
>  scripts/qapi.py                                    | 57 
> ++++++++++++++++------
>  tests/qapi-schema/bad-ident.err                    |  1 +
>  tests/qapi-schema/bad-ident.exit                   |  2 +-
>  tests/qapi-schema/bad-ident.json                   |  2 +-
>  tests/qapi-schema/bad-ident.out                    |  3 --
>  tests/qapi-schema/flat-union-bad-discriminator.err |  2 +-
>  .../flat-union-optional-discriminator.err          |  1 +
>  .../flat-union-optional-discriminator.exit         |  2 +-
>  .../flat-union-optional-discriminator.json         |  2 +-
>  .../flat-union-optional-discriminator.out          |  5 --
>  tests/qapi-schema/union-optional-branch.err        |  1 +
>  tests/qapi-schema/union-optional-branch.exit       |  2 +-
>  tests/qapi-schema/union-optional-branch.json       |  2 +-
>  tests/qapi-schema/union-optional-branch.out        |  3 --
>  14 files changed, 53 insertions(+), 32 deletions(-)
>
> diff --git a/scripts/qapi.py b/scripts/qapi.py
> index c42683b..ed5385a 100644
> --- a/scripts/qapi.py
> +++ b/scripts/qapi.py
> @@ -15,6 +15,7 @@ import re
>  from ordereddict import OrderedDict
>  import os
>  import sys
> +import string
>
>  builtin_types = {
>      'str':      'QTYPE_QSTRING',
> @@ -276,8 +277,27 @@ def discriminator_find_enum_define(expr):
>
>      return find_enum(discriminator_type)
>
> +valid_characters = set(string.ascii_letters + string.digits + '.' + '-' + 
> '_')

strings.ascii_letters depends on the locale...

> +def check_name(expr_info, source, name, allow_optional = False):
> +    membername = name
> +
> +    if not isinstance(name, str):
> +        raise QAPIExprError(expr_info,
> +                            "%s requires a string name" % source)
> +    if name == '**':
> +        return

Doesn't this permit '**' anywhere, not just as pseudo-type in command
arguments and results?

> +    if name.startswith('*'):
> +        membername = name[1:]
> +        if not allow_optional:
> +            raise QAPIExprError(expr_info,
> +                                "%s does not allow optional name '%s'"
> +                                % (source, name))
> +    if not set(membername) <= valid_characters:

... so this check would break if we called locale.setlocale() in this
program.  While I don't think we need to worry about it, I think you
could just as well use something like

    valid_name = re.compile(r"^[A-Za-z0-9-._]+$")

    if not valid_name.match(membername):

> +        raise QAPIExprError(expr_info,
> +                            "%s uses invalid name '%s'" % (source, name))
> +
>  def check_type(expr_info, source, data, allow_array = False,
> -               allowed_metas = [], allow_dict = True):
> +               allowed_metas = [], allow_dict = True, allow_optional = 
> False):
>      global all_names
>
>      if data is None:
> @@ -317,21 +337,23 @@ def check_type(expr_info, source, data, allow_array = 
> False,
>          raise QAPIExprError(expr_info,
>                              "%s should be a type name" % source)
>      for (key, value) in data.items():
> +        check_name(expr_info, "Member of %s" % source, key,
> +                   allow_optional=allow_optional)
>          check_type(expr_info, "Member '%s' of %s" % (key, source), value,
>                     allow_array=True,
>                     allowed_metas=['built-in', 'union', 'alternate', 'struct',
>                                    'enum'],
> -                   allow_dict=True)
> +                   allow_dict=True, allow_optional=True)
>
>  def check_command(expr, expr_info):
>      name = expr['command']
>      check_type(expr_info, "'data' for command '%s'" % name,
>                 expr.get('data'),
> -               allowed_metas=['union', 'struct'])
> +               allowed_metas=['union', 'struct'], allow_optional=True)
>      check_type(expr_info, "'returns' for command '%s'" % name,
>                 expr.get('returns'), allow_array=True,
>                 allowed_metas=['built-in', 'union', 'alternate', 'struct',
> -                              'enum'])
> +                              'enum'], allow_optional=True)
>
>  def check_event(expr, expr_info):
>      global events
> @@ -345,7 +367,8 @@ def check_event(expr, expr_info):
>                              % name)
>      events.append(name)
>      check_type(expr_info, "'data' for event '%s'" % name,
> -               expr.get('data'), allowed_metas=['union', 'struct'])
> +               expr.get('data'), allowed_metas=['union', 'struct'],
> +               allow_optional=True)
>      if params:
>          for argname, argentry, optional, structured in parse_args(params):
>              if structured:
> @@ -385,12 +408,10 @@ def check_union(expr, expr_info):
>                                  "Base '%s' is not a valid base type"
>                                  % base)
>
> -        # The value of member 'discriminator' must name a member of the
> -        # base type.
> -        if not isinstance(discriminator, str):
> -            raise QAPIExprError(expr_info,
> -                                "Flat union '%s' must have a string "
> -                                "discriminator field" % name)
> +        # The value of member 'discriminator' must name a non-optional
> +        # member of the base type.
> +        check_name(expr_info, "Discriminator of flat union '%s'" % name,
> +                   discriminator)
>          discriminator_type = base_fields.get(discriminator)
>          if not discriminator_type:
>              raise QAPIExprError(expr_info,

What happens when I try 'discriminator': '**'?

> @@ -406,6 +427,8 @@ def check_union(expr, expr_info):
>
>      # Check every branch
>      for (key, value) in members.items():
> +        check_name(expr_info, "Member of union '%s'" % name, key)
> +
>          # Each value must name a known type
>          check_type(expr_info, "Member '%s' of union '%s'" % (key, name),
>                     value, allow_array=True,
> @@ -439,6 +462,8 @@ def check_alternate(expr, expr_info):
>
>      # Check every branch
>      for (key, value) in members.items():
> +        check_name(expr_info, "Member of alternate '%s'" % name, key)
> +
>          # Check for conflicts in the generated enum
>          c_key = _generate_enum_string(key)
>          if c_key in values:
> @@ -485,7 +510,8 @@ def check_struct(expr, expr_info):
>      name = expr['type']
>      members = expr['data']
>
> -    check_type(expr_info, "'data' for type '%s'" % name, members)
> +    check_type(expr_info, "'data' for type '%s'" % name, members,
> +               allow_optional=True)
>      check_type(expr_info, "'base' for type '%s'" % name, expr.get('base'),
>                 allowed_metas=['struct'], allow_dict=False)
>
> @@ -676,8 +702,11 @@ def type_name(name):
>          return c_list_type(name[0])
>      return name
>
> -def add_name(name, info, meta, implicit = False):
> +def add_name(name, info, meta, implicit = False, source = None):
>      global all_names
> +    if not source:
> +        source = "'%s'" % meta
> +    check_name(info, source, name)
>      if name in all_names:
>          raise QAPIExprError(info,
>                              "%s '%s' is already defined"
> @@ -691,7 +720,7 @@ def add_name(name, info, meta, implicit = False):
>  def add_struct(definition, info):
>      global struct_types
>      name = definition['type']
> -    add_name(name, info, 'struct')
> +    add_name(name, info, 'struct', source="'type'")
>      struct_types.append(definition)
>
>  def find_struct(name):
[...]



reply via email to

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