qemu with hax to log dma reads & writes jcs.org/2018/11/12/vfio

qapi script: check correctness of union

Since line info is remembered as QAPISchema.line now, this patch
uses it as additional info for every expr in QAPISchema inside qapi.py,
then improves error message with it in checking of exprs.

For common union the patch will check whether base is a valid complex
type if specified. For flat union it will check whether base presents,
whether discriminator is found in base, whether the key of every branch
is correct when discriminator is an enum type.

Signed-off-by: Wenchao Xia <wenchaoqemu@gmail.com>
Reviewed-by: Markus Armbruster <armbru@redhat.com>
Signed-off-by: Luiz Capitulino <lcapitulino@redhat.com>

authored by

Wenchao Xia and committed by
Luiz Capitulino
b86b05ed 515b943a

+151 -3
+86 -2
scripts/qapi.py
··· 50 50 def __str__(self): 51 51 return "%s:%s:%s: %s" % (self.fp.name, self.line, self.col, self.msg) 52 52 53 + class QAPIExprError(Exception): 54 + def __init__(self, expr_info, msg): 55 + self.fp = expr_info['fp'] 56 + self.line = expr_info['line'] 57 + self.msg = msg 58 + 59 + def __str__(self): 60 + return "%s:%s: %s" % (self.fp.name, self.line, self.msg) 61 + 53 62 class QAPISchema: 54 63 55 64 def __init__(self, fp): ··· 64 73 self.accept() 65 74 66 75 while self.tok != None: 67 - self.exprs.append(self.get_expr(False)) 76 + expr_info = {'fp': fp, 'line': self.line} 77 + expr_elem = {'expr': self.get_expr(False), 78 + 'info': expr_info} 79 + self.exprs.append(expr_elem) 68 80 69 81 def accept(self): 70 82 while True: ··· 162 174 raise QAPISchemaError(self, 'Expected "{", "[" or string') 163 175 return expr 164 176 177 + def find_base_fields(base): 178 + base_struct_define = find_struct(base) 179 + if not base_struct_define: 180 + return None 181 + return base_struct_define['data'] 182 + 183 + def check_union(expr, expr_info): 184 + name = expr['union'] 185 + base = expr.get('base') 186 + discriminator = expr.get('discriminator') 187 + members = expr['data'] 188 + 189 + # If the object has a member 'base', its value must name a complex type. 190 + if base: 191 + base_fields = find_base_fields(base) 192 + if not base_fields: 193 + raise QAPIExprError(expr_info, 194 + "Base '%s' is not a valid type" 195 + % base) 196 + 197 + # If the union object has no member 'discriminator', it's an 198 + # ordinary union. 199 + if not discriminator: 200 + enum_define = None 201 + 202 + # Else if the value of member 'discriminator' is {}, it's an 203 + # anonymous union. 204 + elif discriminator == {}: 205 + enum_define = None 206 + 207 + # Else, it's a flat union. 208 + else: 209 + # The object must have a member 'base'. 210 + if not base: 211 + raise QAPIExprError(expr_info, 212 + "Flat union '%s' must have a base field" 213 + % name) 214 + # The value of member 'discriminator' must name a member of the 215 + # base type. 216 + discriminator_type = base_fields.get(discriminator) 217 + if not discriminator_type: 218 + raise QAPIExprError(expr_info, 219 + "Discriminator '%s' is not a member of base " 220 + "type '%s'" 221 + % (discriminator, base)) 222 + enum_define = find_enum(discriminator_type) 223 + 224 + # Check every branch 225 + for (key, value) in members.items(): 226 + # If this named member's value names an enum type, then all members 227 + # of 'data' must also be members of the enum type. 228 + if enum_define and not key in enum_define['enum_values']: 229 + raise QAPIExprError(expr_info, 230 + "Discriminator value '%s' is not found in " 231 + "enum '%s'" % 232 + (key, enum_define["enum_name"])) 233 + # Todo: add checking for values. Key is checked as above, value can be 234 + # also checked here, but we need more functions to handle array case. 235 + 236 + def check_exprs(schema): 237 + for expr_elem in schema.exprs: 238 + expr = expr_elem['expr'] 239 + if expr.has_key('union'): 240 + check_union(expr, expr_elem['info']) 241 + 165 242 def parse_schema(fp): 166 243 try: 167 244 schema = QAPISchema(fp) ··· 171 248 172 249 exprs = [] 173 250 174 - for expr in schema.exprs: 251 + for expr_elem in schema.exprs: 252 + expr = expr_elem['expr'] 175 253 if expr.has_key('enum'): 176 254 add_enum(expr['enum'], expr['data']) 177 255 elif expr.has_key('union'): ··· 180 258 elif expr.has_key('type'): 181 259 add_struct(expr) 182 260 exprs.append(expr) 261 + 262 + try: 263 + check_exprs(schema) 264 + except QAPIExprError, e: 265 + print >>sys.stderr, e 266 + exit(1) 183 267 184 268 return exprs 185 269
+3 -1
tests/Makefile
··· 143 143 qapi-schema-test.json quoted-structural-chars.json \ 144 144 trailing-comma-list.json trailing-comma-object.json \ 145 145 unclosed-list.json unclosed-object.json unclosed-string.json \ 146 - duplicate-key.json) 146 + duplicate-key.json union-invalid-base.json flat-union-no-base.json \ 147 + flat-union-invalid-discriminator.json \ 148 + flat-union-invalid-branch-key.json) 147 149 148 150 GENERATED_HEADERS += tests/test-qapi-types.h tests/test-qapi-visit.h tests/test-qmp-commands.h 149 151
+1
tests/qapi-schema/flat-union-invalid-branch-key.err
··· 1 + <stdin>:13: Discriminator value 'value_wrong' is not found in enum 'TestEnum'
+1
tests/qapi-schema/flat-union-invalid-branch-key.exit
··· 1 + 1
+17
tests/qapi-schema/flat-union-invalid-branch-key.json
··· 1 + { 'enum': 'TestEnum', 2 + 'data': [ 'value1', 'value2' ] } 3 + 4 + { 'type': 'TestBase', 5 + 'data': { 'enum1': 'TestEnum' } } 6 + 7 + { 'type': 'TestTypeA', 8 + 'data': { 'string': 'str' } } 9 + 10 + { 'type': 'TestTypeB', 11 + 'data': { 'integer': 'int' } } 12 + 13 + { 'union': 'TestUnion', 14 + 'base': 'TestBase', 15 + 'discriminator': 'enum1', 16 + 'data': { 'value_wrong': 'TestTypeA', 17 + 'value2': 'TestTypeB' } }
tests/qapi-schema/flat-union-invalid-branch-key.out

This is a binary file and will not be displayed.

+1
tests/qapi-schema/flat-union-invalid-discriminator.err
··· 1 + <stdin>:13: Discriminator 'enum_wrong' is not a member of base type 'TestBase'
+1
tests/qapi-schema/flat-union-invalid-discriminator.exit
··· 1 + 1
+17
tests/qapi-schema/flat-union-invalid-discriminator.json
··· 1 + { 'enum': 'TestEnum', 2 + 'data': [ 'value1', 'value2' ] } 3 + 4 + { 'type': 'TestBase', 5 + 'data': { 'enum1': 'TestEnum' } } 6 + 7 + { 'type': 'TestTypeA', 8 + 'data': { 'string': 'str' } } 9 + 10 + { 'type': 'TestTypeB', 11 + 'data': { 'integer': 'int' } } 12 + 13 + { 'union': 'TestUnion', 14 + 'base': 'TestBase', 15 + 'discriminator': 'enum_wrong', 16 + 'data': { 'value1': 'TestTypeA', 17 + 'value2': 'TestTypeB' } }
tests/qapi-schema/flat-union-invalid-discriminator.out

This is a binary file and will not be displayed.

+1
tests/qapi-schema/flat-union-no-base.err
··· 1 + <stdin>:7: Flat union 'TestUnion' must have a base field
+1
tests/qapi-schema/flat-union-no-base.exit
··· 1 + 1
+10
tests/qapi-schema/flat-union-no-base.json
··· 1 + { 'type': 'TestTypeA', 2 + 'data': { 'string': 'str' } } 3 + 4 + { 'type': 'TestTypeB', 5 + 'data': { 'integer': 'int' } } 6 + 7 + { 'union': 'TestUnion', 8 + 'discriminator': 'enum1', 9 + 'data': { 'value1': 'TestTypeA', 10 + 'value2': 'TestTypeB' } }
tests/qapi-schema/flat-union-no-base.out

This is a binary file and will not be displayed.

+1
tests/qapi-schema/union-invalid-base.err
··· 1 + <stdin>:7: Base 'TestBaseWrong' is not a valid type
+1
tests/qapi-schema/union-invalid-base.exit
··· 1 + 1
+10
tests/qapi-schema/union-invalid-base.json
··· 1 + { 'type': 'TestTypeA', 2 + 'data': { 'string': 'str' } } 3 + 4 + { 'type': 'TestTypeB', 5 + 'data': { 'integer': 'int' } } 6 + 7 + { 'union': 'TestUnion', 8 + 'base': 'TestBaseWrong', 9 + 'data': { 'value1': 'TestTypeA', 10 + 'value2': 'TestTypeB' } }
tests/qapi-schema/union-invalid-base.out

This is a binary file and will not be displayed.