mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-09-15 18:45:23 -04:00
If the server sends corrupted compressed map data, try to disconnect the game instead of outright crashing
This commit is contained in:
parent
c968d14a75
commit
dcf1b9a49e
@ -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 (;;) {
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user