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