265 lines
7.5 KiB
C++
265 lines
7.5 KiB
C++
#include "hb-fuzzer.hh"
|
|
|
|
#include <hb-ot.h>
|
|
#include <string.h>
|
|
|
|
#include <algorithm>
|
|
#include <cmath>
|
|
#include <stdlib.h>
|
|
#include <vector>
|
|
|
|
enum _fuzzing_shape_input_status_t
|
|
{
|
|
HB_FUZZING_SHAPE_INPUT_RAW = 0,
|
|
HB_FUZZING_SHAPE_INPUT_EXTENDED = 1,
|
|
HB_FUZZING_SHAPE_INPUT_MALFORMED = 2,
|
|
};
|
|
|
|
struct _fuzzing_shape_input_t
|
|
{
|
|
hb_blob_t *blob = nullptr;
|
|
hb_face_t *face = nullptr;
|
|
hb_font_t *font = nullptr;
|
|
std::vector<hb_codepoint_t> text;
|
|
std::vector<hb_variation_t> variations;
|
|
|
|
~_fuzzing_shape_input_t ()
|
|
{
|
|
hb_font_destroy (font);
|
|
hb_face_destroy (face);
|
|
hb_blob_destroy (blob);
|
|
}
|
|
};
|
|
|
|
static bool
|
|
_fuzzing_apply_extended_shape_ops (hb_face_t *face,
|
|
std::vector<hb_codepoint_t> *text,
|
|
std::vector<hb_variation_t> *variations,
|
|
const uint8_t *ops,
|
|
size_t ops_len)
|
|
{
|
|
variations->clear ();
|
|
unsigned axis_count = hb_ot_var_get_axis_count (face);
|
|
std::vector<hb_ot_var_axis_info_t> axes;
|
|
if (axis_count)
|
|
{
|
|
axes.resize (axis_count);
|
|
(void) hb_ot_var_get_axis_infos (face, 0, &axis_count, axes.data ());
|
|
}
|
|
|
|
const uint8_t *p = ops;
|
|
const uint8_t *end = ops + ops_len;
|
|
while (p < end)
|
|
{
|
|
uint8_t op;
|
|
if (!_fuzzing_read_value (p, end, &op))
|
|
return false;
|
|
|
|
switch (op)
|
|
{
|
|
case HB_FUZZING_OP_TEXT_ADD:
|
|
case HB_FUZZING_OP_TEXT_DEL:
|
|
{
|
|
uint32_t count;
|
|
if (!_fuzzing_read_u32_value (p, end, &count))
|
|
return false;
|
|
|
|
for (uint32_t i = 0; i < count; i++)
|
|
{
|
|
uint32_t cp;
|
|
if (!_fuzzing_read_u32_value (p, end, &cp))
|
|
return false;
|
|
if (op == HB_FUZZING_OP_TEXT_ADD)
|
|
text->push_back (cp);
|
|
else
|
|
text->erase (std::remove (text->begin (), text->end (), cp), text->end ());
|
|
}
|
|
break;
|
|
}
|
|
|
|
case HB_FUZZING_OP_AXIS_PIN_ALL_TO_DEFAULT:
|
|
variations->clear ();
|
|
break;
|
|
|
|
case HB_FUZZING_OP_AXIS_SET:
|
|
{
|
|
uint32_t count;
|
|
if (!_fuzzing_read_u32_value (p, end, &count))
|
|
return false;
|
|
|
|
for (uint32_t i = 0; i < count; i++)
|
|
{
|
|
uint32_t tag;
|
|
uint8_t mode;
|
|
float minimum, middle, maximum;
|
|
if (!_fuzzing_read_u32_value (p, end, &tag) ||
|
|
!_fuzzing_read_value (p, end, &mode) ||
|
|
!_fuzzing_read_f32_value (p, end, &minimum) ||
|
|
!_fuzzing_read_f32_value (p, end, &middle) ||
|
|
!_fuzzing_read_f32_value (p, end, &maximum))
|
|
return false;
|
|
|
|
if (!axis_count)
|
|
continue;
|
|
|
|
for (unsigned axis_index = 0; axis_index < axis_count; axis_index++)
|
|
{
|
|
if (axes[axis_index].tag != tag)
|
|
continue;
|
|
|
|
float value = axes[axis_index].default_value;
|
|
if (mode == HB_FUZZING_AXIS_PIN_TO_DEFAULT)
|
|
value = axes[axis_index].default_value;
|
|
else if (mode == HB_FUZZING_AXIS_SET_RANGE)
|
|
{
|
|
if (!std::isnan (middle))
|
|
value = middle;
|
|
else if (!std::isnan (minimum))
|
|
value = minimum;
|
|
else if (!std::isnan (maximum))
|
|
value = maximum;
|
|
}
|
|
else
|
|
return false;
|
|
|
|
bool updated = false;
|
|
for (auto &variation : *variations)
|
|
if (variation.tag == tag)
|
|
{
|
|
variation.value = value;
|
|
updated = true;
|
|
break;
|
|
}
|
|
if (!updated)
|
|
variations->push_back ({tag, value});
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
case HB_FUZZING_OP_SET_FLAGS:
|
|
{
|
|
uint32_t ignored;
|
|
if (!_fuzzing_read_u32_value (p, end, &ignored))
|
|
return false;
|
|
break;
|
|
}
|
|
|
|
case HB_FUZZING_OP_KEEP_EVERYTHING:
|
|
break;
|
|
|
|
case HB_FUZZING_OP_SET_CLEAR:
|
|
case HB_FUZZING_OP_SET_INVERT:
|
|
{
|
|
uint8_t ignored;
|
|
if (!_fuzzing_read_value (p, end, &ignored))
|
|
return false;
|
|
break;
|
|
}
|
|
|
|
case HB_FUZZING_OP_SET_ADD_RANGES:
|
|
case HB_FUZZING_OP_SET_DEL_RANGES:
|
|
{
|
|
uint8_t ignored_set_type;
|
|
uint32_t count;
|
|
if (!_fuzzing_read_value (p, end, &ignored_set_type) ||
|
|
!_fuzzing_read_u32_value (p, end, &count))
|
|
return false;
|
|
for (uint32_t i = 0; i < count; i++)
|
|
{
|
|
uint32_t ignored_start, ignored_end;
|
|
if (!_fuzzing_read_u32_value (p, end, &ignored_start) ||
|
|
!_fuzzing_read_u32_value (p, end, &ignored_end))
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
_fuzzing_extract_extended_ops (const uint8_t *data,
|
|
size_t size,
|
|
size_t *font_len,
|
|
const uint8_t **ops,
|
|
size_t *ops_len)
|
|
{
|
|
if (size < sizeof (_fuzzing_extended_magic) + 4)
|
|
return false;
|
|
|
|
size_t magic_offset = size - sizeof (_fuzzing_extended_magic);
|
|
if (0 != memcmp (data + magic_offset, _fuzzing_extended_magic, sizeof (_fuzzing_extended_magic)))
|
|
return false;
|
|
|
|
size_t ops_len_offset = magic_offset - 4;
|
|
uint32_t parsed_ops_len = _fuzzing_read_u32_le (data + ops_len_offset);
|
|
if (parsed_ops_len > ops_len_offset)
|
|
return false;
|
|
|
|
*font_len = ops_len_offset - parsed_ops_len;
|
|
*ops = data + *font_len;
|
|
*ops_len = parsed_ops_len;
|
|
return true;
|
|
}
|
|
|
|
static _fuzzing_shape_input_status_t
|
|
_fuzzing_prepare_shape_input (const uint8_t *data,
|
|
size_t size,
|
|
int x_scale,
|
|
int y_scale,
|
|
_fuzzing_shape_input_t *input)
|
|
{
|
|
size_t font_len = 0;
|
|
const uint8_t *ops = nullptr;
|
|
size_t ops_len = 0;
|
|
bool is_extended = _fuzzing_extract_extended_ops (data, size, &font_len, &ops, &ops_len);
|
|
|
|
input->blob = hb_blob_create ((const char *) data,
|
|
is_extended ? font_len : size,
|
|
HB_MEMORY_MODE_READONLY,
|
|
nullptr, nullptr);
|
|
input->face = hb_face_create (input->blob, 0);
|
|
input->font = hb_font_create (input->face);
|
|
hb_font_set_scale (input->font, x_scale, y_scale);
|
|
|
|
if (is_extended)
|
|
{
|
|
if (!_fuzzing_apply_extended_shape_ops (input->face, &input->text, &input->variations, ops, ops_len))
|
|
return HB_FUZZING_SHAPE_INPUT_MALFORMED;
|
|
|
|
hb_font_set_variations (input->font,
|
|
input->variations.empty () ? nullptr : input->variations.data (),
|
|
input->variations.size ());
|
|
return HB_FUZZING_SHAPE_INPUT_EXTENDED;
|
|
}
|
|
|
|
unsigned num_coords = 0;
|
|
if (size)
|
|
num_coords = data[size - 1];
|
|
num_coords = hb_ot_var_get_axis_count (input->face) > num_coords ? num_coords : hb_ot_var_get_axis_count (input->face);
|
|
int *coords = (int *) calloc (num_coords, sizeof (int));
|
|
if (size > num_coords + 1)
|
|
for (unsigned i = 0; i < num_coords; ++i)
|
|
coords[i] = ((int) data[size - num_coords + i - 1] - 128) * 10;
|
|
hb_font_set_var_coords_normalized (input->font, coords, num_coords);
|
|
free (coords);
|
|
|
|
uint32_t text32[16] = {0};
|
|
unsigned int len = sizeof (text32);
|
|
if (size < len)
|
|
len = size;
|
|
if (len)
|
|
memcpy (text32, data + size - len, len);
|
|
|
|
input->text.assign (text32, text32 + sizeof (text32) / sizeof (text32[0]));
|
|
return HB_FUZZING_SHAPE_INPUT_RAW;
|
|
}
|
|
|