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 {...@@ -1006,6 +1006,7 @@ pub const panic_messages = struct {
1006 pub const for_len_mismatch = "for loop over objects with non-equal lengths";1006 pub const for_len_mismatch = "for loop over objects with non-equal lengths";
1007 pub const memcpy_len_mismatch = "@memcpy arguments have non-equal lengths";1007 pub const memcpy_len_mismatch = "@memcpy arguments have non-equal lengths";
1008 pub const memcpy_alias = "@memcpy arguments alias";1008 pub const memcpy_alias = "@memcpy arguments alias";
1009 pub const noreturn_returned = "'noreturn' function returned";
1009};1010};
10101011
1011pub noinline fn returnError(st: *StackTrace) void {1012pub 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,...@@ -739,9 +739,9 @@ inline fn table_lookup(table: *align(64) const [4][256]u32, idx0: u8, idx1: u8,
739 std.mem.doNotOptimizeAway(t);739 std.mem.doNotOptimizeAway(t);
740 return [4]u32{740 return [4]u32{
741 t[0][idx0 / stride],741 t[0][idx0 / stride],
742 math.rotl(u32, t[1][idx1 / stride], 8),742 math.rotl(u32, (&t[1])[idx1 / stride], 8),
743 math.rotl(u32, t[2][idx2 / stride], 16),743 math.rotl(u32, (&t[2])[idx2 / stride], 16),
744 math.rotl(u32, t[3][idx3 / stride], 24),744 math.rotl(u32, (&t[3])[idx3 / stride], 24),
745 };745 };
746 }746 }
747}747}
src/Sema.zig+36-6
...@@ -7080,15 +7080,38 @@ fn analyzeCall(...@@ -7080,15 +7080,38 @@ fn analyzeCall(
7080 } },7080 } },
7081 });7081 });
7082 sema.appendRefsAssumeCapacity(args);7082 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 }
7083 break :res func_inst;7109 break :res func_inst;
7084 };7110 };
70857111
7086 if (ensure_result_used) {7112 if (ensure_result_used) {
7087 try sema.ensureResultUsed(block, sema.typeOf(result), call_src);7113 try sema.ensureResultUsed(block, sema.typeOf(result), call_src);
7088 }7114 }
7089 if (call_tag == .call_always_tail) {
7090 return sema.handleTailCall(block, call_src, func_ty, result);
7091 }
7092 return result;7115 return result;
7093}7116}
70947117
...@@ -7581,6 +7604,10 @@ fn instantiateGenericCall(...@@ -7581,6 +7604,10 @@ fn instantiateGenericCall(
7581 if (call_tag == .call_always_tail) {7604 if (call_tag == .call_always_tail) {
7582 return sema.handleTailCall(block, call_src, func_ty, result);7605 return sema.handleTailCall(block, call_src, func_ty, result);
7583 }7606 }
7607 if (new_fn_info.return_type.isNoReturn()) {
7608 _ = try block.addNoOp(.unreach);
7609 return Air.Inst.Ref.unreachable_value;
7610 }
7584 return result;7611 return result;
7585}7612}
75867613
...@@ -23440,6 +23467,7 @@ pub const PanicId = enum {...@@ -23440,6 +23467,7 @@ pub const PanicId = enum {
23440 for_len_mismatch,23467 for_len_mismatch,
23441 memcpy_len_mismatch,23468 memcpy_len_mismatch,
23442 memcpy_alias,23469 memcpy_alias,
23470 noreturn_returned,
23443};23471};
2344423472
23445fn addSafetyCheck(23473fn addSafetyCheck(
...@@ -23607,7 +23635,7 @@ fn panicIndexOutOfBounds(...@@ -23607,7 +23635,7 @@ fn panicIndexOutOfBounds(
23607 try sema.safetyCheckFormatted(parent_block, ok, "panicOutOfBounds", &.{ index, len });23635 try sema.safetyCheckFormatted(parent_block, ok, "panicOutOfBounds", &.{ index, len });
23608}23636}
2360923637
23610fn panicStartLargerThanEnd(23638fn panicStartGreaterThanEnd(
23611 sema: *Sema,23639 sema: *Sema,
23612 parent_block: *Block,23640 parent_block: *Block,
23613 start: Air.Inst.Ref,23641 start: Air.Inst.Ref,
...@@ -29464,8 +29492,10 @@ fn analyzeSlice(...@@ -29464,8 +29492,10 @@ fn analyzeSlice(
29464 const slice_sentinel = if (sentinel_opt != .none) sentinel else null;29492 const slice_sentinel = if (sentinel_opt != .none) sentinel else null;
2946529493
29466 // requirement: start <= end29494 // requirement: start <= end
29495 var need_start_gt_end_check = true;
29467 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {29496 if (try sema.resolveDefinedValue(block, end_src, end)) |end_val| {
29468 if (try sema.resolveDefinedValue(block, start_src, start)) |start_val| {29497 if (try sema.resolveDefinedValue(block, start_src, start)) |start_val| {
29498 need_start_gt_end_check = false;
29469 if (!by_length and !(try sema.compareAll(start_val, .lte, end_val, Type.usize))) {29499 if (!by_length and !(try sema.compareAll(start_val, .lte, end_val, Type.usize))) {
29470 return sema.fail(29500 return sema.fail(
29471 block,29501 block,
...@@ -29519,9 +29549,9 @@ fn analyzeSlice(...@@ -29519,9 +29549,9 @@ fn analyzeSlice(
29519 }29549 }
29520 }29550 }
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) {
29523 // requirement: start <= end29553 // requirement: start <= end
29524 try sema.panicStartLargerThanEnd(block, start, end);29554 try sema.panicStartGreaterThanEnd(block, start, end);
29525 }29555 }
29526 const new_len = if (by_length)29556 const new_len = if (by_length)
29527 try sema.coerce(block, Type.usize, uncasted_end_opt, end_src)29557 try sema.coerce(block, Type.usize, uncasted_end_opt, end_src)
src/codegen/llvm.zig-1
...@@ -5030,7 +5030,6 @@ pub const FuncGen = struct {...@@ -5030,7 +5030,6 @@ pub const FuncGen = struct {
5030 }5030 }
50315031
5032 if (return_type.isNoReturn() and attr != .AlwaysTail) {5032 if (return_type.isNoReturn() and attr != .AlwaysTail) {
5033 _ = self.builder.buildUnreachable();
5034 return null;5033 return null;
5035 }5034 }
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