1.. title:: clang-tidy - cppcoreguidelines-avoid-capturing-lambda-coroutines
2
3cppcoreguidelines-avoid-capturing-lambda-coroutines
4===================================================
5
6Flags C++20 coroutine lambdas with non-empty capture lists that may cause
7use-after-free errors and suggests avoiding captures or ensuring the lambda
8closure object has a guaranteed lifetime.
9
10This check implements `CP.51
11<https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#Rcoro-capture>`_
12from the C++ Core Guidelines.
13
14Using coroutine lambdas with non-empty capture lists can be risky, as capturing
15variables can lead to accessing freed memory after the first suspension point.
16This issue can occur even with refcounted smart pointers and copyable types.
17When a lambda expression creates a coroutine, it results in a closure object
18with storage, which is often on the stack and will eventually go out of scope.
19When the closure object goes out of scope, its captures also go out of scope.
20While normal lambdas finish executing before this happens, coroutine lambdas may
21resume from suspension after the closure object has been destructed, resulting
22in use-after-free memory access for all captures.
23
24Consider the following example:
25
26.. code-block:: c++
27
28    int value = get_value();
29    std::shared_ptr<Foo> sharedFoo = get_foo();
30    {
31        const auto lambda = [value, sharedFoo]() -> std::future<void>
32        {
33            co_await something();
34            // "sharedFoo" and "value" have already been destroyed
35            // the "shared" pointer didn't accomplish anything
36        };
37        lambda();
38    } // the lambda closure object has now gone out of scope
39
40In this example, the lambda object is defined with two captures: value and
41``sharedFoo``. When ``lambda()`` is called, the lambda object is created on the
42stack, and the captures are copied into the closure object. When the coroutine
43is suspended, the lambda object goes out of scope, and the closure object is
44destroyed. When the coroutine is resumed, the captured variables may have been
45destroyed, resulting in use-after-free bugs.
46
47In conclusion, the use of coroutine lambdas with non-empty capture lists can
48lead to use-after-free errors when resuming the coroutine after the closure
49object has been destroyed. This check helps prevent such errors by flagging
50C++20 coroutine lambdas with non-empty capture lists and suggesting avoiding
51captures or ensuring the lambda closure object has a guaranteed lifetime.
52
53Following these guidelines can help ensure the safe and reliable use of
54coroutine lambdas in C++ code.
55