memory: do not release pointers after emitting a signal

A signal called is allowed to free ourselves, in which case we're not allowed to use this anymore. Only perform the housekeeping of removing stale events before emit, and in registerListener.
This commit is contained in:
Vaxry 2024-07-08 23:06:28 +02:00
parent a8c3a13570
commit c342d5ca44

View file

@ -9,14 +9,10 @@ using namespace Hyprutils::Memory;
#define WP CWeakPointer #define WP CWeakPointer
void Hyprutils::Signal::CSignal::emit(std::any data) { void Hyprutils::Signal::CSignal::emit(std::any data) {
bool dirty = false;
std::vector<SP<CSignalListener>> listeners; std::vector<SP<CSignalListener>> listeners;
for (auto& l : m_vListeners) { for (auto& l : m_vListeners) {
if (l.expired()) { if (l.expired())
dirty = true;
continue; continue;
}
listeners.emplace_back(l.lock()); listeners.emplace_back(l.lock());
} }
@ -29,10 +25,9 @@ void Hyprutils::Signal::CSignal::emit(std::any data) {
for (auto& l : listeners) { for (auto& l : listeners) {
// if there is only one lock, it means the event is only held by the listeners // if there is only one lock, it means the event is only held by the listeners
// vector and was removed during our iteration // vector and was removed during our iteration
if (l.strongRef() == 1) { if (l.strongRef() == 1)
dirty = true;
continue; continue;
}
l->emit(data); l->emit(data);
} }
@ -43,13 +38,17 @@ void Hyprutils::Signal::CSignal::emit(std::any data) {
// release SPs // release SPs
listeners.clear(); listeners.clear();
if (dirty) // we cannot release any expired refs here as one of the listeners could've removed this object and
std::erase_if(m_vListeners, [](const auto& other) { return other.expired(); }); // as such we'd be doing a UAF
} }
CHyprSignalListener Hyprutils::Signal::CSignal::registerListener(std::function<void(std::any)> handler) { CHyprSignalListener Hyprutils::Signal::CSignal::registerListener(std::function<void(std::any)> handler) {
CHyprSignalListener listener = makeShared<CSignalListener>(handler); CHyprSignalListener listener = makeShared<CSignalListener>(handler);
m_vListeners.emplace_back(listener); m_vListeners.emplace_back(listener);
// housekeeping: remove any stale listeners
std::erase_if(m_vListeners, [](const auto& other) { return other.expired(); });
return listener; return listener;
} }