Black Hat USA 2025 | Breaking Control Flow Integrity by Abusing Modern C++
Why It Matters
Because coroutines are rapidly adopted in modern C++ applications, the ability to bypass CFI threatens the core guarantees of memory‑safe execution, forcing vendors and developers to redesign protection mechanisms.
Key Takeaways
- •C++ coroutines introduce heap‑allocated frames with indirect calls.
- •These frames create new indirect‑branch targets exploitable against CFI.
- •Existing CFI schemes (IBT, CFG, fine‑grain) rely on static call sites.
- •Attackers can hijack coroutine resume pointers to bypass control flow checks.
- •Proposed mitigations include sealing coroutine frames and validating resume indices.
Summary
The Black Hat talk explains how modern C++ coroutines undermine traditional control‑flow‑integrity (CFI) defenses. While CFI has become standard in operating systems and compilers, the presenter shows that coroutine‑generated frames and indirect resume calls open a novel attack surface.
CFI works by building a static control‑flow graph and instrumenting indirect calls—using mechanisms such as Intel CET’s shadow stack and indirect‑branch‑tracking, Microsoft’s Control‑Flow Guard, or fine‑grain type‑based checks. Coroutines, however, are compiled into three hidden functions (creation, resume, destroy) and a heap‑allocated frame that stores a resume pointer, a destroy pointer, a promise object and a suspension index. Because the resume pointer is an indirect call, it falls outside the static graph that CFI expects.
The speaker demonstrates that by corrupting the coroutine frame—e.g., overwriting the resume pointer or the suspension index—an attacker can redirect execution to arbitrary code while the CFI checks still succeed. He cites concrete examples such as co_await’s await_suspend routine, which can be manipulated to invoke malicious code, and shows that even coarse‑grain schemes like Intel CET cannot detect the misuse.
These findings imply that any software relying on CFI must reconsider the security of coroutine‑heavy codebases, especially in high‑performance servers and libraries adopting C++20 features. Mitigations may include sealing coroutine frames, validating resume indices at runtime, or extending CFI to cover heap‑allocated indirect calls.
Comments
Want to join the conversation?
Loading comments...