From 2eea927e95a577fe90e98d756968111e296147a7 Mon Sep 17 00:00:00 2001 From: Acts1631 Date: Sun, 4 Oct 2026 16:34:42 -0400 Subject: [PATCH] VideoCommon: validate restored FIFO pointers Save states restored video FIFO pointers from unchecked offsets. A crafted offset could place a pointer outside its backing allocation and turn the next gather-pipe copy into an out-of-bounds native write. Require pointer offsets to stay within their backing array and reject a state when either offset is invalid or the read pointer follows the write pointer. --- Source/Core/Common/ChunkFile.h | 14 +++++++++++--- Source/Core/VideoCommon/Fifo.cpp | 11 +++++++++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/Source/Core/Common/ChunkFile.h b/Source/Core/Common/ChunkFile.h index a9737c1e0e..7ae4d7e778 100644 --- a/Source/Core/Common/ChunkFile.h +++ b/Source/Core/Common/ChunkFile.h @@ -288,7 +288,7 @@ public: } template - void DoPointer(T*& x, T* const base) + bool DoPointer(T*& x, T* const base, size_t count) { // pointers can be more than 2^31 apart, but you're using this function wrong if you need that // much range @@ -296,15 +296,23 @@ public: Do(offset); if (IsReadMode()) { + if (offset < 0 || static_cast(offset) > count) + { + SetMeasureMode(); + return false; + } x = base + offset; } + return true; } - void DoPointer(void*& x, void* const base) + bool DoPointer(void*& x, void* const base, size_t count) { auto* ptr = static_cast(x); - DoPointer(ptr, static_cast(base)); + if (!DoPointer(ptr, static_cast(base), count)) + return false; x = ptr; + return true; } void DoMarker(const std::string& prevName, u32 arbitraryNumber = 0x42) diff --git a/Source/Core/VideoCommon/Fifo.cpp b/Source/Core/VideoCommon/Fifo.cpp index f183c93655..d396d318c9 100644 --- a/Source/Core/VideoCommon/Fifo.cpp +++ b/Source/Core/VideoCommon/Fifo.cpp @@ -54,9 +54,16 @@ void FifoManager::DoState(PointerWrap& p) { p.DoArray(m_video_buffer, FIFO_SIZE); u8* write_ptr = m_video_buffer_write_ptr; - p.DoPointer(write_ptr, m_video_buffer); + if (!p.DoPointer(write_ptr, m_video_buffer, FIFO_SIZE)) + return; m_video_buffer_write_ptr = write_ptr; - p.DoPointer(m_video_buffer_read_ptr, m_video_buffer); + if (!p.DoPointer(m_video_buffer_read_ptr, m_video_buffer, FIFO_SIZE)) + return; + if (p.IsReadMode() && m_video_buffer_read_ptr > write_ptr) + { + p.SetMeasureMode(); + return; + } if (p.IsReadMode() && m_use_deterministic_gpu_thread) { // We're good and paused, right?