Merge pull request #15235 from Vexu/safety

add runtime safety for noreturn function returning
This commit is contained in:
Veikka Tuominen
2023-05-23 13:34:52 +03:00
committed by GitHub
5 changed files with 63 additions and 10 deletions

View File

@@ -1006,6 +1006,7 @@ pub const panic_messages = struct {
pub const for_len_mismatch = "for loop over objects with non-equal lengths";
pub const memcpy_len_mismatch = "@memcpy arguments have non-equal lengths";
pub const memcpy_alias = "@memcpy arguments alias";
pub const noreturn_returned = "'noreturn' function returned";
};
pub noinline fn returnError(st: *StackTrace) void {

View File

@@ -739,9 +739,9 @@ inline fn table_lookup(table: *align(64) const [4][256]u32, idx0: u8, idx1: u8,
std.mem.doNotOptimizeAway(t);
return [4]u32{
t[0][idx0 / stride],
math.rotl(u32, t[1][idx1 / stride], 8),
math.rotl(u32, t[2][idx2 / stride], 16),
math.rotl(u32, t[3][idx3 / stride], 24),
math.rotl(u32, (&t[1])[idx1 / stride], 8),
math.rotl(u32, (&t[2])[idx2 / stride], 16),
math.rotl(u32, (&t[3])[idx3 / stride], 24),
};
}
}

View File

@@ -7080,15 +7080,38 @@ fn analyzeCall(
} },
});
sema.appendRefsAssumeCapacity(args);
if (call_tag == .call_always_tail) {
if (ensure_result_used) {
try sema.ensureResultUsed(block, sema.typeOf(func_inst), call_src);
}
return sema.handleTailCall(block, call_src, func_ty, func_inst);
} else if (block.wantSafety() and func_ty_info.return_type.isNoReturn()) {
// Function pointers and extern functions aren't guaranteed to
// actually be noreturn so we add a safety check for them.
check: {
var func_val = (try sema.resolveMaybeUndefVal(func)) orelse break :check;
switch (func_val.tag()) {
.function, .decl_ref => {
_ = try block.addNoOp(.unreach);
return Air.Inst.Ref.unreachable_value;
},
else => break :check,
}
}
try sema.safetyPanic(block, .noreturn_returned);
return Air.Inst.Ref.unreachable_value;
} else if (func_ty_info.return_type.isNoReturn()) {
_ = try block.addNoOp(.unreach);
return Air.Inst.Ref.unreachable_value;
}
break :res func_inst;
};
if (ensure_result_used) {
try sema.ensureResultUsed(block, sema.typeOf(result), call_src);
}
if (call_tag == .call_always_tail) {
return sema.handleTailCall(block, call_src, func_ty, result);
}
return result;
}
@@ -7581,6 +7604,10 @@ fn instantiateGenericCall(
if (call_tag == .call_always_tail) {
return sema.handleTailCall(block, call_src, func_ty, result);
}
if (new_fn_info.return_type.isNoReturn()) {
_ = try block.addNoOp(.unreach);
return Air.Inst.Ref.unreachable_value;
}
return result;
}
@@ -23440,6 +23467,7 @@ pub const PanicId = enum {
for_len_mismatch,
memcpy_len_mismatch,
memcpy_alias,
noreturn_returned,
};
fn addSafetyCheck(
@@ -23607,7 +23635,7 @@ fn panicIndexOutOfBounds(
try sema.safetyCheckFormatted(parent_block, ok, "panicOutOfBounds", &.{ index, len });
}
fn panicStartLargerThanEnd(
fn panicStartGreaterThanEnd(
sema: *Sema,
parent_block: *Block,
start: Air.Inst.Ref,
@@ -29464,8 +29492,10 @@ fn analyzeSlice(
const slice_sentinel = if (sentinel_opt != .none) sentinel else null;
// requirement: start <= end
var need_start_gt_end_check = true;
if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
if (try sema.resolveDefinedValue(block, start_src, start)) |start_val| {
need_start_gt_end_check = false;
if (!by_length and !(try sema.compareAll(start_val, .lte, end_val, Type.usize))) {
return sema.fail(
block,
@@ -29519,9 +29549,9 @@ fn analyzeSlice(
}
}
if (!by_length and block.wantSafety() and !block.is_comptime) {
if (!by_length and block.wantSafety() and !block.is_comptime and need_start_gt_end_check) {
// requirement: start <= end
try sema.panicStartLargerThanEnd(block, start, end);
try sema.panicStartGreaterThanEnd(block, start, end);
}
const new_len = if (by_length)
try sema.coerce(block, Type.usize, uncasted_end_opt, end_src)

View File

@@ -5030,7 +5030,6 @@ pub const FuncGen = struct {
}
if (return_type.isNoReturn() and attr != .AlwaysTail) {
_ = self.builder.buildUnreachable();
return null;
}

View File

@@ -0,0 +1,23 @@
const std = @import("std");
pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
_ = stack_trace;
if (std.mem.eql(u8, message, "'noreturn' function returned")) {
std.process.exit(0);
}
std.process.exit(1);
}
const T = struct {
export fn bar() void {
// ...
}
};
extern fn bar() noreturn;
pub fn main() void {
_ = T.bar;
bar();
}
// run
// backend=llvm
// target=native