mirror of https://github.com/OpenRCT2/OpenRCT2.git
Fix memory corruption in ImageTable::AddImage
This commit is contained in:
parent
ddd0df4a60
commit
a7a7b2f1b4
|
@ -15,18 +15,22 @@
|
||||||
#pragma endregion
|
#pragma endregion
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <memory>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include "../core/IStream.hpp"
|
#include "../core/IStream.hpp"
|
||||||
#include "../core/Memory.hpp"
|
|
||||||
#include "../OpenRCT2.h"
|
#include "../OpenRCT2.h"
|
||||||
#include "ImageTable.h"
|
#include "ImageTable.h"
|
||||||
#include "Object.h"
|
#include "Object.h"
|
||||||
|
|
||||||
ImageTable::~ImageTable()
|
ImageTable::~ImageTable()
|
||||||
{
|
{
|
||||||
Memory::Free(_data);
|
if (_data == nullptr)
|
||||||
_data = nullptr;
|
{
|
||||||
_dataSize = 0;
|
for (auto &entry : _entries)
|
||||||
|
{
|
||||||
|
delete entry.offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ImageTable::Read(IReadObjectContext * context, IStream * stream)
|
void ImageTable::Read(IReadObjectContext * context, IStream * stream)
|
||||||
|
@ -49,16 +53,17 @@ void ImageTable::Read(IReadObjectContext * context, IStream * stream)
|
||||||
imageDataSize = (uint32)remainingBytes;
|
imageDataSize = (uint32)remainingBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
_dataSize = imageDataSize;
|
auto dataSize = (size_t)imageDataSize;
|
||||||
_data = Memory::Reallocate(_data, _dataSize);
|
auto data = std::make_unique<uint8[]>(dataSize);
|
||||||
if (_data == nullptr)
|
if (data == nullptr)
|
||||||
{
|
{
|
||||||
context->LogError(OBJECT_ERROR_BAD_IMAGE_TABLE, "Image table too large.");
|
context->LogError(OBJECT_ERROR_BAD_IMAGE_TABLE, "Image table too large.");
|
||||||
throw std::runtime_error("Image table too large.");
|
throw std::runtime_error("Image table too large.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read g1 element headers
|
// Read g1 element headers
|
||||||
uintptr_t imageDataBase = (uintptr_t)_data;
|
uintptr_t imageDataBase = (uintptr_t)data.get();
|
||||||
|
std::vector<rct_g1_element> newEntries;
|
||||||
for (uint32 i = 0; i < numImages; i++)
|
for (uint32 i = 0; i < numImages; i++)
|
||||||
{
|
{
|
||||||
rct_g1_element g1Element;
|
rct_g1_element g1Element;
|
||||||
|
@ -73,23 +78,22 @@ void ImageTable::Read(IReadObjectContext * context, IStream * stream)
|
||||||
g1Element.flags = stream->ReadValue<uint16>();
|
g1Element.flags = stream->ReadValue<uint16>();
|
||||||
g1Element.zoomed_offset = stream->ReadValue<uint16>();
|
g1Element.zoomed_offset = stream->ReadValue<uint16>();
|
||||||
|
|
||||||
_entries.push_back(g1Element);
|
newEntries.push_back(g1Element);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read g1 element data
|
// Read g1 element data
|
||||||
size_t readBytes = (size_t)stream->TryRead(_data, _dataSize);
|
size_t readBytes = (size_t)stream->TryRead(data.get(), dataSize);
|
||||||
|
|
||||||
// If data is shorter than expected (some custom objects are unfortunately like that)
|
// If data is shorter than expected (some custom objects are unfortunately like that)
|
||||||
size_t unreadBytes = _dataSize - readBytes;
|
size_t unreadBytes = dataSize - readBytes;
|
||||||
if (unreadBytes > 0)
|
if (unreadBytes > 0)
|
||||||
{
|
{
|
||||||
uint8 * ptr = (uint8*)(((uintptr_t)_data) + readBytes);
|
std::fill_n(data.get() + readBytes, unreadBytes, 0);
|
||||||
std::fill_n(ptr, unreadBytes, 0);
|
|
||||||
|
|
||||||
context->LogWarning(OBJECT_ERROR_BAD_IMAGE_TABLE, "Image table size shorter than expected.");
|
context->LogWarning(OBJECT_ERROR_BAD_IMAGE_TABLE, "Image table size shorter than expected.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO validate the image data to prevent crashes in-game
|
_data = std::move(data);
|
||||||
|
_entries.insert(_entries.end(), newEntries.begin(), newEntries.end());
|
||||||
}
|
}
|
||||||
catch (const std::exception &)
|
catch (const std::exception &)
|
||||||
{
|
{
|
||||||
|
@ -104,16 +108,12 @@ void ImageTable::AddImage(const rct_g1_element * g1)
|
||||||
auto length = g1_calculate_data_size(g1);
|
auto length = g1_calculate_data_size(g1);
|
||||||
if (length == 0)
|
if (length == 0)
|
||||||
{
|
{
|
||||||
newg1.offset = 0;
|
newg1.offset = nullptr;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
auto dstOffset = _dataSize;
|
newg1.offset = new uint8[length];
|
||||||
_dataSize += length;
|
std::copy_n(g1->offset, length, newg1.offset);
|
||||||
_data = Memory::Reallocate(_data, _dataSize);
|
|
||||||
auto dst = (uint8 *)((size_t)_data + dstOffset);
|
|
||||||
Memory::Copy(dst, g1->offset, length);
|
|
||||||
newg1.offset = dst;
|
|
||||||
}
|
}
|
||||||
_entries.push_back(newg1);
|
_entries.push_back(newg1);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,9 +16,9 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include "../common.h"
|
#include "../common.h"
|
||||||
|
|
||||||
#include "../drawing/Drawing.h"
|
#include "../drawing/Drawing.h"
|
||||||
|
|
||||||
interface IReadObjectContext;
|
interface IReadObjectContext;
|
||||||
|
@ -27,9 +27,8 @@ interface IStream;
|
||||||
class ImageTable
|
class ImageTable
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
std::unique_ptr<uint8[]> _data;
|
||||||
std::vector<rct_g1_element> _entries;
|
std::vector<rct_g1_element> _entries;
|
||||||
void * _data = nullptr;
|
|
||||||
size_t _dataSize = 0;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~ImageTable();
|
~ImageTable();
|
||||||
|
|
Loading…
Reference in New Issue