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.
This commit is contained in:
Acts1631
2026-10-04 16:53:57 -04:00
parent eb236466c4
commit f5438ce0c3
2 changed files with 20 additions and 5 deletions
+11 -3
View File
@@ -288,7 +288,7 @@ public:
}
template <typename T>
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<size_t>(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<u8*>(x);
DoPointer(ptr, static_cast<u8*>(base));
if (!DoPointer(ptr, static_cast<u8*>(base), count))
return false;
x = ptr;
return true;
}
void DoMarker(const std::string& prevName, u32 arbitraryNumber = 0x42)
+9 -2
View File
@@ -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?