authorgravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2023-05-23 13:34:52+03:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2023-05-23 13:34:52+03:00
log4ce1ae71a533055c72367fcf0dae9909762b9c51
tree73b8d06082c718a25d1650ec2a93c99a04bd3fab
parent9d179a98f69dbab393cbb3fc5dd4b64c553a721b
parentebbc521a8742fe264bf9738082ce1e6b1894e04d
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #15235 from Vexu/safety

add runtime safety for noreturn function returning

5 files changed, 63 insertions(+), 10 deletions(-)

lib/std/builtin.zig+1
......@@ -1006,6 +1006,7 @@ pub const panic_messages = struct {
10061006 pub const for_len_mismatch = "for loop over objects with non-equal lengths";
10071007 pub const memcpy_len_mismatch = "@memcpy arguments have non-equal lengths";
10081008 pub const memcpy_alias = "@memcpy arguments alias";
1009 pub const noreturn_returned = "'noreturn' function returned";
10091010};
10101011
10111012pub noinline fn returnError(st: *StackTrace) void {
lib/std/crypto/aes/soft.zig+3-3
......@@ -739,9 +739,9 @@ inline fn table_lookup(table: *align(64) const [4][256]u32, idx0: u8, idx1: u8,
739739 std.mem.doNotOptimizeAway(t);
740740 return [4]u32{
741741 t[0][idx0 / stride],
742 math.rotl(u32, t[1][idx1 / stride], 8),
743 math.rotl(u32, t[2][idx2 / stride], 16),
744 math.rotl(u32, t[3][idx3 / stride], 24),
742 math.rotl(u32, (&t[1])[idx1 / stride], 8),
743 math.rotl(u32, (&t[2])[idx2 / stride], 16),
744 math.rotl(u32, (&t[3])[idx3 / stride], 24),
745745 };
746746 }
747747}
src/Sema.zig+36-6
......@@ -7080,15 +7080,38 @@ fn analyzeCall(
70807080 } },
70817081 });
70827082 sema.appendRefsAssumeCapacity(args);
7083
7084 if (call_tag == .call_always_tail) {
7085 if (ensure_result_used) {
7086 try sema.ensureResultUsed(block, sema.typeOf(func_inst), call_src);
7087 }
7088 return sema.handleTailCall(block, call_src, func_ty, func_inst);
7089 } else if (block.wantSafety() and func_ty_info.return_type.isNoReturn()) {
7090 // Function pointers and extern functions aren't guaranteed to
7091 // actually be noreturn so we add a safety check for them.
7092 check: {
7093 var func_val = (try sema.resolveMaybeUndefVal(func)) orelse break :check;
7094 switch (func_val.tag()) {
7095 .function, .decl_ref => {
7096 _ = try block.addNoOp(.unreach);
7097 return Air.Inst.Ref.unreachable_value;
7098 },
7099 else => break :check,
7100 }
7101 }
7102
7103 try sema.safetyPanic(block, .noreturn_returned);
7104 return Air.Inst.Ref.unreachable_value;
7105 } else if (func_ty_info.return_type.isNoReturn()) {
7106 _ = try block.addNoOp(.unreach);
7107 return Air.Inst.Ref.unreachable_value;
7108 }
70837109 break :res func_inst;
70847110 };
70857111
70867112 if (ensure_result_used) {
70877113 try sema.ensureResultUsed(block, sema.typeOf(result), call_src);
70887114 }
7089 if (call_tag == .call_always_tail) {
7090 return sema.handleTailCall(block, call_src, func_ty, result);
7091 }
70927115 return result;
70937116}
70947117
......@@ -7581,6 +7604,10 @@ fn instantiateGenericCall(
75817604 if (call_tag == .call_always_tail) {
75827605 return sema.handleTailCall(block, call_src, func_ty, result);
75837606 }
7607 if (new_fn_info.return_type.isNoReturn()) {
7608 _ = try block.addNoOp(.unreach);
7609 return Air.Inst.Ref.unreachable_value;
7610 }
75847611 return result;
75857612}
75867613
......@@ -23440,6 +23467,7 @@ pub const PanicId = enum {
2344023467 for_len_mismatch,
2344123468 memcpy_len_mismatch,
2344223469 memcpy_alias,
23470 noreturn_returned,
2344323471};
2344423472
2344523473fn addSafetyCheck(
......@@ -23607,7 +23635,7 @@ fn panicIndexOutOfBounds(
2360723635 try sema.safetyCheckFormatted(parent_block, ok, "panicOutOfBounds", &.{ index, len });
2360823636}
2360923637
23610fn panicStartLargerThanEnd(
23638fn panicStartGreaterThanEnd(
2361123639 sema: *Sema,
2361223640 parent_block: *Block,
2361323641 start: Air.Inst.Ref,
......@@ -29464,8 +29492,10 @@ fn analyzeSlice(
2946429492 const slice_sentinel = if (sentinel_opt != .none) sentinel else null;
2946529493
2946629494 // requirement: start <= end
29495 var need_start_gt_end_check = true;
2946729496 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
2946829497 if (try sema.resolveDefinedValue(block, start_src, start)) |start_val| {
29498 need_start_gt_end_check = false;
2946929499 if (!by_length and !(try sema.compareAll(start_val, .lte, end_val, Type.usize))) {
2947029500 return sema.fail(
2947129501 block,
......@@ -29519,9 +29549,9 @@ fn analyzeSlice(
2951929549 }
2952029550 }
2952129551
29522 if (!by_length and block.wantSafety() and !block.is_comptime) {
29552 if (!by_length and block.wantSafety() and !block.is_comptime and need_start_gt_end_check) {
2952329553 // requirement: start <= end
29524 try sema.panicStartLargerThanEnd(block, start, end);
29554 try sema.panicStartGreaterThanEnd(block, start, end);
2952529555 }
2952629556 const new_len = if (by_length)
2952729557 try sema.coerce(block, Type.usize, uncasted_end_opt, end_src)
src/codegen/llvm.zig-1
......@@ -5030,7 +5030,6 @@ pub const FuncGen = struct {
50305030 }
50315031
50325032 if (return_type.isNoReturn() and attr != .AlwaysTail) {
5033 _ = self.builder.buildUnreachable();
50345033 return null;
50355034 }
50365035
test/cases/safety/noreturn returned.zig created+23
......@@ -0,0 +1,23 @@
1const std = @import("std");
2
3pub fn panic(message: []const u8, stack_trace: ?*std.builtin.StackTrace, _: ?usize) noreturn {
4 _ = stack_trace;
5 if (std.mem.eql(u8, message, "'noreturn' function returned")) {
6 std.process.exit(0);
7 }
8 std.process.exit(1);
9}
10const T = struct {
11 export fn bar() void {
12 // ...
13 }
14};
15
16extern fn bar() noreturn;
17pub fn main() void {
18 _ = T.bar;
19 bar();
20}
21// run
22// backend=llvm
23// target=native