When dispatching a fire_and_forget lambda through a DispatcherQueue, we get a use-after-free:
#include<DispatcherQueue.h>
#define__cpp_coroutines// temporary workaround to get coroutines working in C++20 mode
#include<winrt/base.h>
#include<winrt/Windows.Foundation.h>
#include<winrt/Windows.System.h>
#include<chrono>
#include<iostream>
#include<memory>
winrt::Windows::System::DispatcherQueueController CreateDispatcherController()
{
const DispatcherQueueOptions options = {
.dwSize = sizeof(options),
.threadType = DQTYPE_THREAD_CURRENT,
.apartmentType = DQTAT_COM_STA
};
winrt::com_ptr<ABI::Windows::System::IDispatcherQueueController> controller;
winrt::check_hresult(CreateDispatcherQueueController(options, controller.put()));
return { controller.detach(), winrt::take_ownership_from_abi };
}
intmain()
{
auto controller = CreateDispatcherController();
auto veryimportant = std::make_unique<int>(1337);
controller.DispatcherQueue().TryEnqueue([stillveryimportant = std::move(veryimportant)]() -> winrt::fire_and_forget
{
usingnamespacestd::chrono_literals;co_await 1s;
// use-after-free here
std::cout << *stillveryimportant;
});
MSG msg;
while (GetMessage(&msg, nullptr, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}The lambda internal this is freed on the first coroutine suspension point, meaning accessing captured variables after this point will lead to undefined behavior and crashes.
When dispatching a fire_and_forget lambda through a DispatcherQueue, we get a use-after-free:
The lambda internal
thisis freed on the first coroutine suspension point, meaning accessing captured variables after this point will lead to undefined behavior and crashes.