nostrdb

an unfairly fast embedded nostr database backed by lmdb
git clone git://jb55.com/nostrdb
Log | Files | Refs | Submodules | README | LICENSE

verifier.c (22835B)


      1 /*
      2  * Runtime support for verifying flatbuffers.
      3  *
      4  * Depends mutually on generated verifier functions for table types that
      5  * call into this library.
      6  */
      7 #include <string.h>
      8 
      9 #include "flatcc/flatcc_rtconfig.h"
     10 #include "flatcc/flatcc_flatbuffers.h"
     11 #include "flatcc/flatcc_verifier.h"
     12 #include "flatcc/flatcc_identifier.h"
     13 
     14 /* Customization for testing. */
     15 #if FLATCC_DEBUG_VERIFY
     16 #define FLATCC_VERIFIER_ASSERT_ON_ERROR 1
     17 #include <stdio.h>
     18 #define FLATCC_VERIFIER_ASSERT(cond, reason)                                \
     19     if (!(cond)) { fprintf(stderr, "verifier assert: %s\n",                 \
     20         flatcc_verify_error_string(reason)); FLATCC_ASSERT(0); return reason; }
     21 #endif
     22 
     23 #if FLATCC_TRACE_VERIFY
     24 #include <stdio.h>
     25 #define trace_verify(s, p) \
     26     fprintf(stderr, "trace verify: %s: 0x%02x\n", (s), (unsigned)(size_t)(p));
     27 #else
     28 #define trace_verify(s, p) ((void)0)
     29 #endif
     30 
     31 /* The runtime library does not use the global config file. */
     32 
     33 /* This is a guideline, not an exact measure. */
     34 #ifndef FLATCC_VERIFIER_MAX_LEVELS
     35 #define FLATCC_VERIFIER_MAX_LEVELS 100
     36 #endif
     37 
     38 #ifndef FLATCC_VERIFIER_ASSERT_ON_ERROR
     39 #define FLATCC_VERIFIER_ASSERT_ON_ERROR 0
     40 #endif
     41 
     42 /*
     43  * Generally a check should tell if a buffer is valid or not such
     44  * that runtime can take appropriate actions rather than crash,
     45  * also in debug, but assertions are helpful in debugging a problem.
     46  *
     47  * This must be compiled into the debug runtime library to take effect.
     48  */
     49 #ifndef FLATCC_VERIFIER_ASSERT_ON_ERROR
     50 #define FLATCC_VERIFIER_ASSERT_ON_ERROR 1
     51 #endif
     52 
     53 /* May be redefined for logging purposes. */
     54 #ifndef FLATCC_VERIFIER_ASSERT
     55 #define FLATCC_VERIFIER_ASSERT(cond, reason) FLATCC_ASSERT(cond)
     56 #endif
     57 
     58 #if FLATCC_VERIFIER_ASSERT_ON_ERROR
     59 #define flatcc_verify(cond, reason) if (!(cond)) { FLATCC_VERIFIER_ASSERT(cond, reason); return reason; }
     60 #else
     61 #define flatcc_verify(cond, reason) if (!(cond)) { return reason; }
     62 #endif
     63 
     64 
     65 #define uoffset_t flatbuffers_uoffset_t
     66 #define soffset_t flatbuffers_soffset_t
     67 #define voffset_t flatbuffers_voffset_t
     68 #define utype_t flatbuffers_utype_t
     69 #define thash_t flatbuffers_thash_t
     70 
     71 #define uoffset_size sizeof(uoffset_t)
     72 #define soffset_size sizeof(soffset_t)
     73 #define voffset_size sizeof(voffset_t)
     74 #define utype_size sizeof(utype_t)
     75 #define thash_size sizeof(thash_t)
     76 #define offset_size uoffset_size
     77 
     78 const char *flatcc_verify_error_string(int err)
     79 {
     80     switch (err) {
     81 #define XX(no, str)                                                         \
     82     case flatcc_verify_error_##no:                                          \
     83         return str;
     84         FLATCC_VERIFY_ERROR_MAP(XX)
     85 #undef XX
     86     default:
     87         return "unknown";
     88     }
     89 }
     90 
     91 /* `cond` may have side effects. */
     92 #define verify(cond, reason) do { int c = (cond); flatcc_verify(c, reason); } while(0)
     93 
     94 /*
     95  * Identify checks related to runtime conditions (buffer size and
     96  * alignment) as seperate from those related to buffer content.
     97  */
     98 #define verify_runtime(cond, reason) verify(cond, reason)
     99 
    100 #define check_result(x) if (x) { return (x); }
    101 
    102 #define check_field(td, id, required, base) do {                            \
    103     int ret = get_offset_field(td, id, required, &base);                    \
    104     if (ret || !base) { return ret; }} while (0)
    105 
    106 static inline uoffset_t read_uoffset(const void *p, uoffset_t base)
    107 {
    108     return __flatbuffers_uoffset_read_from_pe((uint8_t *)p + base);
    109 }
    110 
    111 static inline thash_t read_thash_identifier(const char *identifier)
    112 {
    113     return flatbuffers_type_hash_from_string(identifier);
    114 }
    115 
    116 static inline thash_t read_thash(const void *p, uoffset_t base)
    117 {
    118     return __flatbuffers_thash_read_from_pe((uint8_t *)p + base);
    119 }
    120 
    121 static inline voffset_t read_voffset(const void *p, uoffset_t base)
    122 {
    123     return __flatbuffers_voffset_read_from_pe((uint8_t *)p + base);
    124 }
    125 
    126 static inline int check_header(uoffset_t end, uoffset_t base, uoffset_t offset)
    127 {
    128     uoffset_t k = base + offset;
    129 
    130     if (uoffset_size <= voffset_size && k + offset_size < k) {
    131         return 0;
    132     }
    133 
    134     /* The `k > base` rather than `k >= base` is to avoid null offsets. */
    135     return k > base && k + offset_size <= end && !(k & (offset_size - 1));
    136 }
    137 
    138 static inline int check_aligned_header(uoffset_t end, uoffset_t base, uoffset_t offset, uint16_t align)
    139 {
    140     uoffset_t k = base + offset;
    141 
    142     if (uoffset_size <= voffset_size && k + offset_size < k) {
    143         return 0;
    144     }
    145     /* Alignment refers to element 0 and header must also be aligned. */
    146     align = align < uoffset_size ? uoffset_size : align;
    147 
    148     /* Note to self: the builder can also use the mask OR trick to propagate `min_align`. */
    149     return k > base && k + offset_size <= end && !((k + offset_size) & ((offset_size - 1) | (align - 1u)));
    150 }
    151 
    152 static inline int verify_struct(uoffset_t end, uoffset_t base, uoffset_t offset, uoffset_t size, uint16_t align)
    153 {
    154     /* Structs can have zero size so `end` is a valid value. */
    155     if (offset == 0 || base + offset > end) {
    156         return flatcc_verify_error_offset_out_of_range;
    157     }
    158     base += offset;
    159     verify(base + size >= base, flatcc_verify_error_struct_size_overflow);
    160     verify(base + size <= end, flatcc_verify_error_struct_out_of_range);
    161     verify (!(base & (align - 1u)), flatcc_verify_error_struct_unaligned);
    162     return flatcc_verify_ok;
    163 }
    164 
    165 static inline voffset_t read_vt_entry(flatcc_table_verifier_descriptor_t *td, voffset_t id)
    166 {
    167     voffset_t vo = (id + 2u) * sizeof(voffset_t);
    168 
    169     /* Assumes tsize has been verified for alignment. */
    170     if (vo >= td->vsize) {
    171         return 0;
    172     }
    173     return read_voffset(td->vtable, vo);
    174 }
    175 
    176 static inline const void *get_field_ptr(flatcc_table_verifier_descriptor_t *td, voffset_t id)
    177 {
    178     voffset_t vte = read_vt_entry(td, id);
    179     return vte ? (const uint8_t *)td->buf + td->table + vte : 0;
    180 }
    181 
    182 static int verify_field(flatcc_table_verifier_descriptor_t *td,
    183         voffset_t id, int required, uoffset_t size, uint16_t align)
    184 {
    185     uoffset_t k, k2;
    186     voffset_t vte;
    187     uoffset_t base = (uoffset_t)(size_t)td->buf;
    188 
    189 
    190     /*
    191      * Otherwise range check assumptions break, and normal access code likely also.
    192      * We don't require voffset_size < uoffset_size, but some checks are faster if true.
    193      */
    194     FLATCC_ASSERT(uoffset_size >= voffset_size);
    195     FLATCC_ASSERT(soffset_size == uoffset_size);
    196 
    197     vte = read_vt_entry(td, id);
    198     if (!vte) {
    199         verify(!required, flatcc_verify_error_required_field_missing);
    200         return flatcc_verify_ok;
    201     }
    202     trace_verify("table buffer", td->buf);
    203     trace_verify("table", td->table);
    204     trace_verify("id", id);
    205     trace_verify("vte", vte);
    206 
    207     /*
    208      * Note that we don't add td.table to k and we test against table
    209      * size not table end or buffer end. Otherwise it would not be safe
    210      * to optimized out the k <= k2 check for normal uoffset and voffset
    211      * configurations.
    212      */
    213     k = vte;
    214     k2 = k + size;
    215     verify(k2 <= td->tsize, flatcc_verify_error_table_field_out_of_range);
    216     /* This normally optimizes to nop. */
    217     verify(uoffset_size > voffset_size || k <= k2, flatcc_verify_error_table_field_size_overflow);
    218     trace_verify("table + vte", vte + td->table);
    219     k += td->table + base;
    220     trace_verify("entry: buf + table + vte", k);
    221     trace_verify("align", align);
    222     trace_verify("align masked entry", k & (align - 1u));
    223     verify(!(k & (align - 1u)), flatcc_verify_error_table_field_not_aligned);
    224     /* We assume the table size has already been verified. */
    225     return flatcc_verify_ok;
    226 }
    227 
    228 static int get_offset_field(flatcc_table_verifier_descriptor_t *td, voffset_t id, int required, uoffset_t *out)
    229 {
    230     uoffset_t k, k2;
    231     voffset_t vte;
    232 
    233     vte = read_vt_entry(td, id);
    234     if (!vte) {
    235         *out = 0;
    236         if (required) {
    237             return flatcc_verify_error_required_field_missing;
    238         }
    239         /* Missing, but not invalid. */
    240         return flatcc_verify_ok;
    241     }
    242     /*
    243      * Note that we don't add td.table to k and we test against table
    244      * size not table end or buffer end. Otherwise it would not be safe
    245      * to optimized out the k <= k2 check for normal uoffset and voffset
    246      * configurations.
    247      */
    248     k = vte;
    249     k2 = k + offset_size;
    250     verify(k2 <= td->tsize, flatcc_verify_error_table_field_out_of_range);
    251     /* This normally optimizes to nop. */
    252     verify(uoffset_size > voffset_size || k <= k2, flatcc_verify_error_table_field_size_overflow);
    253     k += td->table;
    254     verify(!(k & (offset_size - 1u)), flatcc_verify_error_table_field_not_aligned);
    255     /* We assume the table size has already been verified. */
    256     *out = k;
    257     return flatcc_verify_ok;
    258 }
    259 
    260 static inline int verify_string(const void *buf, uoffset_t end, uoffset_t base, uoffset_t offset)
    261 {
    262     uoffset_t n;
    263 
    264     verify(check_header(end, base, offset), flatcc_verify_error_string_header_out_of_range_or_unaligned);
    265     base += offset;
    266     n = read_uoffset(buf, base);
    267     base += offset_size;
    268     verify(end - base > n, flatcc_verify_error_string_out_of_range);
    269     verify(((uint8_t *)buf + base)[n] == 0, flatcc_verify_error_string_not_zero_terminated);
    270     return flatcc_verify_ok;
    271 }
    272 
    273 /*
    274  * Keep interface somwewhat similar ot flatcc_builder_start_vector.
    275  * `max_count` is a precomputed division to manage overflow check on vector length.
    276  */
    277 static inline int verify_vector(const void *buf, uoffset_t end, uoffset_t base, uoffset_t offset, uoffset_t elem_size, uint16_t align, uoffset_t max_count)
    278 {
    279     uoffset_t n;
    280 
    281     verify(check_aligned_header(end, base, offset, align), flatcc_verify_error_vector_header_out_of_range_or_unaligned);
    282     base += offset;
    283     n = read_uoffset(buf, base);
    284     base += offset_size;
    285     /* `n * elem_size` can overflow uncontrollably otherwise. */
    286     verify(n <= max_count, flatcc_verify_error_vector_count_exceeds_representable_vector_size);
    287     verify(end - base >= n * elem_size, flatcc_verify_error_vector_out_of_range);
    288     return flatcc_verify_ok;
    289 }
    290 
    291 static inline int verify_string_vector(const void *buf, uoffset_t end, uoffset_t base, uoffset_t offset)
    292 {
    293     uoffset_t i, n;
    294 
    295     check_result(verify_vector(buf, end, base, offset, offset_size, offset_size, FLATBUFFERS_COUNT_MAX(offset_size)));
    296     base += offset;
    297     n = read_uoffset(buf, base);
    298     base += offset_size;
    299     for (i = 0; i < n; ++i, base += offset_size) {
    300         check_result(verify_string(buf, end, base, read_uoffset(buf, base)));
    301     }
    302     return flatcc_verify_ok;
    303 }
    304 
    305 static inline int verify_table(const void *buf, uoffset_t end, uoffset_t base, uoffset_t offset,
    306         int ttl, flatcc_table_verifier_f tvf)
    307 {
    308     uoffset_t vbase, vend;
    309     flatcc_table_verifier_descriptor_t td;
    310 
    311     verify((td.ttl = ttl - 1), flatcc_verify_error_max_nesting_level_reached);
    312     verify(check_header(end, base, offset), flatcc_verify_error_table_header_out_of_range_or_unaligned);
    313     td.table = base + offset;
    314     /* Read vtable offset - it is signed, but we want it unsigned, assuming 2's complement works. */
    315     vbase = td.table - read_uoffset(buf, td.table);
    316     verify((soffset_t)vbase >= 0 && !(vbase & (voffset_size - 1)), flatcc_verify_error_vtable_offset_out_of_range_or_unaligned);
    317     verify(vbase + voffset_size <= end, flatcc_verify_error_vtable_header_out_of_range);
    318     /* Read vtable size. */
    319     td.vsize = read_voffset(buf, vbase);
    320     vend = vbase + td.vsize;
    321     verify(vend <= end && !(td.vsize & (voffset_size - 1)), flatcc_verify_error_vtable_size_out_of_range_or_unaligned);
    322     /* Optimizes away overflow check if uoffset_t is large enough. */
    323     verify(uoffset_size > voffset_size || vend >= vbase, flatcc_verify_error_vtable_size_overflow);
    324 
    325     verify(td.vsize >= 2 * voffset_size, flatcc_verify_error_vtable_header_too_small);
    326     /* Read table size. */
    327     td.tsize = read_voffset(buf, vbase + voffset_size);
    328     verify(end - td.table >= td.tsize, flatcc_verify_error_table_size_out_of_range);
    329     td.vtable = (uint8_t *)buf + vbase;
    330     td.buf = buf;
    331     td.end = end;
    332     return tvf(&td);
    333 }
    334 
    335 static inline int verify_table_vector(const void *buf, uoffset_t end, uoffset_t base, uoffset_t offset, int ttl, flatcc_table_verifier_f tvf)
    336 {
    337     uoffset_t i, n;
    338 
    339     verify(ttl-- > 0, flatcc_verify_error_max_nesting_level_reached);
    340     check_result(verify_vector(buf, end, base, offset, offset_size, offset_size, FLATBUFFERS_COUNT_MAX(offset_size)));
    341     base += offset;
    342     n = read_uoffset(buf, base);
    343     base += offset_size;
    344     for (i = 0; i < n; ++i, base += offset_size) {
    345         check_result(verify_table(buf, end, base, read_uoffset(buf, base), ttl, tvf));
    346     }
    347     return flatcc_verify_ok;
    348 }
    349 
    350 static inline int verify_union_vector(const void *buf, uoffset_t end, uoffset_t base, uoffset_t offset,
    351         uoffset_t count, const utype_t *types, int ttl, flatcc_union_verifier_f uvf)
    352 {
    353     uoffset_t i, n, elem;
    354     flatcc_union_verifier_descriptor_t ud;
    355 
    356     verify(ttl-- > 0, flatcc_verify_error_max_nesting_level_reached);
    357     check_result(verify_vector(buf, end, base, offset, offset_size, offset_size, FLATBUFFERS_COUNT_MAX(offset_size)));
    358     base += offset;
    359     n = read_uoffset(buf, base);
    360     verify(n == count, flatcc_verify_error_union_vector_length_mismatch);
    361     base += offset_size;
    362 
    363     ud.buf = buf;
    364     ud.end = end;
    365     ud.ttl = ttl;
    366 
    367     for (i = 0; i < n; ++i, base += offset_size) {
    368         /* Table vectors can never be null, but unions can when the type is NONE. */
    369         elem = read_uoffset(buf, base);
    370         if (elem == 0) {
    371             verify(types[i] == 0, flatcc_verify_error_union_element_absent_without_type_NONE);
    372         } else {
    373             verify(types[i] != 0, flatcc_verify_error_union_element_present_with_type_NONE);
    374             ud.type = types[i];
    375             ud.base = base;
    376             ud.offset = elem;
    377             check_result(uvf(&ud));
    378         }
    379     }
    380     return flatcc_verify_ok;
    381 }
    382 
    383 int flatcc_verify_field(flatcc_table_verifier_descriptor_t *td,
    384         voffset_t id, size_t size, uint16_t align)
    385 {
    386     check_result(verify_field(td, id, 0, (uoffset_t)size, align));
    387     return flatcc_verify_ok;
    388 }
    389 
    390 int flatcc_verify_string_field(flatcc_table_verifier_descriptor_t *td,
    391         voffset_t id, int required)
    392 {
    393     uoffset_t base;
    394 
    395     check_field(td, id, required, base);
    396     return verify_string(td->buf, td->end, base, read_uoffset(td->buf, base));
    397 }
    398 
    399 int flatcc_verify_vector_field(flatcc_table_verifier_descriptor_t *td,
    400         voffset_t id, int required, size_t elem_size, uint16_t align, size_t max_count)
    401 {
    402     uoffset_t base;
    403 
    404     check_field(td, id, required, base);
    405     return verify_vector(td->buf, td->end, base, read_uoffset(td->buf, base),
    406         (uoffset_t)elem_size, align, (uoffset_t)max_count);
    407 }
    408 
    409 int flatcc_verify_string_vector_field(flatcc_table_verifier_descriptor_t *td,
    410     voffset_t id, int required)
    411 {
    412     uoffset_t base;
    413 
    414     check_field(td, id, required, base);
    415     return verify_string_vector(td->buf, td->end, base, read_uoffset(td->buf, base));
    416 }
    417 
    418 int flatcc_verify_table_field(flatcc_table_verifier_descriptor_t *td,
    419     voffset_t id, int required, flatcc_table_verifier_f tvf)
    420 {
    421     uoffset_t base;
    422 
    423     check_field(td, id, required, base);
    424     return verify_table(td->buf, td->end, base, read_uoffset(td->buf, base), td->ttl, tvf);
    425 }
    426 
    427 int flatcc_verify_table_vector_field(flatcc_table_verifier_descriptor_t *td,
    428         voffset_t id, int required, flatcc_table_verifier_f tvf)
    429 {
    430     uoffset_t base;
    431 
    432     check_field(td, id, required, base);
    433     return verify_table_vector(td->buf, td->end, base, read_uoffset(td->buf, base), td->ttl, tvf);
    434 }
    435 
    436 int flatcc_verify_union_table(flatcc_union_verifier_descriptor_t *ud, flatcc_table_verifier_f *tvf)
    437 {
    438     return verify_table(ud->buf, ud->end, ud->base, ud->offset, ud->ttl, tvf);
    439 }
    440 
    441 int flatcc_verify_union_struct(flatcc_union_verifier_descriptor_t *ud, size_t size, uint16_t align)
    442 {
    443     return verify_struct(ud->end, ud->base, ud->offset, (uoffset_t)size, align);
    444 }
    445 
    446 int flatcc_verify_union_string(flatcc_union_verifier_descriptor_t *ud)
    447 {
    448     return verify_string(ud->buf, ud->end, ud->base, ud->offset);
    449 }
    450 
    451 int flatcc_verify_buffer_header(const void *buf, size_t bufsiz, const char *fid)
    452 {
    453     thash_t id, id2;
    454 
    455     verify_runtime(!(((size_t)buf) & (offset_size - 1)), flatcc_verify_error_runtime_buffer_header_not_aligned);
    456     /* -8 ensures no scalar or offset field size can overflow. */
    457     verify_runtime(bufsiz <= FLATBUFFERS_UOFFSET_MAX - 8, flatcc_verify_error_runtime_buffer_size_too_large);
    458     /*
    459      * Even if we specify no fid, the user might later. Therefore
    460      * require space for it. Not all buffer generators will take this
    461      * into account, so it is possible to fail an otherwise valid buffer
    462      * - but such buffers aren't safe.
    463      */
    464     verify(bufsiz >= offset_size + FLATBUFFERS_IDENTIFIER_SIZE, flatcc_verify_error_buffer_header_too_small);
    465     if (fid != 0) {
    466         id2 = read_thash_identifier(fid);
    467         id = read_thash(buf, offset_size);
    468         verify(id2 == 0 || id == id2, flatcc_verify_error_identifier_mismatch);
    469     }
    470     return flatcc_verify_ok;
    471 }
    472 
    473 int flatcc_verify_typed_buffer_header(const void *buf, size_t bufsiz, flatbuffers_thash_t thash)
    474 {
    475     thash_t id, id2;
    476 
    477     verify_runtime(!(((size_t)buf) & (offset_size - 1)), flatcc_verify_error_runtime_buffer_header_not_aligned);
    478     /* -8 ensures no scalar or offset field size can overflow. */
    479     verify_runtime(bufsiz <= FLATBUFFERS_UOFFSET_MAX - 8, flatcc_verify_error_runtime_buffer_size_too_large);
    480     /*
    481      * Even if we specify no fid, the user might later. Therefore
    482      * require space for it. Not all buffer generators will take this
    483      * into account, so it is possible to fail an otherwise valid buffer
    484      * - but such buffers aren't safe.
    485      */
    486     verify(bufsiz >= offset_size + FLATBUFFERS_IDENTIFIER_SIZE, flatcc_verify_error_buffer_header_too_small);
    487     if (thash != 0) {
    488         id2 = thash;
    489         id = read_thash(buf, offset_size);
    490         verify(id2 == 0 || id == id2, flatcc_verify_error_identifier_mismatch);
    491     }
    492     return flatcc_verify_ok;
    493 }
    494 
    495 int flatcc_verify_struct_as_root(const void *buf, size_t bufsiz, const char *fid, size_t size, uint16_t align)
    496 {
    497     check_result(flatcc_verify_buffer_header(buf, bufsiz, fid));
    498     return verify_struct((uoffset_t)bufsiz, 0, read_uoffset(buf, 0), (uoffset_t)size, align);
    499 }
    500 
    501 int flatcc_verify_struct_as_typed_root(const void *buf, size_t bufsiz, flatbuffers_thash_t thash, size_t size, uint16_t align)
    502 {
    503     check_result(flatcc_verify_typed_buffer_header(buf, bufsiz, thash));
    504     return verify_struct((uoffset_t)bufsiz, 0, read_uoffset(buf, 0), (uoffset_t)size, align);
    505 }
    506 
    507 int flatcc_verify_table_as_root(const void *buf, size_t bufsiz, const char *fid, flatcc_table_verifier_f *tvf)
    508 {
    509     check_result(flatcc_verify_buffer_header(buf, (uoffset_t)bufsiz, fid));
    510     return verify_table(buf, (uoffset_t)bufsiz, 0, read_uoffset(buf, 0), FLATCC_VERIFIER_MAX_LEVELS, tvf);
    511 }
    512 
    513 int flatcc_verify_table_as_typed_root(const void *buf, size_t bufsiz, flatbuffers_thash_t thash, flatcc_table_verifier_f *tvf)
    514 {
    515     check_result(flatcc_verify_typed_buffer_header(buf, (uoffset_t)bufsiz, thash));
    516     return verify_table(buf, (uoffset_t)bufsiz, 0, read_uoffset(buf, 0), FLATCC_VERIFIER_MAX_LEVELS, tvf);
    517 }
    518 
    519 int flatcc_verify_struct_as_nested_root(flatcc_table_verifier_descriptor_t *td,
    520         voffset_t id, int required, const char *fid, size_t size, uint16_t align)
    521 {
    522     const uoffset_t *buf;
    523     uoffset_t bufsiz;
    524 
    525     check_result(flatcc_verify_vector_field(td, id, required, align, 1, FLATBUFFERS_COUNT_MAX(1)));
    526     if (0 == (buf = get_field_ptr(td, id))) {
    527         return flatcc_verify_ok;
    528     }
    529     buf = (const uoffset_t *)((size_t)buf + read_uoffset(buf, 0));
    530     bufsiz = read_uoffset(buf, 0);
    531     ++buf;
    532     return flatcc_verify_struct_as_root(buf, bufsiz, fid, size, align);
    533 }
    534 
    535 int flatcc_verify_table_as_nested_root(flatcc_table_verifier_descriptor_t *td,
    536         voffset_t id, int required, const char *fid,
    537         uint16_t align, flatcc_table_verifier_f tvf)
    538 {
    539     const uoffset_t *buf;
    540     uoffset_t bufsiz;
    541 
    542     check_result(flatcc_verify_vector_field(td, id, required, align, 1, FLATBUFFERS_COUNT_MAX(1)));
    543     if (0 == (buf = get_field_ptr(td, id))) {
    544         return flatcc_verify_ok;
    545     }
    546     buf = (const uoffset_t *)((size_t)buf + read_uoffset(buf, 0));
    547     bufsiz = read_uoffset(buf, 0);
    548     ++buf;
    549     /*
    550      * Don't verify nested buffers identifier - information is difficult to get and
    551      * might not be what is desired anyway. User can do it later.
    552      */
    553     check_result(flatcc_verify_buffer_header(buf, bufsiz, fid));
    554     return verify_table(buf, bufsiz, 0, read_uoffset(buf, 0), td->ttl, tvf);
    555 }
    556 
    557 int flatcc_verify_union_field(flatcc_table_verifier_descriptor_t *td,
    558         voffset_t id, int required, flatcc_union_verifier_f uvf)
    559 {
    560     voffset_t vte_type, vte_table;
    561     const uint8_t *type;
    562     uoffset_t base;
    563     flatcc_union_verifier_descriptor_t ud;
    564 
    565     if (0 == (vte_type = read_vt_entry(td, id - 1))) {
    566         vte_table = read_vt_entry(td, id);
    567         verify(vte_table == 0, flatcc_verify_error_union_cannot_have_a_table_without_a_type);
    568         verify(!required, flatcc_verify_error_type_field_absent_from_required_union_field);
    569         return flatcc_verify_ok;
    570     }
    571     /* No need to check required here. */
    572     check_result(verify_field(td, id - 1, 0, 1, 1));
    573     /* Only now is it safe to read the type. */
    574     vte_table = read_vt_entry(td, id);
    575     type = (const uint8_t *)td->buf + td->table + vte_type;
    576     verify(*type || vte_table == 0, flatcc_verify_error_union_type_NONE_cannot_have_a_value);
    577 
    578     if (*type == 0) {
    579         return flatcc_verify_ok;
    580     }
    581     check_field(td, id, required, base);
    582     ud.buf = td->buf;
    583     ud.end = td->end;
    584     ud.ttl = td->ttl;
    585     ud.base = base;
    586     ud.offset = read_uoffset(td->buf, base);
    587     ud.type = *type;
    588     return uvf(&ud);
    589 }
    590 
    591 int flatcc_verify_union_vector_field(flatcc_table_verifier_descriptor_t *td,
    592     flatbuffers_voffset_t id, int required, flatcc_union_verifier_f uvf)
    593 {
    594     voffset_t vte_type, vte_table;
    595     const uoffset_t *buf;
    596     const utype_t *types;
    597     uoffset_t count, base;
    598 
    599     if (0 == (vte_type = read_vt_entry(td, id - 1))) {
    600         if (0 == (vte_table = read_vt_entry(td, id))) {
    601             verify(!required, flatcc_verify_error_type_field_absent_from_required_union_vector_field);
    602         }
    603     }
    604     check_result(flatcc_verify_vector_field(td, id - 1, required,
    605                 utype_size, utype_size, FLATBUFFERS_COUNT_MAX(utype_size)));
    606     if (0 == (buf = get_field_ptr(td, id - 1))) {
    607         return flatcc_verify_ok;
    608     }
    609     buf = (const uoffset_t *)((size_t)buf + read_uoffset(buf, 0));
    610     count = read_uoffset(buf, 0);
    611     ++buf;
    612     types = (utype_t *)buf;
    613 
    614     check_field(td, id, required, base);
    615     return verify_union_vector(td->buf, td->end, base, read_uoffset(td->buf, base),
    616             count, types, td->ttl, uvf);
    617 }