skip to content
==> 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}