Skip to content

Comments

Work around MSVC symmetric transfer use-after-free in transform_awaiter#179

Merged
sgerbino merged 1 commit intocppalliance:developfrom
sgerbino:pr/msvc
Feb 23, 2026
Merged

Work around MSVC symmetric transfer use-after-free in transform_awaiter#179
sgerbino merged 1 commit intocppalliance:developfrom
sgerbino:pr/msvc

Conversation

@sgerbino
Copy link
Collaborator

@sgerbino sgerbino commented Feb 23, 2026

MSVC stores the coroutine_handle<> return value from await_suspend on the coroutine frame via hidden __$ReturnUdt$. After await_suspend publishes the coroutine handle to another thread (e.g. via IOCP), that thread can resume/destroy the frame before __resume reads the handle back for the symmetric transfer tail-call, causing a use-after-free.

On MSVC, call the returned handle's resume() on the machine stack instead of returning it for symmetric transfer. For IOCP awaitables that return noop_coroutine(), this is a no-op.

Summary by CodeRabbit

Bug Fixes

  • Improved Microsoft Visual C++ (MSVC) compiler compatibility for coroutine operations to ensure correct suspension and resumption behavior.

MSVC stores the coroutine_handle<> return value from await_suspend on
the coroutine frame via hidden __$ReturnUdt$. After await_suspend
publishes the coroutine handle to another thread (e.g. via IOCP), that
thread can resume/destroy the frame before __resume reads the handle
back for the symmetric transfer tail-call, causing a use-after-free.

On MSVC, call the returned handle's resume() on the machine stack
instead of returning it for symmetric transfer. For IOCP awaitables
that return noop_coroutine(), this is a no-op.
@coderabbitai
Copy link

coderabbitai bot commented Feb 23, 2026

📝 Walkthrough

Walkthrough

Introduces MSVC-specific conditional logic in three header files' transform_awaiter::await_suspend methods to handle return type variations. When await_suspend returns std::coroutine_handle<>, the MSVC implementation calls .resume() directly; otherwise returns the handle unchanged. Non-MSVC behavior is preserved.

Changes

Cohort / File(s) Summary
MSVC-specific await_suspend workarounds
include/boost/capy/task.hpp, include/boost/capy/when_all.hpp, include/boost/capy/when_any.hpp
Adds conditional return type handling in transform_awaiter::await_suspend. On MSVC, when a_.await_suspend() returns std::coroutine_handle<>, immediately calls .resume() on the handle instead of returning it; otherwise returns the handle as-is. Non-MSVC code paths remain unchanged.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • The usual suspects #125: Modifies coroutine suspension/resumption logic and handles return type differences from await_suspend across multiple code paths.
  • Feature: when_any #101: Introduces when_any implementation with transform_awaiter that uses the same await_suspend patterns being patched here.

Poem

🐰 A hop through MSVC's quirky ways,
Where handles resume in curious days,
No return for the brave, just .resume() right,
While others stay put—all compilers align!

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: a MSVC-specific workaround for a symmetric transfer use-after-free issue in transform_awaiter across multiple files.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cppalliance-bot
Copy link

An automated preview of the documentation is available at https://179.capy.prtest3.cppalliance.org/index.html

If more commits are pushed to the pull request, the docs will rebuild at the same URL.

2026-02-23 14:21:02 UTC

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
include/boost/capy/when_any.hpp (1)

356-364: Same missing-comment issue as when_all.hpp.

Add the MSVC __$ReturnUdt$ workaround comment for parity with task.hpp lines 208–213.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@include/boost/capy/when_any.hpp` around lines 356 - 364, Add the same MSVC
__$ReturnUdt$ workaround comment used in task.hpp near the await_suspend
handling: above the conditional around using R = decltype(a_.await_suspend(h,
&p_->env_)); and the specialized resume/return paths, insert the explanatory
comment about MSVC emitting a bogus __$ReturnUdt$ return type and why the
conditional/resume branch is required so readers understand the
platform-specific workaround for a_.await_suspend(h, &p_->env_).
include/boost/capy/when_all.hpp (1)

218-226: Add the explanatory comment from task.hpp for consistency.

task.hpp lines 208–213 include a clear explanation of the MSVC __$ReturnUdt$ use-after-free. This site has the same workaround but no comment. Future maintainers encountering this #ifdef block need to understand why it exists.

Suggested diff
 `#ifdef` _MSC_VER
+                // Workaround: MSVC stores the coroutine_handle<> return
+                // value on the coroutine frame via hidden __$ReturnUdt$.
+                // After await_suspend publishes the handle to another
+                // thread, that thread can resume/destroy the frame before
+                // __resume reads the handle back for the symmetric
+                // transfer tail-call, causing a use-after-free.
                 using R = decltype(a_.await_suspend(h, &p_->env_));
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@include/boost/capy/when_all.hpp` around lines 218 - 226, The MSVC-specific
`#ifdef` block around a_.await_suspend in when_all.hpp lacks the explanatory
comment present in task.hpp about the MSVC __$ReturnUdt$ use-after-free bug; add
the same explanatory comment (or an appropriately adjusted variant) above the
_MSC_VER branch explaining why we call and resume the returned
std::coroutine_handle<> instead of returning it and how this avoids the
__$ReturnUdt$ use-after-free; locate the block around a_.await_suspend(h,
&p_->env_) (references: a_.await_suspend, p_->env_, _MSC_VER) and insert the
comment text from task.hpp lines 208–213 for consistency.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@include/boost/capy/when_all.hpp`:
- Around line 218-226: The MSVC-specific `#ifdef` block around a_.await_suspend in
when_all.hpp lacks the explanatory comment present in task.hpp about the MSVC
__$ReturnUdt$ use-after-free bug; add the same explanatory comment (or an
appropriately adjusted variant) above the _MSC_VER branch explaining why we call
and resume the returned std::coroutine_handle<> instead of returning it and how
this avoids the __$ReturnUdt$ use-after-free; locate the block around
a_.await_suspend(h, &p_->env_) (references: a_.await_suspend, p_->env_,
_MSC_VER) and insert the comment text from task.hpp lines 208–213 for
consistency.

In `@include/boost/capy/when_any.hpp`:
- Around line 356-364: Add the same MSVC __$ReturnUdt$ workaround comment used
in task.hpp near the await_suspend handling: above the conditional around using
R = decltype(a_.await_suspend(h, &p_->env_)); and the specialized resume/return
paths, insert the explanatory comment about MSVC emitting a bogus __$ReturnUdt$
return type and why the conditional/resume branch is required so readers
understand the platform-specific workaround for a_.await_suspend(h, &p_->env_).

ℹ️ Review info

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between eb1de34 and a9476e7.

📒 Files selected for processing (3)
  • include/boost/capy/task.hpp
  • include/boost/capy/when_all.hpp
  • include/boost/capy/when_any.hpp

@cppalliance-bot
Copy link

GCOVR code coverage report https://179.capy.prtest3.cppalliance.org/gcovr/index.html
LCOV code coverage report https://179.capy.prtest3.cppalliance.org/genhtml/index.html
Coverage Diff Report https://179.capy.prtest3.cppalliance.org/diff-report/index.html

Build time: 2026-02-23 14:33:09 UTC

@sgerbino sgerbino merged commit 505e20c into cppalliance:develop Feb 23, 2026
13 of 14 checks passed
@sgerbino sgerbino deleted the pr/msvc branch February 23, 2026 14:52
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.

2 participants