diff --git a/engine/client/soundlib/snd_main.c b/engine/client/soundlib/snd_main.c index 1bb03370..5daf0676 100644 --- a/engine/client/soundlib/snd_main.c +++ b/engine/client/soundlib/snd_main.c @@ -274,7 +274,7 @@ void FS_FreeStream( stream_t *stream ) stream->format->freefunc( stream ); } -#if XASH_ENGINE_TESTS +#if XASH_LLVM_LIBFUZZER #define IMPLEMENT_SOUNDLIB_FUZZ_TARGET( export, target ) \ int EXPORT export( const uint8_t *Data, size_t Size ); \ int EXPORT export( const uint8_t *Data, size_t Size ) \ @@ -294,4 +294,4 @@ int EXPORT export( const uint8_t *Data, size_t Size ) \ IMPLEMENT_SOUNDLIB_FUZZ_TARGET( Fuzz_Sound_LoadMPG, Sound_LoadMPG ) IMPLEMENT_SOUNDLIB_FUZZ_TARGET( Fuzz_Sound_LoadWAV, Sound_LoadWAV ) -#endif +#endif // XASH_LLVM_LIBFUZZER diff --git a/engine/client/soundlib/snd_mp3.c b/engine/client/soundlib/snd_mp3.c index 31815973..2ea80535 100644 --- a/engine/client/soundlib/snd_mp3.c +++ b/engine/client/soundlib/snd_mp3.c @@ -185,7 +185,7 @@ static qboolean Sound_ParseID3Tag( const byte *buffer, fs_offset_t filesize ) return true; } -#if XASH_ENGINE_TESTS +#if XASH_LLVM_LIBFUZZER int EXPORT Fuzz_Sound_ParseID3Tag( const uint8_t *Data, size_t Size ); int EXPORT Fuzz_Sound_ParseID3Tag( const uint8_t *Data, size_t Size ) { @@ -193,7 +193,7 @@ int EXPORT Fuzz_Sound_ParseID3Tag( const uint8_t *Data, size_t Size ) Sound_ParseID3Tag( Data, Size ); return 0; } -#endif +#endif // XASH_LLVM_LIBFUZZER /* ================================================================= diff --git a/engine/common/imagelib/img_main.c b/engine/common/imagelib/img_main.c index 86e8ed60..015ceddd 100644 --- a/engine/common/imagelib/img_main.c +++ b/engine/common/imagelib/img_main.c @@ -701,6 +701,10 @@ void Test_RunImagelib( void ) Z_Free( rgb.buffer ); } +#endif // XASH_ENGINE_TESTS + +#if XASH_LLVM_LIBFUZZER +#include "sprite.h" #define IMPLEMENT_IMAGELIB_FUZZ_TARGET( export, target ) \ int EXPORT export( const uint8_t *Data, size_t Size ); \ @@ -724,4 +728,29 @@ IMPLEMENT_IMAGELIB_FUZZ_TARGET( Fuzz_Image_LoadPNG, Image_LoadPNG ) IMPLEMENT_IMAGELIB_FUZZ_TARGET( Fuzz_Image_LoadDDS, Image_LoadDDS ) IMPLEMENT_IMAGELIB_FUZZ_TARGET( Fuzz_Image_LoadTGA, Image_LoadTGA ) -#endif /* XASH_ENGINE_TESTS */ +int EXPORT Fuzz_Image_LoadSPR( const uint8_t *Data, size_t Size ); +int EXPORT Fuzz_Image_LoadSPR( const uint8_t *Data, size_t Size ) +{ + rgbdata_t *rgb; + + // the real caller passes only the pixel data size, the frame header is extra + if( Size < sizeof( dspriteframe_t )) + return 0; + + host.type = HOST_NORMAL; + Memory_Init(); + Image_Init(); + + image.hint = IL_HINT_Q1; // installs the Quake palette so the decoder runs + + if( Image_LoadSPR( "#internal.spr", Data, Size - sizeof( dspriteframe_t ))) + { + rgb = ImagePack( "#internal.spr" ); + FS_FreeImage( rgb ); + } + + Image_Shutdown(); + return 0; +} + +#endif // XASH_LLVM_LIBFUZZER diff --git a/engine/common/mod_sprite.c b/engine/common/mod_sprite.c index cf898d80..c7b79a13 100644 --- a/engine/common/mod_sprite.c +++ b/engine/common/mod_sprite.c @@ -302,3 +302,34 @@ void Mod_LoadSpriteModel( model_t *mod, void *buffer, size_t buffersize, qboolea Mod_SpriteLoadTextures( mod, buffer ); #endif } + +#if XASH_LLVM_LIBFUZZER +int EXPORT Fuzz_Mod_LoadSpriteModel( const uint8_t *Data, size_t Size ); +int EXPORT Fuzz_Mod_LoadSpriteModel( const uint8_t *Data, size_t Size ) +{ + model_t mod = { .name = "#internal.spr", .needload = NL_NEEDS_LOADED }; + qboolean loaded = false; + byte *buf; + + if( Size == 0 ) + return 0; + + Memory_Init(); + + // dedicated mode exercises the parser/validator without needing a renderer + host.type = HOST_DEDICATED; + host.mempool = Mem_AllocPool( "fuzzing pool" ); + + // the loader byteswaps the buffer in place, so hand it a writable copy + buf = Mem_Malloc( host.mempool, Size ); + memcpy( buf, Data, Size ); + + Mod_LoadSpriteModel( &mod, buf, Size, &loaded ); + + if( mod.mempool ) + Mem_FreePool( &mod.mempool ); + Mem_FreePool( &host.mempool ); + + return 0; +} +#endif // XASH_LLVM_LIBFUZZER diff --git a/engine/common/model.c b/engine/common/model.c index 97b78e29..46225e16 100644 --- a/engine/common/model.c +++ b/engine/common/model.c @@ -653,7 +653,7 @@ void Mod_NeedCRC( const char *name, qboolean needCRC ) else ClearBits( p->flags, FCRC_SHOULD_CHECKSUM ); } -#if XASH_ENGINE_TESTS +#if XASH_LLVM_LIBFUZZER static const uint8_t *fuzz_data; static size_t fuzz_size; @@ -689,4 +689,4 @@ int EXPORT Fuzz_Mod_LoadModel( const uint8_t *Data, size_t Size ) return 0; } -#endif // XASH_ENGINE_TESTS +#endif // XASH_LLVM_LIBFUZZER diff --git a/engine/wscript b/engine/wscript index 12322c35..feaff82e 100644 --- a/engine/wscript +++ b/engine/wscript @@ -47,9 +47,6 @@ def options(opt): grp.add_option('--enable-static-binary', action = 'store_true', dest = 'STATIC', default = False, help = 'build static binary(not recommended, --single-binary required) [default: %(default)s]') - grp.add_option('--enable-engine-fuzz', action = 'store_true', dest = 'ENGINE_FUZZ', default = False, - help = 'add LLVM libFuzzer [default: %(default)s]' ) - grp.add_option('--enable-ffmpeg', action = 'store_true', dest = 'FFMPEG', default = False, help = 'enable ffmpeg based movie playback [default: %(default)s') @@ -89,9 +86,10 @@ def configure(conf): elif conf.env.DEST_OS == 'dos': conf.options.STATIC = True - if conf.options.ENGINE_FUZZ: + if conf.options.ENABLE_FUZZER: + conf.define('XASH_LLVM_LIBFUZZER', 1) conf.env.append_unique('CFLAGS', '-fsanitize=fuzzer-no-link') - conf.env.append_unique('LINKFLAGS', '-fsanitize=fuzzer') + conf.env.append_unique('LINKFLAGS', '-fsanitize=fuzzer-no-link') # Client-only dependencies if conf.env.CLIENT: diff --git a/utils/run-fuzzer/run-fuzzer.c b/utils/run-fuzzer/run-fuzzer.c index d0bfae0b..b4debf6c 100644 --- a/utils/run-fuzzer/run-fuzzer.c +++ b/utils/run-fuzzer/run-fuzzer.c @@ -1,4 +1,5 @@ #include +#include #include #include @@ -6,12 +7,13 @@ #error #endif -typedef int (*FuzzFunc)(const char *Data, size_t Size); +typedef int (*FuzzFunc)(const uint8_t *Data, size_t Size); -void *handle = NULL; -FuzzFunc f = NULL; +static void *handle = NULL; +static FuzzFunc f = NULL; -int LLVMFuzzerTestOneInput( const char *Data, size_t Size ) +int LLVMFuzzerTestOneInput( const uint8_t *Data, size_t Size ); +int LLVMFuzzerTestOneInput( const uint8_t *Data, size_t Size ) { if( !handle ) handle = dlopen( LIB, RTLD_NOW ); diff --git a/utils/run-fuzzer/wscript b/utils/run-fuzzer/wscript index 4301aa67..4f090d60 100644 --- a/utils/run-fuzzer/wscript +++ b/utils/run-fuzzer/wscript @@ -37,4 +37,6 @@ def build(bld): add_runner_target(bld, 'libxash.so', 'Image_LoadPNG') add_runner_target(bld, 'libxash.so', 'Image_LoadDDS') add_runner_target(bld, 'libxash.so', 'Image_LoadTGA') + add_runner_target(bld, 'libxash.so', 'Image_LoadSPR') add_runner_target(bld, 'libxash.so', 'Mod_LoadModel') + add_runner_target(bld, 'libxash.so', 'Mod_LoadSpriteModel') diff --git a/wscript b/wscript index 5ea70995..eefd8121 100644 --- a/wscript +++ b/wscript @@ -86,6 +86,7 @@ SUBDIRS = [ Subproject('3rdparty/libbacktrace'), Subproject('3rdparty/library_suffix'), Subproject('3rdparty/yy-thunks'), + Subproject('utils/run-fuzzer', lambda x: x.env.ENABLE_FUZZER), # disable only by engine feature, makes no sense to even parse subprojects in dedicated mode Subproject('3rdparty/extras', lambda x: x.env.CLIENT and x.env.DEST_OS != 'android'), @@ -114,7 +115,6 @@ SUBDIRS = [ # enabled optionally Subproject('utils/mdldec', lambda x: x.env.ENABLE_UTILS), Subproject('utils/xar', lambda x: x.env.ENABLE_UTILS and x.env.ENABLE_XAR), - Subproject('utils/run-fuzzer', lambda x: x.env.ENABLE_FUZZER), # enabled on PSVita only Subproject('ref/gl/vgl_shim', lambda x: x.env.DEST_OS == 'psvita'),