// This is a fuzzing harness for Harfbuzz. Since Harfbuzz's input is generally | |
// expected to be controlled by a remote party it's a possible vector for | |
// security issues. | |
// | |
// Fuzzing is a black-box testing scheme where the black-box (Harfbuzz's shaping | |
// engine in this case) is fed random input to see if it will misbehave. | |
// Misbehaviours can often be turned into security or crash issues. | |
// | |
// It's expected that one will generally run this under valgrind in order to get | |
// better detection of problems. | |
#include <stdint.h> | |
#include <stdio.h> | |
#include <ft2build.h> | |
#include FT_FREETYPE_H | |
#include "../../src/harfbuzz-shaper.h" | |
#include "../../src/harfbuzz-global.h" | |
#include "../../src/harfbuzz-gpos.h" | |
extern "C" { | |
#include "../../contrib/harfbuzz-unicode.h" | |
#include "../../contrib/harfbuzz-freetype.h" | |
} | |
static FT_Library freetype; | |
static FT_Face loadFace(const char *path) | |
{ | |
FT_Face face; | |
if (FT_New_Face(freetype, path, /* index */ 0, &face)) | |
return 0; | |
return face; | |
} | |
static const int kWidth = 100; | |
static const int kHeight = 100; | |
static int | |
usage(const char *argv0) { | |
fprintf(stderr, "Usage: %s <TTF file>\n", argv0); | |
return 1; | |
} | |
int | |
main(int argc, char **argv) { | |
FT_Init_FreeType(&freetype); | |
if (argc != 2) | |
return usage(argv[0]); | |
FT_Face face; | |
if (FT_New_Face(freetype, argv[1], 0 /* face index */, &face)) { | |
fprintf(stderr, "Failed to load font file\n"); | |
return 1; | |
} | |
HB_Face hbFace = HB_NewFace(face, hb_freetype_table_sfnt_get); | |
HB_FontRec hbFont; | |
hbFont.klass = &hb_freetype_class; | |
hbFont.userData = face; | |
hbFont.x_ppem = face->size->metrics.x_ppem; | |
hbFont.y_ppem = face->size->metrics.y_ppem; | |
hbFont.x_scale = face->size->metrics.x_scale; | |
hbFont.y_scale = face->size->metrics.y_scale; | |
// This is the maximum number of bytes of input which we'll feed to Harfbuzz | |
// in one shot. We also overload it and make it the size of the output arrays | |
// as well. (Must be a power of two.) | |
static const unsigned kMaxInputBytes = 1024; | |
uint8_t str[kMaxInputBytes]; | |
HB_ShaperItem shaper_item; | |
shaper_item.kerning_applied = false; | |
shaper_item.string = (HB_UChar16 *) str; | |
shaper_item.stringLength = 0; | |
shaper_item.item.bidiLevel = 0; | |
shaper_item.shaperFlags = 0; | |
shaper_item.font = &hbFont; | |
shaper_item.face = hbFace; | |
shaper_item.glyphIndicesPresent = false; | |
shaper_item.initialGlyphCount = 0; | |
HB_Glyph out_glyphs[kMaxInputBytes]; | |
HB_GlyphAttributes out_attrs[kMaxInputBytes]; | |
HB_Fixed out_advs[kMaxInputBytes]; | |
HB_FixedPoint out_offsets[kMaxInputBytes]; | |
unsigned short out_logClusters[kMaxInputBytes]; | |
shaper_item.glyphs = out_glyphs; | |
shaper_item.attributes = out_attrs; | |
shaper_item.advances = out_advs; | |
shaper_item.offsets = out_offsets; | |
shaper_item.log_clusters = out_logClusters; | |
shaper_item.num_glyphs = kMaxInputBytes; | |
FILE *urandom = fopen("/dev/urandom", "rb"); | |
if (!urandom) { | |
fprintf(stderr, "Cannot open /dev/urandom\n"); | |
return 1; | |
} | |
for (;;) { | |
uint16_t len; | |
fread(&len, sizeof(len), 1, urandom); | |
len &= (kMaxInputBytes - 1); | |
len &= ~1; | |
fread(str, len, 1, urandom); | |
ssize_t iterator = 0; | |
for (;;) { | |
if (!hb_utf16_script_run_next(NULL, &shaper_item.item, (uint16_t *) str, len >> 1, &iterator)) | |
break; | |
HB_ShapeItem(&shaper_item); | |
} | |
} | |
HB_FreeFace(hbFace); | |
} |