diff --git a/Source/Core/Common/ChunkFile.h b/Source/Core/Common/ChunkFile.h index e7791839db..9fd785459e 100644 --- a/Source/Core/Common/ChunkFile.h +++ b/Source/Core/Common/ChunkFile.h @@ -47,11 +47,16 @@ public: }; private: - u8** ptr; + u8** m_ptr_current; + u8* m_ptr_end; Mode mode; public: - PointerWrap(u8** ptr_, Mode mode_) : ptr(ptr_), mode(mode_) {} + PointerWrap(u8** ptr, size_t size, Mode mode_) + : m_ptr_current(ptr), m_ptr_end(*ptr + size), mode(mode_) + { + } + void SetMode(Mode mode_) { mode = mode_; } Mode GetMode() const { return mode; } template @@ -209,8 +214,13 @@ public: [[nodiscard]] u8* DoExternal(u32& count) { Do(count); - u8* current = *ptr; - *ptr += count; + u8* current = *m_ptr_current; + *m_ptr_current += count; + if (mode != MODE_MEASURE && *m_ptr_current > m_ptr_end) + { + // trying to read/write past the end of the buffer, prevent this + mode = MODE_MEASURE; + } return current; } @@ -320,26 +330,32 @@ private: DOLPHIN_FORCE_INLINE void DoVoid(void* data, u32 size) { + if (mode != MODE_MEASURE && (*m_ptr_current + size) > m_ptr_end) + { + // trying to read/write past the end of the buffer, prevent this + mode = MODE_MEASURE; + } + switch (mode) { case MODE_READ: - memcpy(data, *ptr, size); + memcpy(data, *m_ptr_current, size); break; case MODE_WRITE: - memcpy(*ptr, data, size); + memcpy(*m_ptr_current, data, size); break; case MODE_MEASURE: break; case MODE_VERIFY: - DEBUG_ASSERT_MSG(COMMON, !memcmp(data, *ptr, size), + DEBUG_ASSERT_MSG(COMMON, !memcmp(data, *m_ptr_current, size), "Savestate verification failure: buf {} != {} (size {}).\n", fmt::ptr(data), - fmt::ptr(*ptr), size); + fmt::ptr(*m_ptr_current), size); break; } - *ptr += size; + *m_ptr_current += size; } }; diff --git a/Source/Core/Core/State.cpp b/Source/Core/Core/State.cpp index b1a3c70910..71e8642f06 100644 --- a/Source/Core/Core/State.cpp +++ b/Source/Core/Core/State.cpp @@ -225,8 +225,8 @@ void LoadFromBuffer(std::vector& buffer) Core::RunOnCPUThread( [&] { - u8* ptr = &buffer[0]; - PointerWrap p(&ptr, PointerWrap::MODE_READ); + u8* ptr = buffer.data(); + PointerWrap p(&ptr, buffer.size(), PointerWrap::MODE_READ); DoState(p); }, true); @@ -237,14 +237,14 @@ void SaveToBuffer(std::vector& buffer) Core::RunOnCPUThread( [&] { u8* ptr = nullptr; - PointerWrap p(&ptr, PointerWrap::MODE_MEASURE); + PointerWrap p_measure(&ptr, 0, PointerWrap::MODE_MEASURE); - DoState(p); + DoState(p_measure); const size_t buffer_size = reinterpret_cast(ptr); buffer.resize(buffer_size); - ptr = &buffer[0]; - p.SetMode(PointerWrap::MODE_WRITE); + ptr = buffer.data(); + PointerWrap p(&ptr, buffer_size, PointerWrap::MODE_WRITE); DoState(p); }, true); @@ -412,20 +412,22 @@ void SaveAs(const std::string& filename, bool wait) [&] { // Measure the size of the buffer. u8* ptr = nullptr; - PointerWrap p(&ptr, PointerWrap::MODE_MEASURE); - DoState(p); + PointerWrap p_measure(&ptr, 0, PointerWrap::MODE_MEASURE); + DoState(p_measure); const size_t buffer_size = reinterpret_cast(ptr); // Then actually do the write. + PointerWrap::Mode p_mode; { std::lock_guard lk(g_cs_current_buffer); g_current_buffer.resize(buffer_size); - ptr = &g_current_buffer[0]; - p.SetMode(PointerWrap::MODE_WRITE); + ptr = g_current_buffer.data(); + PointerWrap p(&ptr, buffer_size, PointerWrap::MODE_WRITE); DoState(p); + p_mode = p.GetMode(); } - if (p.GetMode() == PointerWrap::MODE_WRITE) + if (p_mode == PointerWrap::MODE_WRITE) { Core::DisplayMessage("Saving State...", 1000); @@ -588,8 +590,8 @@ void LoadAs(const std::string& filename) if (!buffer.empty()) { - u8* ptr = &buffer[0]; - PointerWrap p(&ptr, PointerWrap::MODE_READ); + u8* ptr = buffer.data(); + PointerWrap p(&ptr, buffer.size(), PointerWrap::MODE_READ); DoState(p); loaded = true; loadedSuccessfully = (p.GetMode() == PointerWrap::MODE_READ); diff --git a/Source/Core/UICommon/GameFileCache.cpp b/Source/Core/UICommon/GameFileCache.cpp index 1040c24b2f..a41c07553c 100644 --- a/Source/Core/UICommon/GameFileCache.cpp +++ b/Source/Core/UICommon/GameFileCache.cpp @@ -230,14 +230,14 @@ bool GameFileCache::SyncCacheFile(bool save) { // Measure the size of the buffer. u8* ptr = nullptr; - PointerWrap p(&ptr, PointerWrap::MODE_MEASURE); - DoState(&p); + PointerWrap p_measure(&ptr, 0, PointerWrap::MODE_MEASURE); + DoState(&p_measure); const size_t buffer_size = reinterpret_cast(ptr); // Then actually do the write. std::vector buffer(buffer_size); - ptr = &buffer[0]; - p.SetMode(PointerWrap::MODE_WRITE); + ptr = buffer.data(); + PointerWrap p(&ptr, buffer_size, PointerWrap::MODE_WRITE); DoState(&p, buffer_size); if (f.WriteBytes(buffer.data(), buffer.size())) success = true; @@ -248,7 +248,7 @@ bool GameFileCache::SyncCacheFile(bool save) if (!buffer.empty() && f.ReadBytes(buffer.data(), buffer.size())) { u8* ptr = buffer.data(); - PointerWrap p(&ptr, PointerWrap::MODE_READ); + PointerWrap p(&ptr, buffer.size(), PointerWrap::MODE_READ); DoState(&p, buffer.size()); if (p.GetMode() == PointerWrap::MODE_READ) success = true; diff --git a/Source/Core/VideoCommon/TextureCacheBase.cpp b/Source/Core/VideoCommon/TextureCacheBase.cpp index 078abf10b1..b1c9ead97a 100644 --- a/Source/Core/VideoCommon/TextureCacheBase.cpp +++ b/Source/Core/VideoCommon/TextureCacheBase.cpp @@ -459,6 +459,12 @@ void TextureCacheBase::SerializeTexture(AbstractTexture* tex, const TextureConfi // needing to allocate/free an extra buffer. u8* texture_data = p.DoExternal(total_size); + if (p.GetMode() == PointerWrap::MODE_MEASURE) + { + ERROR_LOG_FMT(VIDEO, "Couldn't acquire {} bytes for serializing texture.", total_size); + return; + } + if (!skip_readback) { // Save out each layer of the texture to the pointer.