Fix #6460: Crash when reading corrupt object files

- Move sawyer coding functions to SawyerChunkReader.
- Replace assertions with exceptions to prevent crash on invalid chunks.
This commit is contained in:
Ted John 2017-10-21 14:29:11 +01:00 committed by Michael Steenbeek
parent 5c98c7ee5e
commit 8d1710d798
4 changed files with 141 additions and 6 deletions

View File

@ -46,6 +46,7 @@
- Fix: [#6423] Polish characters now correctly drawn when using the sprite font.
- Fix: [#6445] Guests' favourite ride improperly set when importing from RCT1 or AA.
- Fix: [#6452] Scenario text cut off when switching between 32 and 64-bit builds.
- Fix: [#6460] Crash when reading corrupt object files.
- Fix: Infinite loop when removing scenery elements with >127 base height.
- Fix: Ghosting of transparent map elements when the viewport is moved in OpenGL mode.
- Fix: Clear IME buffer after committing composed text.

View File

@ -363,7 +363,7 @@ private:
std::sort(_items.begin(), _items.end(), [](const ObjectRepositoryItem &a,
const ObjectRepositoryItem &b) -> bool
{
return strcmp(a.Name, b.Name) < 0;
return String::Compare(a.Name, b.Name) < 0;
});
// Fix the IDs

View File

@ -17,13 +17,17 @@
#include "../core/Exception.hpp"
#include "../core/IStream.hpp"
#include "../core/Math.hpp"
#include "../core/Memory.hpp"
#include "SawyerChunkReader.h"
#include "../util/sawyercoding.h"
// Allow chunks to be uncompressed to a maximum of 16 MiB
constexpr size_t MAX_UNCOMPRESSED_CHUNK_SIZE = 16 * 1024 * 1024;
constexpr const char * EXCEPTION_MSG_CORRUPT_CHUNK_SIZE = "Corrupt chunk size.";
constexpr const char * EXCEPTION_MSG_DESTINATION_TOO_SMALL = "Chunk data larger than allocated destination capacity.";
constexpr const char * EXCEPTION_MSG_INVALID_CHUNK_ENCODING = "Invalid chunk encoding.";
constexpr const char * EXCEPTION_MSG_CORRUPT_RLE = "Corrupt RLE compression data.";
class SawyerChunkException : public IOException
{
public:
@ -67,7 +71,7 @@ std::shared_ptr<SawyerChunk> SawyerChunkReader::ReadChunk()
std::unique_ptr<uint8[]> compressedData(new uint8[header.length]);
if (_stream->TryRead(compressedData.get(), header.length) != header.length)
{
throw SawyerChunkException("Corrupt chunk size.");
throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_CHUNK_SIZE);
}
// Allow 16MiB for chunk data
@ -78,7 +82,7 @@ std::shared_ptr<SawyerChunk> SawyerChunkReader::ReadChunk()
throw Exception("Unable to allocate buffer.");
}
size_t uncompressedLength = sawyercoding_read_chunk_buffer(buffer, compressedData.get(), header, bufferSize);
size_t uncompressedLength = DecodeChunk(buffer, bufferSize, compressedData.get(), header);
Guard::Assert(uncompressedLength != 0, "Encountered zero-sized chunk!");
buffer = Memory::Reallocate(buffer, uncompressedLength);
if (buffer == nullptr)
@ -89,7 +93,7 @@ std::shared_ptr<SawyerChunk> SawyerChunkReader::ReadChunk()
return std::make_shared<SawyerChunk>((SAWYER_ENCODING)header.encoding, buffer, uncompressedLength);
}
default:
throw SawyerChunkException("Invalid chunk encoding.");
throw SawyerChunkException(EXCEPTION_MSG_INVALID_CHUNK_ENCODING);
}
}
catch (Exception)
@ -120,3 +124,126 @@ void SawyerChunkReader::ReadChunk(void * dst, size_t length)
}
}
}
size_t SawyerChunkReader::DecodeChunk(void * dst, size_t dstCapacity, const void * src, const sawyercoding_chunk_header &header)
{
size_t resultLength;
switch (header.encoding)
{
case CHUNK_ENCODING_NONE:
if (header.length > dstCapacity)
{
throw SawyerChunkException(EXCEPTION_MSG_DESTINATION_TOO_SMALL);
}
Memory::Copy(dst, src, header.length);
resultLength = header.length;
break;
case CHUNK_ENCODING_RLE:
resultLength = DecodeChunkRLE(dst, dstCapacity, src, header.length);
break;
case CHUNK_ENCODING_RLECOMPRESSED:
{
auto immBufferLength = MAX_UNCOMPRESSED_CHUNK_SIZE;
auto immBuffer = std::make_unique<uint8[]>(immBufferLength);
auto immLength = DecodeChunkRLE(immBuffer.get(), immBufferLength, src, header.length);
resultLength = DecodeChunkRepeat(dst, dstCapacity, immBuffer.get(), immLength);
break;
}
case CHUNK_ENCODING_ROTATE:
resultLength = DecodeChunkRotate(dst, dstCapacity, src, header.length);
break;
default:
throw SawyerChunkException(EXCEPTION_MSG_INVALID_CHUNK_ENCODING);
}
return resultLength;
}
size_t SawyerChunkReader::DecodeChunkRLE(void * dst, size_t dstCapacity, const void * src, size_t srcLength)
{
auto src8 = static_cast<const uint8 *>(src);
auto dst8 = static_cast<uint8 *>(dst);
auto dstEnd = dst8 + dstCapacity;
for (size_t i = 0; i < srcLength; i++)
{
uint8 rleCodeByte = src8[i];
if (rleCodeByte & 128)
{
i++;
size_t count = 257 - rleCodeByte;
if (i >= srcLength)
{
throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_RLE);
}
if (dst8 + count > dstEnd)
{
throw SawyerChunkException(EXCEPTION_MSG_DESTINATION_TOO_SMALL);
}
Memory::Set(dst8, src8[i], count);
dst8 += count;
}
else
{
if (i + 1 >= srcLength)
{
throw SawyerChunkException(EXCEPTION_MSG_CORRUPT_RLE);
}
if (dst8 + rleCodeByte + 1 > dstEnd)
{
throw SawyerChunkException(EXCEPTION_MSG_DESTINATION_TOO_SMALL);
}
Memory::Copy(dst8, src8 + i + 1, rleCodeByte + 1);
dst8 += rleCodeByte + 1;
i += rleCodeByte + 1;
}
}
return (uintptr_t)dst8 - (uintptr_t)dst;
}
size_t SawyerChunkReader::DecodeChunkRepeat(void * dst, size_t dstCapacity, const void * src, size_t srcLength)
{
auto src8 = static_cast<const uint8 *>(src);
auto dst8 = static_cast<uint8 *>(dst);
auto dstEnd = dst8 + dstCapacity;
for (size_t i = 0; i < srcLength; i++)
{
if (src8[i] == 0xFF)
{
*dst8++ = src8[++i];
}
else
{
size_t count = (src8[i] & 7) + 1;
const uint8 * copySrc = dst8 + (sint32)(src8[i] >> 3) - 32;
if (dst8 + count >= dstEnd || copySrc + count >= dstEnd)
{
throw SawyerChunkException(EXCEPTION_MSG_DESTINATION_TOO_SMALL);
}
Memory::Copy(dst8, copySrc, count);
dst8 += count;
}
}
return (uintptr_t)dst8 - (uintptr_t)dst;
}
size_t SawyerChunkReader::DecodeChunkRotate(void * dst, size_t dstCapacity, const void * src, size_t srcLength)
{
if (srcLength > dstCapacity)
{
throw SawyerChunkException(EXCEPTION_MSG_DESTINATION_TOO_SMALL);
}
auto src8 = static_cast<const uint8 *>(src);
auto dst8 = static_cast<uint8 *>(dst);
uint8 code = 1;
for (size_t i = 0; i < srcLength; i++)
{
dst8[i] = ror8(src8[i], code);
code = (code + 2) % 8;
}
return srcLength;
}

View File

@ -20,6 +20,7 @@
#include <memory>
#include "../common.h"
#include "../util/sawyercoding.h"
#include "SawyerChunk.h"
interface IStream;
@ -69,6 +70,12 @@ public:
ReadChunk(&result, sizeof(result));
return result;
}
private:
static size_t DecodeChunk(void * dst, size_t dstCapacity, const void * src, const sawyercoding_chunk_header &header);
static size_t DecodeChunkRLE(void * dst, size_t dstCapacity, const void * src, size_t srcLength);
static size_t DecodeChunkRepeat(void * dst, size_t dstCapacity, const void * src, size_t srcLength);
static size_t DecodeChunkRotate(void * dst, size_t dstCapacity, const void * src, size_t srcLength);
};
#endif