==> tests/fuzz.c <==1/* Hostile modules: random mutations of real modules, and random bytes. Every one must be refused or
2 * run to a result, a trap or an interrupt; none may crash or touch memory it shouldn't (run this
3 * under --profile asan). Deterministic: a fixed seed. */
4#define _XOPEN_SOURCE 700
5#include "mesh/check.h"
6#include "wasm/builder.h"
7#include "wasm/interp.h"
8
9#include <pthread.h>
10#include <stdlib.h>
11#include <string.h>
12#include <time.h>
13
14#include "fixtures/prog.h"
15
16static _Atomic uint64_t epoch;
17static _Atomic int stop;
18
19static void *ticker(void *arg) {
20 (void)arg;
21 struct timespec ms = {0, 1000000};
22 while (!atomic_load(&stop)) {
23 atomic_fetch_add(&epoch, 1);
24 nanosleep(&ms, NULL);
25 }
26 return NULL;
27}
28
29static uint64_t rng = 0x9e3779b97f4a7c15ull;
30static uint32_t next(void) {
31 rng ^= rng << 13;
32 rng ^= rng >> 7;
33 rng ^= rng << 17;
34 return (uint32_t)rng;
35}
36
37static int host_any(wasm_instance *I, void *ctx, uint64_t *io) {
38 (void)I, (void)ctx;
39 io[0] = 1;
40 return WASM_NUM_OK;
41}
42
43static long compiled, instantiated, calls, traps, by_trap[16];
44
45static void try_module(const uint8_t *b, size_t n) {
46 char err[256];
47 wasm_compiled *c = wasm_compile(b, n, WASM_F_INTERP, err, sizeof err);
48 if (!c) return;
49 compiled++;
50 /* Whatever it imports, offer a function of that name and signature. */
51 const wasm_module *m = wasm_compiled_module(c);
52 wasm_host_func hosts[8];
53 char sigs[8][40];
54 size_t nh = 0;
55 for (uint32_t i = 0; i < m->nimports && nh < 8; i++) {
56 const wasm_import *im = &m->imports[i];
57 if (im->kind != WASM_EXTERN_FUNC) continue;
58 const wasm_functype *t = &m->types[im->func_type];
59 if (t->nparams + t->nresults + 2 > sizeof sigs[0]) continue;
60 char *s = sigs[nh];
61 size_t k = 0;
62 for (uint32_t p = 0; p < t->nparams; p++) s[k++] = t->params[p] == WASM_I64 ? 'l' : t->params[p] == WASM_F32 ? 'f' : t->params[p] == WASM_F64 ? 'd' : 'i';
63 s[k++] = '>';
64 for (uint32_t p = 0; p < t->nresults; p++) s[k++] = t->results[p] == WASM_I64 ? 'l' : t->results[p] == WASM_F32 ? 'f' : t->results[p] == WASM_F64 ? 'd' : 'i';
65 s[k] = 0;
66 static char names[8][64], mods[8][64];
67 snprintf(mods[nh], sizeof mods[nh], "%.*s", (int)im->module_len, im->module);
68 snprintf(names[nh], sizeof names[nh], "%.*s", (int)im->name_len, im->name);
69 hosts[nh] = (wasm_host_func){mods[nh], names[nh], s, host_any, NULL};
70 nh++;
71 }
72 wasm_store_limits lim = {16u << 20, 4, 1, 1u << 14, 1000};
73 wasm_instance *I = wasm_instantiate(c, hosts, nh, &lim, NULL, err, sizeof err);
74 if (I) {
75 instantiated++;
76 for (uint32_t f = m->nfunc_imports; f < m->nfuncs && f < m->nfunc_imports + 16; f++) {
77 const wasm_functype *t = wasm_func_type(m, f);
78 uint64_t *io = calloc(t->nparams + t->nresults + 1, sizeof *io);
79 wasm_set_epoch(I, &epoch);
80 wasm_set_deadline(I, atomic_load(&epoch) + 3);
81 int r = wasm_call(I, f, io);
82 calls++;
83 free(io);
84 if (r != WASM_NUM_OK) {
85 traps++;
86 if (r >= 0 && r < 16) by_trap[r]++;
87 break; /* a trapped instance is not called again (as meshd) */
88 }
89 }
90 wasm_instance_free(I);
91 }
92 wasm_compiled_free(c);
93}
94
95/* A module that uses most instructions: the builder's, with a loop, memory, a table, calls. */
96static void seed_module(wasm_buf *out) {
97 wasm_builder w = {0};
98 uint32_t t = wasm_builder_type(&w, "ii>i");
99 wasm_builder_import_func(&w, "mesh", "info", wasm_builder_type(&w, "i>i"));
100 uint32_t f;
101 wasm_buf *b = wasm_builder_func_add(&w, t, "il", &f);
102 static const uint8_t body[] = {0x03, 0x40, 0x20, 2, 0x41, 1, 0x6a, 0x22, 2, 0x20, 0, 0x28, 2, 4, 0x20, 1, 0x36, 2, 8,
103 0x20, 1, 0x10, 0, 0x1a, 0x20, 2, 0x41, 10, 0x49, 0x0d, 0, 0x0b, 0x20, 0, 0x20, 1, 0x11,
104 0, 0, 0x42, 5, 0x21, 3, 0x20, 3, 0xa7, 0x6a};
105 wb_bytes(b, body, sizeof body);
106 wasm_builder_memory(&w, 1, 4, true);
107 wasm_builder_table(&w, 2, 2, true);
108 uint32_t el[] = {1, 1};
109 wasm_builder_elem(&w, 0, el, 2);
110 wasm_builder_data(&w, 0, "abcdefgh", 8);
111 wasm_builder_add_export(&w, "f", WASM_EXTERN_FUNC, f);
112 wasm_builder_finish(&w, out);
113 wasm_builder_free(&w);
114}
115
116int main(void) {
117 pthread_t th;
118 CHECK(pthread_create(&th, NULL, ticker, NULL) == 0);
119 wasm_buf seed;
120 seed_module(&seed);
121 const struct {
122 const uint8_t *b;
123 size_t n;
124 } seeds[] = {{seed.data, seed.len}, {prog_wasm, prog_wasm_len}};
125 uint8_t *buf = malloc(prog_wasm_len + seed.len + 64);
126 for (int round = 0; round < 6000; round++) {
127 const uint8_t *src = seeds[round & 1].b;
128 size_t n = seeds[round & 1].n;
129 memcpy(buf, src, n);
130 int edits = 1 + (int)(next() % 4);
131 for (int e = 0; e < edits; e++) {
132 uint32_t at = next() % (uint32_t)n;
133 switch (next() % 4) {
134 case 0: buf[at] ^= (uint8_t)(1u << (next() % 8)); break;
135 case 1: buf[at] = (uint8_t)next(); break;
136 case 2: buf[at] = (uint8_t)(next() % 2 ? 0xff : 0x00); break;
137 case 3: if (n > 9) n--; break; /* truncate */
138 }
139 }
140 try_module(buf, n);
141 }
142 for (int round = 0; round < 3000; round++) {
143 size_t n = 8 + next() % 64;
144 memcpy(buf, "\0asm\x01\0\0\0", 8);
145 for (size_t i = 8; i < n; i++) buf[i] = (uint8_t)next();
146 try_module(buf, n);
147 }
148 free(buf);
149 wasm_buf_free(&seed);
150 atomic_store(&stop, 1);
151 pthread_join(th, NULL);
152 /* The mutations must have reached every stage, not just the decoder. */
153 CHECK(compiled > 100);
154 CHECK(instantiated > 50);
155 CHECK(calls > 50);
156 fprintf(stderr, "fuzz: %ld compiled, %ld instantiated, %ld calls, %ld traps (", compiled, instantiated, calls, traps);
157 for (int k = 0; k < 16; k++)
158 if (by_trap[k]) fprintf(stderr, " %d:%ld", k, by_trap[k]);
159 fprintf(stderr, " )\n");
160 return mesh_check_done();
161}