Skip to content

Commit 2fa588e

Browse files
committedJun 27, 2018
fix coroutine accessing freed memory
closes #1164
·
0.15.20.3.0
1 parent 19961c5 commit 2fa588e

File tree

3 files changed

+47
-13
lines changed

3 files changed

+47
-13
lines changed
 

‎src/analyze.cpp‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5583,7 +5583,7 @@ void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, TypeT
55835583
return;
55845584
}
55855585
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),
55875587
const_val->data.x_ptr.data.hard_coded_addr.addr);
55885588
return;
55895589
case ConstPtrSpecialDiscard:

‎src/ir.cpp‎

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7112,6 +7112,12 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
71127112
IrInstruction *dest_err_ret_trace_ptr = ir_build_load_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr);
71137113
ir_build_merge_err_ret_traces(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr, dest_err_ret_trace_ptr);
71147114
}
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);
71157121
ir_build_br(irb, scope, node, check_free_block, const_bool_false);
71167122

71177123
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
71267132
incoming_values[1] = const_bool_true;
71277133
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+
71297143
Buf *free_field_name = buf_create_from_str(ASYNC_FREE_FIELD_NAME);
71307144
IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, scope, node,
71317145
ImplicitAllocatorIdLocalVar);
@@ -7152,9 +7166,6 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
71527166
ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, irb->exec->coro_suspend_block, const_bool_false);
71537167

71547168
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);
71587169
ir_build_coro_resume(irb, scope, node, awaiter_handle);
71597170
ir_build_br(irb, scope, node, irb->exec->coro_suspend_block, const_bool_false);
71607171
}

‎test/cases/coroutines.zig‎

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ const assert = std.debug.assert;
55
var x: i32 = 1;
66

77
test "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();
912
comptime assert(@typeOf(p) == promise->void);
1013
cancel p;
1114
assert(x == 2);
@@ -17,8 +20,11 @@ async fn simpleAsyncFn() void {
1720
}
1821

1922
test "coroutine suspend, resume, cancel" {
23+
var da = std.heap.DirectAllocator.init();
24+
defer da.deinit();
25+
2026
seq('a');
21-
const p = try async<std.debug.global_allocator> testAsyncSeq();
27+
const p = try async<&da.allocator> testAsyncSeq();
2228
seq('c');
2329
resume p;
2430
seq('f');
@@ -43,7 +49,10 @@ fn seq(c: u8) void {
4349
}
4450

4551
test "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();
4756
std.debug.assert(!result);
4857
resume a_promise;
4958
std.debug.assert(result);
@@ -64,8 +73,11 @@ var await_a_promise: promise = undefined;
6473
var await_final_result: i32 = 0;
6574

6675
test "coroutine await" {
76+
var da = std.heap.DirectAllocator.init();
77+
defer da.deinit();
78+
6779
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;
6981
await_seq('f');
7082
resume await_a_promise;
7183
await_seq('i');
@@ -100,8 +112,11 @@ fn await_seq(c: u8) void {
100112
var early_final_result: i32 = 0;
101113

102114
test "coroutine await early return" {
115+
var da = std.heap.DirectAllocator.init();
116+
defer da.deinit();
117+
103118
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;
105120
early_seq('f');
106121
assert(early_final_result == 1234);
107122
assert(std.mem.eql(u8, early_points, "abcdef"));
@@ -146,7 +161,9 @@ test "async function with dot syntax" {
146161
suspend;
147162
}
148163
};
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();
150167
cancel p;
151168
assert(S.y == 2);
152169
}
@@ -157,7 +174,9 @@ test "async fn pointer in a struct field" {
157174
bar: async<*std.mem.Allocator> fn (*i32) void,
158175
};
159176
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;
161180
assert(data == 2);
162181
cancel p;
163182
assert(data == 4);
@@ -169,7 +188,9 @@ async<*std.mem.Allocator> fn simpleAsyncFn2(y: *i32) void {
169188
}
170189

171190
test "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;
173194
resume p;
174195
cancel p;
175196
}
@@ -181,7 +202,9 @@ async fn failing() !void {
181202
test "error return trace across suspend points - early return" {
182203
const p = nonFailing();
183204
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);
185208
cancel p2;
186209
}
187210

0 commit comments

Comments
 (0)
Please sign in to comment.