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

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

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