Subject: [PATCH 09/24] wip: prevent page-flip token reuse

From: Justin Youens <[email-masked]>
Date: Sat, 15 Aug 2026 09:09:39 -0500

---
 src/backend/drm/DRM.cpp         | 33 ++++++++++++++++++++++++++++++---
 src/backend/drm/impl/Atomic.cpp |  4 ++++
 src/backend/drm/impl/Legacy.cpp |  4 ++++
 3 files changed, 38 insertions(+), 3 deletions(-)

diff --git a/src/backend/drm/DRM.cpp b/src/backend/drm/DRM.cpp
index 3d0776703ca3ac96271c19025c78383b6109ed6d..67fad21ba461a31f7a9dcbcd434ff33345980292 100644
--- a/src/backend/drm/DRM.cpp
+++ b/src/backend/drm/DRM.cpp
@@ -5,6 +5,7 @@
 #include <aquamarine/backend/drm/Atomic.hpp>
 #include <aquamarine/allocator/GBM.hpp>
 #include <aquamarine/allocator/DRMDumb.hpp>
+#include <atomic>
 #include <cstdint>
 #include <format>
 #include <hyprutils/math/Mat3x3.hpp>
@@ -62,6 +63,16 @@ namespace {
     using Aquamarine::NDRM::NTiledScan::SConnectorView;
     using Aquamarine::NDRM::NTiledScan::eScanCompleteness;
 
+    constexpr uintptr_t nextPageFlipTokenAfter(uintptr_t previous) noexcept {
+        return previous == std::numeric_limits<uintptr_t>::max() ? 0 : previous + 1;
+    }
+
+    static_assert(nextPageFlipTokenAfter(0) == 1);
+    static_assert(nextPageFlipTokenAfter(std::numeric_limits<uintptr_t>::max() - 1) == std::numeric_limits<uintptr_t>::max());
+    static_assert(nextPageFlipTokenAfter(std::numeric_limits<uintptr_t>::max()) == 0);
+
+    std::atomic<uintptr_t>   LAST_PAGE_FLIP_TOKEN = 0;
+
     thread_local const void* LAST_LEGACY_SCAN_KEY      = nullptr;
     thread_local bool        LAST_LEGACY_SCAN_COMPLETE = false;
 
@@ -1728,7 +1739,19 @@ eBackendGPUDriver Aquamarine::CDRMBackend::gpuDriver() {
 }
 
 uintptr_t Aquamarine::CDRMBackend::nextPageFlipID() {
-    return ++m_lastPageFlipID;
+    auto previous = LAST_PAGE_FLIP_TOKEN.load(std::memory_order_relaxed);
+    while (true) {
+        const auto NEXT = nextPageFlipTokenAfter(previous);
+        if (!NEXT)
+            return 0;
+        if (!LAST_PAGE_FLIP_TOKEN.compare_exchange_weak(previous, NEXT, std::memory_order_relaxed, std::memory_order_relaxed))
+            continue;
+
+        // Preserve the installed class layout and keep its existing field as a
+        // per-backend mirror; allocation authority is process-global above.
+        m_lastPageFlipID = NEXT;
+        return NEXT;
+    }
 }
 
 SP<SDRMCRTC> Aquamarine::CDRMBackend::crtcByID(uint32_t id) {
@@ -1737,6 +1760,10 @@ SP<SDRMCRTC> Aquamarine::CDRMBackend::crtcByID(uint32_t id) {
 }
 
 uintptr_t Aquamarine::SDRMCRTC::armPageFlip(CWeakPointer<SDRMConnector> connector, bool async) {
+    const auto FLIP_ID = backend->nextPageFlipID();
+    if (!FLIP_ID)
+        return 0;
+
     if (pendingFlip.id && !(pendingFlip.connector == connector)) {
         if (const auto PREV = pendingFlip.connector.lock()) {
             backend->log(AQ_LOG_ERROR, std::format("drm: crtc {} page-flip slot taken from {}, dropping its frame", id, PREV->szName));
@@ -1744,11 +1771,11 @@ uintptr_t Aquamarine::SDRMCRTC::armPageFlip(CWeakPointer<SDRMConnector> connecto
         }
     }
 
-    pendingFlip.id        = backend->nextPageFlipID();
+    pendingFlip.id        = FLIP_ID;
     pendingFlip.connector = connector;
     pendingFlip.async     = async;
 
-    return pendingFlip.id.value();
+    return FLIP_ID;
 }
 
 void Aquamarine::SDRMCRTC::disarmPageFlip() {
diff --git a/src/backend/drm/impl/Atomic.cpp b/src/backend/drm/impl/Atomic.cpp
index 421acaf728348fa172ccd4334da1404977ef464b..7c0aac1be55cd986aec22676b41903292858f516 100644
--- a/src/backend/drm/impl/Atomic.cpp
+++ b/src/backend/drm/impl/Atomic.cpp
@@ -377,6 +377,10 @@ bool Aquamarine::CDRMAtomicRequest::commit(uint32_t flagssss) {
     // a test never flips.
     const bool WANTSFLIP = conn && conn->crtc && (flagssss & DRM_MODE_PAGE_FLIP_EVENT) && !(flagssss & DRM_MODE_ATOMIC_TEST_ONLY);
     const auto FLIPID    = WANTSFLIP ? conn->crtc->armPageFlip(conn, flagssss & DRM_MODE_PAGE_FLIP_ASYNC) : uintptr_t{0};
+    if (WANTSFLIP && !FLIPID) {
+        backend->log(AQ_LOG_ERROR, "atomic drm request: page-flip token space exhausted");
+        return false;
+    }
 
     if (auto ret = drmModeAtomicCommit(backend->gpu->fd, req, flagssss, rc<void*>(FLIPID)); ret) {
         backend->log((flagssss & DRM_MODE_ATOMIC_TEST_ONLY) ? AQ_LOG_DEBUG : AQ_LOG_ERROR,
diff --git a/src/backend/drm/impl/Legacy.cpp b/src/backend/drm/impl/Legacy.cpp
index 6993e579a8945bca4b5ef11906d3e3cec8501e53..65658fcdd8cb4196cb70d84c12711471a2f7dd4d 100644
--- a/src/backend/drm/impl/Legacy.cpp
+++ b/src/backend/drm/impl/Legacy.cpp
@@ -133,6 +133,10 @@ bool Aquamarine::CDRMLegacyImpl::commitInternal(Hyprutils::Memory::CSharedPointe
         return true;
 
     const auto FLIPID = connector->crtc->armPageFlip(connector, data.flags & DRM_MODE_PAGE_FLIP_ASYNC);
+    if (!FLIPID) {
+        connector->backend->backend->log(AQ_LOG_ERROR, "legacy drm: page-flip token space exhausted");
+        return false;
+    }
 
     if (int ret = drmModePageFlip(connector->backend->gpu->fd, connector->crtc->id, mainFB ? mainFB->id : -1, data.flags, rc<void*>(FLIPID)); ret) {
         connector->backend->backend->log(AQ_LOG_ERROR, std::format("legacy drm: drmModePageFlip failed: {}", strerror(-ret)));