mirror of
git://jb55.com/damus
synced 2024-10-01 17:30:44 +00:00
240 lines
11 KiB
C
240 lines
11 KiB
C
#ifndef FLATCC_VERIFIER_H
|
|
#define FLATCC_VERIFIER_H
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" {
|
|
#endif
|
|
|
|
/*
|
|
* Runtime support for verifying flatbuffers.
|
|
*
|
|
* Link with the verifier implementation file.
|
|
*
|
|
* Note:
|
|
*
|
|
* 1) nested buffers will NOT have their identifier verified.
|
|
* The user may do so subsequently. The reason is in part because
|
|
* the information is not readily avaible without generated reader code,
|
|
* in part because the buffer might use a different, but valid,
|
|
* identifier and the user has no chance of specifiying this in the
|
|
* verifier code. The root verifier also doesn't assume a specific id
|
|
* but accepts a user supplied input which may be null.
|
|
*
|
|
* 2) All offsets in a buffer are verified for alignment relative to the
|
|
* buffer start, but the buffer itself is only assumed to aligned to
|
|
* uoffset_t. A reader should therefore ensure buffer alignment separately
|
|
* before reading the buffer. Nested buffers are in fact checked for
|
|
* alignment, but still only relative to the root buffer.
|
|
*
|
|
* 3) The max nesting level includes nested buffer nestings, so the
|
|
* verifier might fail even if the individual buffers are otherwise ok.
|
|
* This is to prevent abuse with lots of nested buffers.
|
|
*
|
|
*
|
|
* IMPORTANT:
|
|
*
|
|
* Even if verifier passes, the buffer may be invalid to access due to
|
|
* lack of alignemnt in memory, but the verifier is safe to call.
|
|
*
|
|
* NOTE: The buffer is not safe to modify after verification because an
|
|
* attacker may craft overlapping data structures such that modification
|
|
* of one field updates another in a way that violates the buffer
|
|
* constraints. This may also be caused by a clever compression scheme.
|
|
*
|
|
* It is likely faster to rewrite the table although this is also
|
|
* dangerous because an attacker (or even normal user) can draft a DAG
|
|
* that explodes when expanded carelesslessly. A safer approach is to
|
|
* hash all object references written and reuse those that match. This
|
|
* will expand references into other objects while bounding expansion
|
|
* and it will be safe to update assuming shared objects are ok to
|
|
* update.
|
|
*
|
|
*/
|
|
|
|
#include "flatcc_types.h"
|
|
|
|
#define FLATCC_VERIFY_ERROR_MAP(XX)\
|
|
XX(ok, "ok")\
|
|
XX(buffer_header_too_small, "buffer header too small")\
|
|
XX(identifier_mismatch, "identifier mismatch")\
|
|
XX(max_nesting_level_reached, "max nesting level reached")\
|
|
XX(required_field_missing, "required field missing")\
|
|
XX(runtime_buffer_header_not_aligned, "runtime: buffer header not aligned")\
|
|
XX(runtime_buffer_size_too_large, "runtime: buffer size too large")\
|
|
XX(string_not_zero_terminated, "string not zero terminated")\
|
|
XX(string_out_of_range, "string out of range")\
|
|
XX(struct_out_of_range, "struct out of range")\
|
|
XX(struct_size_overflow, "struct size overflow")\
|
|
XX(struct_unaligned, "struct unaligned")\
|
|
XX(table_field_not_aligned, "table field not aligned")\
|
|
XX(table_field_out_of_range, "table field out of range")\
|
|
XX(table_field_size_overflow, "table field size overflow")\
|
|
XX(table_header_out_of_range_or_unaligned, "table header out of range or unaligned")\
|
|
XX(vector_header_out_of_range_or_unaligned, "vector header out of range or unaligned")\
|
|
XX(string_header_out_of_range_or_unaligned, "string header out of range or unaligned")\
|
|
XX(offset_out_of_range, "offset out of range")\
|
|
XX(table_offset_out_of_range_or_unaligned, "table offset out of range or unaligned")\
|
|
XX(table_size_out_of_range, "table size out of range")\
|
|
XX(type_field_absent_from_required_union_field, "type field absent from required union field")\
|
|
XX(type_field_absent_from_required_union_vector_field, "type field absent from required union vector field")\
|
|
XX(union_cannot_have_a_table_without_a_type, "union cannot have a table without a type")\
|
|
XX(union_type_NONE_cannot_have_a_value, "union value field present with type NONE")\
|
|
XX(vector_count_exceeds_representable_vector_size, "vector count exceeds representable vector size")\
|
|
XX(vector_out_of_range, "vector out of range")\
|
|
XX(vtable_header_out_of_range, "vtable header out of range")\
|
|
XX(vtable_header_too_small, "vtable header too small")\
|
|
XX(vtable_offset_out_of_range_or_unaligned, "vtable offset out of range or unaligned")\
|
|
XX(vtable_size_out_of_range_or_unaligned, "vtable size out of range or unaligned")\
|
|
XX(vtable_size_overflow, "vtable size overflow")\
|
|
XX(union_element_absent_without_type_NONE, "union element absent without type NONE")\
|
|
XX(union_element_present_with_type_NONE, "union element present with type NONE")\
|
|
XX(union_vector_length_mismatch, "union type and table vectors have different lengths")\
|
|
XX(union_vector_verification_not_supported, "union vector verification not supported")\
|
|
XX(not_supported, "not supported")
|
|
|
|
|
|
enum flatcc_verify_error_no {
|
|
#define XX(no, str) flatcc_verify_error_##no,
|
|
FLATCC_VERIFY_ERROR_MAP(XX)
|
|
#undef XX
|
|
};
|
|
|
|
#define flatcc_verify_ok flatcc_verify_error_ok
|
|
|
|
const char *flatcc_verify_error_string(int err);
|
|
|
|
/*
|
|
* Type specific table verifier function that checks each known field
|
|
* for existence in the vtable and then calls the appropriate verifier
|
|
* function in this library.
|
|
*
|
|
* The table descriptor values have been verified for bounds, overflow,
|
|
* and alignment, but vtable entries after header must be verified
|
|
* for all fields the table verifier function understands.
|
|
*
|
|
* Calls other typespecific verifier functions recursively whenever a
|
|
* table field, union or table vector is encountered.
|
|
*/
|
|
typedef struct flatcc_table_verifier_descriptor flatcc_table_verifier_descriptor_t;
|
|
struct flatcc_table_verifier_descriptor {
|
|
/* Pointer to buffer. Not assumed to be aligned beyond uoffset_t. */
|
|
const void *buf;
|
|
/* Buffer size. */
|
|
flatbuffers_uoffset_t end;
|
|
/* Time to live: number nesting levels left before failure. */
|
|
int ttl;
|
|
/* Vtable of current table. */
|
|
const void *vtable;
|
|
/* Table offset relative to buffer start */
|
|
flatbuffers_uoffset_t table;
|
|
/* Table end relative to buffer start as per vtable[1] field. */
|
|
flatbuffers_voffset_t tsize;
|
|
/* Size of vtable in bytes. */
|
|
flatbuffers_voffset_t vsize;
|
|
};
|
|
|
|
typedef int flatcc_table_verifier_f(flatcc_table_verifier_descriptor_t *td);
|
|
|
|
typedef struct flatcc_union_verifier_descriptor flatcc_union_verifier_descriptor_t;
|
|
|
|
struct flatcc_union_verifier_descriptor {
|
|
/* Pointer to buffer. Not assumed to be aligned beyond uoffset_t. */
|
|
const void *buf;
|
|
/* Buffer size. */
|
|
flatbuffers_uoffset_t end;
|
|
/* Time to live: number nesting levels left before failure. */
|
|
int ttl;
|
|
/* Type of union value to be verified */
|
|
flatbuffers_utype_t type;
|
|
/* Offset relative to buffer start to where union value offset is stored. */
|
|
flatbuffers_uoffset_t base;
|
|
/* Offset of union value relative to base. */
|
|
flatbuffers_uoffset_t offset;
|
|
};
|
|
|
|
typedef int flatcc_union_verifier_f(flatcc_union_verifier_descriptor_t *ud);
|
|
|
|
/*
|
|
* The `as_root` functions are normally the only functions called
|
|
* explicitly in this interface.
|
|
*
|
|
* If `fid` is null, the identifier is not checked and is allowed to be entirely absent.
|
|
*
|
|
* The buffer must at least be aligned to uoffset_t on systems that
|
|
* require aligned memory addresses. The buffer pointers alignment is
|
|
* not significant to internal verification of the buffer.
|
|
*/
|
|
int flatcc_verify_struct_as_root(const void *buf, size_t bufsiz, const char *fid,
|
|
size_t size, uint16_t align);
|
|
|
|
int flatcc_verify_struct_as_typed_root(const void *buf, size_t bufsiz, flatbuffers_thash_t thash,
|
|
size_t size, uint16_t align);
|
|
|
|
int flatcc_verify_table_as_root(const void *buf, size_t bufsiz, const char *fid,
|
|
flatcc_table_verifier_f *root_tvf);
|
|
|
|
int flatcc_verify_table_as_typed_root(const void *buf, size_t bufsiz, flatbuffers_thash_t thash,
|
|
flatcc_table_verifier_f *root_tvf);
|
|
/*
|
|
* The buffer header is verified by any of the `_as_root` verifiers, but
|
|
* this function may be used as a quick sanity check.
|
|
*/
|
|
int flatcc_verify_buffer_header(const void *buf, size_t bufsiz, const char *fid);
|
|
|
|
int flatcc_verify_typed_buffer_header(const void *buf, size_t bufsiz, flatbuffers_thash_t type_hash);
|
|
|
|
/*
|
|
* The following functions are typically called by a generated table
|
|
* verifier function.
|
|
*/
|
|
|
|
/* Scalar, enum or struct field. */
|
|
int flatcc_verify_field(flatcc_table_verifier_descriptor_t *td,
|
|
flatbuffers_voffset_t id, size_t size, uint16_t align);
|
|
/* Vector of scalars, enums or structs. */
|
|
int flatcc_verify_vector_field(flatcc_table_verifier_descriptor_t *td,
|
|
flatbuffers_voffset_t id, int required, size_t elem_size, uint16_t align, size_t max_count);
|
|
int flatcc_verify_string_field(flatcc_table_verifier_descriptor_t *td,
|
|
flatbuffers_voffset_t id, int required);
|
|
int flatcc_verify_string_vector_field(flatcc_table_verifier_descriptor_t *td,
|
|
flatbuffers_voffset_t id, int required);
|
|
int flatcc_verify_table_field(flatcc_table_verifier_descriptor_t *td,
|
|
flatbuffers_voffset_t id, int required, flatcc_table_verifier_f tvf);
|
|
int flatcc_verify_table_vector_field(flatcc_table_verifier_descriptor_t *td,
|
|
flatbuffers_voffset_t id, int required, flatcc_table_verifier_f tvf);
|
|
/* Table verifiers pass 0 as fid. */
|
|
int flatcc_verify_struct_as_nested_root(flatcc_table_verifier_descriptor_t *td,
|
|
flatbuffers_voffset_t id, int required, const char *fid,
|
|
size_t size, uint16_t align);
|
|
int flatcc_verify_table_as_nested_root(flatcc_table_verifier_descriptor_t *td,
|
|
flatbuffers_voffset_t id, int required, const char *fid,
|
|
uint16_t align, flatcc_table_verifier_f tvf);
|
|
|
|
/*
|
|
* A NONE type will not accept a table being present, and a required
|
|
* union will not accept a type field being absent, and an absent type
|
|
* field will not accept a table field being present.
|
|
*
|
|
* If the above checks out and the type is not NONE, the uvf callback
|
|
* is executed. It must test each known table type and silently accept
|
|
* any unknown table type for forward compatibility. A union table
|
|
* value is verified without the required flag because an absent table
|
|
* encodes a typed NULL value while an absent type field encodes a
|
|
* missing union which fails if required.
|
|
*/
|
|
int flatcc_verify_union_field(flatcc_table_verifier_descriptor_t *td,
|
|
flatbuffers_voffset_t id, int required, flatcc_union_verifier_f uvf);
|
|
|
|
int flatcc_verify_union_vector_field(flatcc_table_verifier_descriptor_t *td,
|
|
flatbuffers_voffset_t id, int required, flatcc_union_verifier_f uvf);
|
|
|
|
int flatcc_verify_union_table(flatcc_union_verifier_descriptor_t *ud, flatcc_table_verifier_f *tvf);
|
|
int flatcc_verify_union_struct(flatcc_union_verifier_descriptor_t *ud, size_t size, uint16_t align);
|
|
int flatcc_verify_union_string(flatcc_union_verifier_descriptor_t *ud);
|
|
|
|
#ifdef __cplusplus
|
|
}
|
|
#endif
|
|
|
|
#endif /* FLATCC_VERIFIER_H */
|