blob: b342c653445b71fddbecd6690053f1c90160e6ba [file] [log] [blame] [edit]
#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;
}