Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

coroutine: fix a use-after-free in maybe_yield #1760

Merged
merged 1 commit into from
Jul 27, 2023

Conversation

michoecho
Copy link
Contributor

maybe_yield_awaiter resumes the coroutine (after it decided to suspend it) by scheduling itself as a task, which -- when executed -- resumes the coroutine.

The awaiter itself is (usually) allocated inside the coroutine frame, and dies after its containing co_await expression is resumed. This is a problem, because the reactor expects tasks to stay alive while they are running. To facilitate current_tasktrace(), a global pointer to the currently running task is needed. Thus, when the reactor starts executing a task, it saves a pointer to it in reactor::_current_task.

Since the awaiter task dies immediately after returning control to its parent, reactor::_current_task is left pointing to some garbage in the coroutine frame and current_tasktrace() will (most likely) segfault when used before the next task switch.

To fix this, we directly schedule the parent coroutine. Making maybe_yield_awaiter a task was an unnecessary indirection anyway.

Refs #1599. It's the same kind of bug.

Fixes #1759

maybe_yield_awaiter resumes the coroutine (after it decided to suspend it)
by scheduling *itself* as a task, which -- when executed -- resumes the
coroutine.

The awaiter itself is (usually) allocated inside the coroutine frame, and
dies after its containing co_await expression is resumed.
This is a problem, because the reactor expects tasks to stay alive while
they are running. To facilitate current_tasktrace(), a global pointer to
the currently running task is needed. Thus, when the reactor starts
executing a task, it saves a pointer to it in reactor::_current_task.

Since the awaiter task dies immediately after returning control to its
parent, reactor::_current_task is left pointing to some garbage in the
coroutine frame and current_tasktrace() will (most likely) segfault when
used before the next task switch.

To fix this, we directly schedule the parent coroutine.
Making maybe_yield_awaiter a task was an unnecessary indirection anyway.

Refs scylladb#1599. It's the same kind of bug.

Fixes scylladb#1759
@michoecho michoecho requested a review from tchaikov July 27, 2023 14:46
Copy link
Contributor

@tchaikov tchaikov left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks great to me!

@tchaikov
Copy link
Contributor

tchaikov commented Jul 27, 2023

@scylladb/seastar-maint hello maintainers, could you help merge this change? so we can unblock scylladb/scylladb#13730 .

@avikivity avikivity merged commit 7fe5bdd into scylladb:master Jul 27, 2023
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

heap-buffer-overflow when using maybe_yield_awaiter
3 participants