<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">