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