qapi: Fix to reject 'data': 'mumble' in struct

A struct's 'data' must be a JSON object defining the struct's members.
The QAPI code generator incorrectly accepts a JSON string instead, and
then crashes in QAPISchema._make_members() called from
._def_struct_type().

Fix to reject it: factor check_type_implicit() out of
check_type_name_or_implicit(), and switch check_struct() to use it
instead.  Also add a test case.

Signed-off-by: Markus Armbruster <armbru@redhat.com>
Message-Id: <20230316071325.492471-9-armbru@redhat.com>
Reviewed-by: Eric Blake <eblake@redhat.com>
[More detailed commit message]
This commit is contained in:
Markus Armbruster 2023-03-16 08:13:19 +01:00
parent 6f2ab6090d
commit e2050ef633
5 changed files with 20 additions and 9 deletions

View File

@ -354,14 +354,14 @@ def check_type_name_or_array(value: Optional[object],
source)
def check_type_name_or_implicit(value: Optional[object],
info: QAPISourceInfo, source: str,
parent_name: Optional[str]) -> None:
def check_type_implicit(value: Optional[object],
info: QAPISourceInfo, source: str,
parent_name: Optional[str]) -> None:
"""
Normalize and validate an optional implicit struct type.
Accept ``None``, ``str``, or a ``dict`` defining an implicit
struct type. The latter is normalized in place.
Accept ``None`` or a ``dict`` defining an implicit struct type.
The latter is normalized in place.
:param value: The value to check.
:param info: QAPI schema source file information.
@ -377,9 +377,6 @@ def check_type_name_or_implicit(value: Optional[object],
if value is None:
return
if isinstance(value, str):
return
if not isinstance(value, dict):
raise QAPISemError(info,
"%s should be an object or type name" % source)
@ -401,6 +398,15 @@ def check_type_name_or_implicit(value: Optional[object],
check_type_name_or_array(arg['type'], info, key_source)
def check_type_name_or_implicit(value: Optional[object],
info: QAPISourceInfo, source: str,
parent_name: Optional[str]) -> None:
if value is None or isinstance(value, str):
return
check_type_implicit(value, info, source, parent_name)
def check_features(features: Optional[object],
info: QAPISourceInfo) -> None:
"""
@ -486,7 +492,7 @@ def check_struct(expr: QAPIExpression) -> None:
name = cast(str, expr['struct']) # Checked in check_exprs
members = expr['data']
check_type_name_or_implicit(members, expr.info, "'data'", name)
check_type_implicit(members, expr.info, "'data'", name)
check_type_name(expr.get('base'), expr.info, "'base'")

View File

@ -164,6 +164,7 @@ schemas = [
'struct-base-clash-deep.json',
'struct-base-clash.json',
'struct-data-invalid.json',
'struct-data-typename.json',
'struct-member-if-invalid.json',
'struct-member-invalid-dict.json',
'struct-member-invalid.json',

View File

@ -0,0 +1,2 @@
struct-data-typename.json: In struct 'Stru2':
struct-data-typename.json:2: 'data' should be an object or type name

View File

@ -0,0 +1,2 @@
{ 'struct': 'Stru1', 'data': {} }
{ 'struct': 'Stru2', 'data': 'Stru1' }