From 78c9e2080c800e9da0792b73ca436ef49384bfb7 Mon Sep 17 00:00:00 2001 From: vaxerski Date: Sat, 16 Aug 2025 16:52:28 +0200 Subject: [PATCH] framescheduler: fix edge case crashes rare UAFs because renderMonitor can call onDisconnect (ugh, that should be changed...) fixes #11073 --- src/helpers/Monitor.cpp | 8 ++++++-- src/helpers/MonitorFrameScheduler.cpp | 22 ++++++++++++++++++++-- src/helpers/MonitorFrameScheduler.hpp | 20 ++++++++++++-------- 3 files changed, 38 insertions(+), 12 deletions(-) diff --git a/src/helpers/Monitor.cpp b/src/helpers/Monitor.cpp index 37f650a1..fa0965b8 100644 --- a/src/helpers/Monitor.cpp +++ b/src/helpers/Monitor.cpp @@ -63,7 +63,10 @@ void CMonitor::onConnect(bool noRule) { g_pEventLoopManager->doLater([] { g_pConfigManager->ensurePersistentWorkspacesPresent(); }); - m_listeners.frame = m_output->events.frame.listen([this] { m_frameScheduler->onFrame(); }); + m_listeners.frame = m_output->events.frame.listen([this] { + if (m_frameScheduler) + m_frameScheduler->onFrame(); + }); m_listeners.commit = m_output->events.commit.listen([this] { if (true) { // FIXME: E->state->committed & WLR_OUTPUT_STATE_BUFFER PROTO::screencopy->onOutputCommit(m_self.lock()); @@ -127,7 +130,8 @@ void CMonitor::onConnect(bool noRule) { applyMonitorRule(&rule); }); - m_frameScheduler = makeUnique(m_self.lock()); + m_frameScheduler = makeUnique(m_self.lock()); + m_frameScheduler->m_self = WP(m_frameScheduler); m_tearingState.canTear = m_output->getBackend()->type() == Aquamarine::AQ_BACKEND_DRM; diff --git a/src/helpers/MonitorFrameScheduler.cpp b/src/helpers/MonitorFrameScheduler.cpp index 1ab4f19b..877e486f 100644 --- a/src/helpers/MonitorFrameScheduler.cpp +++ b/src/helpers/MonitorFrameScheduler.cpp @@ -36,7 +36,16 @@ void CMonitorFrameScheduler::onSyncFired() { m_renderAtFrame = false; // block frame rendering, we already scheduled m_lastRenderBegun = hrc::now(); + + // get a ref to ourselves. renderMonitor can destroy this scheduler if it decides to perform a monitor reload + // FIXME: this is horrible. "renderMonitor" should not be able to do that. + auto self = m_self; + g_pHyprRenderer->renderMonitor(m_monitor.lock(), false); + + if (!self) + return; + onFinishRender(); } @@ -99,14 +108,23 @@ void CMonitorFrameScheduler::onFrame() { Debug::log(TRACE, "CMonitorFrameScheduler: {} -> frame event, render = true, rendering normally.", m_monitor->m_name); m_lastRenderBegun = hrc::now(); + + // get a ref to ourselves. renderMonitor can destroy this scheduler if it decides to perform a monitor reload + // FIXME: this is horrible. "renderMonitor" should not be able to do that. + auto self = m_self; + g_pHyprRenderer->renderMonitor(m_monitor.lock()); + + if (!self) + return; + onFinishRender(); } void CMonitorFrameScheduler::onFinishRender() { m_sync = CEGLSync::create(); // this destroys the old sync - g_pEventLoopManager->doOnReadable(m_sync->fd().duplicate(), [this, mon = m_monitor] { - if (!mon) // might've gotten destroyed + g_pEventLoopManager->doOnReadable(m_sync->fd().duplicate(), [this, self = m_self] { + if (!self) // might've gotten destroyed return; onSyncFired(); }); diff --git a/src/helpers/MonitorFrameScheduler.hpp b/src/helpers/MonitorFrameScheduler.hpp index e6eab9c9..c9b45a64 100644 --- a/src/helpers/MonitorFrameScheduler.hpp +++ b/src/helpers/MonitorFrameScheduler.hpp @@ -22,15 +22,19 @@ class CMonitorFrameScheduler { void onFrame(); private: - bool canRender(); - void onFinishRender(); - bool newSchedulingEnabled(); + bool canRender(); + void onFinishRender(); + bool newSchedulingEnabled(); - bool m_renderAtFrame = true; - bool m_pendingThird = false; - hrc::time_point m_lastRenderBegun; + bool m_renderAtFrame = true; + bool m_pendingThird = false; + hrc::time_point m_lastRenderBegun; - PHLMONITORREF m_monitor; + PHLMONITORREF m_monitor; - UP m_sync; + UP m_sync; + + WP m_self; + + friend class CMonitor; };