Common/PointerWrap: Prevent reads/writes past the end of the buffer.

This commit is contained in:
Admiral H. Curtiss 2022-04-18 04:13:25 +02:00
parent 853cf4f818
commit ef760ee012
No known key found for this signature in database
GPG Key ID: F051B4C4044F33FB
4 changed files with 51 additions and 27 deletions

View File

@ -47,11 +47,16 @@ public:
}; };
private: private:
u8** ptr; u8** m_ptr_current;
u8* m_ptr_end;
Mode mode; Mode mode;
public: 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_; } void SetMode(Mode mode_) { mode = mode_; }
Mode GetMode() const { return mode; } Mode GetMode() const { return mode; }
template <typename K, class V> template <typename K, class V>
@ -209,8 +214,13 @@ public:
[[nodiscard]] u8* DoExternal(u32& count) [[nodiscard]] u8* DoExternal(u32& count)
{ {
Do(count); Do(count);
u8* current = *ptr; u8* current = *m_ptr_current;
*ptr += count; *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; return current;
} }
@ -320,26 +330,32 @@ private:
DOLPHIN_FORCE_INLINE void DoVoid(void* data, u32 size) 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) switch (mode)
{ {
case MODE_READ: case MODE_READ:
memcpy(data, *ptr, size); memcpy(data, *m_ptr_current, size);
break; break;
case MODE_WRITE: case MODE_WRITE:
memcpy(*ptr, data, size); memcpy(*m_ptr_current, data, size);
break; break;
case MODE_MEASURE: case MODE_MEASURE:
break; break;
case MODE_VERIFY: 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), "Savestate verification failure: buf {} != {} (size {}).\n", fmt::ptr(data),
fmt::ptr(*ptr), size); fmt::ptr(*m_ptr_current), size);
break; break;
} }
*ptr += size; *m_ptr_current += size;
} }
}; };

View File

@ -225,8 +225,8 @@ void LoadFromBuffer(std::vector<u8>& buffer)
Core::RunOnCPUThread( Core::RunOnCPUThread(
[&] { [&] {
u8* ptr = &buffer[0]; u8* ptr = buffer.data();
PointerWrap p(&ptr, PointerWrap::MODE_READ); PointerWrap p(&ptr, buffer.size(), PointerWrap::MODE_READ);
DoState(p); DoState(p);
}, },
true); true);
@ -237,14 +237,14 @@ void SaveToBuffer(std::vector<u8>& buffer)
Core::RunOnCPUThread( Core::RunOnCPUThread(
[&] { [&] {
u8* ptr = nullptr; 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<size_t>(ptr); const size_t buffer_size = reinterpret_cast<size_t>(ptr);
buffer.resize(buffer_size); buffer.resize(buffer_size);
ptr = &buffer[0]; ptr = buffer.data();
p.SetMode(PointerWrap::MODE_WRITE); PointerWrap p(&ptr, buffer_size, PointerWrap::MODE_WRITE);
DoState(p); DoState(p);
}, },
true); true);
@ -412,20 +412,22 @@ void SaveAs(const std::string& filename, bool wait)
[&] { [&] {
// Measure the size of the buffer. // Measure the size of the buffer.
u8* ptr = nullptr; 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<size_t>(ptr); const size_t buffer_size = reinterpret_cast<size_t>(ptr);
// Then actually do the write. // Then actually do the write.
PointerWrap::Mode p_mode;
{ {
std::lock_guard lk(g_cs_current_buffer); std::lock_guard lk(g_cs_current_buffer);
g_current_buffer.resize(buffer_size); g_current_buffer.resize(buffer_size);
ptr = &g_current_buffer[0]; ptr = g_current_buffer.data();
p.SetMode(PointerWrap::MODE_WRITE); PointerWrap p(&ptr, buffer_size, PointerWrap::MODE_WRITE);
DoState(p); DoState(p);
p_mode = p.GetMode();
} }
if (p.GetMode() == PointerWrap::MODE_WRITE) if (p_mode == PointerWrap::MODE_WRITE)
{ {
Core::DisplayMessage("Saving State...", 1000); Core::DisplayMessage("Saving State...", 1000);
@ -588,8 +590,8 @@ void LoadAs(const std::string& filename)
if (!buffer.empty()) if (!buffer.empty())
{ {
u8* ptr = &buffer[0]; u8* ptr = buffer.data();
PointerWrap p(&ptr, PointerWrap::MODE_READ); PointerWrap p(&ptr, buffer.size(), PointerWrap::MODE_READ);
DoState(p); DoState(p);
loaded = true; loaded = true;
loadedSuccessfully = (p.GetMode() == PointerWrap::MODE_READ); loadedSuccessfully = (p.GetMode() == PointerWrap::MODE_READ);

View File

@ -230,14 +230,14 @@ bool GameFileCache::SyncCacheFile(bool save)
{ {
// Measure the size of the buffer. // Measure the size of the buffer.
u8* ptr = nullptr; 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<size_t>(ptr); const size_t buffer_size = reinterpret_cast<size_t>(ptr);
// Then actually do the write. // Then actually do the write.
std::vector<u8> buffer(buffer_size); std::vector<u8> buffer(buffer_size);
ptr = &buffer[0]; ptr = buffer.data();
p.SetMode(PointerWrap::MODE_WRITE); PointerWrap p(&ptr, buffer_size, PointerWrap::MODE_WRITE);
DoState(&p, buffer_size); DoState(&p, buffer_size);
if (f.WriteBytes(buffer.data(), buffer.size())) if (f.WriteBytes(buffer.data(), buffer.size()))
success = true; success = true;
@ -248,7 +248,7 @@ bool GameFileCache::SyncCacheFile(bool save)
if (!buffer.empty() && f.ReadBytes(buffer.data(), buffer.size())) if (!buffer.empty() && f.ReadBytes(buffer.data(), buffer.size()))
{ {
u8* ptr = buffer.data(); u8* ptr = buffer.data();
PointerWrap p(&ptr, PointerWrap::MODE_READ); PointerWrap p(&ptr, buffer.size(), PointerWrap::MODE_READ);
DoState(&p, buffer.size()); DoState(&p, buffer.size());
if (p.GetMode() == PointerWrap::MODE_READ) if (p.GetMode() == PointerWrap::MODE_READ)
success = true; success = true;

View File

@ -459,6 +459,12 @@ void TextureCacheBase::SerializeTexture(AbstractTexture* tex, const TextureConfi
// needing to allocate/free an extra buffer. // needing to allocate/free an extra buffer.
u8* texture_data = p.DoExternal(total_size); 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) if (!skip_readback)
{ {
// Save out each layer of the texture to the pointer. // Save out each layer of the texture to the pointer.