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;
for (i = 0; i < PNG_PALETTE; i++) { palette[i] = BITMAPCOL_BLACK; }
Inflate_MakeStream(&compStream, &inflate, stream);
Inflate_MakeStream2(&compStream, &inflate, stream);
ZLibHeader_Init(&zlibHeader);
for (;;) {

View File

@ -154,6 +154,8 @@ enum INFLATE_STATE_ {
#define Inflate_UNSAFE_EnsureBits(state, bitsCount) while (state->NumBits < bitsCount) { Inflate_GetByte(state); }
/* Peeks then consumes given bits */
#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 */
#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;
}
void Inflate_Init(struct InflateState* state, struct Stream* source) {
void Inflate_Init2(struct InflateState* state, struct Stream* source) {
state->State = INFLATE_STATE_HEADER;
state->LastBlock = false;
state->Bits = 0;
@ -340,6 +342,7 @@ void Inflate_Init(struct InflateState* state, struct Stream* source) {
state->AvailOut = 0;
state->Source = source;
state->WindowIndex = 0;
state->result = 0;
}
static const cc_uint8 fixed_lits[INFLATE_MAX_LITS] = {
@ -496,7 +499,7 @@ void Inflate_Process(struct InflateState* state) {
} break;
case 3: {
Logger_Abort("DEFLATE - Invalid block type");
Inflate_Fail(state, INF_ERR_BLOCKTYPE);
} break;
}
@ -509,7 +512,8 @@ void Inflate_Process(struct InflateState* state) {
nlen = Inflate_ReadBits(state, 16);
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->State = INFLATE_STATE_UNCOMPRESSED_DATA;
@ -602,7 +606,7 @@ void Inflate_Process(struct InflateState* state) {
case 16:
Inflate_EnsureBits(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];
break;
@ -621,7 +625,8 @@ void Inflate_Process(struct InflateState* state) {
count = state->NumLits + state->NumDists;
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);
@ -723,7 +728,7 @@ static cc_result Inflate_StreamRead(struct Stream* stream, cc_uint8* data, cc_ui
hasInput = true;
while (state->AvailOut > 0 && hasInput) {
if (state->State == INFLATE_STATE_DONE) break;
if (state->State == INFLATE_STATE_DONE) return state->result;
if (!state->AvailIn) {
/* 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;
}
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);
Inflate_Init(state, underlying);
Inflate_Init2(state, underlying);
stream->Meta.Inflate = state;
stream->Read = Inflate_StreamRead;
}
@ -1151,7 +1156,7 @@ static cc_result Zip_ReadLocalFileHeader(struct ZipState* state, struct ZipEntry
return state->ProcessEntry(&path, &portion, state);
} else if (method == 8) {
Stream_ReadonlyPortion(&portion, stream, compressedSize);
Inflate_MakeStream(&compStream, &inflate, &portion);
Inflate_MakeStream2(&compStream, &inflate, &portion);
return state->ProcessEntry(&path, &compStream, state);
} else {
Platform_Log1("Unsupported.zip entry compression method: %i", &method);

View File

@ -64,17 +64,18 @@ struct InflateState {
} Table; /* union to save on memory */
struct HuffmanTable TableDists; /* Values represent distances back */
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. */
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. */
/* NOTE: This is a low level call - usually you treat as a stream via Inflate_MakeStream. */
void Inflate_Process(struct InflateState* state);
/* Deompresses input data read from another stream using DEFLATE. Read only stream. */
/* 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. */
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

View File

@ -49,6 +49,8 @@ enum ERRORS_ALL {
/* CW map decoding errors */
NBT_ERR_INT32S, NBT_ERR_UNKNOWN, CW_ERR_ROOT_TAG, CW_ERR_STRING_LEN,
/* 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

View File

@ -166,7 +166,7 @@ cc_result Lvl_Load(struct Stream* stream) {
struct LocalPlayer* p = &LocalPlayer_Instance;
struct Stream compStream;
struct InflateState state;
Inflate_MakeStream(&compStream, &state, stream);
Inflate_MakeStream2(&compStream, &state, stream);
if ((res = Map_SkipGZipHeader(stream))) 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 Stream compStream;
struct InflateState state;
Inflate_MakeStream(&compStream, &state, stream);
Inflate_MakeStream2(&compStream, &state, stream);
if ((res = Stream_Read(stream, header, sizeof(header)))) return res;
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;
cc_result res;
Inflate_MakeStream(&compStream, &state, stream);
Inflate_MakeStream2(&compStream, &state, stream);
if ((res = Map_SkipGZipHeader(stream))) 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 Stream compStream;
struct InflateState state;
Inflate_MakeStream(&compStream, &state, stream);
Inflate_MakeStream2(&compStream, &state, stream);
if ((res = Map_SkipGZipHeader(stream))) 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) { }
#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) {
Inflate_MakeStream(&m->stream, &m->inflateState, &map_part);
Inflate_MakeStream2(&m->stream, &m->inflateState, &map_part);
m->index = 0;
m->blocks = NULL;
m->allocFailed = false;
@ -427,6 +437,7 @@ static void FreeMapStates(void) {
static void MapState_Read(struct MapState* m) {
cc_uint32 left, read;
cc_result res;
if (m->allocFailed) return;
if (!m->blocks) {
@ -440,7 +451,9 @@ static void MapState_Read(struct MapState* m) {
}
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;
}
@ -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 */
map_volume = Stream_GetU32_BE(data);
map_gzHeader.done = true;
map_sizeIndex = 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;
map_sizeIndex = MAP_SIZE_LEN;
}
static void Classic_LevelDataChunk(cc_uint8* data) {
@ -508,13 +512,15 @@ static void Classic_LevelDataChunk(cc_uint8* data) {
}
if (map_gzHeader.done) {
if (map_sizeIndex < 4) {
left = 4 - map_sizeIndex;
map.stream.Read(&map.stream, &map_size[map_sizeIndex], left, &read);
if (map_sizeIndex < MAP_SIZE_LEN) {
left = MAP_SIZE_LEN - map_sizeIndex;
res = map.stream.Read(&map.stream, &map_size[map_sizeIndex], left, &read);
if (res) { DisconnectInvalidMap(res); return; }
map_sizeIndex += read;
}
if (map_sizeIndex == 4) {
if (map_sizeIndex == MAP_SIZE_LEN) {
if (!map_volume) map_volume = Stream_GetU32_BE(map_size);
#ifndef EXTENDED_BLOCKS