| #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; |
| } |
| |