1 // ftmutator.cc 2 // 3 // A custom fuzzer mutator to test for FreeType with libFuzzer. 4 // 5 // Copyright 2015-2018 by 6 // David Turner, Robert Wilhelm, and Werner Lemberg. 7 // 8 // This file is part of the FreeType project, and may only be used, 9 // modified, and distributed under the terms of the FreeType project 10 // license, LICENSE.TXT. By continuing to use, modify, or distribute 11 // this file you indicate that you have read the license and 12 // understand and accept it fully. 13 14 15 // Since `tar' is not a valid format for input to FreeType, treat any input 16 // that looks like `tar' as multiple files and mutate them separately. 17 // 18 // In the future, a variation of this may be used to guide mutation on a 19 // logically higher level. 20 21 22 // we use `unique_ptr', `decltype', and other gimmicks defined since C++11 23 #if __cplusplus < 201103L 24 # error "a C++11 compiler is needed" 25 #endif 26 27 #include <cstdint> 28 #include <cassert> 29 #include <cstdio> 30 #include <cstdlib> 31 #include <cstddef> 32 #include <cstring> 33 #include <iostream> 34 35 #include <memory> 36 #include <vector> 37 38 #include <archive.h> 39 #include <archive_entry.h> 40 41 #include "FuzzerInterface.h" 42 43 44 using namespace std; 45 46 47 // This function should be defined by `ftfuzzer.cc'. 48 extern "C" int 49 LLVMFuzzerTestOneInput( const uint8_t* Data, 50 size_t Size ); 51 52 53 static void check_result(struct archive * a,int r)54 check_result( struct archive* a, 55 int r ) 56 { 57 if ( r == ARCHIVE_OK ) 58 return; 59 60 const char* m = archive_error_string( a ); 61 write( 1, m, strlen( m ) ); 62 exit( 1 ); 63 } 64 65 66 static int archive_read_entry_data(struct archive * ar,vector<uint8_t> * vw)67 archive_read_entry_data( struct archive *ar, 68 vector<uint8_t> *vw ) 69 { 70 int r; 71 const uint8_t* buff; 72 size_t size; 73 int64_t offset; 74 75 for (;;) 76 { 77 r = archive_read_data_block( ar, 78 reinterpret_cast<const void**>( &buff ), 79 &size, 80 &offset ); 81 if ( r == ARCHIVE_EOF ) 82 return ARCHIVE_OK; 83 if ( r != ARCHIVE_OK ) 84 return r; 85 86 vw->insert( vw->end(), buff, buff + size ); 87 } 88 } 89 90 91 static vector<vector<uint8_t>> parse_data(const uint8_t * data,size_t size)92 parse_data( const uint8_t* data, 93 size_t size ) 94 { 95 struct archive_entry* entry; 96 int r; 97 vector<vector<uint8_t>> files; 98 99 unique_ptr<struct archive, 100 decltype ( archive_read_free )*> a( archive_read_new(), 101 archive_read_free ); 102 103 // activate reading of uncompressed tar archives 104 archive_read_support_format_tar( a.get() ); 105 106 // the need for `const_cast' was removed with libarchive commit be4d4dd 107 if ( !( r = archive_read_open_memory( 108 a.get(), 109 const_cast<void*>(static_cast<const void*>( data ) ), 110 size ) ) ) 111 { 112 unique_ptr<struct archive, 113 decltype ( archive_read_close )*> a_open( a.get(), 114 archive_read_close ); 115 116 // read files contained in archive 117 for (;;) 118 { 119 r = archive_read_next_header( a_open.get(), &entry ); 120 if ( r == ARCHIVE_EOF ) 121 break; 122 if ( r != ARCHIVE_OK ) 123 break; 124 125 vector<uint8_t> entry_data; 126 r = archive_read_entry_data( a.get(), &entry_data ); 127 if ( entry_data.size() == 0 ) 128 continue; 129 130 files.push_back( move( entry_data ) ); 131 if ( r != ARCHIVE_OK ) 132 break; 133 } 134 } 135 136 return files; 137 } 138 139 140 class FTFuzzer 141 : public fuzzer::UserSuppliedFuzzer 142 { 143 144 public: FTFuzzer(fuzzer::FuzzerRandomBase * Rand)145 FTFuzzer( fuzzer::FuzzerRandomBase* Rand ) 146 : fuzzer::UserSuppliedFuzzer( Rand ) {} 147 148 149 int TargetFunction(const uint8_t * Data,size_t Size)150 TargetFunction( const uint8_t* Data, 151 size_t Size ) 152 { 153 return LLVMFuzzerTestOneInput( Data, Size ); 154 } 155 156 157 // Custom mutator. 158 virtual size_t Mutate(uint8_t * Data,size_t Size,size_t MaxSize)159 Mutate( uint8_t* Data, 160 size_t Size, 161 size_t MaxSize ) 162 { 163 vector<vector<uint8_t>> files = parse_data( Data, Size ); 164 165 // If the file was not recognized as a tar file, treat it as non-tar. 166 if ( files.size() == 0 ) 167 return fuzzer::UserSuppliedFuzzer::Mutate( Data, Size, MaxSize ); 168 169 // This is somewhat `white box' on tar. The tar format uses 512 byte 170 // blocks. One block as header for each file, two empty blocks of 0's 171 // at the end. File data is padded to fill its last block. 172 size_t used_blocks = files.size() + 2; 173 for ( const auto& file : files ) 174 used_blocks += ( file.size() + 511 ) / 512; 175 176 size_t max_blocks = MaxSize / 512; 177 178 // If the input is big, it will need to be downsized. If the original 179 // tar file was too big, it may have been clipped to fit. In this 180 // case it may not be possible to properly write out the data, as 181 // there may not be enough space for the trailing two blocks. Start 182 // dropping file data or files from the end. 183 for ( size_t i = files.size(); 184 i-- > 1 && used_blocks > max_blocks; ) 185 { 186 size_t blocks_to_free = used_blocks - max_blocks; 187 size_t blocks_currently_used_by_file_data = 188 ( files[i].size() + 511 ) / 512; 189 190 if ( blocks_currently_used_by_file_data >= blocks_to_free ) 191 { 192 files[i].resize( ( blocks_currently_used_by_file_data - 193 blocks_to_free ) * 512 ); 194 used_blocks -= blocks_to_free; 195 continue; 196 } 197 198 files.pop_back(); 199 used_blocks -= blocks_currently_used_by_file_data + 1; 200 } 201 202 // If we get down to one file, don't use tar. 203 if ( files.size() == 1 ) 204 { 205 memcpy( Data, files[0].data(), files[0].size() ); 206 return fuzzer::UserSuppliedFuzzer::Mutate( Data, 207 files[0].size(), 208 MaxSize ); 209 } 210 211 size_t free_blocks = max_blocks - used_blocks; 212 213 // Allow each file to use up as much of the currently available space 214 // it can. If it uses or gives up blocks, add them or remove them 215 // from the pool. 216 for ( auto&& file : files ) 217 { 218 size_t blocks_currently_used_by_file = ( file.size() + 511 ) / 512; 219 size_t blocks_available = blocks_currently_used_by_file + 220 free_blocks; 221 size_t max_size = blocks_available * 512; 222 size_t data_size = file.size(); 223 224 file.resize( max_size ); 225 file.resize( fuzzer::UserSuppliedFuzzer::Mutate( file.data(), 226 data_size, 227 max_size ) ); 228 229 size_t blocks_now_used_by_file = ( file.size() + 511 ) / 512; 230 free_blocks = free_blocks + 231 blocks_currently_used_by_file - 232 blocks_now_used_by_file; 233 } 234 235 unique_ptr<struct archive, 236 decltype ( archive_write_free )*> a( archive_write_new(), 237 archive_write_free ); 238 239 check_result( a.get(), archive_write_add_filter_none( a.get() ) ); 240 check_result( a.get(), archive_write_set_format_ustar( a.get() ) ); 241 242 // `used' may not be correct until after the archive is closed. 243 size_t used = 0xbadbeef; 244 check_result( a.get(), archive_write_open_memory( a.get(), 245 Data, 246 MaxSize, 247 &used ) ); 248 249 { 250 unique_ptr<struct archive, 251 decltype ( archive_write_close )*> a_open( a.get(), 252 archive_write_close ); 253 254 int file_index = 0; 255 for ( const auto& file : files ) 256 { 257 unique_ptr<struct archive_entry, 258 decltype ( archive_entry_free )*> 259 e( archive_entry_new2( a_open.get() ), 260 archive_entry_free ); 261 262 char name_buffer[100]; 263 snprintf( name_buffer, 100, "file%d", file_index++ ); 264 265 archive_entry_set_pathname( e.get(), name_buffer ); 266 archive_entry_set_size( e.get(), file.size() ); 267 archive_entry_set_filetype( e.get(), AE_IFREG ); 268 archive_entry_set_perm( e.get(), 0644 ); 269 270 check_result( a_open.get(), 271 archive_write_header( a_open.get(), e.get() ) ); 272 archive_write_data( a_open.get(), file.data(), file.size() ); 273 check_result( a_open.get(), 274 archive_write_finish_entry( a_open.get() ) ); 275 } 276 } 277 278 return used; 279 } 280 281 282 // Cross `Data1' and `Data2', write up to `MaxOutSize' bytes into `Out', 283 // return the number of bytes written, which should be positive. 284 virtual size_t CrossOver(const uint8_t * Data1,size_t Size1,const uint8_t * Data2,size_t Size2,uint8_t * Out,size_t MaxOutSize)285 CrossOver( const uint8_t* Data1, 286 size_t Size1, 287 const uint8_t* Data2, 288 size_t Size2, 289 uint8_t* Out, 290 size_t MaxOutSize ) 291 { 292 return fuzzer::UserSuppliedFuzzer::CrossOver( Data1, 293 Size1, 294 Data2, 295 Size2, 296 Out, 297 MaxOutSize ); 298 } 299 300 }; // end of FTFuzzer class 301 302 303 int main(int argc,char ** argv)304 main( int argc, 305 char* *argv ) 306 { 307 fuzzer::FuzzerRandomLibc Rand( 0 ); 308 FTFuzzer F( &Rand ); 309 310 fuzzer::FuzzerDriver( argc, argv, F ); 311 } 312 313 314 // END 315