summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/freetype/src/tools/ftfuzzer/ftmutator.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/3rdparty/freetype/src/tools/ftfuzzer/ftmutator.cc')
-rw-r--r--src/3rdparty/freetype/src/tools/ftfuzzer/ftmutator.cc314
1 files changed, 314 insertions, 0 deletions
diff --git a/src/3rdparty/freetype/src/tools/ftfuzzer/ftmutator.cc b/src/3rdparty/freetype/src/tools/ftfuzzer/ftmutator.cc
new file mode 100644
index 0000000000..ae4b140404
--- /dev/null
+++ b/src/3rdparty/freetype/src/tools/ftfuzzer/ftmutator.cc
@@ -0,0 +1,314 @@
+// ftmutator.cc
+//
+// A custom fuzzer mutator to test for FreeType with libFuzzer.
+//
+// Copyright 2015-2018 by
+// David Turner, Robert Wilhelm, and Werner Lemberg.
+//
+// This file is part of the FreeType project, and may only be used,
+// modified, and distributed under the terms of the FreeType project
+// license, LICENSE.TXT. By continuing to use, modify, or distribute
+// this file you indicate that you have read the license and
+// understand and accept it fully.
+
+
+// Since `tar' is not a valid format for input to FreeType, treat any input
+// that looks like `tar' as multiple files and mutate them separately.
+//
+// In the future, a variation of this may be used to guide mutation on a
+// logically higher level.
+
+
+// we use `unique_ptr', `decltype', and other gimmicks defined since C++11
+#if __cplusplus < 201103L
+# error "a C++11 compiler is needed"
+#endif
+
+#include <cstdint>
+#include <cassert>
+#include <cstdio>
+#include <cstdlib>
+#include <cstddef>
+#include <cstring>
+#include <iostream>
+
+#include <memory>
+#include <vector>
+
+#include <archive.h>
+#include <archive_entry.h>
+
+#include "FuzzerInterface.h"
+
+
+ using namespace std;
+
+
+ // This function should be defined by `ftfuzzer.cc'.
+ extern "C" int
+ LLVMFuzzerTestOneInput( const uint8_t* Data,
+ size_t Size );
+
+
+ static void
+ check_result( struct archive* a,
+ int r )
+ {
+ if ( r == ARCHIVE_OK )
+ return;
+
+ const char* m = archive_error_string( a );
+ write( 1, m, strlen( m ) );
+ exit( 1 );
+ }
+
+
+ static int
+ archive_read_entry_data( struct archive *ar,
+ vector<uint8_t> *vw )
+ {
+ int r;
+ const uint8_t* buff;
+ size_t size;
+ int64_t offset;
+
+ for (;;)
+ {
+ r = archive_read_data_block( ar,
+ reinterpret_cast<const void**>( &buff ),
+ &size,
+ &offset );
+ if ( r == ARCHIVE_EOF )
+ return ARCHIVE_OK;
+ if ( r != ARCHIVE_OK )
+ return r;
+
+ vw->insert( vw->end(), buff, buff + size );
+ }
+ }
+
+
+ static vector<vector<uint8_t>>
+ parse_data( const uint8_t* data,
+ size_t size )
+ {
+ struct archive_entry* entry;
+ int r;
+ vector<vector<uint8_t>> files;
+
+ unique_ptr<struct archive,
+ decltype ( archive_read_free )*> a( archive_read_new(),
+ archive_read_free );
+
+ // activate reading of uncompressed tar archives
+ archive_read_support_format_tar( a.get() );
+
+ // the need for `const_cast' was removed with libarchive commit be4d4dd
+ if ( !( r = archive_read_open_memory(
+ a.get(),
+ const_cast<void*>(static_cast<const void*>( data ) ),
+ size ) ) )
+ {
+ unique_ptr<struct archive,
+ decltype ( archive_read_close )*> a_open( a.get(),
+ archive_read_close );
+
+ // read files contained in archive
+ for (;;)
+ {
+ r = archive_read_next_header( a_open.get(), &entry );
+ if ( r == ARCHIVE_EOF )
+ break;
+ if ( r != ARCHIVE_OK )
+ break;
+
+ vector<uint8_t> entry_data;
+ r = archive_read_entry_data( a.get(), &entry_data );
+ if ( entry_data.size() == 0 )
+ continue;
+
+ files.push_back( move( entry_data ) );
+ if ( r != ARCHIVE_OK )
+ break;
+ }
+ }
+
+ return files;
+ }
+
+
+ class FTFuzzer
+ : public fuzzer::UserSuppliedFuzzer
+ {
+
+ public:
+ FTFuzzer( fuzzer::FuzzerRandomBase* Rand )
+ : fuzzer::UserSuppliedFuzzer( Rand ) {}
+
+
+ int
+ TargetFunction( const uint8_t* Data,
+ size_t Size )
+ {
+ return LLVMFuzzerTestOneInput( Data, Size );
+ }
+
+
+ // Custom mutator.
+ virtual size_t
+ Mutate( uint8_t* Data,
+ size_t Size,
+ size_t MaxSize )
+ {
+ vector<vector<uint8_t>> files = parse_data( Data, Size );
+
+ // If the file was not recognized as a tar file, treat it as non-tar.
+ if ( files.size() == 0 )
+ return fuzzer::UserSuppliedFuzzer::Mutate( Data, Size, MaxSize );
+
+ // This is somewhat `white box' on tar. The tar format uses 512 byte
+ // blocks. One block as header for each file, two empty blocks of 0's
+ // at the end. File data is padded to fill its last block.
+ size_t used_blocks = files.size() + 2;
+ for ( const auto& file : files )
+ used_blocks += ( file.size() + 511 ) / 512;
+
+ size_t max_blocks = MaxSize / 512;
+
+ // If the input is big, it will need to be downsized. If the original
+ // tar file was too big, it may have been clipped to fit. In this
+ // case it may not be possible to properly write out the data, as
+ // there may not be enough space for the trailing two blocks. Start
+ // dropping file data or files from the end.
+ for ( size_t i = files.size();
+ i-- > 1 && used_blocks > max_blocks; )
+ {
+ size_t blocks_to_free = used_blocks - max_blocks;
+ size_t blocks_currently_used_by_file_data =
+ ( files[i].size() + 511 ) / 512;
+
+ if ( blocks_currently_used_by_file_data >= blocks_to_free )
+ {
+ files[i].resize( ( blocks_currently_used_by_file_data -
+ blocks_to_free ) * 512 );
+ used_blocks -= blocks_to_free;
+ continue;
+ }
+
+ files.pop_back();
+ used_blocks -= blocks_currently_used_by_file_data + 1;
+ }
+
+ // If we get down to one file, don't use tar.
+ if ( files.size() == 1 )
+ {
+ memcpy( Data, files[0].data(), files[0].size() );
+ return fuzzer::UserSuppliedFuzzer::Mutate( Data,
+ files[0].size(),
+ MaxSize );
+ }
+
+ size_t free_blocks = max_blocks - used_blocks;
+
+ // Allow each file to use up as much of the currently available space
+ // it can. If it uses or gives up blocks, add them or remove them
+ // from the pool.
+ for ( auto&& file : files )
+ {
+ size_t blocks_currently_used_by_file = ( file.size() + 511 ) / 512;
+ size_t blocks_available = blocks_currently_used_by_file +
+ free_blocks;
+ size_t max_size = blocks_available * 512;
+ size_t data_size = file.size();
+
+ file.resize( max_size );
+ file.resize( fuzzer::UserSuppliedFuzzer::Mutate( file.data(),
+ data_size,
+ max_size ) );
+
+ size_t blocks_now_used_by_file = ( file.size() + 511 ) / 512;
+ free_blocks = free_blocks +
+ blocks_currently_used_by_file -
+ blocks_now_used_by_file;
+ }
+
+ unique_ptr<struct archive,
+ decltype ( archive_write_free )*> a( archive_write_new(),
+ archive_write_free );
+
+ check_result( a.get(), archive_write_add_filter_none( a.get() ) );
+ check_result( a.get(), archive_write_set_format_ustar( a.get() ) );
+
+ // `used' may not be correct until after the archive is closed.
+ size_t used = 0xbadbeef;
+ check_result( a.get(), archive_write_open_memory( a.get(),
+ Data,
+ MaxSize,
+ &used ) );
+
+ {
+ unique_ptr<struct archive,
+ decltype ( archive_write_close )*> a_open( a.get(),
+ archive_write_close );
+
+ int file_index = 0;
+ for ( const auto& file : files )
+ {
+ unique_ptr<struct archive_entry,
+ decltype ( archive_entry_free )*>
+ e( archive_entry_new2( a_open.get() ),
+ archive_entry_free );
+
+ char name_buffer[100];
+ snprintf( name_buffer, 100, "file%d", file_index++ );
+
+ archive_entry_set_pathname( e.get(), name_buffer );
+ archive_entry_set_size( e.get(), file.size() );
+ archive_entry_set_filetype( e.get(), AE_IFREG );
+ archive_entry_set_perm( e.get(), 0644 );
+
+ check_result( a_open.get(),
+ archive_write_header( a_open.get(), e.get() ) );
+ archive_write_data( a_open.get(), file.data(), file.size() );
+ check_result( a_open.get(),
+ archive_write_finish_entry( a_open.get() ) );
+ }
+ }
+
+ return used;
+ }
+
+
+ // Cross `Data1' and `Data2', write up to `MaxOutSize' bytes into `Out',
+ // return the number of bytes written, which should be positive.
+ virtual size_t
+ CrossOver( const uint8_t* Data1,
+ size_t Size1,
+ const uint8_t* Data2,
+ size_t Size2,
+ uint8_t* Out,
+ size_t MaxOutSize )
+ {
+ return fuzzer::UserSuppliedFuzzer::CrossOver( Data1,
+ Size1,
+ Data2,
+ Size2,
+ Out,
+ MaxOutSize );
+ }
+
+ }; // end of FTFuzzer class
+
+
+ int
+ main( int argc,
+ char* *argv )
+ {
+ fuzzer::FuzzerRandomLibc Rand( 0 );
+ FTFuzzer F( &Rand );
+
+ fuzzer::FuzzerDriver( argc, argv, F );
+ }
+
+
+// END