@@ -62,7 +62,8 @@ fn verify_table(
62
62
63
63
let mut table_verifier = verifier. visit_table ( table_pos) ?;
64
64
65
- for field in & table_object. fields ( ) {
65
+ let fields = table_object. fields ( ) ;
66
+ for field in & fields {
66
67
let field_name = field. name ( ) . to_owned ( ) ;
67
68
table_verifier = match field. type_ ( ) . base_type ( ) {
68
69
BaseType :: UType | BaseType :: UByte => {
@@ -195,91 +196,30 @@ fn verify_vector<'a, 'b, 'c>(
195
196
buf_loc_to_obj_idx : & mut HashMap < usize , i32 > ,
196
197
) -> FlatbufferResult < TableVerifier < ' a , ' b , ' c > > {
197
198
let field_name = field. name ( ) . to_owned ( ) ;
199
+ macro_rules! visit_vec {
200
+ ( $t: ty) => {
201
+ table_verifier
202
+ . visit_field:: <ForwardsUOffset <Vector <$t>>>(
203
+ field_name,
204
+ field. offset( ) ,
205
+ field. required( ) ,
206
+ )
207
+ . map_err( FlatbufferError :: VerificationError )
208
+ } ;
209
+ }
198
210
match field. type_ ( ) . element ( ) {
199
- BaseType :: UType | BaseType :: UByte => table_verifier
200
- . visit_field :: < ForwardsUOffset < Vector < u8 > > > (
201
- field_name,
202
- field. offset ( ) ,
203
- field. required ( ) ,
204
- )
205
- . map_err ( FlatbufferError :: VerificationError ) ,
206
- BaseType :: Bool => table_verifier
207
- . visit_field :: < ForwardsUOffset < Vector < bool > > > (
208
- field_name,
209
- field. offset ( ) ,
210
- field. required ( ) ,
211
- )
212
- . map_err ( FlatbufferError :: VerificationError ) ,
213
- BaseType :: Byte => table_verifier
214
- . visit_field :: < ForwardsUOffset < Vector < i8 > > > (
215
- field_name,
216
- field. offset ( ) ,
217
- field. required ( ) ,
218
- )
219
- . map_err ( FlatbufferError :: VerificationError ) ,
220
- BaseType :: Short => table_verifier
221
- . visit_field :: < ForwardsUOffset < Vector < i16 > > > (
222
- field_name,
223
- field. offset ( ) ,
224
- field. required ( ) ,
225
- )
226
- . map_err ( FlatbufferError :: VerificationError ) ,
227
- BaseType :: UShort => table_verifier
228
- . visit_field :: < ForwardsUOffset < Vector < u16 > > > (
229
- field_name,
230
- field. offset ( ) ,
231
- field. required ( ) ,
232
- )
233
- . map_err ( FlatbufferError :: VerificationError ) ,
234
- BaseType :: Int => table_verifier
235
- . visit_field :: < ForwardsUOffset < Vector < i32 > > > (
236
- field_name,
237
- field. offset ( ) ,
238
- field. required ( ) ,
239
- )
240
- . map_err ( FlatbufferError :: VerificationError ) ,
241
- BaseType :: UInt => table_verifier
242
- . visit_field :: < ForwardsUOffset < Vector < u32 > > > (
243
- field_name,
244
- field. offset ( ) ,
245
- field. required ( ) ,
246
- )
247
- . map_err ( FlatbufferError :: VerificationError ) ,
248
- BaseType :: Long => table_verifier
249
- . visit_field :: < ForwardsUOffset < Vector < i64 > > > (
250
- field_name,
251
- field. offset ( ) ,
252
- field. required ( ) ,
253
- )
254
- . map_err ( FlatbufferError :: VerificationError ) ,
255
- BaseType :: ULong => table_verifier
256
- . visit_field :: < ForwardsUOffset < Vector < u64 > > > (
257
- field_name,
258
- field. offset ( ) ,
259
- field. required ( ) ,
260
- )
261
- . map_err ( FlatbufferError :: VerificationError ) ,
262
- BaseType :: Float => table_verifier
263
- . visit_field :: < ForwardsUOffset < Vector < f32 > > > (
264
- field_name,
265
- field. offset ( ) ,
266
- field. required ( ) ,
267
- )
268
- . map_err ( FlatbufferError :: VerificationError ) ,
269
- BaseType :: Double => table_verifier
270
- . visit_field :: < ForwardsUOffset < Vector < f64 > > > (
271
- field_name,
272
- field. offset ( ) ,
273
- field. required ( ) ,
274
- )
275
- . map_err ( FlatbufferError :: VerificationError ) ,
276
- BaseType :: String => table_verifier
277
- . visit_field :: < ForwardsUOffset < Vector < ForwardsUOffset < & str > > > > (
278
- field_name,
279
- field. offset ( ) ,
280
- field. required ( ) ,
281
- )
282
- . map_err ( FlatbufferError :: VerificationError ) ,
211
+ BaseType :: UType | BaseType :: UByte => visit_vec ! ( u8 ) ,
212
+ BaseType :: Bool => visit_vec ! ( bool ) ,
213
+ BaseType :: Byte => visit_vec ! ( i8 ) ,
214
+ BaseType :: Short => visit_vec ! ( i16 ) ,
215
+ BaseType :: UShort => visit_vec ! ( u16 ) ,
216
+ BaseType :: Int => visit_vec ! ( i32 ) ,
217
+ BaseType :: UInt => visit_vec ! ( u32 ) ,
218
+ BaseType :: Long => visit_vec ! ( i64 ) ,
219
+ BaseType :: ULong => visit_vec ! ( u64 ) ,
220
+ BaseType :: Float => visit_vec ! ( f32 ) ,
221
+ BaseType :: Double => visit_vec ! ( f64 ) ,
222
+ BaseType :: String => visit_vec ! ( ForwardsUOffset <& str >) ,
283
223
BaseType :: Obj => {
284
224
if let Some ( field_pos) = table_verifier. deref ( field. offset ( ) ) ? {
285
225
let verifier = table_verifier. verifier ( ) ;
@@ -333,12 +273,150 @@ fn verify_vector<'a, 'b, 'c>(
333
273
}
334
274
Ok ( table_verifier)
335
275
}
276
+ BaseType :: Union => {
277
+ verify_vector_of_unions ( table_verifier, field, schema, verified, buf_loc_to_obj_idx)
278
+ }
336
279
other => {
337
280
return Err ( FlatbufferError :: UnsupportedVectorElementType ( other) ) ;
338
281
}
339
282
}
340
283
}
341
284
285
+ fn verify_vector_of_unions < ' a , ' b , ' c > (
286
+ mut table_verifier : TableVerifier < ' a , ' b , ' c > ,
287
+ field : & Field ,
288
+ schema : & Schema ,
289
+ verified : & mut [ bool ] ,
290
+ buf_loc_to_obj_idx : & mut HashMap < usize , i32 > ,
291
+ ) -> FlatbufferResult < TableVerifier < ' a , ' b , ' c > > {
292
+ let field_type = field. type_ ( ) ;
293
+ // If the schema is valid, none of these asserts can fail.
294
+ debug_assert_eq ! ( field_type. base_type( ) , BaseType :: Vector ) ;
295
+ debug_assert_eq ! ( field_type. element( ) , BaseType :: Union ) ;
296
+ let child_enum_idx = field_type. index ( ) ;
297
+ let child_enum = schema. enums ( ) . get ( child_enum_idx. try_into ( ) ?) ;
298
+ debug_assert ! ( !child_enum. values( ) . is_empty( ) ) ;
299
+
300
+ // Assuming the schema is valid, the previous field must be the enum vector, which consists of
301
+ // of 1-byte enums.
302
+ let enum_field_offset = field. offset ( ) - u16:: try_from ( SIZE_VOFFSET ) ?;
303
+
304
+ // Either both vectors must be present, or both must be absent.
305
+ let ( value_field_pos, enum_field_pos) = match (
306
+ table_verifier. deref ( field. offset ( ) ) ?,
307
+ table_verifier. deref ( enum_field_offset) ?,
308
+ ) {
309
+ ( Some ( value_field_pos) , Some ( enum_field_pos) ) => ( value_field_pos, enum_field_pos) ,
310
+ ( None , None ) => {
311
+ if field. required ( ) {
312
+ return InvalidFlatbuffer :: new_missing_required ( field. name ( ) . to_owned ( ) ) ?;
313
+ } else {
314
+ return Ok ( table_verifier) ;
315
+ }
316
+ }
317
+ _ => {
318
+ return InvalidFlatbuffer :: new_inconsistent_union (
319
+ format ! ( "{}_type" , field. name( ) ) ,
320
+ field. name ( ) . to_owned ( ) ,
321
+ ) ?;
322
+ }
323
+ } ;
324
+
325
+ let verifier = table_verifier. verifier ( ) ;
326
+ let enum_vector_offset = verifier. get_uoffset ( enum_field_pos) ?;
327
+ let enum_vector_pos = enum_field_pos. saturating_add ( enum_vector_offset. try_into ( ) ?) ;
328
+ let enum_vector_len = verifier. get_uoffset ( enum_vector_pos) ?;
329
+ let enum_vector_start = enum_vector_pos. saturating_add ( SIZE_UOFFSET ) ;
330
+
331
+ let value_vector_offset = verifier. get_uoffset ( value_field_pos) ?;
332
+ let value_vector_pos = value_field_pos. saturating_add ( value_vector_offset. try_into ( ) ?) ;
333
+ let value_vector_len = verifier. get_uoffset ( value_vector_pos) ?;
334
+ let value_vector_start = value_vector_pos. saturating_add ( SIZE_UOFFSET ) ;
335
+
336
+ // Both vectors should have the same length.
337
+ // The C++ verifier instead assumes that the length of the value vector is the length of the enum vector:
338
+ // https://github.com/google/flatbuffers/blob/bd1b2d0bafb8be6059a29487db9e5ace5c32914d/src/reflection.cpp#L147-L162
339
+ // This has been reported at https://github.com/google/flatbuffers/issues/8567
340
+ if enum_vector_len != value_vector_len {
341
+ return InvalidFlatbuffer :: new_inconsistent_union (
342
+ format ! ( "{}_type" , field. name( ) ) ,
343
+ field. name ( ) . to_owned ( ) ,
344
+ ) ?;
345
+ }
346
+
347
+ // Regardless of its contents, the value vector in a vector of unions must be a vector of
348
+ // offsets. Source: https://github.com/dvidelabs/flatcc/blob/master/doc/binary-format.md#unions
349
+ verifier. is_aligned :: < UOffsetT > ( value_vector_start) ?;
350
+ let value_vector_size = value_vector_len. saturating_mul ( SIZE_UOFFSET . try_into ( ) ?) ;
351
+ verifier. range_in_buffer ( value_vector_start, value_vector_size. try_into ( ) ?) ?;
352
+ let value_vector_range = core:: ops:: Range {
353
+ start : value_vector_start,
354
+ end : value_vector_start. saturating_add ( value_vector_size. try_into ( ) ?) ,
355
+ } ;
356
+
357
+ // The enums must have a size of 1 byte, so we just use the length of the vector.
358
+ let enum_vector_size = enum_vector_len;
359
+ verifier. range_in_buffer ( enum_vector_start, enum_vector_size. try_into ( ) ?) ?;
360
+ let enum_vector_range = core:: ops:: Range {
361
+ start : enum_vector_start,
362
+ end : enum_vector_start. saturating_add ( enum_vector_size. try_into ( ) ?) ,
363
+ } ;
364
+
365
+ let enum_values = child_enum. values ( ) ;
366
+
367
+ for ( enum_pos, union_offset_pos) in enum_vector_range. zip ( value_vector_range. step_by ( SIZE_UOFFSET ) ) {
368
+ let enum_value = verifier. get_u8 ( enum_pos) ?;
369
+ if enum_value == 0 {
370
+ // Discriminator is NONE. This should never happen: the C++ implementation forbids it.
371
+ // For example, the C++ JSON parser forbids the type entry to be NONE for a vector of
372
+ // unions: in
373
+ // https://github.com/google/flatbuffers/blob/bd1b2d0bafb8be6059a29487db9e5ace5c32914d/src/idl_parser.cpp#L1383
374
+ // the second argument is 'true', meaning that the NONE entry is skipped for the reverse
375
+ // lookup.
376
+ //
377
+ // This should possibly be an error, but we can be forgiving and just ignore the
378
+ // corresponding union entry entirely.
379
+ continue ;
380
+ }
381
+
382
+ let enum_value: usize = enum_value. into ( ) ;
383
+ let enum_type = if enum_value < enum_values. len ( ) {
384
+ enum_values. get ( enum_value) . union_type ( ) . expect (
385
+ "Schema verification should have checked that every union enum value has a type" ,
386
+ )
387
+ } else {
388
+ return Err ( FlatbufferError :: InvalidUnionEnum ) ;
389
+ } ;
390
+ let union_pos = union_offset_pos. saturating_add ( verifier. get_uoffset ( union_offset_pos) ?. try_into ( ) ?) ;
391
+ verifier. in_buffer :: < u8 > ( union_pos) ?;
392
+
393
+ match enum_type. base_type ( ) {
394
+ BaseType :: String => <& str >:: run_verifier ( verifier, union_pos) ?,
395
+ BaseType :: Obj => {
396
+ let child_obj = schema. objects ( ) . get ( enum_type. index ( ) . try_into ( ) ?) ;
397
+ buf_loc_to_obj_idx. insert ( union_pos, enum_type. index ( ) ) ;
398
+ if child_obj. is_struct ( ) {
399
+ verify_struct ( verifier, & child_obj, union_pos, schema, buf_loc_to_obj_idx) ?
400
+ } else {
401
+ verify_table (
402
+ verifier,
403
+ & child_obj,
404
+ union_pos,
405
+ schema,
406
+ verified,
407
+ buf_loc_to_obj_idx,
408
+ ) ?;
409
+ }
410
+ }
411
+ other => {
412
+ return Err ( FlatbufferError :: UnsupportedUnionElementType ( other) ) ;
413
+ }
414
+ }
415
+ verified[ union_pos] = true ;
416
+ }
417
+ Ok ( table_verifier)
418
+ }
419
+
342
420
fn verify_union < ' a , ' b , ' c > (
343
421
mut table_verifier : TableVerifier < ' a , ' b , ' c > ,
344
422
field : & Field ,
0 commit comments