aboutsummaryrefslogtreecommitdiff
path: root/include/flatcc/flatcc_verifier.h
blob: 7e0d29665144123d73467dade9802fb68aa92088 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
#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/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 */