Skip to content

Commit

Permalink
fix coroutine accessing freed memory
Browse files Browse the repository at this point in the history
closes #1164
  • Loading branch information
andrewrk committed Jun 27, 2018
1 parent 19961c5 commit 2fa588e
Show file tree
Hide file tree
Showing 3 changed files with 47 additions and 13 deletions.
2 changes: 1 addition & 1 deletion src/analyze.cpp
Expand Up @@ -5583,7 +5583,7 @@ void render_const_val_ptr(CodeGen *g, Buf *buf, ConstExprValue *const_val, TypeT
return;
}
case ConstPtrSpecialHardCodedAddr:
buf_appendf(buf, "(*%s)(%" ZIG_PRI_x64 ")", buf_ptr(&type_entry->data.pointer.child_type->name),
buf_appendf(buf, "(%s)(%" ZIG_PRI_x64 ")", buf_ptr(&type_entry->name),
const_val->data.x_ptr.data.hard_coded_addr.addr);
return;
case ConstPtrSpecialDiscard:
Expand Down
17 changes: 14 additions & 3 deletions src/ir.cpp
Expand Up @@ -7112,6 +7112,12 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
IrInstruction *dest_err_ret_trace_ptr = ir_build_load_ptr(irb, scope, node, err_ret_trace_ptr_field_ptr);
ir_build_merge_err_ret_traces(irb, scope, node, coro_promise_ptr, err_ret_trace_ptr, dest_err_ret_trace_ptr);
}
// Before we destroy the coroutine frame, we need to load the target promise into
// a register or local variable which does not get spilled into the frame,
// otherwise llvm tries to access memory inside the destroyed frame.
IrInstruction *unwrapped_await_handle_ptr = ir_build_unwrap_maybe(irb, scope, node,
irb->exec->await_handle_var_ptr, false);
IrInstruction *await_handle_in_block = ir_build_load_ptr(irb, scope, node, unwrapped_await_handle_ptr);
ir_build_br(irb, scope, node, check_free_block, const_bool_false);

ir_set_cursor_at_end_and_append_block(irb, irb->exec->coro_final_cleanup_block);
Expand All @@ -7126,6 +7132,14 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
incoming_values[1] = const_bool_true;
IrInstruction *resume_awaiter = ir_build_phi(irb, scope, node, 2, incoming_blocks, incoming_values);

IrBasicBlock **merge_incoming_blocks = allocate<IrBasicBlock *>(2);
IrInstruction **merge_incoming_values = allocate<IrInstruction *>(2);
merge_incoming_blocks[0] = irb->exec->coro_final_cleanup_block;
merge_incoming_values[0] = ir_build_const_undefined(irb, scope, node);
merge_incoming_blocks[1] = irb->exec->coro_normal_final;
merge_incoming_values[1] = await_handle_in_block;
IrInstruction *awaiter_handle = ir_build_phi(irb, scope, node, 2, merge_incoming_blocks, merge_incoming_values);

Buf *free_field_name = buf_create_from_str(ASYNC_FREE_FIELD_NAME);
IrInstruction *implicit_allocator_ptr = ir_build_get_implicit_allocator(irb, scope, node,
ImplicitAllocatorIdLocalVar);
Expand All @@ -7152,9 +7166,6 @@ bool ir_gen(CodeGen *codegen, AstNode *node, Scope *scope, IrExecutable *ir_exec
ir_build_cond_br(irb, scope, node, resume_awaiter, resume_block, irb->exec->coro_suspend_block, const_bool_false);

ir_set_cursor_at_end_and_append_block(irb, resume_block);
IrInstruction *unwrapped_await_handle_ptr = ir_build_unwrap_maybe(irb, scope, node,
irb->exec->await_handle_var_ptr, false);
IrInstruction *awaiter_handle = ir_build_load_ptr(irb, scope, node, unwrapped_await_handle_ptr);
ir_build_coro_resume(irb, scope, node, awaiter_handle);
ir_build_br(irb, scope, node, irb->exec->coro_suspend_block, const_bool_false);
}
Expand Down
41 changes: 32 additions & 9 deletions test/cases/coroutines.zig
Expand Up @@ -5,7 +5,10 @@ const assert = std.debug.assert;
var x: i32 = 1;

test "create a coroutine and cancel it" {
const p = try async<std.debug.global_allocator> simpleAsyncFn();
var da = std.heap.DirectAllocator.init();
defer da.deinit();

const p = try async<&da.allocator> simpleAsyncFn();
comptime assert(@typeOf(p) == promise->void);
cancel p;
assert(x == 2);
Expand All @@ -17,8 +20,11 @@ async fn simpleAsyncFn() void {
}

test "coroutine suspend, resume, cancel" {
var da = std.heap.DirectAllocator.init();
defer da.deinit();

seq('a');
const p = try async<std.debug.global_allocator> testAsyncSeq();
const p = try async<&da.allocator> testAsyncSeq();
seq('c');
resume p;
seq('f');
Expand All @@ -43,7 +49,10 @@ fn seq(c: u8) void {
}

test "coroutine suspend with block" {
const p = try async<std.debug.global_allocator> testSuspendBlock();
var da = std.heap.DirectAllocator.init();
defer da.deinit();

const p = try async<&da.allocator> testSuspendBlock();
std.debug.assert(!result);
resume a_promise;
std.debug.assert(result);
Expand All @@ -64,8 +73,11 @@ var await_a_promise: promise = undefined;
var await_final_result: i32 = 0;

test "coroutine await" {
var da = std.heap.DirectAllocator.init();
defer da.deinit();

await_seq('a');
const p = async<std.debug.global_allocator> await_amain() catch unreachable;
const p = async<&da.allocator> await_amain() catch unreachable;
await_seq('f');
resume await_a_promise;
await_seq('i');
Expand Down Expand Up @@ -100,8 +112,11 @@ fn await_seq(c: u8) void {
var early_final_result: i32 = 0;

test "coroutine await early return" {
var da = std.heap.DirectAllocator.init();
defer da.deinit();

early_seq('a');
const p = async<std.debug.global_allocator> early_amain() catch unreachable;
const p = async<&da.allocator> early_amain() catch unreachable;
early_seq('f');
assert(early_final_result == 1234);
assert(std.mem.eql(u8, early_points, "abcdef"));
Expand Down Expand Up @@ -146,7 +161,9 @@ test "async function with dot syntax" {
suspend;
}
};
const p = try async<std.debug.global_allocator> S.foo();
var da = std.heap.DirectAllocator.init();
defer da.deinit();
const p = try async<&da.allocator> S.foo();
cancel p;
assert(S.y == 2);
}
Expand All @@ -157,7 +174,9 @@ test "async fn pointer in a struct field" {
bar: async<*std.mem.Allocator> fn (*i32) void,
};
var foo = Foo{ .bar = simpleAsyncFn2 };
const p = (async<std.debug.global_allocator> foo.bar(&data)) catch unreachable;
var da = std.heap.DirectAllocator.init();
defer da.deinit();
const p = (async<&da.allocator> foo.bar(&data)) catch unreachable;
assert(data == 2);
cancel p;
assert(data == 4);
Expand All @@ -169,7 +188,9 @@ async<*std.mem.Allocator> fn simpleAsyncFn2(y: *i32) void {
}

test "async fn with inferred error set" {
const p = (async<std.debug.global_allocator> failing()) catch unreachable;
var da = std.heap.DirectAllocator.init();
defer da.deinit();
const p = (async<&da.allocator> failing()) catch unreachable;
resume p;
cancel p;
}
Expand All @@ -181,7 +202,9 @@ async fn failing() !void {
test "error return trace across suspend points - early return" {
const p = nonFailing();
resume p;
const p2 = try async<std.debug.global_allocator> printTrace(p);
var da = std.heap.DirectAllocator.init();
defer da.deinit();
const p2 = try async<&da.allocator> printTrace(p);
cancel p2;
}

Expand Down

0 comments on commit 2fa588e

Please sign in to comment.