aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2023-04-23 15:39:36 -0700
committerGitHub <noreply@github.com>2023-04-23 15:39:36 -0700
commit2dbcc03fb80e0688bba651821db6488cf950404c (patch)
tree9d5bfa6e85d587d8373bc99a8153c75093ae1a90 /src
parent42ee364e7b698822a69cba4cd2bda17868657e05 (diff)
parent6c1ab376ddcdbb05610487e5b813d42ff37da40d (diff)
downloadzig-2dbcc03fb80e0688bba651821db6488cf950404c.tar.gz
zig-2dbcc03fb80e0688bba651821db6488cf950404c.zip
Merge pull request #15405 from Luukdegram/wasm
wasm: implement more runtime safety checks
Diffstat (limited to 'src')
-rw-r--r--src/Module.zig2
-rw-r--r--src/arch/wasm/CodeGen.zig93
-rw-r--r--src/link/Wasm.zig43
3 files changed, 134 insertions, 4 deletions
diff --git a/src/Module.zig b/src/Module.zig
index fa91e8c1ed..27f7b24e7a 100644
--- a/src/Module.zig
+++ b/src/Module.zig
@@ -6626,7 +6626,7 @@ pub fn backendSupportsFeature(mod: Module, feature: Feature) bool {
.safety_check_formatted => mod.comp.bin_file.options.use_llvm,
.error_return_trace => mod.comp.bin_file.options.use_llvm,
.is_named_enum_value => mod.comp.bin_file.options.use_llvm,
- .error_set_has_value => mod.comp.bin_file.options.use_llvm,
+ .error_set_has_value => mod.comp.bin_file.options.use_llvm or mod.comp.bin_file.options.target.isWasm(),
.field_reordering => mod.comp.bin_file.options.use_llvm,
};
}
diff --git a/src/arch/wasm/CodeGen.zig b/src/arch/wasm/CodeGen.zig
index b94d3993f9..9bf39b73f1 100644
--- a/src/arch/wasm/CodeGen.zig
+++ b/src/arch/wasm/CodeGen.zig
@@ -1946,6 +1946,8 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
.ret_addr => func.airRetAddr(inst),
.tag_name => func.airTagName(inst),
+ .error_set_has_value => func.airErrorSetHasValue(inst),
+
.mul_sat,
.mod,
.assembly,
@@ -1967,7 +1969,6 @@ fn genInst(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
.set_err_return_trace,
.save_err_return_trace_index,
.is_named_enum_value,
- .error_set_has_value,
.addrspace_cast,
.vector_store_elem,
.c_va_arg,
@@ -3338,9 +3339,14 @@ fn airCmpVector(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
fn airCmpLtErrorsLen(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
const un_op = func.air.instructions.items(.data)[inst].un_op;
const operand = try func.resolveInst(un_op);
+ const sym_index = try func.bin_file.getGlobalSymbol("__zig_errors_len", null);
+ const errors_len = WValue{ .memory = sym_index };
- _ = operand;
- return func.fail("TODO implement airCmpLtErrorsLen for wasm", .{});
+ try func.emitWValue(operand);
+ const errors_len_val = try func.load(errors_len, Type.err_int, 0);
+ const result = try func.cmp(.stack, errors_len_val, Type.err_int, .lt);
+
+ return func.finishAir(inst, try result.toLocal(func, Type.bool), &.{un_op});
}
fn airBr(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
@@ -6510,3 +6516,84 @@ fn getTagNameFunction(func: *CodeGen, enum_ty: Type) InnerError!u32 {
const func_type = try genFunctype(arena, .Unspecified, &.{int_tag_ty}, slice_ty, func.target);
return func.bin_file.createFunction(func_name, func_type, &body_list, &relocs);
}
+
+fn airErrorSetHasValue(func: *CodeGen, inst: Air.Inst.Index) InnerError!void {
+ const ty_op = func.air.instructions.items(.data)[inst].ty_op;
+
+ const operand = try func.resolveInst(ty_op.operand);
+ const error_set_ty = func.air.getRefType(ty_op.ty);
+ const result = try func.allocLocal(Type.bool);
+
+ const names = error_set_ty.errorSetNames();
+ var values = try std.ArrayList(u32).initCapacity(func.gpa, names.len);
+ defer values.deinit();
+
+ const module = func.bin_file.base.options.module.?;
+ var lowest: ?u32 = null;
+ var highest: ?u32 = null;
+ for (names) |name| {
+ const err_int = module.global_error_set.get(name).?;
+ if (lowest) |*l| {
+ if (err_int < l.*) {
+ l.* = err_int;
+ }
+ } else {
+ lowest = err_int;
+ }
+ if (highest) |*h| {
+ if (err_int > h.*) {
+ highest = err_int;
+ }
+ } else {
+ highest = err_int;
+ }
+
+ values.appendAssumeCapacity(err_int);
+ }
+
+ // start block for 'true' branch
+ try func.startBlock(.block, wasm.block_empty);
+ // start block for 'false' branch
+ try func.startBlock(.block, wasm.block_empty);
+ // block for the jump table itself
+ try func.startBlock(.block, wasm.block_empty);
+
+ // lower operand to determine jump table target
+ try func.emitWValue(operand);
+ try func.addImm32(@intCast(i32, lowest.?));
+ try func.addTag(.i32_sub);
+
+ // Account for default branch so always add '1'
+ const depth = @intCast(u32, highest.? - lowest.? + 1);
+ const jump_table: Mir.JumpTable = .{ .length = depth };
+ const table_extra_index = try func.addExtra(jump_table);
+ try func.addInst(.{ .tag = .br_table, .data = .{ .payload = table_extra_index } });
+ try func.mir_extra.ensureUnusedCapacity(func.gpa, depth);
+
+ var value: u32 = lowest.?;
+ while (value <= highest.?) : (value += 1) {
+ const idx: u32 = blk: {
+ for (values.items) |val| {
+ if (val == value) break :blk 1;
+ }
+ break :blk 0;
+ };
+ func.mir_extra.appendAssumeCapacity(idx);
+ }
+ try func.endBlock();
+
+ // 'false' branch (i.e. error set does not have value
+ // ensure we set local to 0 in case the local was re-used.
+ try func.addImm32(0);
+ try func.addLabel(.local_set, result.local.value);
+ try func.addLabel(.br, 1);
+ try func.endBlock();
+
+ // 'true' branch
+ try func.addImm32(1);
+ try func.addLabel(.local_set, result.local.value);
+ try func.addLabel(.br, 0);
+ try func.endBlock();
+
+ return func.finishAir(inst, result, &.{ty_op.operand});
+}
diff --git a/src/link/Wasm.zig b/src/link/Wasm.zig
index 2125a8faaa..0fe9ec5e3b 100644
--- a/src/link/Wasm.zig
+++ b/src/link/Wasm.zig
@@ -1209,6 +1209,11 @@ fn resolveLazySymbols(wasm: *Wasm) !void {
try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);
}
}
+ if (wasm.undefs.fetchSwapRemove("__zig_errors_len")) |kv| {
+ const loc = try wasm.createSyntheticSymbol("__zig_errors_len", .data);
+ try wasm.discarded.putNoClobber(wasm.base.allocator, kv.value, loc);
+ _ = wasm.resolved_symbols.swapRemove(kv.value);
+ }
}
// Tries to find a global symbol by its name. Returns null when not found,
@@ -2185,6 +2190,43 @@ fn setupInitFunctions(wasm: *Wasm) !void {
std.sort.sort(InitFuncLoc, wasm.init_funcs.items, {}, InitFuncLoc.lessThan);
}
+/// Generates an atom containing the global error set' size.
+/// This will only be generated if the symbol exists.
+fn setupErrorsLen(wasm: *Wasm) !void {
+ const loc = wasm.findGlobalSymbol("__zig_errors_len") orelse return;
+
+ const errors_len = wasm.base.options.module.?.global_error_set.count();
+ // overwrite existing atom if it already exists (maybe the error set has increased)
+ // if not, allcoate a new atom.
+ const atom_index = if (wasm.symbol_atom.get(loc)) |index| blk: {
+ const atom = wasm.getAtomPtr(index);
+ if (atom.next) |next_atom_index| {
+ const next_atom = wasm.getAtomPtr(next_atom_index);
+ next_atom.prev = atom.prev;
+ atom.next = null;
+ }
+ if (atom.prev) |prev_index| {
+ const prev_atom = wasm.getAtomPtr(prev_index);
+ prev_atom.next = atom.next;
+ atom.prev = null;
+ }
+ atom.deinit(wasm);
+ break :blk index;
+ } else new_atom: {
+ const atom_index = @intCast(Atom.Index, wasm.managed_atoms.items.len);
+ try wasm.symbol_atom.put(wasm.base.allocator, loc, atom_index);
+ try wasm.managed_atoms.append(wasm.base.allocator, undefined);
+ break :new_atom atom_index;
+ };
+ const atom = wasm.getAtomPtr(atom_index);
+ atom.* = Atom.empty;
+ atom.sym_index = loc.index;
+ atom.size = 2;
+ try atom.code.writer(wasm.base.allocator).writeIntLittle(u16, @intCast(u16, errors_len));
+
+ try wasm.parseAtom(atom_index, .{ .data = .read_only });
+}
+
/// Creates a function body for the `__wasm_call_ctors` symbol.
/// Loops over all constructors found in `init_funcs` and calls them
/// 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
// So we can rebuild the binary file on each incremental update
defer wasm.resetState();
try wasm.setupInitFunctions();
+ try wasm.setupErrorsLen();
try wasm.setupStart();
try wasm.setupImports();
if (wasm.base.options.module) |mod| {