Skip to content

Commit 92028d4

Browse files
committed
tier1: fix possible unsafe read in lzss SafeUncompress, make tests pass under asan
1 parent 4f063c4 commit 92028d4

File tree

7 files changed

+115
-25
lines changed

7 files changed

+115
-25
lines changed

engine/common.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -1385,7 +1385,7 @@ bool COM_BufferToBufferDecompress( void *dest, unsigned int *destLen, const void
13851385
if ( pHeader->id == LZSS_ID )
13861386
{
13871387
CLZSS s;
1388-
int nActualDecompressedSize = s.SafeUncompress( (byte *)source, (byte *)dest, *destLen );
1388+
int nActualDecompressedSize = s.SafeUncompress( (byte *)source, sourceLen, (byte *)dest, *destLen );
13891389
if ( nActualDecompressedSize != nDecompressedSize )
13901390
{
13911391
Warning( "NET_BufferToBufferDecompress: header said %d bytes would be decompressed, but we LZSS decompressed %d\n", nDecompressedSize, nActualDecompressedSize );

gameui/BasePanel.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -915,7 +915,7 @@ CBasePanel::CBasePanel() : Panel(NULL, "BaseGameUIPanel")
915915
}
916916
}
917917

918-
// if( IsAndroid() )
918+
if( IsAndroid() )
919919
{
920920
AddUrlButton( this, "vgui/\x64\x69\x73\x63\x6f\x72\x64\x5f\x6c\x6f\x67\x6f", "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x64\x69\x73\x63\x6f\x72\x64\x2e\x67\x67\x2f\x68\x5a\x52\x42\x37\x57\x4d\x67\x47\x77" );
921921
AddUrlButton( this, "vgui/\x74\x77\x69\x74\x74\x65\x72\x5f\x6c\x6f\x67\x6f", "\x68\x74\x74\x70\x73\x3a\x2f\x2f\x74\x77\x69\x74\x74\x65\x72\x2e\x63\x6f\x6d\x2f\x6e\x69\x6c\x6c\x65\x72\x75\x73\x72" );

public/tier0/platform.h

+10
Original file line numberDiff line numberDiff line change
@@ -606,6 +606,16 @@ typedef void * HINSTANCE;
606606
#define FMTFUNCTION( a, b )
607607
#endif
608608

609+
#if defined(__has_feature)
610+
#if __has_feature(address_sanitizer)
611+
#define USING_ASAN 1
612+
#endif
613+
#endif
614+
615+
#if !defined( USING_ASAN ) && defined( __SANITIZE_ADDRESS__ )
616+
#define USING_ASAN 1
617+
#endif
618+
609619
#if COMPILER_CLANG || COMPILER_GCC
610620
#define NO_ASAN __attribute__((no_sanitize("address")))
611621
#else

public/tier1/lzss.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ class CLZSS
3030
unsigned char* CompressNoAlloc( const unsigned char *pInput, int inputlen, unsigned char *pOutput, unsigned int *pOutputSize );
3131
unsigned int Uncompress( const unsigned char *pInput, unsigned char *pOutput );
3232
//unsigned int Uncompress( unsigned char *pInput, CUtlBuffer &buf );
33-
unsigned int SafeUncompress( const unsigned char *pInput, unsigned char *pOutput, unsigned int unBufSize );
33+
unsigned int SafeUncompress( const unsigned char *pInput, unsigned int inputSize, unsigned char *pOutput, unsigned int unBufSize );
3434

3535
static bool IsCompressed( const unsigned char *pInput );
3636
static unsigned int GetActualSize( const unsigned char *pInput );

tier1/lzss.cpp

+23-21
Original file line numberDiff line numberDiff line change
@@ -294,59 +294,61 @@ unsigned int CLZSS::Uncompress( unsigned char *pInput, CUtlBuffer &buf )
294294
}
295295
*/
296296

297-
unsigned int CLZSS::SafeUncompress( const unsigned char *pInput, unsigned char *pOutput, unsigned int unBufSize )
297+
unsigned int CLZSS::SafeUncompress( const unsigned char *pInput, unsigned int inputSize, unsigned char *pOutput, unsigned int unBufSize )
298298
{
299299
unsigned int totalBytes = 0;
300300
int cmdByte = 0;
301301
int getCmdByte = 0;
302302

303303
unsigned int actualSize = GetActualSize( pInput );
304-
if ( !actualSize )
305-
{
306-
// unrecognized
307-
return 0;
308-
}
309304

310-
if ( actualSize > unBufSize )
311-
{
305+
if ( !actualSize ||
306+
actualSize > unBufSize ||
307+
inputSize <= sizeof( lzss_header_t ) )
312308
return 0;
313-
}
309+
310+
const unsigned char *pInputEnd = pInput+inputSize-1;
311+
const unsigned char *pOrigOutput = pOutput;
314312

315313
pInput += sizeof( lzss_header_t );
316314

317315
for ( ;; )
318316
{
319-
if ( !getCmdByte )
317+
if ( !getCmdByte )
320318
{
319+
if( pInput > pInputEnd )
320+
return 0;
321+
321322
cmdByte = *pInput++;
322323
}
323324
getCmdByte = ( getCmdByte + 1 ) & 0x07;
324325

325326
if ( cmdByte & 0x01 )
326327
{
328+
if( pInput+1 > pInputEnd )
329+
return 0;
330+
327331
int position = *pInput++ << LZSS_LOOKSHIFT;
328332
position |= ( *pInput >> LZSS_LOOKSHIFT );
329333
int count = ( *pInput++ & 0x0F ) + 1;
330-
if ( count == 1 )
331-
{
334+
if ( count == 1 )
332335
break;
333-
}
336+
334337
unsigned char *pSource = pOutput - position - 1;
335338

336-
if ( totalBytes + count > unBufSize )
337-
{
339+
if ( totalBytes + count > unBufSize ||
340+
pSource < pOrigOutput )
338341
return 0;
339-
}
340342

341343
for ( int i=0; i<count; i++ )
342-
{
343344
*pOutput++ = *pSource++;
344-
}
345+
345346
totalBytes += count;
346-
}
347-
else
347+
}
348+
else
348349
{
349-
if ( totalBytes + 1 > unBufSize )
350+
if ( totalBytes + 1 > unBufSize ||
351+
pInput > pInputEnd )
350352
return 0;
351353

352354
*pOutput++ = *pInput++;

unittests/tier1test/lzsstest.cpp

+78
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#include "tier0/dbg.h"
2+
#include "unitlib/unitlib.h"
3+
#include "tier1/lzss.h"
4+
5+
#ifdef USING_ASAN
6+
#include <sanitizer/asan_interface.h>
7+
#endif
8+
9+
DEFINE_TESTSUITE( LZSSSafeUncompressTestSuite )
10+
11+
static void SafeUncompressTests()
12+
{
13+
CLZSS lzss;
14+
char poision1[8192];
15+
unsigned char in[256];
16+
17+
char poision2[8192];
18+
19+
unsigned char out[256];
20+
char poision3[8192];
21+
22+
#ifdef USING_ASAN
23+
ASAN_POISON_MEMORY_REGION( poision1, 8192 );
24+
ASAN_POISON_MEMORY_REGION( poision2, 8192 );
25+
ASAN_POISON_MEMORY_REGION( poision3, 8192 );
26+
#endif
27+
28+
lzss_header_t *pHeader = (lzss_header_t*)in;
29+
pHeader->actualSize = sizeof(in)-sizeof(lzss_header_t);
30+
pHeader->id = LZSS_ID;
31+
32+
uint result = 0;
33+
34+
// 0xff bytes test
35+
memset(in+8, 0xFF, sizeof(in)-sizeof(lzss_header_t));
36+
result = lzss.SafeUncompress( in, sizeof(in), out, sizeof(out) );
37+
Shipping_Assert( result == 0 );
38+
39+
// zero bytes test
40+
memset(in+8, 0x0, sizeof(in)-sizeof(lzss_header_t));
41+
result = lzss.SafeUncompress( in, sizeof(in), out, sizeof(out) );
42+
Shipping_Assert( result == 0 );
43+
44+
// valid data, invalid header
45+
pHeader->actualSize = 1;
46+
pHeader->id = LZSS_ID;
47+
result = lzss.SafeUncompress( in, sizeof(in), out, sizeof(out) );
48+
Shipping_Assert( result == 0 );
49+
50+
pHeader->actualSize = 999;
51+
pHeader->id = LZSS_ID;
52+
result = lzss.SafeUncompress( in, sizeof(in), out, sizeof(out) );
53+
Shipping_Assert( result == 0 );
54+
55+
pHeader->actualSize = 999;
56+
pHeader->id = 1337;
57+
result = lzss.SafeUncompress( in, sizeof(in), out, sizeof(out) );
58+
Shipping_Assert( result == 0 );
59+
60+
61+
// valid header, valid data
62+
const unsigned char compressed[] = {0x4c,0x5a,0x53,0x53,0x1a,0x0,0x0,0x0,0x0,0x44,0x6f,0x20,0x79,0x6f,0x75,0x20,0x6c,0x0,0x69,0x6b,0x65,0x20,0x77,0x68,0x61,0x74,0x41,0x0,0xd4,0x73,0x65,0x65,0x3f,0x0,0x0,0x0};
63+
64+
pHeader->actualSize = sizeof(compressed);
65+
pHeader->id = LZSS_ID;
66+
result = lzss.SafeUncompress( compressed, sizeof(compressed), out, sizeof(out) );
67+
68+
const char data[] = "Do you like what you see?";
69+
Shipping_Assert( memcmp(out, data, 26) == 0 );
70+
Shipping_Assert( result == 26 );
71+
}
72+
73+
DEFINE_TESTCASE( LZSSSafeUncompressTest, LZSSSafeUncompressTestSuite )
74+
{
75+
Msg( "Running CLZSS::SafeUncompress tests\n" );
76+
77+
SafeUncompressTests();
78+
}

unittests/tier1test/wscript

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ def configure(conf):
1414
conf.define('TIER1TEST_EXPORTS', 1)
1515

1616
def build(bld):
17-
source = ['commandbuffertest.cpp', 'utlstringtest.cpp', 'tier1test.cpp'] #, 'lzsstest.cpp']
17+
source = ['commandbuffertest.cpp', 'utlstringtest.cpp', 'tier1test.cpp', 'lzsstest.cpp']
1818
includes = ['../../public', '../../public/tier0']
1919
defines = []
2020
libs = ['tier0', 'tier1', 'mathlib', 'unitlib']

0 commit comments

Comments
 (0)