Merge pull request #15405 from Luukdegram/wasm
wasm: implement more runtime safety checks
This commit is contained in:
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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});
|
||||
}
|
||||
|
||||
@@ -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| {
|
||||
|
||||
@@ -401,7 +401,6 @@ test "expected [*c]const u8, found [*:0]const u8" {
|
||||
}
|
||||
|
||||
test "explicit cast from integer to error type" {
|
||||
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
|
||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||
|
||||
Reference in New Issue
Block a user