Warning: That file was not part of the compilation database. It may have many parsing errors.

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
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
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>>
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:
145 FTFuzzer( fuzzer::FuzzerRandomBase* Rand )
146 : fuzzer::UserSuppliedFuzzer( Rand ) {}
147
148
149 int
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
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
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
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

Warning: That file was not part of the compilation database. It may have many parsing errors.