Common/PointerWrap: Prevent reads/writes past the end of the buffer.
This commit is contained in:
parent
853cf4f818
commit
ef760ee012
|
@ -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;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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.
|
||||||
|
|
Loading…
Reference in New Issue