CVE-2017-11721 (published 2017-08-03T08:29Z)

Buffer overflow in ioquake3 before 2017-08-02 allows remote attackers to cause a denial of service (application crash) or possibly have unspecified other impact via a crafted packet.

Severity: HIGH
CVSS v2 base score: 7.5 (AV:N/AC:L/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 9.8, CRITICAL (CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-119
cwe_name: Improper Restriction of Operations within the Bounds of a Memory Buffer
description: The product performs operations on a memory buffer, but it reads from or writes to a memory location outside the buffer's intended boundary. This may result in read or write operations on unexpected memory locations that could be linked to other variables, data structures, or internal program data.
extended_description: 
            Certain languages allow direct addressing of memory locations and do not automatically ensure that these locations are valid for the memory buffer that is being referenced. This can cause read or write operations to be performed on memory locations that may be associated with other variables, data structures, or internal program data.
         
         

Fix information:
repo_url: https://github.com/ioquake/ioq3
commit_details: repo_url: https://github.com/ioquake/ioq3
author: Zack Middleton
author_date: 2017-08-02 14:55:10-05:00
msg: Fix/improve buffer overflow in MSG_ReadBits/MSG_WriteBits

Prevent reading past end of message in MSG_ReadBits. If read past
end of msg->data buffer (16348 bytes) the engine could SEGFAULT.
Make MSG_WriteBits use an exact buffer overflow check instead of
possibly failing with a few bytes left.
repository: repo_url: https://github.com/ioquake/ioq3
repo_name: ioquake/ioq3
description: The ioquake3 community effort to continue supporting/developing id's Quake III Arena
repo_language: C
owner: ioquake
file_changes: filename: huffman.c
change_type: ModificationType.MODIFY
diff: @@ -279,9 +279,14 @@ int Huff_Receive (node_t *node, int *ch, byte *fin) {
 }
 
 /* Get a symbol */
-void Huff_offsetReceive (node_t *node, int *ch, byte *fin, int *offset) {
+void Huff_offsetReceive (node_t *node, int *ch, byte *fin, int *offset, int maxoffset) {
 	bloc = *offset;
 	while (node && node->symbol == INTERNAL_NODE) {
+		if (bloc >= maxoffset) {
+			*ch = 0;
+			*offset = maxoffset + 1;
+			return;
+		}
 		if (get_bit(fin)) {
 			node = node->right;
 		} else {
@@ -298,11 +303,15 @@ void Huff_offsetReceive (node_t *node, int *ch, byte *fin, int *offset) {
 }
 
 /* Send the prefix code for this node */
-static void send(node_t *node, node_t *child, byte *fout) {
+static void send(node_t *node, node_t *child, byte *fout, int maxoffset) {
 	if (node->parent) {
-		send(node->parent, node, fout);
+		send(node->parent, node, fout, maxoffset);
 	}
 	if (child) {
+		if (bloc >= maxoffset) {
+			bloc = maxoffset + 1;
+			return;
+		}
 		if (node->right == child) {
 			add_bit(1, fout);
 		} else {
@@ -312,22 +321,22 @@ static void send(node_t *node, node_t *child, byte *fout) {
 }
 
 /* Send a symbol */
-void Huff_transmit (huff_t *huff, int ch, byte *fout) {
+void Huff_transmit (huff_t *huff, int ch, byte *fout, int maxoffset) {
 	int i;
 	if (huff->loc[ch] == NULL) { 
 		/* node_t hasn't been transmitted, send a NYT, then the symbol */
-		Huff_transmit(huff, NYT, fout);
+		Huff_transmit(huff, NYT, fout, maxoffset);
 		for (i = 7; i >= 0; i--) {
 			add_bit((char)((ch >> i) & 0x1), fout);
 		}
 	} else {
-		send(huff->loc[ch], NULL, fout);
+		send(huff->loc[ch], NULL, fout, maxoffset);
 	}
 }
 
-void Huff_offsetTransmit (huff_t *huff, int ch, byte *fout, int *offset) {
+void Huff_offsetTransmit (huff_t *huff, int ch, byte *fout, int *offset, int maxoffset) {
 	bloc = *offset;
-	send(huff->loc[ch], NULL, fout);
+	send(huff->loc[ch], NULL, fout, maxoffset);
 	*offset = bloc;
 }
 
@@ -413,7 +422,7 @@ void Huff_Compress(msg_t *mbuf, int offset) {
 
 	for (i=0; i<size; i++ ) {
 		ch = buffer[i];
-		Huff_transmit(&huff, ch, seq);						/* Transmit symbol */
+		Huff_transmit(&huff, ch, seq, size<<3);						/* Transmit symbol */
 		Huff_addRef(&huff, (byte)ch);								/* Do update */
 	}
 

programming_language: C
filename: msg.c
change_type: ModificationType.MODIFY
diff: @@ -107,9 +107,7 @@ void MSG_WriteBits( msg_t *msg, int value, int bits ) {
 
 	oldsize += bits;
 
-	// this isn't an exact overflow check, but close enough
-	if ( msg->maxsize - msg->cursize < 4 ) {
-		msg->overflowed = qtrue;
+	if ( msg->overflowed ) {
 		return;
 	}
 
@@ -122,6 +120,11 @@ void MSG_WriteBits( msg_t *msg, int value, int bits ) {
 	}
 
 	if ( msg->oob ) {
+		if ( msg->cursize + ( bits >> 3 ) > msg->maxsize ) {
+			msg->overflowed = qtrue;
+			return;
+		}
+
 		if ( bits == 8 ) {
 			msg->data[msg->cursize] = value;
 			msg->cursize += 1;
@@ -144,6 +147,10 @@ void MSG_WriteBits( msg_t *msg, int value, int bits ) {
 		if ( bits&7 ) {
 			int nbits;
 			nbits = bits&7;
+			if ( msg->bit + nbits > msg->maxsize << 3 ) {
+				msg->overflowed = qtrue;
+				return;
+			}
 			for( i = 0; i < nbits; i++ ) {
 				Huff_putBit( (value & 1), msg->data, &msg->bit );
 				value = (value >> 1);
@@ -152,8 +159,13 @@ void MSG_WriteBits( msg_t *msg, int value, int bits ) {
 		}
 		if ( bits ) {
 			for( i = 0; i < bits; i += 8 ) {
-				Huff_offsetTransmit( &msgHuff.compressor, (value & 0xff), msg->data, &msg->bit );
+				Huff_offsetTransmit( &msgHuff.compressor, (value & 0xff), msg->data, &msg->bit, msg->maxsize << 3 );
 				value = (value >> 8);
+
+				if ( msg->bit > msg->maxsize << 3 ) {
+					msg->overflowed = qtrue;
+					return;
+				}
 			}
 		}
 		msg->cursize = (msg->bit >> 3) + 1;
@@ -167,6 +179,10 @@ int MSG_ReadBits( msg_t *msg, int bits ) {
 	int			i, nbits;
 //	FILE*	fp;
 
+	if ( msg->readcount > msg->cursize ) {
+		return 0;
+	}
+
 	value = 0;
 
 	if ( bits < 0 ) {
@@ -177,6 +193,11 @@ int MSG_ReadBits( msg_t *msg, int bits ) {
 	}
 
 	if (msg->oob) {
+		if (msg->readcount + (bits>>3) > msg->cursize) {
+			msg->readcount = msg->cursize + 1;
+			return 0;
+		}
+
 		if(bits==8)
 		{
 			value = msg->data[msg->readcount];
@@ -204,6 +225,10 @@ int MSG_ReadBits( msg_t *msg, int bits ) {
 		nbits = 0;
 		if (bits&7) {
 			nbits = bits&7;
+			if (msg->bit + nbits > msg->cursize << 3) {
+				msg->readcount = msg->cursize + 1;
+				return 0;
+			}
 			for(i=0;i<nbits;i++) {
 				value |= (Huff_getBit(msg->data, &msg->bit)<<i);
 			}
@@ -212,9 +237,14 @@ int MSG_ReadBits( msg_t *msg, int bits ) {
 		if (bits) {
 //			fp = fopen("c:\\netchan.bin", "a");
 			for(i=0;i<bits;i+=8) {
-				Huff_offsetReceive (msgHuff.decompressor.tree, &get, msg->data, &msg->bit);
+				Huff_offsetReceive (msgHuff.decompressor.tree, &get, msg->data, &msg->bit, msg->cursize<<3);
 //				fwrite(&get, 1, 1, fp);
 				value |= (get<<(i+nbits));
+
+				if (msg->bit > msg->cursize<<3) {
+					msg->readcount = msg->cursize + 1;
+					return 0;
+				}
 			}
 //			fclose(fp);
 		}

programming_language: C
filename: qcommon.h
change_type: ModificationType.MODIFY
diff: @@ -1196,9 +1196,9 @@ void	Huff_Decompress(msg_t *buf, int offset);
 void	Huff_Init(huffman_t *huff);
 void	Huff_addRef(huff_t* huff, byte ch);
 int		Huff_Receive (node_t *node, int *ch, byte *fin);
-void	Huff_transmit (huff_t *huff, int ch, byte *fout);
-void	Huff_offsetReceive (node_t *node, int *ch, byte *fin, int *offset);
-void	Huff_offsetTransmit (huff_t *huff, int ch, byte *fout, int *offset);
+void	Huff_transmit (huff_t *huff, int ch, byte *fout, int maxoffset);
+void	Huff_offsetReceive (node_t *node, int *ch, byte *fin, int *offset, int maxoffset);
+void	Huff_offsetTransmit (huff_t *huff, int ch, byte *fout, int *offset, int maxoffset);
 void	Huff_putBit( int bit, byte *fout, int *offset);
 int		Huff_getBit( byte *fout, int *offset);
 

programming_language: C