From 7469218851e50fe4abffd5384ee10a390b4fee69 Mon Sep 17 00:00:00 2001 From: Arthur Chan Date: Thu, 29 Oct 2026 06:20:13 +0000 Subject: [PATCH 1/3] OSS-Fuzz: Add new fuzzer targets ewf write operations Signed-off-by: Arthur Chan --- ossfuzz/Makefile.am | 20 ++- ossfuzz/corpus/write_fuzzer/chunk | Bin 0 -> 2067 bytes ossfuzz/corpus/write_fuzzer/fmt-0 | Bin 0 -> 114 bytes ossfuzz/corpus/write_fuzzer/fmt-1 | Bin 0 -> 131 bytes ossfuzz/corpus/write_fuzzer/fmt-2 | Bin 0 -> 148 bytes ossfuzz/corpus/write_fuzzer/fmt-3 | Bin 0 -> 165 bytes ossfuzz/corpus/write_fuzzer/fmt-4 | Bin 0 -> 182 bytes ossfuzz/corpus/write_fuzzer/fmt-5 | Bin 0 -> 199 bytes ossfuzz/corpus/write_fuzzer/fmt-6 | Bin 0 -> 216 bytes ossfuzz/corpus/write_fuzzer/fmt-7 | Bin 0 -> 233 bytes ossfuzz/corpus/write_fuzzer/minimal | Bin 0 -> 16 bytes ossfuzz/write_fuzzer.cc | 226 ++++++++++++++++++++++++++++ ossfuzz/write_fuzzer.dict | 16 ++ ossfuzz/write_fuzzer.options | 4 + 14 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 ossfuzz/corpus/write_fuzzer/chunk create mode 100644 ossfuzz/corpus/write_fuzzer/fmt-0 create mode 100644 ossfuzz/corpus/write_fuzzer/fmt-1 create mode 100644 ossfuzz/corpus/write_fuzzer/fmt-2 create mode 100644 ossfuzz/corpus/write_fuzzer/fmt-3 create mode 100644 ossfuzz/corpus/write_fuzzer/fmt-4 create mode 100644 ossfuzz/corpus/write_fuzzer/fmt-5 create mode 100644 ossfuzz/corpus/write_fuzzer/fmt-6 create mode 100644 ossfuzz/corpus/write_fuzzer/fmt-7 create mode 100644 ossfuzz/corpus/write_fuzzer/minimal create mode 100644 ossfuzz/write_fuzzer.cc create mode 100644 ossfuzz/write_fuzzer.dict create mode 100644 ossfuzz/write_fuzzer.options diff --git a/ossfuzz/Makefile.am b/ossfuzz/Makefile.am index 7e4c68c18..78a46751f 100644 --- a/ossfuzz/Makefile.am +++ b/ossfuzz/Makefile.am @@ -12,7 +12,8 @@ AM_CPPFLAGS = \ @LIBBFIO_CPPFLAGS@ bin_PROGRAMS = \ - handle_fuzzer + handle_fuzzer \ + write_fuzzer handle_fuzzer_SOURCES = \ handle_fuzzer.cc \ @@ -31,6 +32,23 @@ handle_fuzzer_LDADD = \ @LIBCLOCALE_LIBADD@ \ @LIBCERROR_LIBADD@ \ @LIBINTL@ + +write_fuzzer_SOURCES = \ + write_fuzzer.cc \ + ossfuzz_libewf.h + +write_fuzzer_LDADD = \ + @LIB_FUZZING_ENGINE@ \ + @LIBBFIO_LIBADD@ \ + @LIBCPATH_LIBADD@ \ + @LIBCFILE_LIBADD@ \ + @LIBUNA_LIBADD@ \ + @LIBCDATA_LIBADD@ \ + ../libewf/libewf.la \ + @LIBCNOTIFY_LIBADD@ \ + @LIBCLOCALE_LIBADD@ \ + @LIBCERROR_LIBADD@ \ + @LIBINTL@ endif DISTCLEANFILES = \ diff --git a/ossfuzz/corpus/write_fuzzer/chunk b/ossfuzz/corpus/write_fuzzer/chunk new file mode 100644 index 0000000000000000000000000000000000000000..4e78bac9d95184dd016dbf3f71326b5f83c39ef2 GIT binary patch literal 2067 zcmZQ)WMpDsVq{=oU~vuebaC}_c4c5>VrF4wW9Q)H;^yJy;};MV5*85^6PJ*bl9rK` zlUGnwQdUt_Q`gYc($>+{(>E|QGBzQN=`{lOV7y6%FfBn%P%M_DlRE4E3c@ms;;T6t8Zv*YHn$5 zYwzgn>h9_7>z^=j(&Q;qr%j(RbJpxRbLY)puyE1hB}NRWEt>3V5)8;K( zw{73CbJy-Yd-v@>aPZLKBS()NKXLNZ=`&}~oxgDL(&Z~xuU)@!^VaPo;%Tz5np>)8{W=zkUDl^Vjb`fB*d-_5W!6kEZ|8{6AX$jh6o-vHk-9 D^~3&y literal 0 HcmV?d00001 diff --git a/ossfuzz/corpus/write_fuzzer/fmt-0 b/ossfuzz/corpus/write_fuzzer/fmt-0 new file mode 100644 index 0000000000000000000000000000000000000000..b65d5ee19cdbfcf2c7b82dfcf7cacbc2c63f5dc9 GIT binary patch literal 114 zcmZQz00Jfl5Rsf%oEo23nwykb#E_C&oLrPyP?DLS$B^3N! literal 0 HcmV?d00001 diff --git a/ossfuzz/corpus/write_fuzzer/fmt-3 b/ossfuzz/corpus/write_fuzzer/fmt-3 new file mode 100644 index 0000000000000000000000000000000000000000..4eca954b2d3644cd392a06db52d7b6bc3383a70f GIT binary patch literal 165 zcmZQ(W?*Jua$sO!U`kFbPL0ni%}q)zVn|6XPAsK*;f^hY literal 0 HcmV?d00001 diff --git a/ossfuzz/corpus/write_fuzzer/fmt-4 b/ossfuzz/corpus/write_fuzzer/fmt-4 new file mode 100644 index 0000000000000000000000000000000000000000..7f6e9b7c54d57660f75601556673443c1902ec62 GIT binary patch literal 182 zcmZQ!U}Ruma$o=w$%)0O@p+}WNvTB)DXGQDMVSR9nfZAPsTGO2nR!6Ly!?{XVg}bi F001wpGKc^G literal 0 HcmV?d00001 diff --git a/ossfuzz/corpus/write_fuzzer/fmt-5 b/ossfuzz/corpus/write_fuzzer/fmt-5 new file mode 100644 index 0000000000000000000000000000000000000000..2842d54788afd00907e624fdb96cefa5c6db93d3 GIT binary patch literal 199 zcmZQ&WMX7sa$sO!U`kFbPL0ni%}q)zVn|6XPA +#include +#include +#include +#include +#include + +#include + +#include + +/* Note that some of the OSS-Fuzz engines use C++ + */ +extern "C" { + +#include "ossfuzz_libewf.h" + +static const uint8_t kFormats[] = { + LIBEWF_FORMAT_ENCASE5, + LIBEWF_FORMAT_ENCASE6, + LIBEWF_FORMAT_ENCASE7, + LIBEWF_FORMAT_SMART, + LIBEWF_FORMAT_FTK_IMAGER, + LIBEWF_FORMAT_LINEN6, + LIBEWF_FORMAT_EWF, + LIBEWF_FORMAT_EWFX +}; + +static const uint8_t kMediaTypes[] = { + LIBEWF_MEDIA_TYPE_REMOVABLE, + LIBEWF_MEDIA_TYPE_FIXED, + LIBEWF_MEDIA_TYPE_OPTICAL, + LIBEWF_MEDIA_TYPE_MEMORY +}; + +static const int8_t kCompressionLevels[] = { + LIBEWF_COMPRESSION_NONE, + LIBEWF_COMPRESSION_FAST, + LIBEWF_COMPRESSION_BEST +}; + +static const uint32_t kSectorsPerChunk[] = { 1, 16, 64, 256 }; +static const uint32_t kBytesPerSector[] = { 512, 1024, 2048, 4096 }; + +/* A failure is not fatal: some identifiers are rejected for some formats. + */ +static void set_header( + libewf_handle_t *handle, + const char *identifier, + const std::string &value ) +{ + if( value.empty() ) + { + return; + } + libewf_handle_set_utf8_header_value( + handle, + (uint8_t *) identifier, + strlen( identifier ), + (uint8_t *) value.data(), + value.size(), + NULL ); +} + +int LLVMFuzzerTestOneInput( + const uint8_t *data, + size_t size ) +{ + FuzzedDataProvider provider( data, size ); + + libewf_handle_t *handle = NULL; + char filename[ 64 ]; + char *filenames[ 1 ]; + + uint8_t format = kFormats[ provider.ConsumeIntegralInRange( + 0, ( sizeof( kFormats ) / sizeof( kFormats[ 0 ] ) ) - 1 ) ]; + uint8_t media_type = kMediaTypes[ provider.ConsumeIntegralInRange( + 0, ( sizeof( kMediaTypes ) / sizeof( kMediaTypes[ 0 ] ) ) - 1 ) ]; + int8_t compression_level = kCompressionLevels[ + provider.ConsumeIntegralInRange( + 0, ( sizeof( kCompressionLevels ) / sizeof( kCompressionLevels[ 0 ] ) ) - 1 ) ]; + uint32_t sectors_per_chunk = kSectorsPerChunk[ + provider.ConsumeIntegralInRange( + 0, ( sizeof( kSectorsPerChunk ) / sizeof( kSectorsPerChunk[ 0 ] ) ) - 1 ) ]; + uint32_t bytes_per_sector = kBytesPerSector[ + provider.ConsumeIntegralInRange( + 0, ( sizeof( kBytesPerSector ) / sizeof( kBytesPerSector[ 0 ] ) ) - 1 ) ]; + uint8_t media_flags = provider.ConsumeIntegralInRange( 0, 0x0e ); + uint32_t error_granularity = provider.ConsumeIntegralInRange( 1, 64 ); + int write_rounds = provider.ConsumeIntegralInRange( 1, 4 ); + + std::string case_number = provider.ConsumeRandomLengthString( 32 ); + std::string description = provider.ConsumeRandomLengthString( 48 ); + std::string examiner_name = provider.ConsumeRandomLengthString( 32 ); + std::string notes = provider.ConsumeRandomLengthString( 48 ); + + /* libewf_handle_open(WRITE) treats the name as a basename and appends + * the segment extension itself. Passing ".E01" makes it look for a + * second extension and the open fails. + */ + snprintf( + filename, sizeof( filename ), "/tmp/ewf-%d", (int) getpid() ); + filenames[ 0 ] = filename; + + if( libewf_handle_initialize( &handle, NULL ) != 1 ) + { + return( 0 ); + } + if( libewf_handle_open( + handle, filenames, 1, LIBEWF_OPEN_WRITE, NULL ) != 1 ) + { + goto on_error; + } + libewf_handle_set_format( handle, format, NULL ); + libewf_handle_set_bytes_per_sector( handle, bytes_per_sector, NULL ); + libewf_handle_set_sectors_per_chunk( handle, sectors_per_chunk, NULL ); +#if defined( MEMORY_SANITIZER ) + /* System zlib is not MSan-instrumented in this image, so a deflate + * buffer looks uninitialized when it is written back. Keep compression + * off under MSan only; ASan/UBSan still sweep the compression levels. + */ + libewf_handle_set_compression_values( + handle, LIBEWF_COMPRESSION_NONE, 0, NULL ); +#else + libewf_handle_set_compression_values( + handle, compression_level, 0, NULL ); +#endif + libewf_handle_set_media_type( handle, media_type, NULL ); + libewf_handle_set_media_flags( handle, media_flags, NULL ); + libewf_handle_set_error_granularity( handle, error_granularity, NULL ); + /* Bound the image so write + finalize stay inside one input. + */ + libewf_handle_set_media_size( + handle, + (size64_t) bytes_per_sector * (size64_t) ( 1 + ( write_rounds * 4 ) ), + NULL ); + libewf_handle_set_header_values_date_format( + handle, LIBEWF_DATE_FORMAT_ISO8601, NULL ); + + set_header( handle, "case_number", case_number ); + set_header( handle, "description", description ); + set_header( handle, "examiner_name", examiner_name ); + set_header( handle, "notes", notes ); + set_header( handle, "acquiry_date", std::string( "2026-01-01 00:00:00" ) ); + + /* Only set the hash when the input actually supplies 16 bytes. + * ConsumeData zero-fills a short read, which MSan then reports as + * an uninitialized value inside libewf_hash_values_parse_md5_hash. + */ + if( provider.remaining_bytes() >= 16 ) + { + std::vector md5 = provider.ConsumeBytes( 16 ); + + libewf_handle_set_md5_hash( handle, md5.data(), 16, NULL ); + } + if( provider.remaining_bytes() >= 16 ) + { + std::vector guid_bytes = provider.ConsumeBytes( 16 ); + + libewf_handle_set_segment_file_set_identifier( + handle, guid_bytes.data(), 16, NULL ); + } + + for( int i = 0; i < 2; i++ ) + { + uint64_t start = provider.ConsumeIntegralInRange( 0, 1024 ); + uint64_t count = provider.ConsumeIntegralInRange( 1, 32 ); + + libewf_handle_append_session( handle, start, count, NULL ); + libewf_handle_append_track( handle, start, count, NULL ); + libewf_handle_append_acquiry_error( handle, start, count, NULL ); + } + for( int round = 0; round < write_rounds; round++ ) + { + size_t want = provider.ConsumeIntegralInRange( 1, 2048 ); + std::vector bytes = provider.ConsumeBytes( want ); + + if( bytes.empty() ) + { + break; + } + libewf_handle_write_buffer( + handle, bytes.data(), bytes.size(), NULL ); + } + libewf_handle_write_finalize( handle, NULL ); + libewf_handle_close( handle, NULL ); + +on_error: + if( handle != NULL ) + { + libewf_handle_free( &handle, NULL ); + } + unlink( filename ); + { + char segment_name[ 72 ]; + + snprintf( + segment_name, sizeof( segment_name ), "%s.E01", filename ); + unlink( segment_name ); + snprintf( + segment_name, sizeof( segment_name ), "%s.E02", filename ); + unlink( segment_name ); + snprintf( + segment_name, sizeof( segment_name ), "%s.s01", filename ); + unlink( segment_name ); + } + return( 0 ); +} + +} /* extern "C" */ diff --git a/ossfuzz/write_fuzzer.dict b/ossfuzz/write_fuzzer.dict new file mode 100644 index 000000000..ce08942e1 --- /dev/null +++ b/ossfuzz/write_fuzzer.dict @@ -0,0 +1,16 @@ +# Forensic header identifiers the write path stores in the EWF header section. +"case_number" +"description" +"examiner_name" +"evidence_number" +"notes" +"acquiry_date" +"system_date" +"acquiry_operating_system" +"acquiry_software_version" +"password" +"model" +"serial_number" +"2026-01-01 00:00:00" +"EV" +"E01" diff --git a/ossfuzz/write_fuzzer.options b/ossfuzz/write_fuzzer.options new file mode 100644 index 000000000..8a0c8f4a6 --- /dev/null +++ b/ossfuzz/write_fuzzer.options @@ -0,0 +1,4 @@ +[libfuzzer] +dict = write_fuzzer.dict +max_len = 4096 +timeout = 25 From 7342ab45dc445f05eac589ca147e678eaddeb92e Mon Sep 17 00:00:00 2001 From: Joachim Metz Date: Tue, 10 Nov 2026 07:19:47 +0100 Subject: [PATCH 2/3] Changes for dpkg build test --- dpkg/source/local-options | 1 + 1 file changed, 1 insertion(+) diff --git a/dpkg/source/local-options b/dpkg/source/local-options index 66ab99cfb..761baa8a1 100644 --- a/dpkg/source/local-options +++ b/dpkg/source/local-options @@ -12,6 +12,7 @@ extend-diff-ignore = "(^|/)include/lib.+\.h$" extend-diff-ignore = "(^|/)include/lib.+/.+\.h$" extend-diff-ignore = "(^|/)lib.+/lib.+_definitions\.h$" extend-diff-ignore = "(^|/)lib.+/lib.+\.rc$" +extend-diff-ignore = "(^|/)ossfuzz/corpus/.+$" extend-diff-ignore = "(^|/)po/Makevars$" extend-diff-ignore = "(^|/)po/Makevars\.template$" extend-diff-ignore = "(^|/)tests/.+\.(in|ps1|spec|sh|toml|py|at)$" From 6966f89449e7eed9f80457719eb87f1cb085cd3d Mon Sep 17 00:00:00 2001 From: Joachim Metz Date: Tue, 10 Nov 2026 18:59:08 +0100 Subject: [PATCH 3/3] Changes for dpkg build test --- dpkg/source/local-options | 1 + 1 file changed, 1 insertion(+) diff --git a/dpkg/source/local-options b/dpkg/source/local-options index 761baa8a1..b16269455 100644 --- a/dpkg/source/local-options +++ b/dpkg/source/local-options @@ -12,6 +12,7 @@ extend-diff-ignore = "(^|/)include/lib.+\.h$" extend-diff-ignore = "(^|/)include/lib.+/.+\.h$" extend-diff-ignore = "(^|/)lib.+/lib.+_definitions\.h$" extend-diff-ignore = "(^|/)lib.+/lib.+\.rc$" +extend-diff-ignore = "(^|/)ossfuzz/.+_fuzzer\.(dict|options)$" extend-diff-ignore = "(^|/)ossfuzz/corpus/.+$" extend-diff-ignore = "(^|/)po/Makevars$" extend-diff-ignore = "(^|/)po/Makevars\.template$"