==> src/validate.c <==1/* Validation: the module's rules, then each function body's type check. */
2#include "wasm/validate.h"
3
4#include "wasm/instr.h"
5#include "wasm/reader.h"
6
7#include <stdlib.h>
8#include <string.h>
9
10const char *wasm_feature_name(wasm_features f) {
11 switch (f) {
12 case WASM_F_MUTABLE_GLOBAL: return "mutable global";
13 case WASM_F_SIGN_EXT: return "sign extension operations";
14 case WASM_F_SAT_FLOAT_TO_INT: return "saturating float to int conversions";
15 case WASM_F_MULTI_VALUE: return "multi-value";
16 case WASM_F_REFERENCE_TYPES: return "reference types";
17 case WASM_F_BULK_MEMORY: return "bulk memory";
18 case WASM_F_SIMD: return "SIMD";
19 case WASM_F_RELAXED_SIMD: return "relaxed SIMD";
20 case WASM_F_THREADS: return "threads";
21 case WASM_F_EXTENDED_CONST: return "extended constant expressions";
22 case WASM_F_MULTI_MEMORY: return "multi-memory";
23 case WASM_F_MEMORY64: return "memory64";
24 default: return "this proposal's";
25 }
26}
27
28/* ---- the module's rules ------------------------------------------------------------------- */
29
30typedef struct {
31 const wasm_module *m;
32 wasm_features f;
33 wasm_validate_report *rep;
34 wasm_reader r; /* for messages: offsets are into m->bytes */
35 bool *declared; /* functions a ref.func may name */
36} M;
37
38static bool need(M *v, wasm_features feat, size_t at) {
39 if (v->f & feat) return true;
40 return wasm_rd_fail_at(&v->r, at, "%s support is not enabled", wasm_feature_name(feat));
41}
42
43static bool valtype_ok(M *v, wasm_valtype t, size_t at) {
44 if (t == WASM_V128) return need(v, WASM_F_SIMD, at);
45 if (wasm_is_ref(t)) return need(v, WASM_F_REFERENCE_TYPES, at);
46 return true;
47}
48
49static size_t off(M *v, const void *p) { return (size_t)((const uint8_t *)p - v->m->bytes); }
50
51static bool types_ok(M *v) {
52 for (uint32_t i = 0; i < v->m->ntypes; i++) {
53 const wasm_functype *t = &v->m->types[i];
54 for (uint32_t k = 0; k < t->nparams; k++)
55 if (!valtype_ok(v, t->params[k], off(v, &t->params[k]))) return false;
56 for (uint32_t k = 0; k < t->nresults; k++)
57 if (!valtype_ok(v, t->results[k], off(v, &t->results[k]))) return false;
58 if (t->nresults > 1 && !need(v, WASM_F_MULTI_VALUE, off(v, t->results))) return false;
59 }
60 return true;
61}
62
63static bool mem_ok(M *v, const wasm_limits *l, size_t at) {
64 bool is64 = l->flags & WASM_LIMITS_64;
65 if (is64 && !need(v, WASM_F_MEMORY64, at)) return false;
66 if (l->flags & WASM_LIMITS_SHARED) {
67 if (!need(v, WASM_F_THREADS, at)) return false;
68 if (v->rep) v->rep->uses_threads = true;
69 if (!(l->flags & WASM_LIMITS_HAS_MAX)) return wasm_rd_fail_at(&v->r, at, "shared memory must have maximum size");
70 }
71 uint64_t pages = is64 ? (1ull << 48) : WASM_MAX_PAGES;
72 if (l->min > pages)
73 return wasm_rd_fail_at(&v->r, at, "memory size must be at most %llu pages (4GiB)", (unsigned long long)pages);
74 if (l->flags & WASM_LIMITS_HAS_MAX) {
75 if (l->max > pages)
76 return wasm_rd_fail_at(&v->r, at, "memory size must be at most %llu pages (4GiB)", (unsigned long long)pages);
77 if (l->max < l->min) return wasm_rd_fail_at(&v->r, at, "size minimum must not be greater than maximum");
78 }
79 return true;
80}
81
82static bool table_ok(M *v, const wasm_tabletype *t, size_t at) {
83 if (t->elem == WASM_EXTERNREF && !need(v, WASM_F_REFERENCE_TYPES, at)) return false;
84 if (t->limits.min > WASM_MAX_TABLE) return wasm_rd_fail_at(&v->r, at, "minimum table size is out of bounds");
85 if ((t->limits.flags & WASM_LIMITS_HAS_MAX) && t->limits.max < t->limits.min)
86 return wasm_rd_fail_at(&v->r, at, "size minimum must not be greater than maximum");
87 return true;
88}
89
90/* A constant expression of type want. Constant: the t.const ops, ref.null, ref.func, global.get
91 * of an imported immutable global (or, as wasmparser, any earlier immutable one), and with
92 * extended constant expressions i32/i64 add, sub and mul. */
93static bool const_ok(M *v, const wasm_expr *e, wasm_valtype want, uint32_t globals_visible) {
94 wasm_reader r = v->r;
95 r.p = e->start;
96 r.end = e->start + e->len;
97 wasm_valtype stack[64];
98 uint32_t sp = 0;
99 wasm_instr in;
100 for (;;) {
101 if (!wasm_rd_instr(&r, &in)) {
102 v->r = r;
103 return false;
104 }
105 if (in.op == WASM_OP_END) break;
106 wasm_valtype push = 0;
107 uint8_t pops = 0;
108 switch (in.op) {
109 case WASM_OP_I32_CONST: push = WASM_I32; break;
110 case WASM_OP_I64_CONST: push = WASM_I64; break;
111 case WASM_OP_F32_CONST: push = WASM_F32; break;
112 case WASM_OP_F64_CONST: push = WASM_F64; break;
113 case WASM_OP_REF_NULL:
114 if (!need(v, WASM_F_REFERENCE_TYPES, in.offset)) return false;
115 push = in.reftype;
116 break;
117 case WASM_OP_REF_FUNC:
118 if (!need(v, WASM_F_REFERENCE_TYPES, in.offset)) return false;
119 if (in.a >= v->m->nfuncs)
120 return wasm_rd_fail_at(&v->r, in.offset, "unknown function %u: function index out of bounds", in.a);
121 v->declared[in.a] = true;
122 push = WASM_FUNCREF;
123 break;
124 case WASM_OP_GLOBAL_GET:
125 if (in.a >= globals_visible || in.a >= v->m->nglobals)
126 return wasm_rd_fail_at(&v->r, in.offset, "unknown global %u: global index out of bounds", in.a);
127 if (v->m->globals[in.a].mut)
128 return wasm_rd_fail_at(&v->r, in.offset, "constant expression required: global.get of mutable global");
129 push = v->m->globals[in.a].type;
130 break;
131 case 0x6a: case 0x6b: case 0x6c: /* i32 add sub mul */
132 if (!need(v, WASM_F_EXTENDED_CONST, in.offset)) return false;
133 pops = 2;
134 push = WASM_I32;
135 break;
136 case 0x7c: case 0x7d: case 0x7e: /* i64 add sub mul */
137 if (!need(v, WASM_F_EXTENDED_CONST, in.offset)) return false;
138 pops = 2;
139 push = WASM_I64;
140 break;
141 default:
142 return wasm_rd_fail_at(&v->r, in.offset, "constant expression required: non-constant operator: %s", in.info.name);
143 }
144 for (uint8_t k = 0; k < pops; k++) {
145 if (sp == 0 || stack[sp - 1] != push)
146 return wasm_rd_fail_at(&v->r, in.offset, "type mismatch: expected %s", wasm_valtype_name(push));
147 sp--;
148 }
149 if (sp == sizeof stack / sizeof stack[0])
150 return wasm_rd_fail_at(&v->r, in.offset, "constant expression is too deep");
151 stack[sp++] = push;
152 }
153 if (sp != 1 || stack[0] != want)
154 return wasm_rd_fail_at(&v->r, e->offset, "type mismatch: expected %s", wasm_valtype_name(want));
155 return true;
156}
157
158static bool module_ok(M *v) {
159 const wasm_module *m = v->m;
160 if (!types_ok(v)) return false;
161 for (uint32_t i = 0; i < m->nimports; i++) {
162 const wasm_import *im = &m->imports[i];
163 size_t at = off(v, im->name);
164 switch (im->kind) {
165 case WASM_EXTERN_FUNC:
166 if (im->func_type >= m->ntypes) return wasm_rd_fail_at(&v->r, at, "unknown type: type index out of bounds");
167 break;
168 case WASM_EXTERN_TABLE:
169 if (!table_ok(v, &im->table, at)) return false;
170 break;
171 case WASM_EXTERN_MEMORY:
172 if (!mem_ok(v, &im->mem, at)) return false;
173 break;
174 case WASM_EXTERN_GLOBAL:
175 if (!valtype_ok(v, im->global.type, at)) return false;
176 if (im->global.mut && !need(v, WASM_F_MUTABLE_GLOBAL, at)) return false;
177 break;
178 }
179 }
180 for (uint32_t i = m->nfunc_imports; i < m->nfuncs; i++)
181 if (m->funcs[i] >= m->ntypes)
182 return wasm_rd_fail_at(&v->r, m->codes ? m->codes[i - m->nfunc_imports].offset : 0,
183 "unknown type: type index out of bounds");
184 if (m->ntables > 1 && !need(v, WASM_F_REFERENCE_TYPES, 0)) return false;
185 for (uint32_t i = m->ntable_imports; i < m->ntables; i++)
186 if (!table_ok(v, &m->tables[i], 0)) return false;
187 if (m->nmems > 1 && !(v->f & WASM_F_MULTI_MEMORY)) return wasm_rd_fail_at(&v->r, 0, "multiple memories");
188 for (uint32_t i = m->nmem_imports; i < m->nmems; i++)
189 if (!mem_ok(v, &m->mems[i], 0)) return false;
190 for (uint32_t i = m->nglobal_imports; i < m->nglobals; i++) {
191 const wasm_expr *e = &m->global_inits[i - m->nglobal_imports];
192 if (!valtype_ok(v, m->globals[i].type, e->offset)) return false;
193 if (!const_ok(v, e, m->globals[i].type, i)) return false;
194 }
195 for (uint32_t i = 0; i < m->nexports; i++) {
196 const wasm_export *e = &m->exports[i];
197 size_t at = off(v, e->name);
198 for (uint32_t k = 0; k < i; k++)
199 if (m->exports[k].name_len == e->name_len && memcmp(m->exports[k].name, e->name, e->name_len) == 0)
200 return wasm_rd_fail_at(&v->r, at, "duplicate export name");
201 uint32_t n = e->kind == WASM_EXTERN_FUNC ? m->nfuncs
202 : e->kind == WASM_EXTERN_TABLE ? m->ntables
203 : e->kind == WASM_EXTERN_MEMORY ? m->nmems
204 : m->nglobals;
205 if (e->index >= n) return wasm_rd_fail_at(&v->r, at, "exported %s index out of bounds",
206 e->kind == WASM_EXTERN_FUNC ? "function" : e->kind == WASM_EXTERN_TABLE ? "table" : e->kind == WASM_EXTERN_MEMORY ? "memory" : "global");
207 if (e->kind == WASM_EXTERN_FUNC) v->declared[e->index] = true;
208 if (e->kind == WASM_EXTERN_GLOBAL && m->globals[e->index].mut && !need(v, WASM_F_MUTABLE_GLOBAL, at)) return false;
209 }
210 if (m->has_start) {
211 if (m->start >= m->nfuncs) return wasm_rd_fail_at(&v->r, 0, "unknown function %u: start function index out of bounds", m->start);
212 const wasm_functype *t = wasm_func_type(m, m->start);
213 if (t->nparams || t->nresults) return wasm_rd_fail_at(&v->r, 0, "invalid start function type");
214 }
215 for (uint32_t i = 0; i < m->nelems; i++) {
216 const wasm_elem *e = &m->elems[i];
217 size_t at = e->offset_in_binary;
218 bool mvp = e->mode == WASM_SEG_ACTIVE && e->table == 0 && e->funcs && e->type == WASM_FUNCREF;
219 if (!mvp && !need(v, WASM_F_BULK_MEMORY | WASM_F_REFERENCE_TYPES, at)) return false;
220 if (e->type == WASM_EXTERNREF && !need(v, WASM_F_REFERENCE_TYPES, at)) return false;
221 if (e->mode == WASM_SEG_ACTIVE) {
222 if (e->table >= m->ntables) return wasm_rd_fail_at(&v->r, at, "unknown table %u: table index out of bounds", e->table);
223 if (m->tables[e->table].elem != e->type)
224 return wasm_rd_fail_at(&v->r, at, "type mismatch: invalid element type");
225 if (!const_ok(v, &e->offset, WASM_I32, m->nglobals)) return false;
226 }
227 for (uint32_t k = 0; k < e->count; k++) {
228 if (e->funcs) {
229 if (e->funcs[k] >= m->nfuncs)
230 return wasm_rd_fail_at(&v->r, at, "unknown function %u: function index out of bounds", e->funcs[k]);
231 v->declared[e->funcs[k]] = true;
232 } else if (!const_ok(v, &e->exprs[k], e->type, m->nglobals)) {
233 return false;
234 }
235 }
236 }
237 for (uint32_t i = 0; i < m->ndatas; i++) {
238 const wasm_data *d = &m->datas[i];
239 if (d->mode == WASM_SEG_PASSIVE && !need(v, WASM_F_BULK_MEMORY, d->offset_in_binary)) return false;
240 if (d->mode == WASM_SEG_ACTIVE) {
241 if (d->mem >= m->nmems) return wasm_rd_fail_at(&v->r, d->offset_in_binary, "unknown memory %u", d->mem);
242 wasm_valtype it = (m->mems[d->mem].flags & WASM_LIMITS_64) ? WASM_I64 : WASM_I32;
243 if (!const_ok(v, &d->offset, it, m->nglobals)) return false;
244 }
245 }
246 return true;
247}
248
249/* ---- function bodies ---------------------------------------------------------------------- */
250
251#define UNKNOWN 0 /* a polymorphic stack's value: matches anything */
252
253typedef struct {
254 uint32_t op; /* block, loop, if, else, or 0 for the function */
255 int64_t bt; /* block type (or for the function: -1 and its type in F.ft) */
256 wasm_valtype one; /* a value-type block type's type */
257 uint32_t height; /* operand stack height at entry, params excluded */
258 bool unreachable;
259} frame;
260
261typedef struct {
262 M *v;
263 wasm_reader *r; /* the module's: one failure for everything */
264 const wasm_functype *ft;
265 wasm_valtype *locals;
266 uint32_t nlocals;
267 wasm_valtype *stack;
268 uint32_t sp, scap;
269 frame *ctl;
270 uint32_t nctl, ccap;
271} F;
272
273static bool fail(F *x, size_t at, const char *fmt, const char *a, const char *b) {
274 return wasm_rd_fail_at(x->r, at, fmt, a, b);
275}
276
277static void block_types(F *x, const frame *fr, bool params, const wasm_valtype **ts, uint32_t *n) {
278 if (fr->op == 0) {
279 *ts = params ? NULL : x->ft->results;
280 *n = params ? 0 : x->ft->nresults;
281 } else if (fr->bt >= 0) {
282 const wasm_functype *t = &x->v->m->types[fr->bt];
283 *ts = params ? t->params : t->results;
284 *n = params ? t->nparams : t->nresults;
285 } else if (fr->bt == WASM_BT_EMPTY || params) {
286 *ts = NULL;
287 *n = 0;
288 } else {
289 *ts = &fr->one;
290 *n = 1;
291 }
292}
293
294/* The types a branch to fr carries: a loop's params, else its results. */
295static void label_types(F *x, const frame *fr, const wasm_valtype **ts, uint32_t *n) {
296 block_types(x, fr, fr->op == WASM_OP_LOOP, ts, n);
297}
298
299static bool push(F *x, wasm_valtype t) {
300 if (x->sp == x->scap) {
301 uint32_t cap = x->scap ? x->scap * 2 : 64;
302 wasm_valtype *s = realloc(x->stack, cap);
303 if (!s) return wasm_rd_fail(x->r, "out of memory");
304 x->stack = s;
305 x->scap = cap;
306 }
307 x->stack[x->sp++] = t;
308 return true;
309}
310
311/* Pops one operand; *t is UNKNOWN below an unreachable frame's height. */
312static bool pop(F *x, size_t at, wasm_valtype *t) {
313 frame *fr = &x->ctl[x->nctl - 1];
314 if (x->sp == fr->height) {
315 if (fr->unreachable) {
316 *t = UNKNOWN;
317 return true;
318 }
319 return wasm_rd_fail_at(x->r, at, "type mismatch: operand stack empty");
320 }
321 *t = x->stack[--x->sp];
322 return true;
323}
324
325static bool pop_expect(F *x, size_t at, wasm_valtype want) {
326 wasm_valtype t;
327 if (!pop(x, at, &t)) return false;
328 if (t != UNKNOWN && want != UNKNOWN && t != want)
329 return fail(x, at, "type mismatch: expected %s, found %s", wasm_valtype_name(want), wasm_valtype_name(t));
330 return true;
331}
332
333static bool pop_types(F *x, size_t at, const wasm_valtype *ts, uint32_t n) {
334 for (uint32_t i = n; i-- > 0;)
335 if (!pop_expect(x, at, ts[i])) return false;
336 return true;
337}
338
339static bool push_types(F *x, const wasm_valtype *ts, uint32_t n) {
340 for (uint32_t i = 0; i < n; i++)
341 if (!push(x, ts[i])) return false;
342 return true;
343}
344
345static bool push_ctrl(F *x, uint32_t op, int64_t bt, wasm_valtype one) {
346 if (x->nctl == x->ccap) {
347 uint32_t cap = x->ccap ? x->ccap * 2 : 16;
348 frame *c = realloc(x->ctl, cap * sizeof *c);
349 if (!c) return wasm_rd_fail(x->r, "out of memory");
350 x->ctl = c;
351 x->ccap = cap;
352 }
353 frame *fr = &x->ctl[x->nctl++];
354 fr->op = op;
355 fr->bt = bt;
356 fr->one = one;
357 fr->height = x->sp;
358 fr->unreachable = false;
359 const wasm_valtype *ts;
360 uint32_t n;
361 block_types(x, fr, true, &ts, &n);
362 return push_types(x, ts, n);
363}
364
365/* Pops the innermost frame, checking its results are exactly what's on the stack. */
366static bool pop_ctrl(F *x, size_t at, frame *out) {
367 frame *fr = &x->ctl[x->nctl - 1];
368 const wasm_valtype *ts;
369 uint32_t n;
370 block_types(x, fr, false, &ts, &n);
371 if (!pop_types(x, at, ts, n)) return false;
372 if (x->sp != fr->height) return wasm_rd_fail_at(x->r, at, "type mismatch: values remaining on stack at end of block");
373 *out = *fr;
374 x->nctl--;
375 return true;
376}
377
378static void unreachable(F *x) {
379 frame *fr = &x->ctl[x->nctl - 1];
380 x->sp = fr->height;
381 fr->unreachable = true;
382}
383
384static bool blocktype_ok(F *x, const wasm_instr *in) {
385 if (in->blocktype >= 0) {
386 if ((uint64_t)in->blocktype >= x->v->m->ntypes)
387 return wasm_rd_fail_at(x->r, in->offset, "unknown type: type index out of bounds");
388 const wasm_functype *t = &x->v->m->types[in->blocktype];
389 if ((t->nparams || t->nresults > 1) && !need(x->v, WASM_F_MULTI_VALUE, in->offset)) return false;
390 return true;
391 }
392 if (in->blocktype != WASM_BT_EMPTY) return valtype_ok(x->v, in->bt_val, in->offset);
393 return true;
394}
395
396static const frame *label(F *x, const wasm_instr *in, uint32_t depth) {
397 if (depth >= x->nctl) {
398 wasm_rd_fail_at(x->r, in->offset, "unknown label: branch depth too large");
399 return NULL;
400 }
401 return &x->ctl[x->nctl - 1 - depth];
402}
403
404static bool memarg_ok(F *x, const wasm_instr *in, uint32_t natural) {
405 const wasm_module *m = x->v->m;
406 if (in->mem != 0 && !need(x->v, WASM_F_MULTI_MEMORY, in->offset)) return false;
407 if (in->mem >= m->nmems) return wasm_rd_fail_at(x->r, in->offset, "unknown memory %u", in->mem);
408 if (in->align > 63 || (1ull << in->align) > natural)
409 return wasm_rd_fail_at(x->r, in->offset, "alignment must not be larger than natural");
410 if (!(m->mems[in->mem].flags & WASM_LIMITS_64) && in->moffset > UINT32_MAX)
411 return wasm_rd_fail_at(x->r, in->offset, "offset out of range: must be <= 2**32");
412 return true;
413}
414
415static wasm_valtype addr_type(F *x, uint32_t mem) {
416 return mem < x->v->m->nmems && (x->v->m->mems[mem].flags & WASM_LIMITS_64) ? WASM_I64 : WASM_I32;
417}
418
419static bool table_index(F *x, const wasm_instr *in, uint32_t t) {
420 if (t >= x->v->m->ntables) return wasm_rd_fail_at(x->r, in->offset, "unknown table %u: table index out of bounds", t);
421 return true;
422}
423
424static bool plain(F *x, const wasm_instr *in) {
425 const char *sig = in->info.sig;
426 const char *gt = strchr(sig, '>');
427 for (const char *c = gt; c-- > sig;)
428 if (!pop_expect(x, in->offset, wasm_sig_type(*c))) return false;
429 for (const char *c = gt + 1; *c; c++)
430 if (!push(x, wasm_sig_type(*c))) return false;
431 return true;
432}
433
434/* One instruction of a type-checked body. *done: the function's own frame ended. */
435static bool instr(F *x, const wasm_instr *in, bool *done) {
436 M *v = x->v;
437 const wasm_module *m = v->m;
438 size_t at = in->offset;
439 wasm_valtype t, u;
440 if (in->info.sig && in->info.imm != WASM_IMM_MEMARG && in->op != WASM_OP_MEMORY_SIZE && in->op != WASM_OP_MEMORY_GROW)
441 return plain(x, in);
442 switch (in->op) {
443 case WASM_OP_UNREACHABLE: unreachable(x); return true;
444 case WASM_OP_NOP: return true;
445 case WASM_OP_BLOCK:
446 case WASM_OP_LOOP:
447 case WASM_OP_IF: {
448 if (!blocktype_ok(x, in)) return false;
449 if (in->op == WASM_OP_IF && !pop_expect(x, at, WASM_I32)) return false;
450 frame tmp = {in->op, in->blocktype, in->bt_val, 0, false};
451 const wasm_valtype *ts;
452 uint32_t n;
453 block_types(x, &tmp, true, &ts, &n);
454 if (!pop_types(x, at, ts, n)) return false;
455 return push_ctrl(x, in->op, in->blocktype, in->bt_val);
456 }
457 case WASM_OP_ELSE: {
458 frame fr;
459 if (x->ctl[x->nctl - 1].op != WASM_OP_IF) return wasm_rd_fail_at(x->r, at, "else found outside of an `if` block");
460 if (!pop_ctrl(x, at, &fr)) return false;
461 return push_ctrl(x, WASM_OP_ELSE, fr.bt, fr.one);
462 }
463 case WASM_OP_END: {
464 frame fr;
465 if (!pop_ctrl(x, at, &fr)) return false;
466 /* An if without else must have matching params and results (it falls through empty). */
467 if (fr.op == WASM_OP_IF) {
468 const wasm_valtype *pin, *pout;
469 uint32_t nin, nout;
470 block_types(x, &fr, true, &pin, &nin);
471 block_types(x, &fr, false, &pout, &nout);
472 if (nin != nout || (nin && memcmp(pin, pout, nin) != 0))
473 return wasm_rd_fail_at(x->r, at, "type mismatch: else branch missing");
474 }
475 if (fr.op == 0) {
476 *done = true;
477 return true;
478 }
479 const wasm_valtype *ts;
480 uint32_t n;
481 block_types(x, &fr, false, &ts, &n);
482 return push_types(x, ts, n);
483 }
484 case WASM_OP_BR:
485 case WASM_OP_BR_IF: {
486 const frame *fr = label(x, in, in->a);
487 if (!fr) return false;
488 const wasm_valtype *ts;
489 uint32_t n;
490 label_types(x, fr, &ts, &n);
491 if (in->op == WASM_OP_BR_IF && !pop_expect(x, at, WASM_I32)) return false;
492 if (!pop_types(x, at, ts, n)) return false;
493 if (in->op == WASM_OP_BR) unreachable(x);
494 else if (!push_types(x, ts, n)) return false;
495 return true;
496 }
497 case WASM_OP_BR_TABLE: {
498 if (!pop_expect(x, at, WASM_I32)) return false;
499 uint32_t *targets = malloc(((size_t)in->ntargets + 1) * sizeof *targets);
500 if (!targets) return wasm_rd_fail(x->r, "out of memory");
501 wasm_instr_targets(in, targets);
502 const frame *def = label(x, in, targets[in->ntargets]);
503 bool ok = def != NULL;
504 const wasm_valtype *dts = NULL;
505 uint32_t dn = 0;
506 if (ok) label_types(x, def, &dts, &dn);
507 for (uint32_t i = 0; ok && i < in->ntargets; i++) {
508 const frame *fr = label(x, in, targets[i]);
509 if (!fr) {
510 ok = false;
511 break;
512 }
513 const wasm_valtype *ts;
514 uint32_t n;
515 label_types(x, fr, &ts, &n);
516 if (n != dn) {
517 ok = wasm_rd_fail_at(x->r, at, "type mismatch: br_table target labels have different number of types");
518 break;
519 }
520 /* Each target's types must match the operands (checked without consuming them). */
521 uint32_t sp = x->sp;
522 ok = pop_types(x, at, ts, n);
523 x->sp = sp;
524 }
525 free(targets);
526 if (!ok) return false;
527 if (!pop_types(x, at, dts, dn)) return false;
528 unreachable(x);
529 return true;
530 }
531 case WASM_OP_RETURN:
532 if (!pop_types(x, at, x->ft->results, x->ft->nresults)) return false;
533 unreachable(x);
534 return true;
535 case WASM_OP_CALL: {
536 if (in->a >= m->nfuncs) return wasm_rd_fail_at(x->r, at, "unknown function %u: function index out of bounds", in->a);
537 const wasm_functype *ft = wasm_func_type(m, in->a);
538 return pop_types(x, at, ft->params, ft->nparams) && push_types(x, ft->results, ft->nresults);
539 }
540 case WASM_OP_CALL_INDIRECT: {
541 if (in->b != 0 && !need(v, WASM_F_REFERENCE_TYPES, at)) return false;
542 if (!table_index(x, in, in->b)) return false;
543 if (m->tables[in->b].elem != WASM_FUNCREF)
544 return wasm_rd_fail_at(x->r, at, "indirect calls must go through a table with type <= funcref");
545 if (in->a >= m->ntypes) return wasm_rd_fail_at(x->r, at, "unknown type: type index out of bounds");
546 const wasm_functype *ft = &m->types[in->a];
547 return pop_expect(x, at, WASM_I32) && pop_types(x, at, ft->params, ft->nparams) &&
548 push_types(x, ft->results, ft->nresults);
549 }
550 case WASM_OP_DROP: return pop(x, at, &t);
551 case WASM_OP_SELECT: {
552 if (!pop_expect(x, at, WASM_I32) || !pop(x, at, &t) || !pop(x, at, &u)) return false;
553 if (t != UNKNOWN && u != UNKNOWN && t != u)
554 return fail(x, at, "type mismatch: select operands have different types (%s, %s)", wasm_valtype_name(u), wasm_valtype_name(t));
555 wasm_valtype r = t != UNKNOWN ? t : u;
556 if (r != UNKNOWN && !wasm_is_num(r) && r != WASM_V128)
557 return wasm_rd_fail_at(x->r, at, "type mismatch: select only takes integral types");
558 return push(x, r);
559 }
560 case WASM_OP_SELECT_T:
561 if (!valtype_ok(v, in->select_type, at)) return false;
562 return pop_expect(x, at, WASM_I32) && pop_expect(x, at, in->select_type) &&
563 pop_expect(x, at, in->select_type) && push(x, in->select_type);
564 case WASM_OP_LOCAL_GET:
565 case WASM_OP_LOCAL_SET:
566 case WASM_OP_LOCAL_TEE: {
567 if (in->a >= x->nlocals) return wasm_rd_fail_at(x->r, at, "unknown local %u: local index out of bounds", in->a);
568 wasm_valtype lt = x->locals[in->a];
569 if (in->op == WASM_OP_LOCAL_GET) return push(x, lt);
570 if (!pop_expect(x, at, lt)) return false;
571 return in->op == WASM_OP_LOCAL_TEE ? push(x, lt) : true;
572 }
573 case WASM_OP_GLOBAL_GET:
574 case WASM_OP_GLOBAL_SET:
575 if (in->a >= m->nglobals) return wasm_rd_fail_at(x->r, at, "unknown global %u: global index out of bounds", in->a);
576 if (in->op == WASM_OP_GLOBAL_GET) return push(x, m->globals[in->a].type);
577 if (!m->globals[in->a].mut) return wasm_rd_fail_at(x->r, at, "global is immutable: cannot modify it with `global.set`");
578 return pop_expect(x, at, m->globals[in->a].type);
579 case WASM_OP_TABLE_GET:
580 if (!table_index(x, in, in->a)) return false;
581 return pop_expect(x, at, WASM_I32) && push(x, m->tables[in->a].elem);
582 case WASM_OP_TABLE_SET:
583 if (!table_index(x, in, in->a)) return false;
584 return pop_expect(x, at, m->tables[in->a].elem) && pop_expect(x, at, WASM_I32);
585 case WASM_OP_MEMORY_SIZE:
586 case WASM_OP_MEMORY_GROW: {
587 if (in->mem != 0 && !need(v, WASM_F_MULTI_MEMORY, at)) return false;
588 if (in->mem >= m->nmems) return wasm_rd_fail_at(x->r, at, "unknown memory %u", in->mem);
589 wasm_valtype it = addr_type(x, in->mem);
590 if (in->op == WASM_OP_MEMORY_GROW && !pop_expect(x, at, it)) return false;
591 return push(x, it);
592 }
593 case WASM_OP_REF_NULL: return push(x, in->reftype);
594 case WASM_OP_REF_IS_NULL:
595 if (!pop(x, at, &t)) return false;
596 if (t != UNKNOWN && !wasm_is_ref(t))
597 return fail(x, at, "type mismatch: expected %s, found %s", "reference", wasm_valtype_name(t));
598 return push(x, WASM_I32);
599 case WASM_OP_REF_FUNC:
600 if (in->a >= m->nfuncs) return wasm_rd_fail_at(x->r, at, "unknown function %u: function index out of bounds", in->a);
601 if (!v->declared[in->a]) return wasm_rd_fail_at(x->r, at, "undeclared function reference");
602 return push(x, WASM_FUNCREF);
603 case WASM_OP_MEMORY_INIT:
604 if (!m->has_datacount) return wasm_rd_fail_at(x->r, at, "data count section required");
605 if (in->mem >= m->nmems) return wasm_rd_fail_at(x->r, at, "unknown memory %u", in->mem);
606 if (in->a >= m->datacount) return wasm_rd_fail_at(x->r, at, "unknown data segment %u", in->a);
607 return pop_expect(x, at, WASM_I32) && pop_expect(x, at, WASM_I32) && pop_expect(x, at, addr_type(x, in->mem));
608 case WASM_OP_DATA_DROP:
609 if (!m->has_datacount) return wasm_rd_fail_at(x->r, at, "data count section required");
610 if (in->a >= m->datacount) return wasm_rd_fail_at(x->r, at, "unknown data segment %u", in->a);
611 return true;
612 case WASM_OP_MEMORY_COPY:
613 if ((in->mem || in->b) && !need(v, WASM_F_MULTI_MEMORY, at)) return false;
614 if (in->mem >= m->nmems || in->b >= m->nmems) return wasm_rd_fail_at(x->r, at, "unknown memory %u", in->mem >= m->nmems ? in->mem : in->b);
615 return pop_expect(x, at, addr_type(x, in->mem)) && pop_expect(x, at, addr_type(x, in->b)) &&
616 pop_expect(x, at, addr_type(x, in->mem));
617 case WASM_OP_MEMORY_FILL:
618 if (in->mem != 0 && !need(v, WASM_F_MULTI_MEMORY, at)) return false;
619 if (in->mem >= m->nmems) return wasm_rd_fail_at(x->r, at, "unknown memory %u", in->mem);
620 return pop_expect(x, at, addr_type(x, in->mem)) && pop_expect(x, at, WASM_I32) && pop_expect(x, at, addr_type(x, in->mem));
621 case WASM_OP_TABLE_INIT:
622 if (!table_index(x, in, in->b)) return false;
623 if (in->a >= m->nelems) return wasm_rd_fail_at(x->r, at, "unknown elem segment %u: segment index out of bounds", in->a);
624 if (m->elems[in->a].type != m->tables[in->b].elem) return wasm_rd_fail_at(x->r, at, "type mismatch");
625 return pop_expect(x, at, WASM_I32) && pop_expect(x, at, WASM_I32) && pop_expect(x, at, WASM_I32);
626 case WASM_OP_ELEM_DROP:
627 if (in->a >= m->nelems) return wasm_rd_fail_at(x->r, at, "unknown elem segment %u: segment index out of bounds", in->a);
628 return true;
629 case WASM_OP_TABLE_COPY:
630 if (!table_index(x, in, in->a) || !table_index(x, in, in->b)) return false;
631 if (m->tables[in->a].elem != m->tables[in->b].elem) return wasm_rd_fail_at(x->r, at, "type mismatch");
632 return pop_expect(x, at, WASM_I32) && pop_expect(x, at, WASM_I32) && pop_expect(x, at, WASM_I32);
633 case WASM_OP_TABLE_GROW:
634 if (!table_index(x, in, in->a)) return false;
635 return pop_expect(x, at, WASM_I32) && pop_expect(x, at, m->tables[in->a].elem) && push(x, WASM_I32);
636 case WASM_OP_TABLE_SIZE:
637 if (!table_index(x, in, in->a)) return false;
638 return push(x, WASM_I32);
639 case WASM_OP_TABLE_FILL:
640 if (!table_index(x, in, in->a)) return false;
641 return pop_expect(x, at, WASM_I32) && pop_expect(x, at, m->tables[in->a].elem) && pop_expect(x, at, WASM_I32);
642 default: break;
643 }
644 if (in->info.imm == WASM_IMM_MEMARG) {
645 if (!memarg_ok(x, in, in->info.mem_bytes)) return false;
646 /* A load or store: the address is the memory's index type, then the signature. */
647 const char *sig = in->info.sig;
648 const char *gt = strchr(sig, '>');
649 for (const char *c = gt; c-- > sig + 1;)
650 if (!pop_expect(x, at, wasm_sig_type(*c))) return false;
651 if (!pop_expect(x, at, addr_type(x, in->mem))) return false;
652 for (const char *c = gt + 1; *c; c++)
653 if (!push(x, wasm_sig_type(*c))) return false;
654 return true;
655 }
656 return wasm_rd_fail_at(x->r, at, "internal: unhandled instruction %s", in->info.name);
657}
658
659/* The rest of a body past its first SIMD instruction: structure and features only. */
660static bool walk_rest(F *x, uint32_t depth) {
661 wasm_instr in;
662 while (depth > 0) {
663 if (wasm_rd_eof(x->r)) return wasm_rd_fail(x->r, "control frames remain at end of function: END opcode expected");
664 if (!wasm_rd_instr(x->r, &in)) return false;
665 if (in.info.feature && !need(x->v, in.info.feature, in.offset)) return false;
666 if (in.info.feature == WASM_F_THREADS && x->v->rep) x->v->rep->uses_threads = true;
667 if (in.op == WASM_OP_BLOCK || in.op == WASM_OP_LOOP || in.op == WASM_OP_IF) depth++;
668 else if (in.op == WASM_OP_END) depth--;
669 }
670 return true;
671}
672
673static bool body_ok(M *v, uint32_t index) {
674 const wasm_module *m = v->m;
675 const wasm_code *c = &m->codes[index - m->nfunc_imports];
676 F x = {0};
677 x.v = v;
678 x.r = &v->r;
679 v->r.p = c->body.start;
680 v->r.end = c->body.start + c->body.len;
681 x.ft = wasm_func_type(m, index);
682 x.nlocals = (uint32_t)(x.ft->nparams + c->nlocals);
683 bool ok = false;
684 if (!(x.locals = malloc(x.nlocals ? x.nlocals : 1))) {
685 wasm_rd_fail(x.r, "out of memory");
686 goto out;
687 }
688 if (x.ft->nparams) memcpy(x.locals, x.ft->params, x.ft->nparams);
689 uint32_t k = x.ft->nparams;
690 for (uint32_t g = 0; g < c->nlocal_groups; g++) {
691 if (!valtype_ok(v, c->locals[g].type, c->offset)) goto out;
692 memset(x.locals + k, c->locals[g].type, c->locals[g].count);
693 k += c->locals[g].count;
694 }
695 if (!push_ctrl(&x, 0, -1, 0)) goto out;
696 wasm_instr in;
697 bool done = false;
698 while (!done) {
699 if (wasm_rd_eof(x.r)) {
700 wasm_rd_fail(x.r, "control frames remain at end of function: END opcode expected");
701 goto out;
702 }
703 if (!wasm_rd_instr(x.r, &in)) goto out;
704 if (in.info.feature && !need(v, in.info.feature, in.offset)) goto out;
705 if (in.info.feature & (WASM_F_SIMD | WASM_F_RELAXED_SIMD | WASM_F_THREADS)) {
706 /* Enabled, but not type-checked here (see validate.h): walk the rest. */
707 if (v->rep) {
708 if (in.info.feature == WASM_F_THREADS) v->rep->uses_threads = true;
709 else v->rep->uses_simd = true;
710 }
711 if (!walk_rest(&x, x.nctl)) goto out;
712 break;
713 }
714 if (!instr(&x, &in, &done)) goto out;
715 }
716 if (!wasm_rd_eof(x.r)) {
717 wasm_rd_fail(x.r, "operators remaining after end of function");
718 goto out;
719 }
720 ok = true;
721out:
722 free(x.locals);
723 free(x.stack);
724 free(x.ctl);
725 return ok;
726}
727
728bool wasm_validate(const wasm_module *m, wasm_features features, wasm_validate_report *report, char *err,
729 size_t errlen) {
730 M v = {0};
731 v.m = m;
732 v.f = features;
733 v.rep = report;
734 if (report) memset(report, 0, sizeof *report);
735 v.r = wasm_reader_at(m->bytes, m->bytes, m->bytes + m->len, err, errlen);
736 v.declared = calloc(m->nfuncs ? m->nfuncs : 1, sizeof *v.declared);
737 if (!v.declared) return wasm_rd_fail(&v.r, "out of memory");
738 bool ok = module_ok(&v);
739 for (uint32_t i = m->nfunc_imports; ok && i < m->nfuncs; i++) ok = body_ok(&v, i);
740 free(v.declared);
741 return ok && !v.r.failed;
742}