1 // ftfuzzer.cc
2 //
3 //   A fuzzing function to test 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 // we use `unique_ptr', `decltype', and other gimmicks defined since C++11
16 #if __cplusplus < 201103L
17 #  error "a C++11 compiler is needed"
18 #endif
19 
20 #include <archive.h>
21 #include <archive_entry.h>
22 
23 #include <assert.h>
24 #include <stdint.h>
25 
26 #include <memory>
27 #include <vector>
28 
29 
30   using namespace std;
31 
32 
33 #include <ft2build.h>
34 
35 #include FT_FREETYPE_H
36 #include FT_GLYPH_H
37 #include FT_CACHE_H
38 #include FT_CACHE_CHARMAP_H
39 #include FT_CACHE_IMAGE_H
40 #include FT_CACHE_SMALL_BITMAPS_H
41 #include FT_SYNTHESIS_H
42 #include FT_ADVANCES_H
43 #include FT_OUTLINE_H
44 #include FT_BBOX_H
45 #include FT_MODULE_H
46 #include FT_DRIVER_H
47 #include FT_MULTIPLE_MASTERS_H
48 
49 
50   static FT_Library  library;
51   static int         InitResult;
52 
53 
54   struct FT_Global
55   {
FT_GlobalFT_Global56     FT_Global()
57     {
58       InitResult = FT_Init_FreeType( &library );
59       if ( InitResult )
60         return;
61 
62       // try to activate Adobe's CFF engine; it might not be the default
63       unsigned int  cff_hinting_engine = FT_HINTING_ADOBE;
64       FT_Property_Set( library,
65                        "cff",
66                        "hinting-engine", &cff_hinting_engine );
67     }
68 
~FT_GlobalFT_Global69     ~FT_Global()
70     {
71       FT_Done_FreeType( library );
72     }
73   };
74 
75   FT_Global  global_ft;
76 
77 
78   // We want to select n values at random (without repetition),
79   // with 0 < n <= N.  The algorithm is taken from TAoCP, Vol. 2
80   // (Algorithm S, selection sampling technique)
81   struct Random
82   {
83     int  n;
84     int  N;
85 
86     int  t; // total number of values so far
87     int  m; // number of selected values so far
88 
89     uint32_t  r; // the current pseudo-random number
90 
RandomRandom91     Random( int n_,
92             int N_ )
93     : n( n_ ),
94       N( N_ )
95     {
96       t = 0;
97       m = 0;
98 
99       // Ideally, this should depend on the input file,
100       // for example, taking the sha256 as input;
101       // however, this is overkill for fuzzying tests.
102       r = 12345;
103     }
104 
getRandom105     int get()
106     {
107       if ( m >= n )
108         return -1;
109 
110     Redo:
111       // We can't use `rand': different C libraries might provide
112       // different implementations of this function.  As a replacement,
113       // we use a 32bit version of the `xorshift' algorithm.
114       r ^= r << 13;
115       r ^= r >> 17;
116       r ^= r << 5;
117 
118       double  U = double( r ) / UINT32_MAX;
119 
120       if ( ( N - t ) * U >= ( n - m ) )
121       {
122         t++;
123         goto Redo;
124       }
125 
126       t++;
127       m++;
128 
129       return t;
130     }
131   };
132 
133 
134   static int
archive_read_entry_data(struct archive * ar,vector<FT_Byte> * vw)135   archive_read_entry_data( struct archive   *ar,
136                            vector<FT_Byte>  *vw )
137   {
138     int             r;
139     const FT_Byte*  buff;
140     size_t          size;
141     int64_t         offset;
142 
143     for (;;)
144     {
145       r = archive_read_data_block( ar,
146                                    reinterpret_cast<const void**>( &buff ),
147                                    &size,
148                                    &offset );
149       if ( r == ARCHIVE_EOF )
150         return ARCHIVE_OK;
151       if ( r != ARCHIVE_OK )
152         return r;
153 
154       vw->insert( vw->end(), buff, buff + size );
155     }
156   }
157 
158 
159   static vector<vector<FT_Byte>>
parse_data(const uint8_t * data,size_t size)160   parse_data( const uint8_t*  data,
161               size_t          size )
162   {
163     struct archive_entry*    entry;
164     int                      r;
165     vector<vector<FT_Byte>>  files;
166 
167     unique_ptr<struct  archive,
168                decltype ( archive_read_free )*>  a( archive_read_new(),
169                                                     archive_read_free );
170 
171     // activate reading of uncompressed tar archives
172     archive_read_support_format_tar( a.get() );
173 
174     // the need for `const_cast' was removed with libarchive commit be4d4dd
175     if ( !( r = archive_read_open_memory(
176                   a.get(),
177                   const_cast<void*>(static_cast<const void*>( data ) ),
178                   size ) ) )
179     {
180       unique_ptr<struct  archive,
181                  decltype ( archive_read_close )*>  a_open( a.get(),
182                                                             archive_read_close );
183 
184       // read files contained in archive
185       for (;;)
186       {
187         r = archive_read_next_header( a_open.get(), &entry );
188         if ( r == ARCHIVE_EOF )
189           break;
190         if ( r != ARCHIVE_OK )
191           break;
192 
193         vector<FT_Byte>  entry_data;
194         r = archive_read_entry_data( a.get(), &entry_data );
195         if ( r != ARCHIVE_OK )
196           break;
197 
198         files.push_back( move( entry_data ) );
199       }
200     }
201 
202     if ( files.size() == 0 )
203       files.emplace_back( data, data + size );
204 
205     return files;
206   }
207 
208 
209   static void
setIntermediateAxis(FT_Face face)210   setIntermediateAxis( FT_Face  face )
211   {
212     // only handle Multiple Masters and GX variation fonts
213     if ( !FT_HAS_MULTIPLE_MASTERS( face ) )
214       return;
215 
216     // get variation data for current instance
217     FT_MM_Var*  variations_ptr = nullptr;
218     if ( FT_Get_MM_Var( face, &variations_ptr ) )
219       return;
220 
221     unique_ptr<FT_MM_Var,
222                decltype ( free )*>  variations( variations_ptr, free );
223     vector<FT_Fixed>                coords( variations->num_axis );
224 
225     // select an arbitrary instance
226     for ( unsigned int  i = 0; i < variations->num_axis; i++ )
227       coords[i] = ( variations->axis[i].minimum +
228                     variations->axis[i].def     ) / 2;
229 
230     if ( FT_Set_Var_Design_Coordinates( face,
231                                         FT_UInt( coords.size() ),
232                                         coords.data() ) )
233       return;
234   }
235 
236 
237   // the interface function to the libFuzzer library
238   extern "C" int
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size_)239   LLVMFuzzerTestOneInput( const uint8_t*  data,
240                           size_t          size_ )
241   {
242     assert( !InitResult );
243 
244     if ( size_ < 1 )
245       return 0;
246 
247     const vector<vector<FT_Byte>>&  files = parse_data( data, size_ );
248 
249     FT_Face         face;
250     FT_Int32        load_flags  = FT_LOAD_DEFAULT;
251 #if 0
252     FT_Render_Mode  render_mode = FT_RENDER_MODE_NORMAL;
253 #endif
254 
255     // We use a conservative approach here, at the cost of calling
256     // `FT_New_Face' quite often.  The idea is that the fuzzer should be
257     // able to try all faces and named instances of a font, expecting that
258     // some faces don't work for various reasons, e.g., a broken subfont, or
259     // an unsupported NFNT bitmap font in a Mac dfont resource that holds
260     // more than a single font.
261 
262     // get number of faces
263     if ( FT_New_Memory_Face( library,
264                              files[0].data(),
265                              (FT_Long)files[0].size(),
266                              -1,
267                              &face ) )
268       return 0;
269     long  num_faces = face->num_faces;
270     FT_Done_Face( face );
271 
272     // loop over up to 20 arbitrarily selected faces
273     // from index range [0;num-faces-1]
274     long  max_face_cnt = num_faces < 20
275                            ? num_faces
276                            : 20;
277 
278     Random  faces_pool( (int)max_face_cnt, (int)num_faces );
279 
280     for ( long  face_cnt = 0;
281           face_cnt < max_face_cnt;
282           face_cnt++ )
283     {
284       long  face_index = faces_pool.get() - 1;
285 
286       // get number of instances
287       if ( FT_New_Memory_Face( library,
288                                files[0].data(),
289                                (FT_Long)files[0].size(),
290                                -( face_index + 1 ),
291                                &face ) )
292         continue;
293       long  num_instances = face->style_flags >> 16;
294       FT_Done_Face( face );
295 
296       // loop over the face without instance (index 0)
297       // and up to 20 arbitrarily selected instances
298       // from index range [1;num_instances]
299       long  max_instance_cnt = num_instances < 20
300                                  ? num_instances
301                                  : 20;
302 
303       Random  instances_pool( (int)max_instance_cnt, (int)num_instances );
304 
305       for ( long  instance_cnt = 0;
306             instance_cnt <= max_instance_cnt;
307             instance_cnt++ )
308       {
309         long  instance_index = 0;
310 
311         if ( !instance_cnt )
312         {
313           if ( FT_New_Memory_Face( library,
314                                    files[0].data(),
315                                    (FT_Long)files[0].size(),
316                                    face_index,
317                                    &face ) )
318             continue;
319         }
320         else
321         {
322           instance_index = instances_pool.get();
323 
324           if ( FT_New_Memory_Face( library,
325                                    files[0].data(),
326                                    (FT_Long)files[0].size(),
327                                    ( instance_index << 16 ) + face_index,
328                                    &face ) )
329             continue;
330         }
331 
332         // if we have more than a single input file coming from an archive,
333         // attach them (starting with the second file) using the order given
334         // in the archive
335         for ( size_t  files_index = 1;
336               files_index < files.size();
337               files_index++ )
338         {
339           FT_Open_Args  open_args = {};
340           open_args.flags         = FT_OPEN_MEMORY;
341           open_args.memory_base   = files[files_index].data();
342           open_args.memory_size   = (FT_Long)files[files_index].size();
343 
344           // the last archive element will be eventually used as the
345           // attachment
346           FT_Attach_Stream( face, &open_args );
347         }
348 
349         // loop over an arbitrary size for outlines
350         // and up to ten arbitrarily selected bitmap strike sizes
351         // from the range [0;num_fixed_sizes - 1]
352         int  max_size_cnt = face->num_fixed_sizes < 10
353                               ? face->num_fixed_sizes
354                               : 10;
355 
356         Random sizes_pool( max_size_cnt, face->num_fixed_sizes );
357 
358         for ( int  size_cnt = 0;
359               size_cnt <= max_size_cnt;
360               size_cnt++ )
361         {
362           FT_Int32  flags = load_flags;
363 
364           int  size_index = 0;
365 
366           if ( !size_cnt )
367           {
368             // set up 20pt at 72dpi as an arbitrary size
369             if ( FT_Set_Char_Size( face, 20 * 64, 20 * 64, 72, 72 ) )
370               continue;
371             flags |= FT_LOAD_NO_BITMAP;
372           }
373           else
374           {
375             // bitmap strikes are not active for font variations
376             if ( instance_index )
377               continue;
378 
379             size_index = sizes_pool.get() - 1;
380 
381             if ( FT_Select_Size( face, size_index ) )
382               continue;
383             flags |= FT_LOAD_COLOR;
384           }
385 
386           // test MM interface only for a face without a selected instance
387           // and without a selected bitmap strike
388           if ( !instance_index && !size_cnt )
389             setIntermediateAxis( face );
390 
391           // loop over all glyphs
392           for ( unsigned int  glyph_index = 0;
393                 glyph_index < (unsigned int)face->num_glyphs;
394                 glyph_index++ )
395           {
396             if ( FT_Load_Glyph( face, glyph_index, flags ) )
397               continue;
398 
399             // Rendering is the most expensive and the least interesting part.
400             //
401             // if ( FT_Render_Glyph( face->glyph, render_mode) )
402             //   continue;
403             // FT_GlyphSlot_Embolden( face->glyph );
404 
405 #if 0
406             FT_Glyph  glyph;
407             if ( !FT_Get_Glyph( face->glyph, &glyph ) )
408               FT_Done_Glyph( glyph );
409 
410             FT_Outline*  outline = &face->glyph->outline;
411             FT_Matrix    rot30   = { 0xDDB4, -0x8000, 0x8000, 0xDDB4 };
412 
413             FT_Outline_Transform( outline, &rot30 );
414 
415             FT_BBox  bbox;
416             FT_Outline_Get_BBox( outline, &bbox );
417 #endif
418           }
419         }
420         FT_Done_Face( face );
421       }
422     }
423 
424     return 0;
425   }
426 
427 
428 // END
429