<table border="1" cellspacing="0" cellpadding="8">
    <tr>
        <th>Issue</th>
        <td>
            <a href=https://github.com/llvm/llvm-project/issues/127499>127499</a>
        </td>
    </tr>

    <tr>
        <th>Summary</th>
        <td>
            [coro] Use-after-free of callee-destructed parameter in coroutine which doesn't suspend
        </td>
    </tr>

    <tr>
      <th>Labels</th>
      <td>
            coroutines
      </td>
    </tr>

    <tr>
      <th>Assignees</th>
      <td>
      </td>
    </tr>

    <tr>
      <th>Reporter</th>
      <td>
          zmodem
      </td>
    </tr>
</table>

<pre>
    Consider:

```
$ cat /tmp/a.cc
#include <coroutine>
#include <stdio.h>

class BasicCoroutine { // Borrowed from https://theshoemaker.de/posts/yet-another-cpp-coroutine-tutorial
public:
 struct Promise {
    BasicCoroutine get_return_object() { return BasicCoroutine {}; }
    void unhandled_exception() noexcept { }
    void return_void() noexcept { }
    std::suspend_never initial_suspend() noexcept { return {}; }
    std::suspend_never final_suspend() noexcept { return {}; }
  };
  using promise_type = Promise;
};

struct [[clang::trivial_abi]] Trivial {
  Trivial(int x) : x(x) {}
  ~Trivial() { printf("~Trivial @ %p: %d\n", this, x); }
  int x;
};

BasicCoroutine coro(Trivial t) {
 co_return;
}

int main() {
  Trivial t(42);
  coro(t);
}

$ build/bin/clang++ -std=c++20 /tmp/a.cc -fsanitize=address && ASAN_OPTIONS=external_symbolizer_path=$PWD/build/bin/llvm-symbolizer ./a.out
~Trivial @ 0x7c00cfc20058: 42
=================================================================
==3352321==ERROR: AddressSanitizer: heap-use-after-free on address 0x7c00cfc20054 at pc 0x56082ebe8441 bp 0x7ffdcd77b600 sp 0x7ffdcd77b5f8
READ of size 4 at 0x7c00cfc20054 thread T0
    #0 0x56082ebe8440 in Trivial::~Trivial() (/work/llvm-project/a.out+0x114440)
    #1 0x56082ebe7843 in coro(Trivial) (/work/llvm-project/a.out+0x113843)
    #2 0x56082ebe7c8b in main (/work/llvm-project/a.out+0x113c8b)
    #3 0x7fd0d0a40c89 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
 #4 0x7fd0d0a40d44 in __libc_start_main csu/../csu/libc-start.c:360:3
    #5 0x56082eb01380 in _start (/work/llvm-project/a.out+0x2d380)

0x7c00cfc20054 is located 20 bytes inside of 32-byte region [0x7c00cfc20040,0x7c00cfc20060)
freed by thread T0 here:
    #0 0x56082ebe6c52 in operator delete(void*, unsigned long) /work/llvm-project/compiler-rt/lib/asan/asan_new_delete.cpp:155:3
    #1 0x56082ebe7829 in coro(Trivial) (/work/llvm-project/a.out+0x113829)
    #2 0x56082ebe7c8b in main (/work/llvm-project/a.out+0x113c8b)
    #3 0x7fd0d0a40c89 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16

previously allocated by thread T0 here:
    #0 0x56082ebe5fed in operator new(unsigned long) /work/llvm-project/compiler-rt/lib/asan/asan_new_delete.cpp:86:3
    #1 0x56082ebe74fa in coro(Trivial) (/work/llvm-project/a.out+0x1134fa)
    #2 0x56082ebe7c8b in main (/work/llvm-project/a.out+0x113c8b)
    #3 0x7fd0d0a40c89 in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16

SUMMARY: AddressSanitizer: heap-use-after-free (/work/llvm-project/a.out+0x114440) in Trivial::~Trivial()
Shadow bytes around the buggy address:
 0x7c00cfc1fd80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 0x7c00cfc1fe00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 0x7c00cfc1fe80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 0x7c00cfc1ff00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
 0x7c00cfc1ff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x7c00cfc20000: fa fa 00 00 00 fa fa fa fd fd[fd]fd fa fa fa fa
 0x7c00cfc20080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
 0x7c00cfc20100: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
 0x7c00cfc20180: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
 0x7c00cfc20200: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
 0x7c00cfc20280: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
Shadow byte legend (one shadow byte represents 8 application bytes):
  Addressable: 00
  Partially addressable: 01 02 03 04 05 06 07 
  Heap left redzone: fa
  Freed heap region:       fd
  Stack left redzone:      f1
 Stack mid redzone:       f2
  Stack right redzone:     f3
  Stack after return:      f5
  Stack use after scope:   f8
  Global redzone: f9
  Global init order:       f6
  Poisoned by user:        f7
  Container overflow:      fc
  Array cookie:            ac
  Intra object redzone: bb
  ASan internal:           fe
  Left alloca redzone:     ca
  Right alloca redzone:    cb
==3352321==ABORTING
```

Note the `[[clang::trivial_abi]]` attribute, which means the `t` parameter should be destructed at the end of `coro`. The same problem applies without the attribute when targeting Windows with the MSVC ABI.

Clang emits a move-constructed copy of `t` to go in the coro frame, and a call to destruct the original `t` at the end of the ramp-up. However, it also puts the original `t` in the frame, and since `coro` doesn't suspend, the frame is deleted before reaching the destructor call, which ends up dereferencing a pointer into the deleted coro frame.
</pre>
<img width="1" height="1" alt="" src="http://email.email.llvm.org/o/eJzcWE1z27bW_jXw5ow0EEhK1MILyYrbzLxNMnb6du5KAwKHIm4ogAOAlpVFf_sdANSn3TZJfRe3Ho5FAgfP-cY5AHdObTTiLSmWpFjd8N43xt5-3RqJ25vKyP3tndFOSbQkWxAanykdHrogLAfBPRB277cdYfd8LEQcz5QWbS8RSHYnjDW9VxpJ9u7FpPNSmXEzTNGFaLlzsOROibvDOiCzZeBB2D0sjbVmhxJqa7bQeN-5IFqc9A26xuCWf0E7lkjYfWecd4Td79GPuDa-QTsSXTc6ijTyvTdW8ZbQRddXrRJJUXDe9sLDJ2u2ykUJwijAtWgb9GuLvrd6bap_o_CElYTNo8hp_BVlyGxFsiWEnwT6ZJSEXjdcyxblGp8Fdl4ZPYBpk0aSIS5XDczD-59TOy-DbtnC9a5DLdcan9CC0sor3q6H0dcwBj1eE_wPQGulfxAyfcbX3im9gS55YO33XQiY1cEliepITuhicFmM5aVoud4k0bxVT0FDXilSrEixgs9p5OjV4ZuwUmkPz9F_2SK8lM-DMw_y_X6iPbi5s0r7Og6w34_QOQXCii7gEFZIUtxpwhhhd-Ab5cJvgD5XPfF-odZV-ITQJaw88PEH-egChBlC8QQSEQLwlit9kvlc6wBR5iwJEycGFv44dEQKCV_1qpWE3VcB8D7ZmS0JW8IoRsNKpE9GL_cFGNWOh2D7iiRbcSktOgeETQmbwuJx8WH98dPn9x8_PJJshc8ebQyh_bYyrfqKdt1x35BsRVj-6bdV4H8hR9s-bUcnahhHrqb3hC4unEKfZ4JSUQtGaVEG9-QsaJat_uefgxZZVrCMTdLHu4eHjw9BzUWy-OPgg7CjQ4O8G_UOR7z2aEe1RQSj4eCcC1vlwD10AuhzMaUlwwrLPJ9A1QWyupZCzmbVlFJwFyNFXRK6eHi3WIGpwamvCBHpCts3FrmEz3TYWAjL6CUrCkofMzUm9nUuhp_7nbFfDvHQWZO25CEU2JI-TyZ5ntMQ2kdGkzNGszLPAqPLNPse-KzMswt4dg4vyirAh3T8dkRRVheIWTSwpJLynIpyHhDX61ZVYu08t34teNuuIw_hesLuxyEd3N5J7EI51J1vA8tXFoRSvIh5MZkGhoRl-Tk3mecvuF0zSq-BYBQJxqGoZlMa_p-UKE5moZOsjO5NgN9oGCazcnAkoYureFIOWiO4RwmMQrX36EDFViaEYcZGYQgsbpTRoWScLw_hcXc-MB3YhPyQUO1P0QoNWhxahpdROxUFC2qZDi33xoLEFj0SVqZyvQhloNexCZPQmrCThkD7A9WF2XaqRTuyPtk3mMNxPfysNe7WicNYdKHyTIri0uaXkc7mfzfS2fwfE-mhAbT4pEzv2j3w9hA93-7tokZ54W2NO8LK_6J_y-mfuDev-d90b17zf5J7H3_95ZfFw7--pxZ-Z0n5iwoVZGi4NLthP-LW9FqCbxCqfrPZH-ruEGHHHWhSyzJsnkDpdz1XIEjfAuQtJKnfQpL6RyWJfdG78_09iVPz8Bxp02d4JNSSFMvwbxU-jhP8QiRGaXkG9M3PFciEvgXIW0jC3kIS9qOSnKUKtLhBLUM6Go3gzmYsdhYdau-gBN51rRI8nJxTisUTTNquh5TnVYspbOLoJ27D6bc95t5xfgKUAc2A5kALoFOgM4hLfkbeQYu1B4vyq9GY1Itz97FBCDvJ0FqEufRXy0jx6Ln48mJ5opgEikSwjSf7y3mo2RmEVZvmBUadnVHEfQwOJ8IDk-KMonc4UDlhugEl9usAP7Wm4u2FjvPzCaWVB2PT1dBBwGkyqlHO6FQ7e3dOAPUsUtwZ7bnSaME8oa1bszsJKJK7rOV7EMZ8UWcWiH88UbzX3nJIly7nYlZVAnjkOhyp4znyEqHGSPF_wQmp0F_bUSR3PkQbv0oiqtePW4vlx4fP7z_8dH1TRhcfjMe414eRv7yjIFMK3Hurqj40jHewa5RoYItcuwOKD0Qdt3yL0YeN6VsJFYLEdBeCMpy0AnXIHlOHRbEfmNIxfG4QHN8idNZULW5T9qCDnfKN6dO6owiwa1CD53aDXukN_Ka0NLtEHCl_efz_O1gs34-TtndBN8Ct8g44bM0TjoTRR7GE6faDQFELb2BjQvkMUEFEqINaQXGuJXAIdT1QHVSLhMaqjdLhYD_AXGobXi3fdqO-G8PPZodPaAOiCj51Brreu9dxBkEuZHBKCzyzIEiDThM283C86Lo7LQsnkNSsBZfUxoa9iosmGC8QHRQxNup2cjFq6aDvQKLFGi1qEZZw6EyM5hDTZkBI6CdzjW_kbSbn2Zzf4O1kls1LmuWT4qa5Lctqzud5wZlAISXyaSmYnNJiXoqS0umNumWUFZRNZpM8z7JiTOfllFMs5zmntBZzklPcctWOQxs0NnZzo5zr8XbCZvl8ftPyClsXL5IZO16vOsIYKVY39jY2T1W_cSSnrXLenXC88m28go6GLVbw69WFRB0thDg6i-tT2A89brodSyZ84Zmb3ra3l5fFG-WbvhoLsx1au5cdXlQwtJmDjk-37D8BAAD__xOS6Sc">