Home | History | Annotate | Download | only in ftfuzzer
      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   {
     56     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 
     69     ~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 
     91     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 
    105     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
    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>>
    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
    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
    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