authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-08-03 02:11:52-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2019-08-03 02:11:52-04:00
loge444e737b786afd0c5aba2ea04c901f89c57813e
tree6740bacbb1cb2189a5a9715af9046f921c8f6319
parent24d78177eec4d8fc3aa8ca99dd50788e38f9f8b6
signaturelock-open Commit is signed but in an unrecognized format.

add runtime safety for resuming an awaiting function


5 files changed, 79 insertions(+), 7 deletions(-)

BRANCH_TODO+3-1
...@@ -6,7 +6,6 @@...@@ -6,7 +6,6 @@
6 * @asyncCall with an async function pointer6 * @asyncCall with an async function pointer
7 * cancel7 * cancel
8 * defer and errdefer8 * defer and errdefer
9 * safety for resuming when it is awaiting
10 * safety for double await9 * safety for double await
11 * implicit cast of normal function to async function should be allowed when it is inferred to be async10 * implicit cast of normal function to async function should be allowed when it is inferred to be async
12 * go over the commented out tests11 * go over the commented out tests
...@@ -19,3 +18,6 @@...@@ -19,3 +18,6 @@
19 * make sure there are safety tests for all the new safety features (search the new PanicFnId enum values)18 * make sure there are safety tests for all the new safety features (search the new PanicFnId enum values)
20 * error return tracing19 * error return tracing
21 * compile error for casting a function to a non-async function pointer, but then later it gets inferred to be an async function20 * compile error for casting a function to a non-async function pointer, but then later it gets inferred to be an async function
21 * compile error for copying a frame
22 * compile error for resuming a const frame pointer
23 * runtime safety enabling/disabling scope has to be coordinated across resume/await/calls/return
src/all_types.hpp+1
...@@ -1552,6 +1552,7 @@ enum PanicMsgId {...@@ -1552,6 +1552,7 @@ enum PanicMsgId {
1552 PanicMsgIdBadResume,1552 PanicMsgIdBadResume,
1553 PanicMsgIdBadAwait,1553 PanicMsgIdBadAwait,
1554 PanicMsgIdBadReturn,1554 PanicMsgIdBadReturn,
1555 PanicMsgIdResumedAnAwaitingFn,
15551556
1556 PanicMsgIdCount,1557 PanicMsgIdCount,
1557};1558};
src/codegen.cpp+42-5
...@@ -877,6 +877,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {...@@ -877,6 +877,8 @@ static Buf *panic_msg_buf(PanicMsgId msg_id) {
877 return buf_create_from_str("async function awaited twice");877 return buf_create_from_str("async function awaited twice");
878 case PanicMsgIdBadReturn:878 case PanicMsgIdBadReturn:
879 return buf_create_from_str("async function returned twice");879 return buf_create_from_str("async function returned twice");
880 case PanicMsgIdResumedAnAwaitingFn:
881 return buf_create_from_str("awaiting function resumed");
880 }882 }
881 zig_unreachable();883 zig_unreachable();
882}884}
...@@ -2018,7 +2020,10 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns...@@ -2018,7 +2020,10 @@ static LLVMValueRef ir_render_return(CodeGen *g, IrExecutable *executable, IrIns
2018 }2020 }
2019 result_ptr_as_usize = LLVMBuildPtrToInt(g->builder, result_ptr, usize_type_ref, "");2021 result_ptr_as_usize = LLVMBuildPtrToInt(g->builder, result_ptr, usize_type_ref, "");
2020 } else {2022 } else {
2021 result_ptr_as_usize = LLVMGetUndef(usize_type_ref);2023 // For debug safety, this value has to be anything other than all 1's, which signals
2024 // that it is being resumed. 0 is a bad choice since null pointers are special.
2025 result_ptr_as_usize = ir_want_runtime_safety(g, &return_instruction->base) ?
2026 LLVMConstInt(usize_type_ref, 1, false) : LLVMGetUndef(usize_type_ref);
2022 }2027 }
2023 LLVMValueRef zero = LLVMConstNull(usize_type_ref);2028 LLVMValueRef zero = LLVMConstNull(usize_type_ref);
2024 LLVMValueRef all_ones = LLVMConstAllOnes(usize_type_ref);2029 LLVMValueRef all_ones = LLVMConstAllOnes(usize_type_ref);
...@@ -3582,8 +3587,9 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr...@@ -3582,8 +3587,9 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
3582 LLVMBuildStore(g->builder, gen_param_values.at(arg_i), arg_ptr);3587 LLVMBuildStore(g->builder, gen_param_values.at(arg_i), arg_ptr);
3583 }3588 }
3584 }3589 }
3590 LLVMTypeRef usize_type_ref = g->builtin_types.entry_usize->llvm_type;
3585 if (instruction->is_async) {3591 if (instruction->is_async) {
3586 LLVMValueRef args[] = {frame_result_loc, LLVMGetUndef(g->builtin_types.entry_usize->llvm_type)};3592 LLVMValueRef args[] = {frame_result_loc, LLVMGetUndef(usize_type_ref)};
3587 ZigLLVMBuildCall(g->builder, fn_val, args, 2, llvm_cc, fn_inline, "");3593 ZigLLVMBuildCall(g->builder, fn_val, args, 2, llvm_cc, fn_inline, "");
3588 return nullptr;3594 return nullptr;
3589 } else if (callee_is_async) {3595 } else if (callee_is_async) {
...@@ -3591,8 +3597,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr...@@ -3591,8 +3597,7 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
3591 LLVMValueRef split_llvm_fn = make_fn_llvm_value(g, g->cur_fn);3597 LLVMValueRef split_llvm_fn = make_fn_llvm_value(g, g->cur_fn);
3592 LLVMValueRef fn_ptr_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, coro_fn_ptr_index, "");3598 LLVMValueRef fn_ptr_ptr = LLVMBuildStructGEP(g->builder, g->cur_ret_ptr, coro_fn_ptr_index, "");
3593 LLVMBuildStore(g->builder, split_llvm_fn, fn_ptr_ptr);3599 LLVMBuildStore(g->builder, split_llvm_fn, fn_ptr_ptr);
35943600 LLVMValueRef args[] = {frame_result_loc, LLVMGetUndef(usize_type_ref)};
3595 LLVMValueRef args[] = {frame_result_loc, LLVMGetUndef(g->builtin_types.entry_usize->llvm_type)};
3596 LLVMValueRef call_inst = ZigLLVMBuildCall(g->builder, fn_val, args, 2, llvm_cc, fn_inline, "");3601 LLVMValueRef call_inst = ZigLLVMBuildCall(g->builder, fn_val, args, 2, llvm_cc, fn_inline, "");
3597 ZigLLVMSetTailCall(call_inst);3602 ZigLLVMSetTailCall(call_inst);
3598 LLVMBuildRetVoid(g->builder);3603 LLVMBuildRetVoid(g->builder);
...@@ -3601,6 +3606,21 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr...@@ -3601,6 +3606,21 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
3601 g->cur_ret_ptr = LLVMGetParam(split_llvm_fn, 0);3606 g->cur_ret_ptr = LLVMGetParam(split_llvm_fn, 0);
3602 LLVMBasicBlockRef call_bb = LLVMAppendBasicBlock(split_llvm_fn, "CallResume");3607 LLVMBasicBlockRef call_bb = LLVMAppendBasicBlock(split_llvm_fn, "CallResume");
3603 LLVMPositionBuilderAtEnd(g->builder, call_bb);3608 LLVMPositionBuilderAtEnd(g->builder, call_bb);
3609
3610 if (ir_want_runtime_safety(g, &instruction->base)) {
3611 LLVMBasicBlockRef bad_resume_block = LLVMAppendBasicBlock(split_llvm_fn, "BadResume");
3612 LLVMBasicBlockRef ok_resume_block = LLVMAppendBasicBlock(split_llvm_fn, "OkResume");
3613 LLVMValueRef arg_val = LLVMGetParam(split_llvm_fn, 1);
3614 LLVMValueRef all_ones = LLVMConstAllOnes(usize_type_ref);
3615 LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntNE, arg_val, all_ones, "");
3616 LLVMBuildCondBr(g->builder, ok_bit, ok_resume_block, bad_resume_block);
3617
3618 LLVMPositionBuilderAtEnd(g->builder, bad_resume_block);
3619 gen_safety_crash(g, PanicMsgIdResumedAnAwaitingFn);
3620
3621 LLVMPositionBuilderAtEnd(g->builder, ok_resume_block);
3622 }
3623
3604 render_async_var_decls(g, instruction->base.scope);3624 render_async_var_decls(g, instruction->base.scope);
36053625
3606 if (type_has_bits(src_return_type)) {3626 if (type_has_bits(src_return_type)) {
...@@ -5139,6 +5159,21 @@ static LLVMValueRef ir_render_await(CodeGen *g, IrExecutable *executable, IrInst...@@ -5139,6 +5159,21 @@ static LLVMValueRef ir_render_await(CodeGen *g, IrExecutable *executable, IrInst
5139 g->cur_ret_ptr = LLVMGetParam(split_llvm_fn, 0);5159 g->cur_ret_ptr = LLVMGetParam(split_llvm_fn, 0);
5140 LLVMBasicBlockRef call_bb = LLVMAppendBasicBlock(split_llvm_fn, "AwaitResume");5160 LLVMBasicBlockRef call_bb = LLVMAppendBasicBlock(split_llvm_fn, "AwaitResume");
5141 LLVMPositionBuilderAtEnd(g->builder, call_bb);5161 LLVMPositionBuilderAtEnd(g->builder, call_bb);
5162
5163 if (ir_want_runtime_safety(g, &instruction->base)) {
5164 LLVMBasicBlockRef bad_resume_block = LLVMAppendBasicBlock(split_llvm_fn, "BadResume");
5165 LLVMBasicBlockRef ok_resume_block = LLVMAppendBasicBlock(split_llvm_fn, "OkResume");
5166 LLVMValueRef arg_val = LLVMGetParam(split_llvm_fn, 1);
5167 LLVMValueRef all_ones = LLVMConstAllOnes(usize_type_ref);
5168 LLVMValueRef ok_bit = LLVMBuildICmp(g->builder, LLVMIntNE, arg_val, all_ones, "");
5169 LLVMBuildCondBr(g->builder, ok_bit, ok_resume_block, bad_resume_block);
5170
5171 LLVMPositionBuilderAtEnd(g->builder, bad_resume_block);
5172 gen_safety_crash(g, PanicMsgIdResumedAnAwaitingFn);
5173
5174 LLVMPositionBuilderAtEnd(g->builder, ok_resume_block);
5175 }
5176
5142 render_async_var_decls(g, instruction->base.scope);5177 render_async_var_decls(g, instruction->base.scope);
51435178
5144 if (type_has_bits(result_type)) {5179 if (type_has_bits(result_type)) {
...@@ -5178,7 +5213,9 @@ static LLVMValueRef ir_render_coro_resume(CodeGen *g, IrExecutable *executable,...@@ -5178,7 +5213,9 @@ static LLVMValueRef ir_render_coro_resume(CodeGen *g, IrExecutable *executable,
5178 LLVMValueRef fn_ptr_ptr = LLVMBuildStructGEP(g->builder, frame, coro_fn_ptr_index, "");5213 LLVMValueRef fn_ptr_ptr = LLVMBuildStructGEP(g->builder, frame, coro_fn_ptr_index, "");
5179 LLVMValueRef uncasted_fn_val = LLVMBuildLoad(g->builder, fn_ptr_ptr, "");5214 LLVMValueRef uncasted_fn_val = LLVMBuildLoad(g->builder, fn_ptr_ptr, "");
5180 LLVMValueRef fn_val = LLVMBuildIntToPtr(g->builder, uncasted_fn_val, anyframe_fn_type(g), "");5215 LLVMValueRef fn_val = LLVMBuildIntToPtr(g->builder, uncasted_fn_val, anyframe_fn_type(g), "");
5181 LLVMValueRef args[] = {frame, LLVMGetUndef(usize_type_ref)};5216 LLVMValueRef arg_val = ir_want_runtime_safety(g, &instruction->base) ?
5217 LLVMConstAllOnes(usize_type_ref) : LLVMGetUndef(usize_type_ref);
5218 LLVMValueRef args[] = {frame, arg_val};
5182 ZigLLVMBuildCall(g->builder, fn_val, args, 2, LLVMFastCallConv, ZigLLVM_FnInlineAuto, "");5219 ZigLLVMBuildCall(g->builder, fn_val, args, 2, LLVMFastCallConv, ZigLLVM_FnInlineAuto, "");
5183 return nullptr;5220 return nullptr;
5184}5221}
test/runtime_safety.zig+32
...@@ -1,6 +1,38 @@...@@ -1,6 +1,38 @@
1const tests = @import("tests.zig");1const tests = @import("tests.zig");
22
3pub fn addCases(cases: *tests.CompareOutputContext) void {3pub fn addCases(cases: *tests.CompareOutputContext) void {
4 cases.addRuntimeSafety("resuming a function which is awaiting a frame",
5 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
6 \\ @import("std").os.exit(126);
7 \\}
8 \\pub fn main() void {
9 \\ var frame = async first();
10 \\ resume frame;
11 \\}
12 \\fn first() void {
13 \\ var frame = async other();
14 \\ await frame;
15 \\}
16 \\fn other() void {
17 \\ suspend;
18 \\}
19 );
20 cases.addRuntimeSafety("resuming a function which is awaiting a call",
21 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
22 \\ @import("std").os.exit(126);
23 \\}
24 \\pub fn main() void {
25 \\ var frame = async first();
26 \\ resume frame;
27 \\}
28 \\fn first() void {
29 \\ other();
30 \\}
31 \\fn other() void {
32 \\ suspend;
33 \\}
34 );
35
4 cases.addRuntimeSafety("invalid resume of async function",36 cases.addRuntimeSafety("invalid resume of async function",
5 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {37 \\pub fn panic(message: []const u8, stack_trace: ?*@import("builtin").StackTrace) noreturn {
6 \\ @import("std").os.exit(126);38 \\ @import("std").os.exit(126);
test/stage1/behavior/coroutines.zig+1-1
...@@ -89,7 +89,7 @@ test "calling an inferred async function" {...@@ -89,7 +89,7 @@ test "calling an inferred async function" {
89 var other_frame: *@Frame(other) = undefined;89 var other_frame: *@Frame(other) = undefined;
9090
91 fn doTheTest() void {91 fn doTheTest() void {
92 const p = async first();92 _ = async first();
93 expect(x == 1);93 expect(x == 1);
94 resume other_frame.*;94 resume other_frame.*;
95 expect(x == 2);95 expect(x == 2);