==> src/decode.c <==1/* The sections of a module, in order. */
2#include "wasm/decode.h"
3
4#include "wasm/instr.h"
5#include "wasm/reader.h"
6
7#include <stdlib.h>
8#include <string.h>
9
10/* calloc of n elements, at least one so a zero count still gives a non-NULL array. */
11static void *alloc(size_t n, size_t size) { return calloc(n ? n : 1, size); }
12
13static bool oom(wasm_reader *r) { return wasm_rd_fail(r, "out of memory"); }
14
15static wasm_valtype valtype(wasm_reader *r) {
16 size_t at = wasm_rd_offset(r);
17 uint8_t t = wasm_rd_u8(r);
18 if (!r->failed && !wasm_is_valtype(t)) wasm_rd_fail_at(r, at, "invalid value type");
19 return t;
20}
21
22static void limits(wasm_reader *r, wasm_limits *l, bool table) {
23 size_t at = wasm_rd_offset(r);
24 uint8_t f = wasm_rd_u8(r);
25 if (r->failed) return;
26 if (f > 0x07 || (table && f > 0x01)) {
27 wasm_rd_fail_at(r, at, table ? "invalid table resizable limits flags" : "invalid memory limits flags");
28 return;
29 }
30 l->flags = f;
31 bool is64 = f & WASM_LIMITS_64;
32 l->min = is64 ? wasm_rd_u64(r) : wasm_rd_u32(r);
33 if (f & WASM_LIMITS_HAS_MAX) l->max = is64 ? wasm_rd_u64(r) : wasm_rd_u32(r);
34}
35
36static void tabletype(wasm_reader *r, wasm_tabletype *t) {
37 size_t at = wasm_rd_offset(r);
38 t->elem = wasm_rd_u8(r);
39 if (!r->failed && !wasm_is_ref(t->elem)) {
40 wasm_rd_fail_at(r, at, "malformed reference type");
41 return;
42 }
43 limits(r, &t->limits, true);
44}
45
46static void globaltype(wasm_reader *r, wasm_globaltype *g) {
47 g->type = valtype(r);
48 size_t at = wasm_rd_offset(r);
49 uint8_t m = wasm_rd_u8(r);
50 if (!r->failed && m > 1) wasm_rd_fail_at(r, at, "malformed mutability");
51 g->mut = m == 1;
52}
53
54/* A constant expression: instructions up to the `end` that closes it (constant expressions have
55 * no blocks, so the first `end` does). Which instructions are constant is the validator's. */
56static void expr(wasm_reader *r, wasm_expr *e) {
57 e->start = r->p;
58 e->offset = wasm_rd_offset(r);
59 wasm_instr in;
60 for (;;) {
61 if (!wasm_rd_instr(r, &in)) return;
62 if (in.op == WASM_OP_END) break;
63 }
64 e->len = (size_t)(r->p - e->start);
65}
66
67static bool types(wasm_reader *r, wasm_module *m) {
68 m->ntypes = wasm_rd_count(r, 3, "types");
69 if (!(m->types = alloc(m->ntypes, sizeof *m->types))) return oom(r);
70 for (uint32_t i = 0; i < m->ntypes && !r->failed; i++) {
71 size_t at = wasm_rd_offset(r);
72 uint8_t form = wasm_rd_u8(r);
73 if (r->failed) break;
74 if (form != WASM_FUNC_FORM) return wasm_rd_fail_at(r, at, "invalid leading byte in type definition (0x%x)", form);
75 wasm_functype *t = &m->types[i];
76 t->nparams = wasm_rd_count(r, 1, "function params");
77 t->params = r->p;
78 for (uint32_t k = 0; k < t->nparams && !r->failed; k++) valtype(r);
79 t->nresults = wasm_rd_count(r, 1, "function results");
80 t->results = r->p;
81 for (uint32_t k = 0; k < t->nresults && !r->failed; k++) valtype(r);
82 }
83 return !r->failed;
84}
85
86static bool imports(wasm_reader *r, wasm_module *m) {
87 m->nimports = wasm_rd_count(r, 4, "imports");
88 if (!(m->imports = alloc(m->nimports, sizeof *m->imports))) return oom(r);
89 for (uint32_t i = 0; i < m->nimports && !r->failed; i++) {
90 wasm_import *im = &m->imports[i];
91 im->module = wasm_rd_name(r, &im->module_len);
92 im->name = wasm_rd_name(r, &im->name_len);
93 size_t at = wasm_rd_offset(r);
94 im->kind = wasm_rd_u8(r);
95 if (r->failed) break;
96 switch (im->kind) {
97 case WASM_EXTERN_FUNC: im->func_type = wasm_rd_u32(r); m->nfunc_imports++; break;
98 case WASM_EXTERN_TABLE: tabletype(r, &im->table); m->ntable_imports++; break;
99 case WASM_EXTERN_MEMORY: limits(r, &im->mem, false); m->nmem_imports++; break;
100 case WASM_EXTERN_GLOBAL: globaltype(r, &im->global); m->nglobal_imports++; break;
101 case WASM_EXTERN_TAG: return wasm_rd_fail_at(r, at, "exceptions proposal not enabled");
102 default: return wasm_rd_fail_at(r, at, "invalid leading byte (0x%x) for external kind", im->kind);
103 }
104 }
105 return !r->failed;
106}
107
108static bool functions(wasm_reader *r, wasm_module *m, uint32_t *ndefined) {
109 *ndefined = wasm_rd_count(r, 1, "functions");
110 m->nfuncs = m->nfunc_imports + *ndefined;
111 if (!(m->funcs = alloc(m->nfuncs, sizeof *m->funcs))) return oom(r);
112 uint32_t f = 0;
113 for (uint32_t i = 0; i < m->nimports; i++)
114 if (m->imports[i].kind == WASM_EXTERN_FUNC) m->funcs[f++] = m->imports[i].func_type;
115 for (uint32_t i = 0; i < *ndefined && !r->failed; i++) m->funcs[f++] = wasm_rd_u32(r);
116 return !r->failed;
117}
118
119/* No function section: the imported functions alone. */
120static bool functions_none(wasm_reader *r, wasm_module *m) {
121 m->nfuncs = m->nfunc_imports;
122 if (!(m->funcs = alloc(m->nfuncs, sizeof *m->funcs))) return oom(r);
123 uint32_t f = 0;
124 for (uint32_t i = 0; i < m->nimports; i++)
125 if (m->imports[i].kind == WASM_EXTERN_FUNC) m->funcs[f++] = m->imports[i].func_type;
126 return true;
127}
128
129static bool tables(wasm_reader *r, wasm_module *m, uint32_t n) {
130 m->ntables = m->ntable_imports + n;
131 if (!(m->tables = alloc(m->ntables, sizeof *m->tables))) return oom(r);
132 uint32_t t = 0;
133 for (uint32_t i = 0; i < m->nimports; i++)
134 if (m->imports[i].kind == WASM_EXTERN_TABLE) m->tables[t++] = m->imports[i].table;
135 for (uint32_t i = 0; i < n && !r->failed; i++) tabletype(r, &m->tables[t++]);
136 return !r->failed;
137}
138
139static bool memories(wasm_reader *r, wasm_module *m, uint32_t n) {
140 m->nmems = m->nmem_imports + n;
141 if (!(m->mems = alloc(m->nmems, sizeof *m->mems))) return oom(r);
142 uint32_t k = 0;
143 for (uint32_t i = 0; i < m->nimports; i++)
144 if (m->imports[i].kind == WASM_EXTERN_MEMORY) m->mems[k++] = m->imports[i].mem;
145 for (uint32_t i = 0; i < n && !r->failed; i++) limits(r, &m->mems[k++], false);
146 return !r->failed;
147}
148
149static bool globals(wasm_reader *r, wasm_module *m, uint32_t n) {
150 m->nglobals = m->nglobal_imports + n;
151 if (!(m->globals = alloc(m->nglobals, sizeof *m->globals)) || !(m->global_inits = alloc(n, sizeof *m->global_inits)))
152 return oom(r);
153 uint32_t g = 0;
154 for (uint32_t i = 0; i < m->nimports; i++)
155 if (m->imports[i].kind == WASM_EXTERN_GLOBAL) m->globals[g++] = m->imports[i].global;
156 for (uint32_t i = 0; i < n && !r->failed; i++) {
157 globaltype(r, &m->globals[g++]);
158 expr(r, &m->global_inits[i]);
159 }
160 return !r->failed;
161}
162
163static bool exports(wasm_reader *r, wasm_module *m) {
164 m->nexports = wasm_rd_count(r, 3, "exports");
165 if (!(m->exports = alloc(m->nexports, sizeof *m->exports))) return oom(r);
166 for (uint32_t i = 0; i < m->nexports && !r->failed; i++) {
167 wasm_export *e = &m->exports[i];
168 e->name = wasm_rd_name(r, &e->name_len);
169 size_t at = wasm_rd_offset(r);
170 e->kind = wasm_rd_u8(r);
171 if (!r->failed && e->kind > WASM_EXTERN_GLOBAL)
172 return wasm_rd_fail_at(r, at, "invalid leading byte (0x%x) for external kind", e->kind);
173 e->index = wasm_rd_u32(r);
174 }
175 return !r->failed;
176}
177
178static bool elements(wasm_reader *r, wasm_module *m) {
179 m->nelems = wasm_rd_count(r, 2, "element segments");
180 if (!(m->elems = alloc(m->nelems, sizeof *m->elems))) return oom(r);
181 for (uint32_t i = 0; i < m->nelems && !r->failed; i++) {
182 wasm_elem *e = &m->elems[i];
183 e->offset_in_binary = wasm_rd_offset(r);
184 uint32_t flags = wasm_rd_u32(r);
185 if (r->failed) break;
186 if (flags > 7) return wasm_rd_fail_at(r, e->offset_in_binary, "invalid flags byte in element segment");
187 /* bit 0: passive or declarative (bit 1 then says which); bit 1 on an active one: a table
188 * index; bit 2: expressions instead of function indices. */
189 e->mode = !(flags & 1) ? WASM_SEG_ACTIVE : (flags & 2) ? WASM_SEG_DECLARATIVE : WASM_SEG_PASSIVE;
190 if (e->mode == WASM_SEG_ACTIVE) {
191 e->table = (flags & 2) ? wasm_rd_u32(r) : 0;
192 expr(r, &e->offset);
193 }
194 bool exprs = flags & 4;
195 e->type = WASM_FUNCREF;
196 if (flags & 3) {
197 size_t at = wasm_rd_offset(r);
198 uint8_t k = wasm_rd_u8(r);
199 if (r->failed) break;
200 if (exprs) {
201 if (!wasm_is_ref(k)) return wasm_rd_fail_at(r, at, "malformed reference type");
202 e->type = k;
203 } else if (k != 0x00) {
204 return wasm_rd_fail_at(r, at, "invalid external kind in element segment");
205 }
206 }
207 e->count = wasm_rd_count(r, 1, "elements");
208 if (r->failed) break;
209 if (exprs) {
210 if (!(e->exprs = alloc(e->count, sizeof *e->exprs))) return oom(r);
211 for (uint32_t k = 0; k < e->count && !r->failed; k++) expr(r, &e->exprs[k]);
212 } else {
213 if (!(e->funcs = alloc(e->count, sizeof *e->funcs))) return oom(r);
214 for (uint32_t k = 0; k < e->count && !r->failed; k++) e->funcs[k] = wasm_rd_u32(r);
215 }
216 }
217 return !r->failed;
218}
219
220static bool codes(wasm_reader *r, wasm_module *m, uint32_t ndefined) {
221 size_t at = wasm_rd_offset(r);
222 m->ncodes = wasm_rd_count(r, 1, "function bodies");
223 if (r->failed) return false;
224 if (m->ncodes != ndefined) return wasm_rd_fail_at(r, at, "function and code section have inconsistent lengths");
225 if (!(m->codes = alloc(m->ncodes, sizeof *m->codes))) return oom(r);
226 for (uint32_t i = 0; i < m->ncodes && !r->failed; i++) {
227 wasm_code *c = &m->codes[i];
228 c->offset = wasm_rd_offset(r);
229 uint32_t size = wasm_rd_u32(r);
230 const uint8_t *body = wasm_rd_bytes(r, size);
231 if (!body) break;
232 wasm_reader b = *r;
233 b.p = body;
234 b.end = body + size;
235 c->nlocal_groups = wasm_rd_count(&b, 2, "local declarations");
236 if (!b.failed && !(c->locals = alloc(c->nlocal_groups, sizeof *c->locals))) return oom(r);
237 for (uint32_t k = 0; k < c->nlocal_groups && !b.failed; k++) {
238 size_t lat = wasm_rd_offset(&b);
239 c->locals[k].count = wasm_rd_u32(&b);
240 c->locals[k].type = valtype(&b);
241 c->nlocals += c->locals[k].count;
242 if (!b.failed && c->nlocals > WASM_MAX_LOCALS) wasm_rd_fail_at(&b, lat, "too many locals");
243 }
244 if (b.failed) {
245 *r = b;
246 break;
247 }
248 c->body.start = b.p;
249 c->body.len = (size_t)(b.end - b.p);
250 c->body.offset = wasm_rd_offset(&b);
251 if (c->body.len == 0) return wasm_rd_fail_at(r, c->body.offset, "unexpected end-of-file");
252 }
253 return !r->failed;
254}
255
256static bool datas(wasm_reader *r, wasm_module *m) {
257 size_t at = wasm_rd_offset(r);
258 m->ndatas = wasm_rd_count(r, 2, "data segments");
259 if (r->failed) return false;
260 if (m->has_datacount && m->ndatas != m->datacount)
261 return wasm_rd_fail_at(r, at, "data count and data section have inconsistent lengths");
262 if (!(m->datas = alloc(m->ndatas, sizeof *m->datas))) return oom(r);
263 for (uint32_t i = 0; i < m->ndatas && !r->failed; i++) {
264 wasm_data *d = &m->datas[i];
265 d->offset_in_binary = wasm_rd_offset(r);
266 uint32_t flags = wasm_rd_u32(r);
267 if (r->failed) break;
268 if (flags > 2) return wasm_rd_fail_at(r, d->offset_in_binary, "invalid flags byte in data segment");
269 d->mode = flags == 1 ? WASM_SEG_PASSIVE : WASM_SEG_ACTIVE;
270 if (flags == 2) d->mem = wasm_rd_u32(r);
271 if (d->mode == WASM_SEG_ACTIVE) expr(r, &d->offset);
272 d->len = wasm_rd_u32(r);
273 d->bytes = wasm_rd_bytes(r, d->len);
274 }
275 return !r->failed;
276}
277
278/* Where a section may appear: its rank in the order the spec fixes (custom sections anywhere). */
279static int rank(uint8_t id) {
280 switch (id) {
281 case WASM_SEC_TYPE: return 1;
282 case WASM_SEC_IMPORT: return 2;
283 case WASM_SEC_FUNCTION: return 3;
284 case WASM_SEC_TABLE: return 4;
285 case WASM_SEC_MEMORY: return 5;
286 case WASM_SEC_TAG: return 6;
287 case WASM_SEC_GLOBAL: return 7;
288 case WASM_SEC_EXPORT: return 8;
289 case WASM_SEC_START: return 9;
290 case WASM_SEC_ELEMENT: return 10;
291 case WASM_SEC_DATACOUNT: return 11;
292 case WASM_SEC_CODE: return 12;
293 case WASM_SEC_DATA: return 13;
294 default: return -1;
295 }
296}
297
298bool wasm_decode(const uint8_t *bytes, size_t len, wasm_module *m, char *err, size_t errlen) {
299 memset(m, 0, sizeof *m);
300 m->bytes = bytes;
301 m->len = len;
302 wasm_reader r = wasm_reader_at(bytes, bytes, bytes + len, err, errlen);
303 const uint8_t *magic = wasm_rd_bytes(&r, 4);
304 if (!magic || memcmp(magic, "\0asm", 4) != 0) {
305 if (!r.failed) wasm_rd_fail_at(&r, 0, "magic header not detected: bad magic number");
306 goto fail;
307 }
308 uint32_t version = wasm_rd_le32(&r);
309 if (r.failed) goto fail;
310 if (version != 1) {
311 wasm_rd_fail_at(&r, 4, "unknown binary version: 0x%x", version);
312 goto fail;
313 }
314 int last = 0;
315 uint32_t ndefined = 0, ntables = 0, nmems = 0, nglobals = 0;
316 bool have_funcs = false;
317 bool tables_done = false, mems_done = false, globals_done = false;
318 while (!wasm_rd_eof(&r) && !r.failed) {
319 size_t sec_at = wasm_rd_offset(&r);
320 uint8_t id = wasm_rd_u8(&r);
321 uint32_t size = wasm_rd_u32(&r);
322 const uint8_t *body = wasm_rd_bytes(&r, size);
323 if (!body) break;
324 wasm_reader s = r;
325 s.p = body;
326 s.end = body + size;
327 if (id == WASM_SEC_CUSTOM) {
328 uint32_t n;
329 wasm_rd_name(&s, &n);
330 if (s.failed) r = s;
331 continue;
332 }
333 int k = rank(id);
334 if (k < 0) {
335 wasm_rd_fail_at(&r, sec_at, "malformed section id: %u", id);
336 break;
337 }
338 if (k <= last) {
339 wasm_rd_fail_at(&r, sec_at, "section out of order");
340 break;
341 }
342 last = k;
343 switch (id) {
344 case WASM_SEC_TYPE: types(&s, m); break;
345 case WASM_SEC_IMPORT: imports(&s, m); break;
346 case WASM_SEC_FUNCTION: functions(&s, m, &ndefined); have_funcs = true; break;
347 case WASM_SEC_TABLE:
348 ntables = wasm_rd_count(&s, 3, "tables");
349 tables(&s, m, ntables);
350 tables_done = true;
351 break;
352 case WASM_SEC_MEMORY:
353 nmems = wasm_rd_count(&s, 2, "memories");
354 memories(&s, m, nmems);
355 mems_done = true;
356 break;
357 case WASM_SEC_TAG: wasm_rd_fail_at(&s, sec_at, "exceptions proposal not enabled"); break;
358 case WASM_SEC_GLOBAL:
359 nglobals = wasm_rd_count(&s, 3, "globals");
360 globals(&s, m, nglobals);
361 globals_done = true;
362 break;
363 case WASM_SEC_EXPORT: exports(&s, m); break;
364 case WASM_SEC_START:
365 m->has_start = true;
366 m->start = wasm_rd_u32(&s);
367 break;
368 case WASM_SEC_ELEMENT: elements(&s, m); break;
369 case WASM_SEC_DATACOUNT:
370 m->has_datacount = true;
371 m->datacount = wasm_rd_u32(&s);
372 break;
373 case WASM_SEC_CODE:
374 m->code_section_offset = sec_at;
375 codes(&s, m, ndefined);
376 break;
377 case WASM_SEC_DATA: datas(&s, m); break;
378 }
379 if (s.failed) {
380 r = s;
381 break;
382 }
383 if (!wasm_rd_eof(&s)) {
384 wasm_rd_fail_at(&r, wasm_rd_offset(&s), "section size mismatch: unexpected data at the end of the section");
385 break;
386 }
387 }
388 if (r.failed) goto fail;
389 /* Absent sections: the index spaces still hold the imports. */
390 {
391 wasm_reader z = wasm_reader_at(bytes, bytes + len, bytes + len, err, errlen);
392 if (!have_funcs && !functions_none(&z, m)) goto oomfail;
393 if (!tables_done && !tables(&z, m, 0)) goto oomfail;
394 if (!mems_done && !memories(&z, m, 0)) goto oomfail;
395 if (!globals_done && !globals(&z, m, 0)) goto oomfail;
396 }
397 if (ndefined && !m->codes) {
398 wasm_rd_fail_at(&r, len, "function and code section have inconsistent lengths");
399 goto fail;
400 }
401 if (m->has_datacount && m->datacount && !m->datas) {
402 wasm_rd_fail_at(&r, len, "data count and data section have inconsistent lengths");
403 goto fail;
404 }
405 return true;
406oomfail:
407 if (!r.failed) wasm_rd_fail_at(&r, len, "out of memory");
408fail:
409 wasm_module_free(m);
410 return false;
411}