validate.c (41965B)
1 #include "wasm/wasm.h" 2 3 typedef struct WasmValStack { 4 WasmValType vals[256]; 5 uint32_t depth; 6 KitSrcLoc loc; 7 } WasmValStack; 8 9 typedef struct WasmControlFrame { 10 uint8_t kind; 11 uint32_t height; 12 int seen_else; 13 int unreachable; 14 } WasmControlFrame; 15 16 static WasmValType wasm_global_init_type(const WasmInsn* in) { 17 switch (in->kind) { 18 case WASM_INSN_I32_CONST: 19 return WASM_VAL_I32; 20 case WASM_INSN_I64_CONST: 21 return WASM_VAL_I64; 22 case WASM_INSN_F32_CONST: 23 return WASM_VAL_F32; 24 case WASM_INSN_F64_CONST: 25 return WASM_VAL_F64; 26 default: 27 return 0; 28 } 29 } 30 31 static uint64_t wasm_memory_initial_bytes(KitCompiler* c, 32 const WasmMemory* mem) { 33 if (mem->min_pages > UINT64_MAX / 65536u) 34 wasm_error(c, wasm_loc(0, 0), "wasm: memory minimum overflows"); 35 return mem->min_pages * 65536u; 36 } 37 38 static uint32_t wasm_mem_align_log2(uint32_t width) { 39 uint32_t lg = 0; 40 while (width > 1u) { 41 width >>= 1u; 42 lg++; 43 } 44 return lg; 45 } 46 47 static void wasm_validate_memarg(KitCompiler* c, const WasmInsn* in, 48 const char* what) { 49 uint32_t max_align = wasm_mem_align_log2(wasm_mem_width(in->kind)); 50 if (in->align > max_align) 51 wasm_error(c, in->loc, "wasm: bad %s alignment", what); 52 } 53 54 static void wasm_stack_push(KitCompiler* c, WasmValStack* s, WasmValType vt) { 55 if (s->depth >= 256u) wasm_error(c, s->loc, "wasm: operand stack too deep"); 56 s->vals[s->depth++] = vt; 57 } 58 59 static int wasm_stack_pop(KitCompiler* c, WasmValStack* s, 60 WasmControlFrame* frames, uint32_t nframes, 61 WasmValType expected, const char* what) { 62 WasmControlFrame* top = &frames[nframes - 1u]; 63 if (s->depth <= top->height) { 64 if (top->unreachable) return 1; 65 wasm_error(c, s->loc, "wasm: operand stack underflow"); 66 } 67 if (expected && s->vals[s->depth - 1u] != expected) 68 wasm_error(c, s->loc, "wasm: %s type mismatch (expected=0x%x got=0x%x)", 69 what, (unsigned)expected, (unsigned)s->vals[s->depth - 1u]); 70 s->depth--; 71 return 1; 72 } 73 74 static WasmValType wasm_stack_pop_any(KitCompiler* c, WasmValStack* s, 75 WasmControlFrame* frames, 76 uint32_t nframes, const char* what) { 77 WasmControlFrame* top = &frames[nframes - 1u]; 78 WasmValType vt; 79 if (s->depth <= top->height) { 80 if (top->unreachable) return WASM_VAL_I32; 81 wasm_error(c, s->loc, "wasm: operand stack underflow"); 82 } 83 vt = s->vals[s->depth - 1u]; 84 if (!vt) wasm_error(c, s->loc, "wasm: %s type mismatch", what); 85 s->depth--; 86 return vt; 87 } 88 89 static void wasm_stack_pop_ref(KitCompiler* c, WasmValStack* s, 90 WasmControlFrame* frames, uint32_t nframes, 91 const char* what) { 92 WasmValType vt = wasm_stack_pop_any(c, s, frames, nframes, what); 93 if (!wasm_is_ref_type(vt)) 94 wasm_error(c, s->loc, "wasm: %s type mismatch", what); 95 } 96 97 static void wasm_mark_unreachable(WasmValStack* s, WasmControlFrame* frames, 98 uint32_t nframes) { 99 WasmControlFrame* top = &frames[nframes - 1u]; 100 s->depth = top->height; 101 top->unreachable = 1; 102 } 103 104 /* Double the control-frame stack. Caller passes the current capacity by 105 * reference; it is updated to the new capacity. */ 106 static WasmControlFrame* wasm_ctrl_grow(KitCompiler* c, WasmModule* m, 107 WasmControlFrame* frames, uint32_t* cap, 108 KitSrcLoc loc) { 109 uint32_t nc = *cap * 2u; 110 WasmControlFrame* p = (WasmControlFrame*)wasm_realloc( 111 m->heap, frames, sizeof(WasmControlFrame) * *cap, 112 sizeof(WasmControlFrame) * nc); 113 if (!p) wasm_error(c, loc, "wasm: out of memory"); 114 *cap = nc; 115 return p; 116 } 117 118 void wasm_validate(WasmModule* m, KitCompiler* c) { 119 uint32_t i, j; 120 for (i = 0; i < m->ntypes; ++i) { 121 for (j = 0; j < m->types[i].nparams; ++j) 122 if (!wasm_is_frontend_value_type(m->types[i].params[j])) 123 wasm_error(c, wasm_loc(0, 0), "wasm: unsupported parameter type"); 124 for (j = 0; j < m->types[i].nresults; ++j) 125 if (!wasm_is_frontend_value_type(m->types[i].results[j])) 126 wasm_error(c, wasm_loc(0, 0), "wasm: unsupported result type"); 127 } 128 for (i = 0; i < m->nmemories; ++i) { 129 if (m->memories[i].has_max && 130 m->memories[i].max_pages < m->memories[i].min_pages) 131 wasm_error(c, wasm_loc(0, 0), "wasm: memory maximum below minimum"); 132 if (m->memories[i].shared && !m->memories[i].has_max) 133 wasm_error(c, wasm_loc(0, 0), "wasm: shared memory requires maximum"); 134 } 135 for (i = 0; i < m->ntables; ++i) { 136 if (m->tables[i].elem_type != WASM_VAL_FUNCREF) 137 wasm_error(c, wasm_loc(0, 0), 138 "wasm: reference type is unsupported for tables"); 139 if (m->tables[i].has_max && m->tables[i].max < m->tables[i].min) 140 wasm_error(c, wasm_loc(0, 0), "wasm: table maximum below minimum"); 141 } 142 for (i = 0; i < m->nglobals; ++i) { 143 WasmGlobal* g = &m->globals[i]; 144 if (!wasm_is_num_type(g->type)) 145 wasm_error(c, wasm_loc(0, 0), "wasm: unsupported global type"); 146 if (!g->is_import && wasm_global_init_type(&g->init) != g->type) 147 wasm_error(c, wasm_loc(0, 0), "wasm: global initializer type mismatch"); 148 } 149 for (i = 0; i < m->nexports; ++i) { 150 WasmExport* ex = &m->exports[i]; 151 if ((ex->kind == 0 && ex->index >= m->nfuncs) || 152 (ex->kind == 1 && ex->index >= m->ntables) || 153 (ex->kind == 2 && ex->index >= m->nmemories) || 154 (ex->kind == 3 && ex->index >= m->nglobals)) 155 wasm_error(c, wasm_loc(0, 0), "wasm: export index out of range"); 156 } 157 if (m->has_start) { 158 if (m->start_func >= m->nfuncs) 159 wasm_error(c, wasm_loc(0, 0), "wasm: start function index out of range"); 160 if (m->funcs[m->start_func].nparams || m->funcs[m->start_func].nresults) 161 wasm_error(c, wasm_loc(0, 0), 162 "wasm: start function must have no params or results"); 163 } 164 for (i = 0; i < m->ndata; ++i) { 165 const WasmDataSegment* d = &m->data[i]; 166 uint64_t memory_bytes; 167 uint64_t offset; 168 if (d->mode != WASM_SEG_ACTIVE) continue; 169 if (d->memidx >= m->nmemories) 170 wasm_error(c, wasm_loc(0, 0), "wasm: data memory index out of range"); 171 if (d->offset < 0) 172 wasm_error(c, wasm_loc(0, 0), "wasm: bad data offset"); 173 memory_bytes = wasm_memory_initial_bytes(c, &m->memories[d->memidx]); 174 offset = (uint64_t)d->offset; 175 if (offset > memory_bytes || d->nbytes > memory_bytes - offset) 176 wasm_error(c, wasm_loc(0, 0), "wasm: data segment out of range"); 177 } 178 for (i = 0; i < m->nelems; ++i) { 179 if (m->elems[i].elem_type != WASM_VAL_FUNCREF) 180 wasm_error(c, wasm_loc(0, 0), 181 "wasm: unsupported element segment type"); 182 if (m->elems[i].mode == WASM_SEG_ACTIVE) { 183 uint32_t table_min; 184 uint64_t offset; 185 if (m->elems[i].tableidx >= m->ntables) 186 wasm_error(c, wasm_loc(0, 0), 187 "wasm: element table index out of range"); 188 table_min = m->tables[m->elems[i].tableidx].min; 189 if (m->elems[i].offset < 0) 190 wasm_error(c, wasm_loc(0, 0), "wasm: element segment out of range"); 191 offset = (uint64_t)m->elems[i].offset; 192 if (offset > table_min || m->elems[i].nfuncs > table_min - offset) 193 wasm_error(c, wasm_loc(0, 0), "wasm: element segment out of range"); 194 } 195 for (j = 0; j < m->elems[i].nfuncs; ++j) 196 if (m->elems[i].funcs[j] >= m->nfuncs) 197 wasm_error(c, wasm_loc(0, 0), 198 "wasm: element function index out of range"); 199 } 200 for (i = 0; i < m->nfuncs; ++i) wasm_validate_func(c, m, &m->funcs[i]); 201 } 202 203 void wasm_validate_func(KitCompiler* c, WasmModule* m, WasmFunc* f) { 204 WasmValStack stack; 205 /* The control stack grows with structured-block nesting. Deeply nested 206 * shapes — e.g. a switch with hundreds of cases lowered to a tower of 207 * blocks — can run far past any fixed cap, so grow it on demand. On the 208 * error path wasm_error aborts the whole compile, so the leak is moot 209 * (matching the wasm frontend's growable control stack in lang/wasm/cg.c). */ 210 uint32_t control_cap = 64u; 211 WasmControlFrame* control = (WasmControlFrame*)m->heap->alloc( 212 m->heap, sizeof(WasmControlFrame) * control_cap, 213 _Alignof(WasmControlFrame)); 214 uint32_t ncontrol = 1; 215 uint32_t j; 216 if (!control) wasm_error(c, f->loc, "wasm: out of memory"); 217 memset(&stack, 0, sizeof stack); 218 memset(control, 0, sizeof(WasmControlFrame) * control_cap); 219 control[0].kind = 0xffu; 220 control[0].height = 0; 221 if (f->is_import) { 222 if (f->ninsns) 223 wasm_error(c, wasm_loc(0, 0), "wasm: imported function has body"); 224 m->heap->free(m->heap, control, sizeof(WasmControlFrame) * control_cap); 225 return; 226 } 227 if (f->nresults > 1u) 228 wasm_error(c, wasm_loc(0, 0), "wasm: multi-result unsupported"); 229 { 230 for (j = 0; j < f->ninsns; ++j) { 231 WasmInsn* in = &f->insns[j]; 232 WasmValType vt, src, dst; 233 stack.loc = in->loc; 234 #define wasm_loc(line, col) (in->loc) 235 switch (in->kind) { 236 case WASM_INSN_F32_CONST: 237 wasm_stack_push(c, &stack, WASM_VAL_F32); 238 break; 239 case WASM_INSN_F64_CONST: 240 wasm_stack_push(c, &stack, WASM_VAL_F64); 241 break; 242 case WASM_INSN_I32_CONST: 243 wasm_stack_push(c, &stack, WASM_VAL_I32); 244 break; 245 case WASM_INSN_I64_CONST: 246 wasm_stack_push(c, &stack, WASM_VAL_I64); 247 break; 248 case WASM_INSN_LOCAL_GET: 249 if (in->imm < 0 || 250 (uint64_t)in->imm >= (uint64_t)f->nparams + f->nlocals) 251 wasm_error(c, wasm_loc(0, 0), "wasm: local index out of range"); 252 wasm_stack_push(c, &stack, 253 wasm_func_local_type(f, (uint32_t)in->imm)); 254 break; 255 case WASM_INSN_LOCAL_SET: 256 case WASM_INSN_LOCAL_TEE: 257 if (in->imm < 0 || 258 (uint64_t)in->imm >= (uint64_t)f->nparams + f->nlocals) 259 wasm_error(c, wasm_loc(0, 0), "wasm: local index out of range"); 260 wasm_stack_pop(c, &stack, control, ncontrol, 261 wasm_func_local_type(f, (uint32_t)in->imm), "local"); 262 if (in->kind == WASM_INSN_LOCAL_TEE) 263 wasm_stack_push(c, &stack, 264 wasm_func_local_type(f, (uint32_t)in->imm)); 265 break; 266 case WASM_INSN_CALL: 267 case WASM_INSN_RETURN_CALL: 268 if (in->imm < 0 || (uint64_t)in->imm >= m->nfuncs) 269 wasm_error(c, wasm_loc(0, 0), "wasm: call index out of range"); 270 if (in->kind == WASM_INSN_RETURN_CALL) { 271 wasm_require_feature(c, m, WASM_FEATURE_TAIL_CALLS, "tail calls", 272 "return_call"); 273 if (m->funcs[in->imm].nresults != f->nresults || 274 (f->nresults && m->funcs[in->imm].results[0] != f->results[0])) 275 wasm_error(c, wasm_loc(0, 0), 276 "wasm: return_call result type mismatch"); 277 } 278 for (uint32_t k = 0; k < m->funcs[in->imm].nparams; ++k) { 279 uint32_t param = m->funcs[in->imm].nparams - 1u - k; 280 wasm_stack_pop(c, &stack, control, ncontrol, 281 m->funcs[in->imm].params[param], "call argument"); 282 } 283 if (in->kind == WASM_INSN_RETURN_CALL) { 284 wasm_mark_unreachable(&stack, control, ncontrol); 285 } else if (m->funcs[in->imm].nresults) { 286 wasm_stack_push(c, &stack, m->funcs[in->imm].results[0]); 287 } 288 break; 289 case WASM_INSN_CALL_INDIRECT: { 290 WasmFuncType* t; 291 if (in->imm < 0 || (uint64_t)in->imm >= m->ntypes) 292 wasm_error(c, wasm_loc(0, 0), 293 "wasm: call_indirect type index out of range"); 294 if (in->align >= m->ntables) 295 wasm_error(c, wasm_loc(0, 0), 296 "wasm: call_indirect table index out of range"); 297 t = &m->types[in->imm]; 298 wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, 299 "call_indirect index"); 300 for (uint32_t k = 0; k < t->nparams; ++k) { 301 uint32_t param = t->nparams - 1u - k; 302 wasm_stack_pop(c, &stack, control, ncontrol, t->params[param], 303 "call_indirect argument"); 304 } 305 if (t->nresults) wasm_stack_push(c, &stack, t->results[0]); 306 break; 307 } 308 case WASM_INSN_RETURN_CALL_INDIRECT: { 309 WasmFuncType* t; 310 wasm_require_feature(c, m, WASM_FEATURE_TAIL_CALLS, "tail calls", 311 "return_call_indirect"); 312 if (in->imm < 0 || (uint64_t)in->imm >= m->ntypes) 313 wasm_error(c, wasm_loc(0, 0), 314 "wasm: return_call_indirect type index out of range"); 315 if (in->align >= m->ntables) 316 wasm_error(c, wasm_loc(0, 0), 317 "wasm: return_call_indirect table index out of range"); 318 t = &m->types[in->imm]; 319 if (t->nresults != f->nresults || 320 (f->nresults && t->results[0] != f->results[0])) 321 wasm_error(c, wasm_loc(0, 0), 322 "wasm: return_call_indirect result type mismatch"); 323 wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, 324 "return_call_indirect index"); 325 for (uint32_t k = 0; k < t->nparams; ++k) { 326 uint32_t param = t->nparams - 1u - k; 327 wasm_stack_pop(c, &stack, control, ncontrol, t->params[param], 328 "return_call_indirect argument"); 329 } 330 wasm_mark_unreachable(&stack, control, ncontrol); 331 break; 332 } 333 case WASM_INSN_REF_NULL: 334 wasm_require_feature(c, m, WASM_FEATURE_TYPED_FUNC_REFS, 335 "typed function references", "ref.null"); 336 if (!wasm_is_ref_type((WasmValType)in->imm)) 337 wasm_error(c, wasm_loc(0, 0), "wasm: bad ref.null type"); 338 if ((WasmValType)in->imm != WASM_VAL_FUNCREF) 339 wasm_error(c, wasm_loc(0, 0), "wasm: unsupported reference type"); 340 wasm_stack_push(c, &stack, (WasmValType)in->imm); 341 break; 342 case WASM_INSN_REF_FUNC: 343 wasm_require_feature(c, m, WASM_FEATURE_TYPED_FUNC_REFS, 344 "typed function references", "ref.func"); 345 if (in->imm < 0 || (uint64_t)in->imm >= m->nfuncs) 346 wasm_error(c, wasm_loc(0, 0), "wasm: ref.func index out of range"); 347 wasm_stack_push(c, &stack, WASM_VAL_FUNCREF); 348 break; 349 case WASM_INSN_REF_IS_NULL: 350 wasm_require_feature(c, m, WASM_FEATURE_TYPED_FUNC_REFS, 351 "typed function references", "ref.is_null"); 352 wasm_stack_pop_ref(c, &stack, control, ncontrol, "ref.is_null"); 353 wasm_stack_push(c, &stack, WASM_VAL_I32); 354 break; 355 case WASM_INSN_CALL_REF: 356 case WASM_INSN_RETURN_CALL_REF: { 357 WasmFuncType* t; 358 wasm_require_feature(c, m, WASM_FEATURE_TYPED_FUNC_REFS, 359 "typed function references", "call_ref"); 360 if (in->kind == WASM_INSN_RETURN_CALL_REF) 361 wasm_require_feature(c, m, WASM_FEATURE_TAIL_CALLS, "tail calls", 362 "return_call_ref"); 363 if (in->imm < 0 || (uint64_t)in->imm >= m->ntypes) 364 wasm_error(c, wasm_loc(0, 0), 365 "wasm: call_ref type index out of range"); 366 t = &m->types[in->imm]; 367 if (in->kind == WASM_INSN_RETURN_CALL_REF && 368 (t->nresults != f->nresults || 369 (f->nresults && t->results[0] != f->results[0]))) 370 wasm_error(c, wasm_loc(0, 0), 371 "wasm: return_call_ref result type mismatch"); 372 wasm_stack_pop_ref(c, &stack, control, ncontrol, "call_ref callee"); 373 for (uint32_t k = 0; k < t->nparams; ++k) { 374 uint32_t param = t->nparams - 1u - k; 375 wasm_stack_pop(c, &stack, control, ncontrol, t->params[param], 376 "call_ref argument"); 377 } 378 if (in->kind == WASM_INSN_RETURN_CALL_REF) 379 wasm_mark_unreachable(&stack, control, ncontrol); 380 else if (t->nresults) 381 wasm_stack_push(c, &stack, t->results[0]); 382 break; 383 } 384 case WASM_INSN_GLOBAL_GET: 385 if (in->imm < 0 || (uint64_t)in->imm >= m->nglobals) 386 wasm_error(c, wasm_loc(0, 0), "wasm: global index out of range"); 387 wasm_stack_push(c, &stack, m->globals[in->imm].type); 388 break; 389 case WASM_INSN_GLOBAL_SET: 390 if (in->imm < 0 || (uint64_t)in->imm >= m->nglobals) 391 wasm_error(c, wasm_loc(0, 0), "wasm: global index out of range"); 392 if (!m->globals[in->imm].mutable_) 393 wasm_error(c, wasm_loc(0, 0), "wasm: global is immutable"); 394 wasm_stack_pop(c, &stack, control, ncontrol, m->globals[in->imm].type, 395 "global"); 396 break; 397 case WASM_INSN_RETURN: 398 if (f->nresults) 399 wasm_stack_pop(c, &stack, control, ncontrol, f->results[0], 400 "return"); 401 wasm_mark_unreachable(&stack, control, ncontrol); 402 break; 403 case WASM_INSN_DROP: 404 wasm_stack_pop(c, &stack, control, ncontrol, 0, "drop"); 405 break; 406 case WASM_INSN_I32_EQZ: 407 wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, "eqz"); 408 wasm_stack_push(c, &stack, WASM_VAL_I32); 409 break; 410 case WASM_INSN_I64_EQZ: 411 wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I64, "eqz"); 412 wasm_stack_push(c, &stack, WASM_VAL_I32); 413 break; 414 case WASM_INSN_BLOCK: 415 case WASM_INSN_LOOP: 416 if (ncontrol == control_cap) 417 control = wasm_ctrl_grow(c, m, control, &control_cap, in->loc); 418 control[ncontrol].kind = in->kind; 419 control[ncontrol].height = stack.depth; 420 control[ncontrol].seen_else = 0; 421 control[ncontrol].unreachable = 0; 422 ncontrol++; 423 break; 424 case WASM_INSN_IF: 425 wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, "if"); 426 if (ncontrol == control_cap) 427 control = wasm_ctrl_grow(c, m, control, &control_cap, in->loc); 428 control[ncontrol].kind = in->kind; 429 control[ncontrol].height = stack.depth; 430 control[ncontrol].seen_else = 0; 431 control[ncontrol].unreachable = 0; 432 ncontrol++; 433 break; 434 case WASM_INSN_ELSE: 435 if (ncontrol <= 1u || control[ncontrol - 1u].kind != WASM_INSN_IF) 436 wasm_error(c, wasm_loc(0, 0), "wasm: else without if"); 437 if (!control[ncontrol - 1u].unreachable && 438 stack.depth != control[ncontrol - 1u].height) 439 wasm_error(c, wasm_loc(0, 0), "wasm: if branch result mismatch"); 440 stack.depth = control[ncontrol - 1u].height; 441 control[ncontrol - 1u].seen_else = 1; 442 control[ncontrol - 1u].unreachable = 0; 443 break; 444 case WASM_INSN_END: 445 if (ncontrol <= 1u) 446 wasm_error(c, wasm_loc(0, 0), "wasm: end without block"); 447 if (!control[ncontrol - 1u].unreachable && 448 stack.depth != control[ncontrol - 1u].height) 449 wasm_error(c, wasm_loc(0, 0), "wasm: block result mismatch"); 450 stack.depth = control[ncontrol - 1u].height; 451 ncontrol--; 452 break; 453 case WASM_INSN_BR: 454 if (in->imm < 0 || (uint64_t)in->imm >= ncontrol - 1u) 455 wasm_error(c, wasm_loc(0, 0), "wasm: branch depth out of range"); 456 wasm_mark_unreachable(&stack, control, ncontrol); 457 break; 458 case WASM_INSN_BR_IF: 459 if (in->imm < 0 || (uint64_t)in->imm >= ncontrol - 1u) 460 wasm_error(c, wasm_loc(0, 0), "wasm: branch depth out of range"); 461 wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, "br_if"); 462 break; 463 case WASM_INSN_BR_TABLE: 464 if (in->ntargets == 0) 465 wasm_error(c, wasm_loc(0, 0), "wasm: br_table without targets"); 466 wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, 467 "br_table selector"); 468 for (uint32_t k = 0; k < in->ntargets; ++k) 469 if (in->targets[k] >= ncontrol - 1u) 470 wasm_error(c, wasm_loc(0, 0), 471 "wasm: br_table depth out of range"); 472 wasm_mark_unreachable(&stack, control, ncontrol); 473 break; 474 case WASM_INSN_SELECT: { 475 WasmValType rhs, lhs; 476 wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, "select"); 477 if (stack.depth <= control[ncontrol - 1u].height && 478 control[ncontrol - 1u].unreachable) { 479 in->type = WASM_VAL_I32; 480 break; 481 } 482 if (stack.depth < control[ncontrol - 1u].height + 2u) 483 wasm_error(c, wasm_loc(0, 0), "wasm: operand stack underflow"); 484 rhs = stack.vals[--stack.depth]; 485 lhs = stack.vals[--stack.depth]; 486 if (lhs != rhs) 487 wasm_error(c, wasm_loc(0, 0), "wasm: select type mismatch"); 488 in->type = (uint8_t)lhs; 489 wasm_stack_push(c, &stack, lhs); 490 break; 491 } 492 case WASM_INSN_MEMORY_SIZE: 493 if (in->memidx >= m->nmemories) 494 wasm_error(c, wasm_loc(0, 0), "wasm: memory.size without memory"); 495 wasm_stack_push( 496 c, &stack, 497 m->memories[in->memidx].is64 ? WASM_VAL_I64 : WASM_VAL_I32); 498 break; 499 case WASM_INSN_MEMORY_GROW: 500 if (in->memidx >= m->nmemories) 501 wasm_error(c, wasm_loc(0, 0), "wasm: memory.grow without memory"); 502 wasm_stack_pop( 503 c, &stack, control, ncontrol, 504 m->memories[in->memidx].is64 ? WASM_VAL_I64 : WASM_VAL_I32, 505 "memory.grow"); 506 wasm_stack_push( 507 c, &stack, 508 m->memories[in->memidx].is64 ? WASM_VAL_I64 : WASM_VAL_I32); 509 break; 510 case WASM_INSN_ATOMIC_FENCE: 511 wasm_require_feature(c, m, WASM_FEATURE_THREADS, "threads", 512 "atomic.fence"); 513 break; 514 case WASM_INSN_I32_ATOMIC_LOAD: 515 case WASM_INSN_I64_ATOMIC_LOAD: 516 case WASM_INSN_I32_ATOMIC_LOAD8_U: 517 case WASM_INSN_I32_ATOMIC_LOAD16_U: 518 case WASM_INSN_I64_ATOMIC_LOAD8_U: 519 case WASM_INSN_I64_ATOMIC_LOAD16_U: 520 case WASM_INSN_I64_ATOMIC_LOAD32_U: 521 wasm_require_feature(c, m, WASM_FEATURE_THREADS, "threads", 522 "atomic load"); 523 if (in->memidx >= m->nmemories) 524 wasm_error(c, wasm_loc(0, 0), "wasm: atomic load without memory"); 525 if (!m->memories[in->memidx].shared) 526 wasm_error(c, wasm_loc(0, 0), 527 "wasm: atomic load requires shared memory"); 528 wasm_validate_memarg(c, in, "atomic"); 529 wasm_stack_pop( 530 c, &stack, control, ncontrol, 531 m->memories[in->memidx].is64 ? WASM_VAL_I64 : WASM_VAL_I32, 532 "atomic load"); 533 wasm_stack_push(c, &stack, wasm_atomic_value_type(in->kind)); 534 break; 535 case WASM_INSN_I32_ATOMIC_STORE: 536 case WASM_INSN_I64_ATOMIC_STORE: 537 case WASM_INSN_I32_ATOMIC_STORE8: 538 case WASM_INSN_I32_ATOMIC_STORE16: 539 case WASM_INSN_I64_ATOMIC_STORE8: 540 case WASM_INSN_I64_ATOMIC_STORE16: 541 case WASM_INSN_I64_ATOMIC_STORE32: 542 wasm_require_feature(c, m, WASM_FEATURE_THREADS, "threads", 543 "atomic store"); 544 if (in->memidx >= m->nmemories) 545 wasm_error(c, wasm_loc(0, 0), "wasm: atomic store without memory"); 546 if (!m->memories[in->memidx].shared) 547 wasm_error(c, wasm_loc(0, 0), 548 "wasm: atomic store requires shared memory"); 549 wasm_validate_memarg(c, in, "atomic"); 550 wasm_stack_pop(c, &stack, control, ncontrol, 551 wasm_atomic_value_type(in->kind), "atomic store"); 552 wasm_stack_pop( 553 c, &stack, control, ncontrol, 554 m->memories[in->memidx].is64 ? WASM_VAL_I64 : WASM_VAL_I32, 555 "atomic store"); 556 break; 557 case WASM_INSN_I32_ATOMIC_RMW_ADD: 558 case WASM_INSN_I64_ATOMIC_RMW_ADD: 559 case WASM_INSN_I32_ATOMIC_RMW_SUB: 560 case WASM_INSN_I64_ATOMIC_RMW_SUB: 561 case WASM_INSN_I32_ATOMIC_RMW_AND: 562 case WASM_INSN_I64_ATOMIC_RMW_AND: 563 case WASM_INSN_I32_ATOMIC_RMW_OR: 564 case WASM_INSN_I64_ATOMIC_RMW_OR: 565 case WASM_INSN_I32_ATOMIC_RMW_XOR: 566 case WASM_INSN_I64_ATOMIC_RMW_XOR: 567 case WASM_INSN_I32_ATOMIC_RMW_XCHG: 568 case WASM_INSN_I64_ATOMIC_RMW_XCHG: 569 wasm_require_feature(c, m, WASM_FEATURE_THREADS, "threads", 570 "atomic rmw"); 571 if (in->memidx >= m->nmemories) 572 wasm_error(c, wasm_loc(0, 0), "wasm: atomic rmw without memory"); 573 if (!m->memories[in->memidx].shared) 574 wasm_error(c, wasm_loc(0, 0), 575 "wasm: atomic rmw requires shared memory"); 576 wasm_validate_memarg(c, in, "atomic"); 577 wasm_stack_pop(c, &stack, control, ncontrol, 578 wasm_atomic_value_type(in->kind), "atomic rmw"); 579 wasm_stack_pop( 580 c, &stack, control, ncontrol, 581 m->memories[in->memidx].is64 ? WASM_VAL_I64 : WASM_VAL_I32, 582 "atomic rmw"); 583 wasm_stack_push(c, &stack, wasm_atomic_value_type(in->kind)); 584 break; 585 case WASM_INSN_I32_ATOMIC_RMW_CMPXCHG: 586 case WASM_INSN_I64_ATOMIC_RMW_CMPXCHG: 587 wasm_require_feature(c, m, WASM_FEATURE_THREADS, "threads", 588 "atomic cmpxchg"); 589 if (in->memidx >= m->nmemories) 590 wasm_error(c, wasm_loc(0, 0), 591 "wasm: atomic cmpxchg without memory"); 592 if (!m->memories[in->memidx].shared) 593 wasm_error(c, wasm_loc(0, 0), 594 "wasm: atomic cmpxchg requires shared memory"); 595 wasm_validate_memarg(c, in, "atomic"); 596 wasm_stack_pop(c, &stack, control, ncontrol, 597 wasm_atomic_value_type(in->kind), "atomic cmpxchg"); 598 wasm_stack_pop(c, &stack, control, ncontrol, 599 wasm_atomic_value_type(in->kind), "atomic cmpxchg"); 600 wasm_stack_pop( 601 c, &stack, control, ncontrol, 602 m->memories[in->memidx].is64 ? WASM_VAL_I64 : WASM_VAL_I32, 603 "atomic cmpxchg"); 604 wasm_stack_push(c, &stack, wasm_atomic_value_type(in->kind)); 605 break; 606 case WASM_INSN_I32_ATOMIC_WAIT: 607 case WASM_INSN_I64_ATOMIC_WAIT: 608 wasm_require_feature(c, m, WASM_FEATURE_THREADS, "threads", 609 "atomic wait"); 610 if (in->memidx >= m->nmemories) 611 wasm_error(c, wasm_loc(0, 0), "wasm: atomic wait without memory"); 612 if (!m->memories[in->memidx].shared) 613 wasm_error(c, wasm_loc(0, 0), 614 "wasm: atomic wait requires shared memory"); 615 wasm_validate_memarg(c, in, "atomic"); 616 wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I64, 617 "atomic wait timeout"); 618 wasm_stack_pop(c, &stack, control, ncontrol, 619 wasm_atomic_value_type(in->kind), 620 "atomic wait expected"); 621 wasm_stack_pop( 622 c, &stack, control, ncontrol, 623 m->memories[in->memidx].is64 ? WASM_VAL_I64 : WASM_VAL_I32, 624 "atomic wait address"); 625 wasm_stack_push(c, &stack, WASM_VAL_I32); 626 break; 627 case WASM_INSN_MEMORY_ATOMIC_NOTIFY: 628 wasm_require_feature(c, m, WASM_FEATURE_THREADS, "threads", 629 "atomic notify"); 630 if (in->memidx >= m->nmemories) 631 wasm_error(c, wasm_loc(0, 0), "wasm: atomic notify without memory"); 632 if (!m->memories[in->memidx].shared) 633 wasm_error(c, wasm_loc(0, 0), 634 "wasm: atomic notify requires shared memory"); 635 wasm_validate_memarg(c, in, "atomic"); 636 wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, 637 "atomic notify count"); 638 wasm_stack_pop( 639 c, &stack, control, ncontrol, 640 m->memories[in->memidx].is64 ? WASM_VAL_I64 : WASM_VAL_I32, 641 "atomic notify address"); 642 wasm_stack_push(c, &stack, WASM_VAL_I32); 643 break; 644 case WASM_INSN_I32_LOAD: 645 case WASM_INSN_I64_LOAD: 646 case WASM_INSN_F32_LOAD: 647 case WASM_INSN_F64_LOAD: 648 case WASM_INSN_I32_LOAD8_S: 649 case WASM_INSN_I32_LOAD8_U: 650 case WASM_INSN_I32_LOAD16_S: 651 case WASM_INSN_I32_LOAD16_U: 652 case WASM_INSN_I64_LOAD8_S: 653 case WASM_INSN_I64_LOAD8_U: 654 case WASM_INSN_I64_LOAD16_S: 655 case WASM_INSN_I64_LOAD16_U: 656 case WASM_INSN_I64_LOAD32_S: 657 case WASM_INSN_I64_LOAD32_U: 658 if (in->memidx >= m->nmemories) 659 wasm_error(c, wasm_loc(0, 0), "wasm: load without memory"); 660 wasm_validate_memarg(c, in, "load"); 661 wasm_stack_pop( 662 c, &stack, control, ncontrol, 663 m->memories[in->memidx].is64 ? WASM_VAL_I64 : WASM_VAL_I32, 664 "load"); 665 wasm_stack_push(c, &stack, wasm_load_result_type(in->kind)); 666 break; 667 case WASM_INSN_I32_STORE: 668 case WASM_INSN_I64_STORE: 669 case WASM_INSN_F32_STORE: 670 case WASM_INSN_F64_STORE: 671 case WASM_INSN_I32_STORE8: 672 case WASM_INSN_I32_STORE16: 673 case WASM_INSN_I64_STORE8: 674 case WASM_INSN_I64_STORE16: 675 case WASM_INSN_I64_STORE32: 676 if (in->memidx >= m->nmemories) 677 wasm_error(c, wasm_loc(0, 0), "wasm: store without memory"); 678 wasm_validate_memarg(c, in, "store"); 679 wasm_stack_pop(c, &stack, control, ncontrol, 680 wasm_store_value_type(in->kind), "store"); 681 wasm_stack_pop( 682 c, &stack, control, ncontrol, 683 m->memories[in->memidx].is64 ? WASM_VAL_I64 : WASM_VAL_I32, 684 "store"); 685 break; 686 case WASM_INSN_UNREACHABLE: 687 wasm_mark_unreachable(&stack, control, ncontrol); 688 break; 689 case WASM_INSN_NOP: 690 break; 691 case WASM_INSN_MEMORY_COPY: { 692 WasmValType dst_vt, src_vt; 693 wasm_require_feature(c, m, WASM_FEATURE_BULK_MEMORY, "bulk memory", 694 "memory.copy"); 695 if (in->memidx >= m->nmemories || in->aux_idx >= m->nmemories) 696 wasm_error(c, wasm_loc(0, 0), 697 "wasm: memory.copy memory index out of range"); 698 dst_vt = m->memories[in->memidx].is64 ? WASM_VAL_I64 : WASM_VAL_I32; 699 src_vt = m->memories[in->aux_idx].is64 ? WASM_VAL_I64 : WASM_VAL_I32; 700 wasm_stack_pop(c, &stack, control, ncontrol, dst_vt, "memory.copy n"); 701 wasm_stack_pop(c, &stack, control, ncontrol, src_vt, 702 "memory.copy src"); 703 wasm_stack_pop(c, &stack, control, ncontrol, dst_vt, 704 "memory.copy dst"); 705 break; 706 } 707 case WASM_INSN_MEMORY_FILL: { 708 WasmValType vt; 709 wasm_require_feature(c, m, WASM_FEATURE_BULK_MEMORY, "bulk memory", 710 "memory.fill"); 711 if (in->memidx >= m->nmemories) 712 wasm_error(c, wasm_loc(0, 0), 713 "wasm: memory.fill memory index out of range"); 714 vt = m->memories[in->memidx].is64 ? WASM_VAL_I64 : WASM_VAL_I32; 715 wasm_stack_pop(c, &stack, control, ncontrol, vt, "memory.fill n"); 716 wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, 717 "memory.fill value"); 718 wasm_stack_pop(c, &stack, control, ncontrol, vt, "memory.fill dst"); 719 break; 720 } 721 case WASM_INSN_MEMORY_INIT: { 722 WasmValType vt; 723 wasm_require_feature(c, m, WASM_FEATURE_BULK_MEMORY, "bulk memory", 724 "memory.init"); 725 if (in->imm < 0 || (uint64_t)in->imm >= m->ndata) 726 wasm_error(c, wasm_loc(0, 0), 727 "wasm: memory.init data index out of range"); 728 if (m->data[in->imm].mode != WASM_SEG_PASSIVE) 729 wasm_error(c, wasm_loc(0, 0), 730 "wasm: memory.init requires passive data segment"); 731 if (in->memidx >= m->nmemories) 732 wasm_error(c, wasm_loc(0, 0), 733 "wasm: memory.init memory index out of range"); 734 vt = m->memories[in->memidx].is64 ? WASM_VAL_I64 : WASM_VAL_I32; 735 wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, 736 "memory.init n"); 737 wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, 738 "memory.init src"); 739 wasm_stack_pop(c, &stack, control, ncontrol, vt, "memory.init dst"); 740 break; 741 } 742 case WASM_INSN_DATA_DROP: 743 wasm_require_feature(c, m, WASM_FEATURE_BULK_MEMORY, "bulk memory", 744 "data.drop"); 745 if (in->imm < 0 || (uint64_t)in->imm >= m->ndata) 746 wasm_error(c, wasm_loc(0, 0), 747 "wasm: data.drop data index out of range"); 748 if (m->data[in->imm].mode != WASM_SEG_PASSIVE) 749 wasm_error(c, wasm_loc(0, 0), 750 "wasm: data.drop requires passive data segment"); 751 break; 752 case WASM_INSN_TABLE_COPY: 753 wasm_require_feature(c, m, WASM_FEATURE_BULK_MEMORY, "bulk memory", 754 "table.copy"); 755 if (in->imm < 0 || (uint64_t)in->imm >= m->ntables || 756 in->aux_idx >= m->ntables) 757 wasm_error(c, wasm_loc(0, 0), 758 "wasm: table.copy table index out of range"); 759 wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, 760 "table.copy n"); 761 wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, 762 "table.copy src"); 763 wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, 764 "table.copy dst"); 765 break; 766 case WASM_INSN_TABLE_INIT: 767 wasm_require_feature(c, m, WASM_FEATURE_BULK_MEMORY, "bulk memory", 768 "table.init"); 769 if (in->imm < 0 || (uint64_t)in->imm >= m->nelems) 770 wasm_error(c, wasm_loc(0, 0), 771 "wasm: table.init elem index out of range"); 772 if (m->elems[in->imm].mode != WASM_SEG_PASSIVE) 773 wasm_error(c, wasm_loc(0, 0), 774 "wasm: table.init requires passive element segment"); 775 if (in->aux_idx >= m->ntables) 776 wasm_error(c, wasm_loc(0, 0), 777 "wasm: table.init table index out of range"); 778 wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, 779 "table.init n"); 780 wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, 781 "table.init src"); 782 wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, 783 "table.init dst"); 784 break; 785 case WASM_INSN_ELEM_DROP: 786 wasm_require_feature(c, m, WASM_FEATURE_BULK_MEMORY, "bulk memory", 787 "elem.drop"); 788 if (in->imm < 0 || (uint64_t)in->imm >= m->nelems) 789 wasm_error(c, wasm_loc(0, 0), 790 "wasm: elem.drop elem index out of range"); 791 if (m->elems[in->imm].mode != WASM_SEG_PASSIVE) 792 wasm_error(c, wasm_loc(0, 0), 793 "wasm: elem.drop requires passive element segment"); 794 break; 795 case WASM_INSN_TABLE_SIZE: 796 wasm_require_feature(c, m, WASM_FEATURE_BULK_MEMORY, "bulk memory", 797 "table.size"); 798 if (in->imm < 0 || (uint64_t)in->imm >= m->ntables) 799 wasm_error(c, wasm_loc(0, 0), 800 "wasm: table.size table index out of range"); 801 wasm_stack_push(c, &stack, WASM_VAL_I32); 802 break; 803 case WASM_INSN_TABLE_GROW: 804 wasm_require_feature(c, m, WASM_FEATURE_BULK_MEMORY, "bulk memory", 805 "table.grow"); 806 if (in->imm < 0 || (uint64_t)in->imm >= m->ntables) 807 wasm_error(c, wasm_loc(0, 0), 808 "wasm: table.grow table index out of range"); 809 wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, 810 "table.grow delta"); 811 wasm_stack_pop_ref(c, &stack, control, ncontrol, "table.grow value"); 812 wasm_stack_push(c, &stack, WASM_VAL_I32); 813 break; 814 case WASM_INSN_TABLE_FILL: 815 wasm_require_feature(c, m, WASM_FEATURE_BULK_MEMORY, "bulk memory", 816 "table.fill"); 817 if (in->imm < 0 || (uint64_t)in->imm >= m->ntables) 818 wasm_error(c, wasm_loc(0, 0), 819 "wasm: table.fill table index out of range"); 820 wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, 821 "table.fill n"); 822 wasm_stack_pop_ref(c, &stack, control, ncontrol, "table.fill value"); 823 wasm_stack_pop(c, &stack, control, ncontrol, WASM_VAL_I32, 824 "table.fill dst"); 825 break; 826 case WASM_INSN_I32_TRUNC_SAT_F32_S: 827 case WASM_INSN_I32_TRUNC_SAT_F64_S: 828 case WASM_INSN_I32_TRUNC_SAT_F32_U: 829 case WASM_INSN_I32_TRUNC_SAT_F64_U: 830 case WASM_INSN_I64_TRUNC_SAT_F32_S: 831 case WASM_INSN_I64_TRUNC_SAT_F64_S: 832 case WASM_INSN_I64_TRUNC_SAT_F32_U: 833 case WASM_INSN_I64_TRUNC_SAT_F64_U: { 834 WasmValType sat_src = (in->kind == WASM_INSN_I32_TRUNC_SAT_F32_S || 835 in->kind == WASM_INSN_I32_TRUNC_SAT_F32_U || 836 in->kind == WASM_INSN_I64_TRUNC_SAT_F32_S || 837 in->kind == WASM_INSN_I64_TRUNC_SAT_F32_U) 838 ? WASM_VAL_F32 839 : WASM_VAL_F64; 840 WasmValType sat_dst = (in->kind == WASM_INSN_I32_TRUNC_SAT_F32_S || 841 in->kind == WASM_INSN_I32_TRUNC_SAT_F32_U || 842 in->kind == WASM_INSN_I32_TRUNC_SAT_F64_S || 843 in->kind == WASM_INSN_I32_TRUNC_SAT_F64_U) 844 ? WASM_VAL_I32 845 : WASM_VAL_I64; 846 wasm_require_feature(c, m, WASM_FEATURE_NONTRAPPING_FTOI, 847 "non-trapping float-to-int", "trunc_sat"); 848 wasm_stack_pop(c, &stack, control, ncontrol, sat_src, "trunc_sat"); 849 wasm_stack_push(c, &stack, sat_dst); 850 break; 851 } 852 default: 853 if (wasm_int_unop_kind(in->kind, &vt)) { 854 wasm_stack_pop(c, &stack, control, ncontrol, vt, "unary operand"); 855 wasm_stack_push(c, &stack, vt); 856 break; 857 } 858 if (wasm_fp_unop_kind(in->kind, &vt)) { 859 wasm_stack_pop(c, &stack, control, ncontrol, vt, 860 "fp unary operand"); 861 wasm_stack_push(c, &stack, vt); 862 break; 863 } 864 if (wasm_fp_binop_kind(in->kind, &vt)) { 865 wasm_stack_pop(c, &stack, control, ncontrol, vt, "fp operand"); 866 wasm_stack_pop(c, &stack, control, ncontrol, vt, "fp operand"); 867 wasm_stack_push(c, &stack, vt); 868 break; 869 } 870 if (wasm_fp_cmp_kind(in->kind, &vt)) { 871 wasm_stack_pop(c, &stack, control, ncontrol, vt, "fp compare"); 872 wasm_stack_pop(c, &stack, control, ncontrol, vt, "fp compare"); 873 wasm_stack_push(c, &stack, WASM_VAL_I32); 874 break; 875 } 876 if (wasm_conversion_kind(in->kind, &src, &dst)) { 877 wasm_stack_pop(c, &stack, control, ncontrol, src, "conversion"); 878 wasm_stack_push(c, &stack, dst); 879 break; 880 } 881 { 882 KitCgIntCmpOp cmp; 883 WasmValType rhs, lhs; 884 rhs = stack.depth > control[ncontrol - 1u].height 885 ? stack.vals[stack.depth - 1u] 886 : WASM_VAL_I32; 887 wasm_stack_pop(c, &stack, control, ncontrol, 0, "operand"); 888 lhs = stack.depth > control[ncontrol - 1u].height 889 ? stack.vals[stack.depth - 1u] 890 : rhs; 891 wasm_stack_pop(c, &stack, control, ncontrol, rhs, "operand"); 892 if (lhs != rhs) 893 wasm_error( 894 c, wasm_loc(0, 0), 895 "wasm: operand type mismatch (kind=0x%x lhs=0x%x rhs=0x%x)", 896 (unsigned)in->kind, (unsigned)lhs, (unsigned)rhs); 897 wasm_stack_push( 898 c, &stack, 899 wasm_int_cmp_op(in->kind, &cmp) ? WASM_VAL_I32 : lhs); 900 break; 901 } 902 #undef wasm_loc 903 } 904 } 905 if (ncontrol != 1u) 906 wasm_error(c, wasm_loc(0, 0), "wasm: unterminated control block"); 907 if (!control[0].unreachable) { 908 if (f->nresults) { 909 if (stack.depth != 1u || stack.vals[0] != f->results[0]) 910 wasm_error(c, wasm_loc(0, 0), "wasm: function result type mismatch"); 911 } else if (stack.depth != 0) { 912 wasm_error(c, wasm_loc(0, 0), "wasm: function leaves extra values"); 913 } 914 } 915 } 916 m->heap->free(m->heap, control, sizeof(WasmControlFrame) * control_cap); 917 }