When test-web has completed running all tests there is a pending DeferredInvoke in the main threads event queue. For Unix pthread, the main threads event queue is leaked as the destructor callback in pthread_key_create is not invoked. For Windows pthreads4w, the destructor callback is invoked when the main thread is exiting. When the main threads event queue is destroyed, the pending DeferredInvoke event is destroyed which causes a leftover WebContentClient reference to also get destroyed; however, the static WebContentClient::s_clients HashTable has already been destroyed at this point, so we get a UAF in the WebContentClient destructor and ASAN reports that error. The reason why cleaning up the pending deferred invoke results in a WebContentClient instance also being cleaned up is that class inherits from IPC::ConnectionBase which is a Core::EventReceiver. The deferred_invoke() method exposed on event receivers takes a strong reference to itself to ensure it is still alive by the time the event loop is ready to execute the function. There are a couple places in IPC::ConnectionBase::drain_messages_from_peer() that utilized deferred invocation which is why we have a leftover WebContentClient that has past its useful lifetime at the end of TestWeb::run_tests(). Instead of holding onto a strong reference when the event has not yet been processed, we take a weak reference and only grab a strong ref if the receiver is alive when the event loop is about to execute our function.
77 lines
1.6 KiB
C++
77 lines
1.6 KiB
C++
/*
|
|
* Copyright (c) 2018-2021, Andreas Kling <andreas@ladybird.org>
|
|
* Copyright (c) 2022, the SerenityOS developers.
|
|
*
|
|
* SPDX-License-Identifier: BSD-2-Clause
|
|
*/
|
|
|
|
#include <AK/Assertions.h>
|
|
#include <AK/Badge.h>
|
|
#include <AK/JsonObject.h>
|
|
#include <AK/WeakPtr.h>
|
|
#include <LibCore/Event.h>
|
|
#include <LibCore/EventLoop.h>
|
|
#include <LibCore/EventReceiver.h>
|
|
|
|
namespace Core {
|
|
|
|
EventReceiver::EventReceiver() = default;
|
|
|
|
EventReceiver::~EventReceiver()
|
|
{
|
|
stop_timer();
|
|
}
|
|
|
|
void EventReceiver::event(Core::Event& event)
|
|
{
|
|
switch (event.type()) {
|
|
case Core::Event::Timer:
|
|
if (!m_timer_id)
|
|
break; // Too late, the timer was already stopped.
|
|
return timer_event(static_cast<TimerEvent&>(event));
|
|
case Core::Event::Invalid:
|
|
VERIFY_NOT_REACHED();
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void EventReceiver::timer_event(Core::TimerEvent&)
|
|
{
|
|
}
|
|
|
|
void EventReceiver::start_timer(int ms)
|
|
{
|
|
if (m_timer_id) {
|
|
dbgln("{} {:p} already has a timer!", class_name(), this);
|
|
VERIFY_NOT_REACHED();
|
|
}
|
|
|
|
m_timer_id = Core::EventLoop::register_timer(*this, ms, true);
|
|
}
|
|
|
|
void EventReceiver::stop_timer()
|
|
{
|
|
if (!m_timer_id)
|
|
return;
|
|
Core::EventLoop::unregister_timer(m_timer_id);
|
|
m_timer_id = 0;
|
|
}
|
|
|
|
void EventReceiver::deferred_invoke(Function<void()> invokee)
|
|
{
|
|
Core::deferred_invoke([invokee = move(invokee), weak_this = make_weak_ptr()] {
|
|
auto strong_this = weak_this.strong_ref();
|
|
if (!strong_this)
|
|
return;
|
|
invokee();
|
|
});
|
|
}
|
|
|
|
void EventReceiver::dispatch_event(Core::Event& e)
|
|
{
|
|
event(e);
|
|
}
|
|
|
|
}
|