If the server sends corrupted compressed map data, try to disconnect the game instead of outright crashing

This commit is contained in:
UnknownShadow200 2020-08-02 10:49:04 +10:00
parent c968d14a75
commit dcf1b9a49e
6 changed files with 47 additions and 33 deletions

View File

@ -377,7 +377,7 @@ cc_result Png_Decode(struct Bitmap* bmp, struct Stream* stream) {
trnsCol = BITMAPCOL_BLACK; trnsCol = BITMAPCOL_BLACK;
for (i = 0; i < PNG_PALETTE; i++) { palette[i] = BITMAPCOL_BLACK; } for (i = 0; i < PNG_PALETTE; i++) { palette[i] = BITMAPCOL_BLACK; }
Inflate_MakeStream(&compStream, &inflate, stream); Inflate_MakeStream2(&compStream, &inflate, stream);
ZLibHeader_Init(&zlibHeader); ZLibHeader_Init(&zlibHeader);
for (;;) { for (;;) {

View File

@ -154,6 +154,8 @@ enum INFLATE_STATE_ {
#define Inflate_UNSAFE_EnsureBits(state, bitsCount) while (state->NumBits < bitsCount) { Inflate_GetByte(state); } #define Inflate_UNSAFE_EnsureBits(state, bitsCount) while (state->NumBits < bitsCount) { Inflate_GetByte(state); }
/* Peeks then consumes given bits */ /* Peeks then consumes given bits */
#define Inflate_ReadBits(state, bitsCount) Inflate_PeekBits(state, bitsCount); Inflate_ConsumeBits(state, bitsCount); #define Inflate_ReadBits(state, bitsCount) Inflate_PeekBits(state, bitsCount); Inflate_ConsumeBits(state, bitsCount);
/* Sets to given result and sets state to DONE */
#define Inflate_Fail(state, res) state->result = res; state->State = INFLATE_STATE_DONE;
/* Goes to the next state, after having read data of a block */ /* Goes to the next state, after having read data of a block */
#define Inflate_NextBlockState(state) (state->LastBlock ? INFLATE_STATE_DONE : INFLATE_STATE_HEADER) #define Inflate_NextBlockState(state) (state->LastBlock ? INFLATE_STATE_DONE : INFLATE_STATE_HEADER)
@ -329,7 +331,7 @@ static int Huffman_Unsafe_Decode_Slow(struct InflateState* state, struct Huffman
return -1; return -1;
} }
void Inflate_Init(struct InflateState* state, struct Stream* source) { void Inflate_Init2(struct InflateState* state, struct Stream* source) {
state->State = INFLATE_STATE_HEADER; state->State = INFLATE_STATE_HEADER;
state->LastBlock = false; state->LastBlock = false;
state->Bits = 0; state->Bits = 0;
@ -340,6 +342,7 @@ void Inflate_Init(struct InflateState* state, struct Stream* source) {
state->AvailOut = 0; state->AvailOut = 0;
state->Source = source; state->Source = source;
state->WindowIndex = 0; state->WindowIndex = 0;
state->result = 0;
} }
static const cc_uint8 fixed_lits[INFLATE_MAX_LITS] = { static const cc_uint8 fixed_lits[INFLATE_MAX_LITS] = {
@ -496,7 +499,7 @@ void Inflate_Process(struct InflateState* state) {
} break; } break;
case 3: { case 3: {
Logger_Abort("DEFLATE - Invalid block type"); Inflate_Fail(state, INF_ERR_BLOCKTYPE);
} break; } break;
} }
@ -509,7 +512,8 @@ void Inflate_Process(struct InflateState* state) {
nlen = Inflate_ReadBits(state, 16); nlen = Inflate_ReadBits(state, 16);
if (len != (nlen ^ 0xFFFFUL)) { if (len != (nlen ^ 0xFFFFUL)) {
Logger_Abort("DEFLATE - Uncompressed block LEN check failed"); Inflate_Fail(state, INF_ERR_BLOCKTYPE);
return;
} }
state->Index = len; /* Reuse for 'uncompressed length' */ state->Index = len; /* Reuse for 'uncompressed length' */
state->State = INFLATE_STATE_UNCOMPRESSED_DATA; state->State = INFLATE_STATE_UNCOMPRESSED_DATA;
@ -602,7 +606,7 @@ void Inflate_Process(struct InflateState* state) {
case 16: case 16:
Inflate_EnsureBits(state, 2); Inflate_EnsureBits(state, 2);
repeatCount = Inflate_ReadBits(state, 2); repeatCount = Inflate_ReadBits(state, 2);
if (!state->Index) Logger_Abort("DEFLATE - Tried to repeat invalid byte"); if (!state->Index) { Inflate_Fail(state, INF_ERR_REPEAT_BEG); return; }
repeatCount += 3; repeatValue = state->Buffer[state->Index - 1]; repeatCount += 3; repeatValue = state->Buffer[state->Index - 1];
break; break;
@ -621,7 +625,8 @@ void Inflate_Process(struct InflateState* state) {
count = state->NumLits + state->NumDists; count = state->NumLits + state->NumDists;
if (state->Index + repeatCount > count) { if (state->Index + repeatCount > count) {
Logger_Abort("DEFLATE - Tried to repeat past end"); Inflate_Fail(state, INF_ERR_REPEAT_END);
return;
} }
Mem_Set(&state->Buffer[state->Index], repeatValue, repeatCount); Mem_Set(&state->Buffer[state->Index], repeatValue, repeatCount);
@ -723,7 +728,7 @@ static cc_result Inflate_StreamRead(struct Stream* stream, cc_uint8* data, cc_ui
hasInput = true; hasInput = true;
while (state->AvailOut > 0 && hasInput) { while (state->AvailOut > 0 && hasInput) {
if (state->State == INFLATE_STATE_DONE) break; if (state->State == INFLATE_STATE_DONE) return state->result;
if (!state->AvailIn) { if (!state->AvailIn) {
/* Fully used up input buffer. Cycle back to start. */ /* Fully used up input buffer. Cycle back to start. */
@ -748,9 +753,9 @@ static cc_result Inflate_StreamRead(struct Stream* stream, cc_uint8* data, cc_ui
return 0; return 0;
} }
void Inflate_MakeStream(struct Stream* stream, struct InflateState* state, struct Stream* underlying) { void Inflate_MakeStream2(struct Stream* stream, struct InflateState* state, struct Stream* underlying) {
Stream_Init(stream); Stream_Init(stream);
Inflate_Init(state, underlying); Inflate_Init2(state, underlying);
stream->Meta.Inflate = state; stream->Meta.Inflate = state;
stream->Read = Inflate_StreamRead; stream->Read = Inflate_StreamRead;
} }
@ -1151,7 +1156,7 @@ static cc_result Zip_ReadLocalFileHeader(struct ZipState* state, struct ZipEntry
return state->ProcessEntry(&path, &portion, state); return state->ProcessEntry(&path, &portion, state);
} else if (method == 8) { } else if (method == 8) {
Stream_ReadonlyPortion(&portion, stream, compressedSize); Stream_ReadonlyPortion(&portion, stream, compressedSize);
Inflate_MakeStream(&compStream, &inflate, &portion); Inflate_MakeStream2(&compStream, &inflate, &portion);
return state->ProcessEntry(&path, &compStream, state); return state->ProcessEntry(&path, &compStream, state);
} else { } else {
Platform_Log1("Unsupported.zip entry compression method: %i", &method); Platform_Log1("Unsupported.zip entry compression method: %i", &method);

View File

@ -64,17 +64,18 @@ struct InflateState {
} Table; /* union to save on memory */ } Table; /* union to save on memory */
struct HuffmanTable TableDists; /* Values represent distances back */ struct HuffmanTable TableDists; /* Values represent distances back */
cc_uint8 Window[INFLATE_WINDOW_SIZE]; /* Holds circular buffer of recent output data, used for LZ77 */ cc_uint8 Window[INFLATE_WINDOW_SIZE]; /* Holds circular buffer of recent output data, used for LZ77 */
cc_result result;
}; };
/* Initialises DEFLATE decompressor state to defaults. */ /* Initialises DEFLATE decompressor state to defaults. */
CC_API void Inflate_Init(struct InflateState* state, struct Stream* source); CC_API void Inflate_Init2(struct InflateState* state, struct Stream* source);
/* Attempts to decompress as much of the currently pending data as possible. */ /* Attempts to decompress as much of the currently pending data as possible. */
/* NOTE: This is a low level call - usually you treat as a stream via Inflate_MakeStream. */ /* NOTE: This is a low level call - usually you treat as a stream via Inflate_MakeStream. */
void Inflate_Process(struct InflateState* state); void Inflate_Process(struct InflateState* state);
/* Deompresses input data read from another stream using DEFLATE. Read only stream. */ /* Deompresses input data read from another stream using DEFLATE. Read only stream. */
/* NOTE: This only uncompresses pure DEFLATE compressed data. */ /* NOTE: This only uncompresses pure DEFLATE compressed data. */
/* If data starts with a GZIP or ZLIB header, use GZipHeader_Read or ZLibHeader_Read to first skip it. */ /* If data starts with a GZIP or ZLIB header, use GZipHeader_Read or ZLibHeader_Read to first skip it. */
CC_API void Inflate_MakeStream(struct Stream* stream, struct InflateState* state, struct Stream* underlying); CC_API void Inflate_MakeStream2(struct Stream* stream, struct InflateState* state, struct Stream* underlying);
#define DEFLATE_BLOCK_SIZE 16384 #define DEFLATE_BLOCK_SIZE 16384

View File

@ -49,6 +49,8 @@ enum ERRORS_ALL {
/* CW map decoding errors */ /* CW map decoding errors */
NBT_ERR_INT32S, NBT_ERR_UNKNOWN, CW_ERR_ROOT_TAG, CW_ERR_STRING_LEN, NBT_ERR_INT32S, NBT_ERR_UNKNOWN, CW_ERR_ROOT_TAG, CW_ERR_STRING_LEN,
/* OpenAL initing errors */ /* OpenAL initing errors */
AL_ERR_INIT_DEVICE, AL_ERR_INIT_CONTEXT AL_ERR_INIT_DEVICE, AL_ERR_INIT_CONTEXT,
/* Inflate errors */
INF_ERR_BLOCKTYPE, INF_ERR_LEN_VERIFY, INF_ERR_REPEAT_BEG, INF_ERR_REPEAT_END
}; };
#endif #endif

View File

@ -166,7 +166,7 @@ cc_result Lvl_Load(struct Stream* stream) {
struct LocalPlayer* p = &LocalPlayer_Instance; struct LocalPlayer* p = &LocalPlayer_Instance;
struct Stream compStream; struct Stream compStream;
struct InflateState state; struct InflateState state;
Inflate_MakeStream(&compStream, &state, stream); Inflate_MakeStream2(&compStream, &state, stream);
if ((res = Map_SkipGZipHeader(stream))) return res; if ((res = Map_SkipGZipHeader(stream))) return res;
if ((res = Stream_Read(&compStream, header, sizeof(header)))) return res; if ((res = Stream_Read(&compStream, header, sizeof(header)))) return res;
@ -241,7 +241,7 @@ cc_result Fcm_Load(struct Stream* stream) {
struct LocalPlayer* p = &LocalPlayer_Instance; struct LocalPlayer* p = &LocalPlayer_Instance;
struct Stream compStream; struct Stream compStream;
struct InflateState state; struct InflateState state;
Inflate_MakeStream(&compStream, &state, stream); Inflate_MakeStream2(&compStream, &state, stream);
if ((res = Stream_Read(stream, header, sizeof(header)))) return res; if ((res = Stream_Read(stream, header, sizeof(header)))) return res;
if (Stream_GetU32_LE(&header[0]) != 0x0FC2AF40UL) return FCM_ERR_IDENTIFIER; if (Stream_GetU32_LE(&header[0]) != 0x0FC2AF40UL) return FCM_ERR_IDENTIFIER;
@ -690,7 +690,7 @@ cc_result Cw_Load(struct Stream* stream) {
Vec3* spawn; IVec3 pos; Vec3* spawn; IVec3 pos;
cc_result res; cc_result res;
Inflate_MakeStream(&compStream, &state, stream); Inflate_MakeStream2(&compStream, &state, stream);
if ((res = Map_SkipGZipHeader(stream))) return res; if ((res = Map_SkipGZipHeader(stream))) return res;
if ((res = compStream.ReadU8(&compStream, &tag))) return res; if ((res = compStream.ReadU8(&compStream, &tag))) return res;
@ -899,7 +899,7 @@ cc_result Dat_Load(struct Stream* stream) {
struct LocalPlayer* p = &LocalPlayer_Instance; struct LocalPlayer* p = &LocalPlayer_Instance;
struct Stream compStream; struct Stream compStream;
struct InflateState state; struct InflateState state;
Inflate_MakeStream(&compStream, &state, stream); Inflate_MakeStream2(&compStream, &state, stream);
if ((res = Map_SkipGZipHeader(stream))) return res; if ((res = Map_SkipGZipHeader(stream))) return res;
if ((res = Stream_Read(&compStream, header, sizeof(header)))) return res; if ((res = Stream_Read(&compStream, header, sizeof(header)))) return res;

View File

@ -409,8 +409,18 @@ static void Classic_Handshake(cc_uint8* data) {
static void Classic_Ping(cc_uint8* data) { } static void Classic_Ping(cc_uint8* data) { }
#define MAP_SIZE_LEN 4
static void DisconnectInvalidMap(cc_result res) {
static const String title = String_FromConst("Disconnected");
String tmp; char tmpBuffer[STRING_SIZE];
String_InitArray(tmp, tmpBuffer);
String_Format1(&tmp, "Server sent corrupted map data (error %h)", &res);
Game_Disconnect(&title, &tmp); return;
}
static void MapState_Init(struct MapState* m) { static void MapState_Init(struct MapState* m) {
Inflate_MakeStream(&m->stream, &m->inflateState, &map_part); Inflate_MakeStream2(&m->stream, &m->inflateState, &map_part);
m->index = 0; m->index = 0;
m->blocks = NULL; m->blocks = NULL;
m->allocFailed = false; m->allocFailed = false;
@ -427,6 +437,7 @@ static void FreeMapStates(void) {
static void MapState_Read(struct MapState* m) { static void MapState_Read(struct MapState* m) {
cc_uint32 left, read; cc_uint32 left, read;
cc_result res;
if (m->allocFailed) return; if (m->allocFailed) return;
if (!m->blocks) { if (!m->blocks) {
@ -440,7 +451,9 @@ static void MapState_Read(struct MapState* m) {
} }
left = map_volume - m->index; left = map_volume - m->index;
m->stream.Read(&m->stream, &m->blocks[m->index], left, &read); res = m->stream.Read(&m->stream, &m->blocks[m->index], left, &read);
if (res) DisconnectInvalidMap(res);
m->index += read; m->index += read;
} }
@ -471,16 +484,7 @@ static void Classic_LevelInit(cc_uint8* data) {
/* Fast map puts volume in header, and uses raw DEFLATE without GZIP header/footer */ /* Fast map puts volume in header, and uses raw DEFLATE without GZIP header/footer */
map_volume = Stream_GetU32_BE(data); map_volume = Stream_GetU32_BE(data);
map_gzHeader.done = true; map_gzHeader.done = true;
map_sizeIndex = 4; map_sizeIndex = MAP_SIZE_LEN;
}
static void DisconnectInvalidMap(cc_result res) {
static const String title = String_FromConst("Disconnected");
String tmp; char tmpBuffer[STRING_SIZE];
String_InitArray(tmp, tmpBuffer);
String_Format1(&tmp, "Server sent corrupted map data (error %h)", &res);
Game_Disconnect(&title, &tmp); return;
} }
static void Classic_LevelDataChunk(cc_uint8* data) { static void Classic_LevelDataChunk(cc_uint8* data) {
@ -508,13 +512,15 @@ static void Classic_LevelDataChunk(cc_uint8* data) {
} }
if (map_gzHeader.done) { if (map_gzHeader.done) {
if (map_sizeIndex < 4) { if (map_sizeIndex < MAP_SIZE_LEN) {
left = 4 - map_sizeIndex; left = MAP_SIZE_LEN - map_sizeIndex;
map.stream.Read(&map.stream, &map_size[map_sizeIndex], left, &read); res = map.stream.Read(&map.stream, &map_size[map_sizeIndex], left, &read);
if (res) { DisconnectInvalidMap(res); return; }
map_sizeIndex += read; map_sizeIndex += read;
} }
if (map_sizeIndex == 4) { if (map_sizeIndex == MAP_SIZE_LEN) {
if (!map_volume) map_volume = Stream_GetU32_BE(map_size); if (!map_volume) map_volume = Stream_GetU32_BE(map_size);
#ifndef EXTENDED_BLOCKS #ifndef EXTENDED_BLOCKS