On Sat, 05 Aug 2017 at 12:24:19 +0100, Simon McVittie wrote: > Again, I don't have time to handle this for stable right now, so > security or games team members are very welcome to do so. I'll prepare > a stable update during Debconf if nobody gets there first, assuming I > can find a stable user willing to test a game from contrib.
I have prepared proposed stable-security updates and borrowed a stable machine to smoke-test them (thanks to Andy Simpkins). I forget whether you're interested in fixing contrib or not, so I'm doing iortcw as well as ioquake3 (openjk is experimental-only so is not relevant here). Let me know if I should redirect the iortcw update to the release team. Here is some text which might be useful for a DSA: ----8<---- A read buffer overflow was discovered in the idtech3 (Quake III Arena) family of game engines. This allows remote attackers to cause a denial of service (application crash) or possibly have unspecified other impact via a crafted packet. (CVE-2017-11721) In Debian, this issue affects the ioquake3, iortcw and openjk packages. For the stable distribution (stretch), this issue has been fixed in ioquake3 version 1.36+u20161101+dfsg1-2+deb9u1 and in iortcw version 1.50a+dfsg1-3+deb9u1. In the unstable distribution (sid), this issue has been fixed in ioquake3 version 1.36+u20170803+dfsg1-1 and in iortcw version 1.51+dfsg1-3. In the experimental distribution this issue has been fixed in openjk version 0~20170718+dfsg1-2. ---->8---- Proposed debdiffs attached. OK to upload? Regards, S
diffstat for iortcw-1.50a+dfsg1 iortcw-1.50a+dfsg1 changelog | 8 patches/debian/Remove-support-for-downloading-executable-updates.patch | 2 patches/security/All-Fix-improve-buffer-overflow-in-MSG_ReadBits-MSG_Write.patch | 626 ++++++++++ patches/series | 1 4 files changed, 636 insertions(+), 1 deletion(-) diff -Nru iortcw-1.50a+dfsg1/debian/changelog iortcw-1.50a+dfsg1/debian/changelog --- iortcw-1.50a+dfsg1/debian/changelog 2017-03-14 05:37:19.000000000 -0400 +++ iortcw-1.50a+dfsg1/debian/changelog 2017-08-08 14:57:52.000000000 -0400 @@ -1,3 +1,11 @@ +iortcw (1.50a+dfsg1-3+deb9u1) stretch-security; urgency=medium + + * d/p/security/All-Fix-improve-buffer-overflow-in-MSG_ReadBits-MSG_Write.patch: + Add patch (from ioquake3 via upstream) to fix a read buffer overflow + in MSG_ReadBits (CVE-2017-11721) + + -- Simon McVittie <s...@debian.org> Tue, 08 Aug 2017 14:57:52 -0400 + iortcw (1.50a+dfsg1-3) unstable; urgency=high * d/gbp.conf: switch branch to debian/stretch for updates during freeze diff -Nru iortcw-1.50a+dfsg1/debian/patches/debian/Remove-support-for-downloading-executable-updates.patch iortcw-1.50a+dfsg1/debian/patches/debian/Remove-support-for-downloading-executable-updates.patch --- iortcw-1.50a+dfsg1/debian/patches/debian/Remove-support-for-downloading-executable-updates.patch 2017-03-14 05:37:19.000000000 -0400 +++ iortcw-1.50a+dfsg1/debian/patches/debian/Remove-support-for-downloading-executable-updates.patch 2017-08-08 14:57:52.000000000 -0400 @@ -219,7 +219,7 @@ // DHM - Nerve diff --git a/MP/code/qcommon/qcommon.h b/MP/code/qcommon/qcommon.h -index 1f23d0f..b1fff59 100644 +index 02ef8e8..96a9081 100644 --- a/MP/code/qcommon/qcommon.h +++ b/MP/code/qcommon/qcommon.h @@ -1258,12 +1258,6 @@ void Sys_StartProcess( char *cmdline, qboolean doexit ); // NERVE - S diff -Nru iortcw-1.50a+dfsg1/debian/patches/security/All-Fix-improve-buffer-overflow-in-MSG_ReadBits-MSG_Write.patch iortcw-1.50a+dfsg1/debian/patches/security/All-Fix-improve-buffer-overflow-in-MSG_ReadBits-MSG_Write.patch --- iortcw-1.50a+dfsg1/debian/patches/security/All-Fix-improve-buffer-overflow-in-MSG_ReadBits-MSG_Write.patch 1969-12-31 19:00:00.000000000 -0500 +++ iortcw-1.50a+dfsg1/debian/patches/security/All-Fix-improve-buffer-overflow-in-MSG_ReadBits-MSG_Write.patch 2017-08-08 14:57:52.000000000 -0400 @@ -0,0 +1,626 @@ +From: MAN-AT-ARMS <m4n4t4...@gmail.com> +Date: Thu, 3 Aug 2017 00:06:37 -0400 +Subject: All: Fix/improve buffer overflow in MSG_ReadBits/MSG_WriteBits + +Origin: upstream, commit:260c39a29af517a08b3ee1a0e78ad654bdd70934 +Bug-CVE: CVE-2017-11721 +Bug-Debian: https://bugs.debian.org/870811 +--- + MP/code/qcommon/huffman.c | 49 ++++++++++++++++++++--------------- + MP/code/qcommon/msg.c | 45 +++++++++++++++++++++++++------- + MP/code/qcommon/qcommon.h | 6 ++--- + SP/code/qcommon/huffman.c | 49 ++++++++++++++++++++--------------- + SP/code/qcommon/msg.c | 65 ++++++++++++++++++++++++++++++++++------------- + SP/code/qcommon/qcommon.h | 6 ++--- + 6 files changed, 145 insertions(+), 75 deletions(-) + +diff --git a/MP/code/qcommon/huffman.c b/MP/code/qcommon/huffman.c +index 00b007e..88b972c 100644 +--- a/MP/code/qcommon/huffman.c ++++ b/MP/code/qcommon/huffman.c +@@ -36,7 +36,7 @@ If you have questions concerning this license or the applicable additional terms + + static int bloc = 0; + +-void Huff_putBit( int bit, byte *fout, int *offset ) { ++void Huff_putBit( int bit, byte *fout, int *offset ) { + bloc = *offset; + if ( ( bloc & 7 ) == 0 ) { + fout[( bloc >> 3 )] = 0; +@@ -46,17 +46,15 @@ void Huff_putBit( int bit, byte *fout, int *offset ) { + *offset = bloc; + } + +-int Huff_getBloc(void) +-{ ++int Huff_getBloc( void ) { + return bloc; + } + +-void Huff_setBloc(int _bloc) +-{ ++void Huff_setBloc( int _bloc ) { + bloc = _bloc; + } + +-int Huff_getBit( byte *fin, int *offset ) { ++int Huff_getBit( byte *fin, int *offset ) { + int t; + bloc = *offset; + t = ( fin[( bloc >> 3 )] >> ( bloc & 7 ) ) & 0x1; +@@ -286,9 +284,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 { +@@ -305,11 +308,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 { +@@ -319,22 +326,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; + } + +@@ -374,17 +381,17 @@ void Huff_Decompress( msg_t *mbuf, int offset ) { + seq[j] = 0; + break; + } +- Huff_Receive( huff.tree, &ch, buffer ); /* Get a character */ +- if ( ch == NYT ) { /* We got a NYT, get the symbol associated with it */ ++ Huff_Receive( huff.tree, &ch, buffer ); /* Get a character */ ++ if ( ch == NYT ) { /* We got a NYT, get the symbol associated with it */ + ch = 0; + for ( i = 0; i < 8; i++ ) { + ch = ( ch << 1 ) + get_bit( buffer ); + } + } + +- seq[j] = ch; /* Write symbol */ ++ seq[j] = ch; /* Write symbol */ + +- Huff_addRef( &huff, (byte)ch ); /* Increment node */ ++ Huff_addRef( &huff, (byte)ch ); /* Increment node */ + } + mbuf->cursize = cch + offset; + Com_Memcpy( mbuf->data + offset, seq, cch ); +@@ -420,11 +427,11 @@ 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_addRef( &huff, (byte)ch ); /* Do update */ ++ Huff_transmit( &huff, ch, seq, size << 3 ); /* Transmit symbol */ ++ Huff_addRef( &huff, (byte)ch ); /* Do update */ + } + +- bloc += 8; // next byte ++ bloc += 8; // next byte + + mbuf->cursize = ( bloc >> 3 ) + offset; + Com_Memcpy( mbuf->data + offset, seq, ( bloc >> 3 ) ); +diff --git a/MP/code/qcommon/msg.c b/MP/code/qcommon/msg.c +index 56ef5fc..e8a5e7e 100644 +--- a/MP/code/qcommon/msg.c ++++ b/MP/code/qcommon/msg.c +@@ -114,9 +114,7 @@ void MSG_WriteBits( msg_t *msg, int value, int bits ) { + + msg->uncompsize += bits; // NERVE - SMF - net debugging + +- // this isn't an exact overflow check, but close enough +- if ( msg->maxsize - msg->cursize < 4 ) { +- msg->overflowed = qtrue; ++ if ( msg->overflowed ) { + return; + } + +@@ -128,6 +126,11 @@ void MSG_WriteBits( msg_t *msg, int value, int bits ) { + bits = -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; +@@ -150,6 +153,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 ); +@@ -158,8 +165,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; +@@ -173,6 +185,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 ) { +@@ -183,6 +199,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]; + msg->readcount += 1; +@@ -205,6 +226,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 ); + } +@@ -213,9 +238,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); + } +@@ -280,8 +310,8 @@ void MSG_WriteLong( msg_t *sb, int c ) { + } + + void MSG_WriteFloat( msg_t *sb, float f ) { +- + floatint_t dat; ++ + dat.f = f; + MSG_WriteBits( sb, dat.i, 32 ); + } +@@ -409,7 +439,6 @@ int MSG_ReadLong( msg_t *msg ) { + } + + float MSG_ReadFloat( msg_t *msg ) { +- + floatint_t dat; + + dat.i = MSG_ReadBits( msg, 32 ); +@@ -462,7 +491,6 @@ char *MSG_ReadBigString( msg_t *msg ) { + if ( c == '%' ) { + c = '.'; + } +- + // don't allow higher ascii values + if ( c > 127 ) { + c = '.'; +@@ -491,7 +519,6 @@ char *MSG_ReadStringLine( msg_t *msg ) { + if ( c == '%' ) { + c = '.'; + } +- + // don't allow higher ascii values + if ( c > 127 ) { + c = '.'; +diff --git a/MP/code/qcommon/qcommon.h b/MP/code/qcommon/qcommon.h +index 1f23d0f..02ef8e8 100644 +--- a/MP/code/qcommon/qcommon.h ++++ b/MP/code/qcommon/qcommon.h +@@ -1327,9 +1327,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 ); + +diff --git a/SP/code/qcommon/huffman.c b/SP/code/qcommon/huffman.c +index 00b007e..88b972c 100644 +--- a/SP/code/qcommon/huffman.c ++++ b/SP/code/qcommon/huffman.c +@@ -36,7 +36,7 @@ If you have questions concerning this license or the applicable additional terms + + static int bloc = 0; + +-void Huff_putBit( int bit, byte *fout, int *offset ) { ++void Huff_putBit( int bit, byte *fout, int *offset ) { + bloc = *offset; + if ( ( bloc & 7 ) == 0 ) { + fout[( bloc >> 3 )] = 0; +@@ -46,17 +46,15 @@ void Huff_putBit( int bit, byte *fout, int *offset ) { + *offset = bloc; + } + +-int Huff_getBloc(void) +-{ ++int Huff_getBloc( void ) { + return bloc; + } + +-void Huff_setBloc(int _bloc) +-{ ++void Huff_setBloc( int _bloc ) { + bloc = _bloc; + } + +-int Huff_getBit( byte *fin, int *offset ) { ++int Huff_getBit( byte *fin, int *offset ) { + int t; + bloc = *offset; + t = ( fin[( bloc >> 3 )] >> ( bloc & 7 ) ) & 0x1; +@@ -286,9 +284,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 { +@@ -305,11 +308,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 { +@@ -319,22 +326,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; + } + +@@ -374,17 +381,17 @@ void Huff_Decompress( msg_t *mbuf, int offset ) { + seq[j] = 0; + break; + } +- Huff_Receive( huff.tree, &ch, buffer ); /* Get a character */ +- if ( ch == NYT ) { /* We got a NYT, get the symbol associated with it */ ++ Huff_Receive( huff.tree, &ch, buffer ); /* Get a character */ ++ if ( ch == NYT ) { /* We got a NYT, get the symbol associated with it */ + ch = 0; + for ( i = 0; i < 8; i++ ) { + ch = ( ch << 1 ) + get_bit( buffer ); + } + } + +- seq[j] = ch; /* Write symbol */ ++ seq[j] = ch; /* Write symbol */ + +- Huff_addRef( &huff, (byte)ch ); /* Increment node */ ++ Huff_addRef( &huff, (byte)ch ); /* Increment node */ + } + mbuf->cursize = cch + offset; + Com_Memcpy( mbuf->data + offset, seq, cch ); +@@ -420,11 +427,11 @@ 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_addRef( &huff, (byte)ch ); /* Do update */ ++ Huff_transmit( &huff, ch, seq, size << 3 ); /* Transmit symbol */ ++ Huff_addRef( &huff, (byte)ch ); /* Do update */ + } + +- bloc += 8; // next byte ++ bloc += 8; // next byte + + mbuf->cursize = ( bloc >> 3 ) + offset; + Com_Memcpy( mbuf->data + offset, seq, ( bloc >> 3 ) ); +diff --git a/SP/code/qcommon/msg.c b/SP/code/qcommon/msg.c +index 350cfd0..f88e88c 100644 +--- a/SP/code/qcommon/msg.c ++++ b/SP/code/qcommon/msg.c +@@ -87,14 +87,13 @@ void MSG_BeginReadingOOB( msg_t *msg ) { + msg->oob = qtrue; + } + +-void MSG_Copy(msg_t *buf, byte *data, int length, msg_t *src) +-{ +- if (length<src->cursize) { +- Com_Error( ERR_DROP, "MSG_Copy: can't copy into a smaller msg_t buffer"); ++void MSG_Copy( msg_t *buf, byte *data, int length, msg_t *src ) { ++ if ( length < src->cursize ) { ++ Com_Error( ERR_DROP, "MSG_Copy: can't copy into a smaller msg_t buffer" ); + } +- Com_Memcpy(buf, src, sizeof(msg_t)); ++ Com_Memcpy( buf, src, sizeof( msg_t ) ); + buf->data = data; +- Com_Memcpy(buf->data, src->data, src->cursize); ++ Com_Memcpy( buf->data, src->data, src->cursize ); + } + + /* +@@ -111,9 +110,8 @@ 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; + } + +@@ -125,13 +123,18 @@ void MSG_WriteBits( msg_t *msg, int value, int bits ) { + bits = -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; + msg->bit += 8; + } else if ( bits == 16 ) { + short temp = value; +- ++ + CopyLittleShort(&msg->data[msg->cursize], &temp); + msg->cursize += 2; + msg->bit += 16; +@@ -147,6 +150,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 ); +@@ -155,8 +162,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; +@@ -170,6 +182,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 ) { +@@ -180,6 +196,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]; + msg->readcount += 1; +@@ -202,6 +223,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 ); + } +@@ -210,9 +235,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); + } +@@ -607,7 +637,7 @@ void MSG_WriteDeltaKey( msg_t *msg, int key, int oldV, int newV, int bits ) { + + int MSG_ReadDeltaKey( msg_t *msg, int key, int oldV, int bits ) { + if ( MSG_ReadBits( msg, 1 ) ) { +- return MSG_ReadBits( msg, bits ) ^ ( key & kbitmask[bits - 1] ); ++ return MSG_ReadBits( msg, bits ) ^ ( key & kbitmask[ bits - 1 ] ); + } + return oldV; + } +@@ -643,9 +673,9 @@ usercmd_t communication + */ + + /* +-===================== ++======================== + MSG_WriteDeltaUsercmdKey +-===================== ++======================== + */ + void MSG_WriteDeltaUsercmdKey( msg_t *msg, int key, usercmd_t *from, usercmd_t *to ) { + if ( to->serverTime - from->serverTime < 256 ) { +@@ -690,9 +720,9 @@ void MSG_WriteDeltaUsercmdKey( msg_t *msg, int key, usercmd_t *from, usercmd_t * + + + /* +-===================== ++======================= + MSG_ReadDeltaUsercmdKey +-===================== ++======================= + */ + void MSG_ReadDeltaUsercmdKey( msg_t *msg, int key, usercmd_t *from, usercmd_t *to ) { + if ( MSG_ReadBits( msg, 1 ) ) { +@@ -896,7 +926,6 @@ void MSG_ReportChangeVectors_f( void ) { + #endif + } + +- + typedef struct { + char *name; + int offset; +diff --git a/SP/code/qcommon/qcommon.h b/SP/code/qcommon/qcommon.h +index f424036..e481bd2 100644 +--- a/SP/code/qcommon/qcommon.h ++++ b/SP/code/qcommon/qcommon.h +@@ -1241,9 +1241,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 ); + diff -Nru iortcw-1.50a+dfsg1/debian/patches/series iortcw-1.50a+dfsg1/debian/patches/series --- iortcw-1.50a+dfsg1/debian/patches/series 2017-03-14 05:37:19.000000000 -0400 +++ iortcw-1.50a+dfsg1/debian/patches/series 2017-08-08 14:57:52.000000000 -0400 @@ -2,6 +2,7 @@ security/All-Don-t-open-.pk3-files-as-OpenAL-drivers.patch security/All-Merge-some-file-writing-extension-checks.patch Don-t-require-.git-index-to-exist.patch +security/All-Fix-improve-buffer-overflow-in-MSG_ReadBits-MSG_Write.patch debian/Disable-client-side-auto-download-by-default.patch debian/File-access-methods-prevent-overwriting-DLLs-CVE-201.patch debian/Remove-support-for-downloading-executable-updates.patch
diffstat for ioquake3-1.36+u20161101+dfsg1 ioquake3-1.36+u20161101+dfsg1 changelog | 13 patches/debian/Add-a-special-vmMagic-that-causes-equivalent-native-.patch | 10 patches/debian/Run-in-a-window-by-default-on-new-installations.patch | 4 patches/security/Fix-improve-buffer-overflow-in-MSG_ReadBits-MSG_WriteBits.patch | 213 ++++++++++ patches/series | 1 5 files changed, 233 insertions(+), 8 deletions(-) diff -Nru ioquake3-1.36+u20161101+dfsg1/debian/changelog ioquake3-1.36+u20161101+dfsg1/debian/changelog --- ioquake3-1.36+u20161101+dfsg1/debian/changelog 2017-03-14 06:14:37.000000000 -0400 +++ ioquake3-1.36+u20161101+dfsg1/debian/changelog 2017-08-08 14:58:14.000000000 -0400 @@ -1,3 +1,14 @@ +ioquake3 (1.36+u20161101+dfsg1-2+deb9u1) stretch-security; urgency=medium + + * Reference CVE-2017-6903 in previous changelog entry + * Add patch from upstream: + + Address read buffer overflow in + MSG_ReadBits (CVE-2017-11721) (Closes: #870725) + + Check buffer boundary exactly in MSG_WriteBits, instead of + potentially failing with a few bytes still available + + -- Simon McVittie <s...@debian.org> Tue, 08 Aug 2017 14:58:14 -0400 + ioquake3 (1.36+u20161101+dfsg1-2) unstable; urgency=high * d/gbp.conf: switch branch to debian/stretch for updates during freeze @@ -13,7 +24,7 @@ command - refuse to overwrite files other than *.cfg with the writeconfig console command - (Closes: #857699) + (Closes: #857699; CVE-2017-6903) * Add patch adapted from openarena to request confirmation before enabling auto-downloading if the native-code Quake III Arena UI is in use. Unfortunately this is not the case with quake3_46, but diff -Nru ioquake3-1.36+u20161101+dfsg1/debian/patches/debian/Add-a-special-vmMagic-that-causes-equivalent-native-.patch ioquake3-1.36+u20161101+dfsg1/debian/patches/debian/Add-a-special-vmMagic-that-causes-equivalent-native-.patch --- ioquake3-1.36+u20161101+dfsg1/debian/patches/debian/Add-a-special-vmMagic-that-causes-equivalent-native-.patch 2017-03-14 06:14:37.000000000 -0400 +++ ioquake3-1.36+u20161101+dfsg1/debian/patches/debian/Add-a-special-vmMagic-that-causes-equivalent-native-.patch 2017-08-08 14:58:14.000000000 -0400 @@ -26,10 +26,10 @@ 4 files changed, 64 insertions(+), 5 deletions(-) diff --git a/code/qcommon/files.c b/code/qcommon/files.c -index 3b4a8ae..3fcb125 100644 +index 27f5713..dc9b504 100644 --- a/code/qcommon/files.c +++ b/code/qcommon/files.c -@@ -1418,7 +1418,7 @@ Return the searchpath in "startSearch". +@@ -1426,7 +1426,7 @@ Return the searchpath in "startSearch". ================= */ @@ -38,7 +38,7 @@ { searchpath_t *search, *lastSearch; directory_t *dir; -@@ -1442,7 +1442,7 @@ int FS_FindVM(void **startSearch, char *found, int foundlen, const char *name, i +@@ -1450,7 +1450,7 @@ int FS_FindVM(void **startSearch, char *found, int foundlen, const char *name, i while(search) { @@ -47,7 +47,7 @@ { dir = search->dir; -@@ -1465,7 +1465,7 @@ int FS_FindVM(void **startSearch, char *found, int foundlen, const char *name, i +@@ -1473,7 +1473,7 @@ int FS_FindVM(void **startSearch, char *found, int foundlen, const char *name, i return VMI_COMPILED; } } @@ -57,7 +57,7 @@ pack = search->pack; diff --git a/code/qcommon/qcommon.h b/code/qcommon/qcommon.h -index b965690..6847ee1 100644 +index da89f15..8648afe 100644 --- a/code/qcommon/qcommon.h +++ b/code/qcommon/qcommon.h @@ -627,7 +627,7 @@ qboolean FS_FileExists( const char *file ); diff -Nru ioquake3-1.36+u20161101+dfsg1/debian/patches/debian/Run-in-a-window-by-default-on-new-installations.patch ioquake3-1.36+u20161101+dfsg1/debian/patches/debian/Run-in-a-window-by-default-on-new-installations.patch --- ioquake3-1.36+u20161101+dfsg1/debian/patches/debian/Run-in-a-window-by-default-on-new-installations.patch 2017-03-14 06:14:37.000000000 -0400 +++ ioquake3-1.36+u20161101+dfsg1/debian/patches/debian/Run-in-a-window-by-default-on-new-installations.patch 2017-08-08 14:58:14.000000000 -0400 @@ -33,10 +33,10 @@ r_customwidth = ri.Cvar_Get( "r_customwidth", "1600", CVAR_ARCHIVE | CVAR_LATCH ); r_customheight = ri.Cvar_Get( "r_customheight", "1024", CVAR_ARCHIVE | CVAR_LATCH ); diff --git a/code/renderergl2/tr_init.c b/code/renderergl2/tr_init.c -index 30803e2..c60ad40 100644 +index 7b9c21a..035e02c 100644 --- a/code/renderergl2/tr_init.c +++ b/code/renderergl2/tr_init.c -@@ -1186,8 +1186,8 @@ void R_Register( void ) +@@ -1182,8 +1182,8 @@ void R_Register( void ) ri.Cvar_CheckRange( r_ext_multisample, 0, 4, qtrue ); r_overBrightBits = ri.Cvar_Get ("r_overBrightBits", "1", CVAR_ARCHIVE | CVAR_LATCH ); r_ignorehwgamma = ri.Cvar_Get( "r_ignorehwgamma", "0", CVAR_ARCHIVE | CVAR_LATCH); diff -Nru ioquake3-1.36+u20161101+dfsg1/debian/patches/security/Fix-improve-buffer-overflow-in-MSG_ReadBits-MSG_WriteBits.patch ioquake3-1.36+u20161101+dfsg1/debian/patches/security/Fix-improve-buffer-overflow-in-MSG_ReadBits-MSG_WriteBits.patch --- ioquake3-1.36+u20161101+dfsg1/debian/patches/security/Fix-improve-buffer-overflow-in-MSG_ReadBits-MSG_WriteBits.patch 1969-12-31 19:00:00.000000000 -0500 +++ ioquake3-1.36+u20161101+dfsg1/debian/patches/security/Fix-improve-buffer-overflow-in-MSG_ReadBits-MSG_WriteBits.patch 2017-08-08 14:58:14.000000000 -0400 @@ -0,0 +1,213 @@ +From: Zack Middleton <z...@cloemail.com> +Date: Wed, 2 Aug 2017 14:55:10 -0500 +Subject: 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. + +Origin: upstream, commit:d2b1d124d4055c2fcbe5126863487c52fd58cca1 +Bug-CVE: CVE-2017-11721 +Bug-Debian: https://bugs.debian.org/870725 +--- + code/qcommon/huffman.c | 27 ++++++++++++++++++--------- + code/qcommon/msg.c | 40 +++++++++++++++++++++++++++++++++++----- + code/qcommon/qcommon.h | 6 +++--- + 3 files changed, 56 insertions(+), 17 deletions(-) + +diff --git a/code/qcommon/huffman.c b/code/qcommon/huffman.c +index c1b9f24..1f42498 100644 +--- a/code/qcommon/huffman.c ++++ b/code/qcommon/huffman.c +@@ -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 */ + } + +diff --git a/code/qcommon/msg.c b/code/qcommon/msg.c +index 20dec91..eb767be 100644 +--- a/code/qcommon/msg.c ++++ b/code/qcommon/msg.c +@@ -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); + } +diff --git a/code/qcommon/qcommon.h b/code/qcommon/qcommon.h +index f8cd035..da89f15 100644 +--- a/code/qcommon/qcommon.h ++++ b/code/qcommon/qcommon.h +@@ -1191,9 +1191,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); + diff -Nru ioquake3-1.36+u20161101+dfsg1/debian/patches/series ioquake3-1.36+u20161101+dfsg1/debian/patches/series --- ioquake3-1.36+u20161101+dfsg1/debian/patches/series 2017-03-14 06:14:37.000000000 -0400 +++ ioquake3-1.36+u20161101+dfsg1/debian/patches/series 2017-08-08 14:58:14.000000000 -0400 @@ -1,6 +1,7 @@ security/Don-t-load-.pk3s-as-.dlls-and-don-t-load-user-config-file.patch security/Don-t-open-.pk3-files-as-OpenAL-drivers.patch security/Merge-some-file-writing-extension-checks-from-OpenJK.patch +security/Fix-improve-buffer-overflow-in-MSG_ReadBits-MSG_WriteBits.patch debian/Add-sv_dorestart-which-can-be-set-by-game-code-to-re.patch debian/Let-servers-set-sv_fps-too.patch debian/Add-a-special-vmMagic-that-causes-equivalent-native-.patch