| author | |
| committer | |
| log | 2dbcc03fb80e0688bba651821db6488cf950404c |
| tree | 9d5bfa6e85d587d8373bc99a8153c75093ae1a90 |
| parent | 42ee364e7b698822a69cba4cd2bda17868657e05 |
| parent | 6c1ab376ddcdbb05610487e5b813d42ff37da40d |
| signature |
wasm: implement more runtime safety checks4 files changed, 134 insertions(+), 5 deletions(-)
src/Module.zig+1-1| ... | ... | @@ -6626,7 +6626,7 @@ pub fn backendSupportsFeature(mod: Module, feature: Feature) bool { |
| 6626 | 6626 | .safety_check_formatted => mod.comp.bin_file.options.use_llvm, |
| 6627 | 6627 | .error_return_trace => mod.comp.bin_file.options.use_llvm, |
| 6628 | 6628 | .is_named_enum_value => mod.comp.bin_file.options.use_llvm, |
| 6629 | .error_set_has_value => mod.comp.bin_file.options.use_llvm, | |
| 6629 | .error_set_has_value => mod.comp.bin_file.options.use_llvm or mod.comp.bin_file.options.target.isWasm(), | |
| 6630 | 6630 | .field_reordering => mod.comp.bin_file.options.use_llvm, |
| 6631 | 6631 | }; |
| 6632 | 6632 | } |
src/arch/wasm/CodeGen.zig+90-3| ... | ... | @@ -1946,6 +1946,8 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1946 | 1946 | .ret_addr => func.airRetAddr(inst), |
| 1947 | 1947 | .tag_name => func.airTagName(inst), |
| 1948 | 1948 | |
| 1949 | .error_set_has_value => func.airErrorSetHasValue(inst), | |
| 1950 | ||
| 1949 | 1951 | .mul_sat, |
| 1950 | 1952 | .mod, |
| 1951 | 1953 | .assembly, |
| ... | ... | @@ -1967,7 +1969,6 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 1967 | 1969 | .set_err_return_trace, |
| 1968 | 1970 | .save_err_return_trace_index, |
| 1969 | 1971 | .is_named_enum_value, |
| 1970 | .error_set_has_value, | |
| 1971 | 1972 | .addrspace_cast, |
| 1972 | 1973 | .vector_store_elem, |
| 1973 | 1974 | .c_va_arg, |
| ... | ... | @@ -3338,9 +3339,14 @@ fn airCmpVector(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3338 | 3339 | fn airCmpLtErrorsLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| 3339 | 3340 | const un_op = func.air.instructions.items(.data)[inst].un_op; |
| 3340 | 3341 | const operand = try func.resolveInst(un_op); |
| 3342 | const sym_index = try func.bin_file.getGlobalSymbol("__zig_errors_len", null); | |
| 3343 | const errors_len = WValue{ .memory = sym_index }; | |
| 3341 | 3344 | |
| 3342 | _ = operand; | |
| 3343 | return func.fail("TODO implement airCmpLtErrorsLen for wasm", .{}); | |
| 3345 | try func.emitWValue(operand); | |
| 3346 | const errors_len_val = try func.load(errors_len, Type.err_int, 0); | |
| 3347 | const result = try func.cmp(.stack, errors_len_val, Type.err_int, .lt); | |
| 3348 | ||
| 3349 | return func.finishAir(inst, try result.toLocal(func, Type.bool), &.{un_op}); | |
| 3344 | 3350 | } |
| 3345 | 3351 | |
| 3346 | 3352 | fn airBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { |
| ... | ... | @@ -6510,3 +6516,84 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 { |
| 6510 | 6516 | const func_type = try genFunctype(arena, .Unspecified, &.{int_tag_ty}, slice_ty, func.target); |
| 6511 | 6517 | return func.bin_file.createFunction(func_name, func_type, &body_list, &relocs); |
| 6512 | 6518 | } |
| 6519 | ||
| 6520 | fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void { | |
| 6521 | const ty_op = func.air.instructions.items(.data)[inst].ty_op; | |
| 6522 | ||
| 6523 | const operand = try func.resolveInst(ty_op.operand); | |
| 6524 | const error_set_ty = func.air.getRefType(ty_op.ty); | |
| 6525 | const result = try func.allocLocal(Type.bool); | |
| 6526 | ||
| 6527 | const names = error_set_ty.errorSetNames(); | |
| 6528 | var values = try std.ArrayList(u32).initCapacity(func.gpa, names.len); | |
| 6529 | defer values.deinit(); | |
| 6530 | ||
| 6531 | const module = func.bin_file.base.options.module.?; | |
| 6532 | var lowest: ?u32 = null; | |
| 6533 | var highest: ?u32 = null; | |
| 6534 | for (names) |name| { | |
| 6535 | const err_int = module.global_error_set.get(name).?; | |
| 6536 | if (lowest) |*l| { | |
| 6537 | if (err_int < l.*) { | |
| 6538 | l.* = err_int; | |
| 6539 | } | |
| 6540 | } else { | |
| 6541 | lowest = err_int; | |
| 6542 | } | |
| 6543 | if (highest) |*h| { | |
| 6544 | if (err_int > h.*) { | |
| 6545 | highest = err_int; | |
| 6546 | } | |
| 6547 | } else { | |
| 6548 | highest = err_int; | |
| 6549 | } | |
| 6550 | ||
| 6551 | values.appendAssumeCapacity(err_int); | |
| 6552 | } | |
| 6553 | ||
| 6554 | // start block for 'true' branch | |
| 6555 | try func.startBlock(.block, wasm.block_empty); | |
| 6556 | // start block for 'false' branch | |
| 6557 | try func.startBlock(.block, wasm.block_empty); | |
| 6558 | // block for the jump table itself | |
| 6559 | try func.startBlock(.block, wasm.block_empty); | |
| 6560 | ||
| 6561 | // lower operand to determine jump table target | |
| 6562 | try func.emitWValue(operand); | |
| 6563 | try func.addImm32(@intCast(i32, lowest.?)); | |
| 6564 | try func.addTag(.i32_sub); | |
| 6565 | ||
| 6566 | // Account for default branch so always add '1' | |
| 6567 | const depth = @intCast(u32, highest.? - lowest.? + 1); | |
| 6568 | const jump_table: Mir.JumpTable = .{ .length = depth }; | |
| 6569 | const table_extra_index = try func.addExtra(jump_table); | |
| 6570 | try func.addInst(.{ .tag = .br_table, .data = .{ .payload = table_extra_index } }); | |
| 6571 | try func.mir_extra.ensureUnusedCapacity(func.gpa, depth); | |
| 6572 | ||
| 6573 | var value: u32 = lowest.?; | |
| 6574 | while (value <= highest.?) : (value += 1) { | |
| 6575 | const idx: u32 = blk: { | |
| 6576 | for (values.items) |val| { | |
| 6577 | if (val == value) break :blk 1; | |
| 6578 | } | |
| 6579 | break :blk 0; | |
| 6580 | }; | |
| 6581 | func.mir_extra.appendAssumeCapacity(idx); | |
| 6582 | } | |
| 6583 | try func.endBlock(); | |
| 6584 | ||
| 6585 | // 'false' branch (i.e. error set does not have value | |
| 6586 | // ensure we set local to 0 in case the local was re-used. | |
| 6587 | try func.addImm32(0); | |
| 6588 | try func.addLabel(.local_set, result.local.value); | |
| 6589 | try func.addLabel(.br, 1); | |
| 6590 | try func.endBlock(); | |
| 6591 | ||
| 6592 | // 'true' branch | |
| 6593 | try func.addImm32(1); | |
| 6594 | try func.addLabel(.local_set, result.local.value); | |
| 6595 | try func.addLabel(.br, 0); | |
| 6596 | try func.endBlock(); | |
| 6597 | ||
| 6598 | return func.finishAir(inst, result, &.{ty_op.operand}); | |
| 6599 | } |
src/link/Wasm.zig+43| ... | ... | @@ -1209,6 +1209,11 @@ fn resolveLazySymbols(wasm: *Wasm) !void { |
| 1209 | 1209 | try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); |
| 1210 | 1210 | } |
| 1211 | 1211 | } |
| 1212 | if (wasm.undefs.fetchSwapRemove("__zig_errors_len")) |kv| { | |
| 1213 | const loc = try wasm.createSyntheticSymbol("__zig_errors_len", .data); | |
| 1214 | try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc); | |
| 1215 | _ = wasm.resolved_symbols.swapRemove(kv.value); | |
| 1216 | } | |
| 1212 | 1217 | } |
| 1213 | 1218 | |
| 1214 | 1219 | // Tries to find a global symbol by its name. Returns null when not found, |
| ... | ... | @@ -2185,6 +2190,43 @@ fn setupInitFunctions(wasm: *Wasm) !void { |
| 2185 | 2190 | std.sort.sort(InitFuncLoc, wasm.init_funcs.items, {}, InitFuncLoc.lessThan); |
| 2186 | 2191 | } |
| 2187 | 2192 | |
| 2193 | /// Generates an atom containing the global error set' size. | |
| 2194 | /// This will only be generated if the symbol exists. | |
| 2195 | fn setupErrorsLen(wasm: *Wasm) !void { | |
| 2196 | const loc = wasm.findGlobalSymbol("__zig_errors_len") orelse return; | |
| 2197 | ||
| 2198 | const errors_len = wasm.base.options.module.?.global_error_set.count(); | |
| 2199 | // overwrite existing atom if it already exists (maybe the error set has increased) | |
| 2200 | // if not, allcoate a new atom. | |
| 2201 | const atom_index = if (wasm.symbol_atom.get(loc)) |index| blk: { | |
| 2202 | const atom = wasm.getAtomPtr(index); | |
| 2203 | if (atom.next) |next_atom_index| { | |
| 2204 | const next_atom = wasm.getAtomPtr(next_atom_index); | |
| 2205 | next_atom.prev = atom.prev; | |
| 2206 | atom.next = null; | |
| 2207 | } | |
| 2208 | if (atom.prev) |prev_index| { | |
| 2209 | const prev_atom = wasm.getAtomPtr(prev_index); | |
| 2210 | prev_atom.next = atom.next; | |
| 2211 | atom.prev = null; | |
| 2212 | } | |
| 2213 | atom.deinit(wasm); | |
| 2214 | break :blk index; | |
| 2215 | } else new_atom: { | |
| 2216 | const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len); | |
| 2217 | try wasm.symbol_atom.put(wasm.base.allocator, loc, atom_index); | |
| 2218 | try wasm.managed_atoms.append(wasm.base.allocator, undefined); | |
| 2219 | break :new_atom atom_index; | |
| 2220 | }; | |
| 2221 | const atom = wasm.getAtomPtr(atom_index); | |
| 2222 | atom.* = Atom.empty; | |
| 2223 | atom.sym_index = loc.index; | |
| 2224 | atom.size = 2; | |
| 2225 | try atom.code.writer(wasm.base.allocator).writeIntLittle(u16, @intCast(u16, errors_len)); | |
| 2226 | ||
| 2227 | try wasm.parseAtom(atom_index, .{ .data = .read_only }); | |
| 2228 | } | |
| 2229 | ||
| 2188 | 2230 | /// Creates a function body for the `__wasm_call_ctors` symbol. |
| 2189 | 2231 | /// Loops over all constructors found in `init_funcs` and calls them |
| 2190 | 2232 | /// respectively based on their priority which was sorted by `setupInitFunctions`. |
| ... | ... | @@ -3317,6 +3359,7 @@ pub fn flushModule(wasm: *Wasm, comp: *Compilation, prog_node: *std.Progress.Nod |
| 3317 | 3359 | // So we can rebuild the binary file on each incremental update |
| 3318 | 3360 | defer wasm.resetState(); |
| 3319 | 3361 | try wasm.setupInitFunctions(); |
| 3362 | try wasm.setupErrorsLen(); | |
| 3320 | 3363 | try wasm.setupStart(); |
| 3321 | 3364 | try wasm.setupImports(); |
| 3322 | 3365 | if (wasm.base.options.module) |mod| { |
test/behavior/cast.zig-1| ... | ... | @@ -401,7 +401,6 @@ test "expected [*c]const u8, found [*:0]const u8" { |
| 401 | 401 | } |
| 402 | 402 | |
| 403 | 403 | test "explicit cast from integer to error type" { |
| 404 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | |
| 405 | 404 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; |
| 406 | 405 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 407 | 406 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |