authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-06-27 18:45:21-04:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2018-06-27 18:45:21-04:00
log2fa588e81d60cfe319446bd0483c6bf296f40c40
treee1cf362f872de63e4c14cd869c1a8549ff7e2519
parent19961c50e4db10fc4ada428928a7f5d1a2966da6

fix coroutine accessing freed memory

closes #1164

3 files changed, 47 insertions(+), 13 deletions(-)

src/analyze.cpp+1-1
...@@ -5583,7 +5583,7 @@ void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, TypeT...@@ -5583,7 +5583,7 @@ void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, TypeT
5583 return;5583 return;
5584 }5584 }
5585 case ConstPtrSpecialHardCodedAddr:5585 case ConstPtrSpecialHardCodedAddr:
5586 buf_appendf(buf, "(*%s)(%" ZIG_PRI_x64 ")", buf_ptr(&type_entry->data.pointer.child_type->name),5586 buf_appendf(buf, "(%s)(%" ZIG_PRI_x64 ")", buf_ptr(&type_entry->name),
5587 const_val->data.x_ptr.data.hard_coded_addr.addr);5587 const_val->data.x_ptr.data.hard_coded_addr.addr);
5588 return;5588 return;
5589 case ConstPtrSpecialDiscard:5589 case ConstPtrSpecialDiscard:
src/ir.cpp+14-3
...@@ -7112,6 +7112,12 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec...@@ -7112,6 +7112,12 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
7112 IrInstruction *dest_err_ret_trace_ptr = ir_build_load_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr);7112 IrInstruction *dest_err_ret_trace_ptr = ir_build_load_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr);
7113 ir_build_merge_err_ret_traces(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr, dest_err_ret_trace_ptr);7113 ir_build_merge_err_ret_traces(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr, dest_err_ret_trace_ptr);
7114 }7114 }
7115 // Before we destroy the coroutine frame, we need to load the target promise into
7116 // a register or local variable which does not get spilled into the frame,
7117 // otherwise llvm tries to access memory inside the destroyed frame.
7118 IrInstruction *unwrapped_await_handle_ptr = ir_build_unwrap_maybe(irb, scope, node,
7119 irb->exec->await_handle_var_ptr, false);
7120 IrInstruction *await_handle_in_block = ir_build_load_ptr(irb, scope, node, unwrapped_await_handle_ptr);
7115 ir_build_br(irb, scope, node, check_free_block, const_bool_false);7121 ir_build_br(irb, scope, node, check_free_block, const_bool_false);
71167122
7117 ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_final_cleanup_block);7123 ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_final_cleanup_block);
...@@ -7126,6 +7132,14 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec...@@ -7126,6 +7132,14 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
7126 incoming_values[1] = const_bool_true;7132 incoming_values[1] = const_bool_true;
7127 IrInstruction *resume_awaiter = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values);7133 IrInstruction *resume_awaiter = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values);
71287134
7135 IrBasicBlock **merge_incoming_blocks = allocate<IrBasicBlock *>(2);
7136 IrInstruction **merge_incoming_values = allocate<IrInstruction *>(2);
7137 merge_incoming_blocks[0] = irb->exec->coro_final_cleanup_block;
7138 merge_incoming_values[0] = ir_build_const_undefined(irb, scope, node);
7139 merge_incoming_blocks[1] = irb->exec->coro_normal_final;
7140 merge_incoming_values[1] = await_handle_in_block;
7141 IrInstruction *awaiter_handle = ir_build_phi(irb, scope, node, 2, merge_incoming_blocks, merge_incoming_values);
7142
7129 Buf *free_field_name = buf_create_from_str(ASYNC_FREE_FIELD_NAME);7143 Buf *free_field_name = buf_create_from_str(ASYNC_FREE_FIELD_NAME);
7130 IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, scope, node,7144 IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, scope, node,
7131 ImplicitAllocatorIdLocalVar);7145 ImplicitAllocatorIdLocalVar);
...@@ -7152,9 +7166,6 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec...@@ -7152,9 +7166,6 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
7152 ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, irb->exec->coro_suspend_block, const_bool_false);7166 ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, irb->exec->coro_suspend_block, const_bool_false);
71537167
7154 ir_set_cursor_at_end_and_append_block(irb, resume_block);7168 ir_set_cursor_at_end_and_append_block(irb, resume_block);
7155 IrInstruction *unwrapped_await_handle_ptr = ir_build_unwrap_maybe(irb, scope, node,
7156 irb->exec->await_handle_var_ptr, false);
7157 IrInstruction *awaiter_handle = ir_build_load_ptr(irb, scope, node, unwrapped_await_handle_ptr);
7158 ir_build_coro_resume(irb, scope, node, awaiter_handle);7169 ir_build_coro_resume(irb, scope, node, awaiter_handle);
7159 ir_build_br(irb, scope, node, irb->exec->coro_suspend_block, const_bool_false);7170 ir_build_br(irb, scope, node, irb->exec->coro_suspend_block, const_bool_false);
7160 }7171 }
test/cases/coroutines.zig+32-9
...@@ -5,7 +5,10 @@ const assert = std.debug.assert;...@@ -5,7 +5,10 @@ const assert = std.debug.assert;
5var x: i32 = 1;5var x: i32 = 1;
66
7test "create a coroutine and cancel it" {7test "create a coroutine and cancel it" {
8 const p = try async<std.debug.global_allocator> simpleAsyncFn();8 var da = std.heap.DirectAllocator.init();
9 defer da.deinit();
10
11 const p = try async<&da.allocator> simpleAsyncFn();
9 comptime assert(@typeOf(p) == promise->void);12 comptime assert(@typeOf(p) == promise->void);
10 cancel p;13 cancel p;
11 assert(x == 2);14 assert(x == 2);
...@@ -17,8 +20,11 @@ async fn simpleAsyncFn() void {...@@ -17,8 +20,11 @@ async fn simpleAsyncFn() void {
17}20}
1821
19test "coroutine suspend, resume, cancel" {22test "coroutine suspend, resume, cancel" {
23 var da = std.heap.DirectAllocator.init();
24 defer da.deinit();
25
20 seq('a');26 seq('a');
21 const p = try async<std.debug.global_allocator> testAsyncSeq();27 const p = try async<&da.allocator> testAsyncSeq();
22 seq('c');28 seq('c');
23 resume p;29 resume p;
24 seq('f');30 seq('f');
...@@ -43,7 +49,10 @@ fn seq(c: u8) void {...@@ -43,7 +49,10 @@ fn seq(c: u8) void {
43}49}
4450
45test "coroutine suspend with block" {51test "coroutine suspend with block" {
46 const p = try async<std.debug.global_allocator> testSuspendBlock();52 var da = std.heap.DirectAllocator.init();
53 defer da.deinit();
54
55 const p = try async<&da.allocator> testSuspendBlock();
47 std.debug.assert(!result);56 std.debug.assert(!result);
48 resume a_promise;57 resume a_promise;
49 std.debug.assert(result);58 std.debug.assert(result);
...@@ -64,8 +73,11 @@ var await_a_promise: promise = undefined;...@@ -64,8 +73,11 @@ var await_a_promise: promise = undefined;
64var await_final_result: i32 = 0;73var await_final_result: i32 = 0;
6574
66test "coroutine await" {75test "coroutine await" {
76 var da = std.heap.DirectAllocator.init();
77 defer da.deinit();
78
67 await_seq('a');79 await_seq('a');
68 const p = async<std.debug.global_allocator> await_amain() catch unreachable;80 const p = async<&da.allocator> await_amain() catch unreachable;
69 await_seq('f');81 await_seq('f');
70 resume await_a_promise;82 resume await_a_promise;
71 await_seq('i');83 await_seq('i');
...@@ -100,8 +112,11 @@ fn await_seq(c: u8) void {...@@ -100,8 +112,11 @@ fn await_seq(c: u8) void {
100var early_final_result: i32 = 0;112var early_final_result: i32 = 0;
101113
102test "coroutine await early return" {114test "coroutine await early return" {
115 var da = std.heap.DirectAllocator.init();
116 defer da.deinit();
117
103 early_seq('a');118 early_seq('a');
104 const p = async<std.debug.global_allocator> early_amain() catch unreachable;119 const p = async<&da.allocator> early_amain() catch unreachable;
105 early_seq('f');120 early_seq('f');
106 assert(early_final_result == 1234);121 assert(early_final_result == 1234);
107 assert(std.mem.eql(u8, early_points, "abcdef"));122 assert(std.mem.eql(u8, early_points, "abcdef"));
...@@ -146,7 +161,9 @@ test "async function with dot syntax" {...@@ -146,7 +161,9 @@ test "async function with dot syntax" {
146 suspend;161 suspend;
147 }162 }
148 };163 };
149 const p = try async<std.debug.global_allocator> S.foo();164 var da = std.heap.DirectAllocator.init();
165 defer da.deinit();
166 const p = try async<&da.allocator> S.foo();
150 cancel p;167 cancel p;
151 assert(S.y == 2);168 assert(S.y == 2);
152}169}
...@@ -157,7 +174,9 @@ test "async fn pointer in a struct field" {...@@ -157,7 +174,9 @@ test "async fn pointer in a struct field" {
157 bar: async<*std.mem.Allocator> fn (*i32) void,174 bar: async<*std.mem.Allocator> fn (*i32) void,
158 };175 };
159 var foo = Foo{ .bar = simpleAsyncFn2 };176 var foo = Foo{ .bar = simpleAsyncFn2 };
160 const p = (async<std.debug.global_allocator> foo.bar(&data)) catch unreachable;177 var da = std.heap.DirectAllocator.init();
178 defer da.deinit();
179 const p = (async<&da.allocator> foo.bar(&data)) catch unreachable;
161 assert(data == 2);180 assert(data == 2);
162 cancel p;181 cancel p;
163 assert(data == 4);182 assert(data == 4);
...@@ -169,7 +188,9 @@ async<*std.mem.Allocator> fn simpleAsyncFn2(y: *i32) void {...@@ -169,7 +188,9 @@ async<*std.mem.Allocator> fn simpleAsyncFn2(y: *i32) void {
169}188}
170189
171test "async fn with inferred error set" {190test "async fn with inferred error set" {
172 const p = (async<std.debug.global_allocator> failing()) catch unreachable;191 var da = std.heap.DirectAllocator.init();
192 defer da.deinit();
193 const p = (async<&da.allocator> failing()) catch unreachable;
173 resume p;194 resume p;
174 cancel p;195 cancel p;
175}196}
...@@ -181,7 +202,9 @@ async fn failing() !void {...@@ -181,7 +202,9 @@ async fn failing() !void {
181test "error return trace across suspend points - early return" {202test "error return trace across suspend points - early return" {
182 const p = nonFailing();203 const p = nonFailing();
183 resume p;204 resume p;
184 const p2 = try async<std.debug.global_allocator> printTrace(p);205 var da = std.heap.DirectAllocator.init();
206 defer da.deinit();
207 const p2 = try async<&da.allocator> printTrace(p);
185 cancel p2;208 cancel p2;
186}209}
187210