Coverage Report

Created: 2026-10-10 06:15

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/harfbuzz/test/fuzzing/hb-shape-input.hh
Line
Count
Source
1
#include "hb-fuzzer.hh"
2
3
#include <hb-ot.h>
4
#include <string.h>
5
6
#include <algorithm>
7
#include <cmath>
8
#include <stdlib.h>
9
#include <vector>
10
11
enum _fuzzing_shape_input_status_t
12
{
13
  HB_FUZZING_SHAPE_INPUT_RAW = 0,
14
  HB_FUZZING_SHAPE_INPUT_EXTENDED = 1,
15
  HB_FUZZING_SHAPE_INPUT_MALFORMED = 2,
16
};
17
18
struct _fuzzing_shape_input_t
19
{
20
  hb_blob_t *blob = nullptr;
21
  hb_face_t *face = nullptr;
22
  hb_font_t *font = nullptr;
23
  std::vector<hb_codepoint_t> text;
24
  std::vector<hb_variation_t> variations;
25
26
  ~_fuzzing_shape_input_t ()
27
18.2k
  {
28
18.2k
    hb_font_destroy (font);
29
18.2k
    hb_face_destroy (face);
30
18.2k
    hb_blob_destroy (blob);
31
18.2k
  }
32
};
33
34
static bool
35
_fuzzing_apply_extended_shape_ops (hb_face_t *face,
36
                                   std::vector<hb_codepoint_t> *text,
37
                                   std::vector<hb_variation_t> *variations,
38
                                   const uint8_t *ops,
39
                                   size_t ops_len)
40
738
{
41
738
  variations->clear ();
42
738
  unsigned axis_count = hb_ot_var_get_axis_count (face);
43
738
  std::vector<hb_ot_var_axis_info_t> axes;
44
738
  if (axis_count)
45
160
  {
46
160
    axes.resize (axis_count);
47
160
    (void) hb_ot_var_get_axis_infos (face, 0, &axis_count, axes.data ());
48
160
  }
49
50
738
  const uint8_t *p = ops;
51
738
  const uint8_t *end = ops + ops_len;
52
3.12k
  while (p < end)
53
2.67k
  {
54
2.67k
    uint8_t op;
55
2.67k
    if (!_fuzzing_read_value (p, end, &op))
56
0
      return false;
57
58
2.67k
    switch (op)
59
2.67k
    {
60
437
      case HB_FUZZING_OP_TEXT_ADD:
61
529
      case HB_FUZZING_OP_TEXT_DEL:
62
529
      {
63
529
        uint32_t count;
64
529
        if (!_fuzzing_read_u32_value (p, end, &count))
65
7
          return false;
66
67
4.13k
        for (uint32_t i = 0; i < count; i++)
68
3.70k
        {
69
3.70k
          uint32_t cp;
70
3.70k
          if (!_fuzzing_read_u32_value (p, end, &cp))
71
89
            return false;
72
3.61k
          if (op == HB_FUZZING_OP_TEXT_ADD)
73
2.49k
            text->push_back (cp);
74
1.11k
          else
75
1.11k
            text->erase (std::remove (text->begin (), text->end (), cp), text->end ());
76
3.61k
        }
77
433
        break;
78
522
      }
79
80
433
      case HB_FUZZING_OP_AXIS_PIN_ALL_TO_DEFAULT:
81
335
        variations->clear ();
82
335
        break;
83
84
186
      case HB_FUZZING_OP_AXIS_SET:
85
186
      {
86
186
        uint32_t count;
87
186
        if (!_fuzzing_read_u32_value (p, end, &count))
88
2
          return false;
89
90
2.14k
        for (uint32_t i = 0; i < count; i++)
91
2.04k
        {
92
2.04k
          uint32_t tag;
93
2.04k
          uint8_t mode;
94
2.04k
          float minimum, middle, maximum;
95
2.04k
          if (!_fuzzing_read_u32_value (p, end, &tag) ||
96
2.03k
              !_fuzzing_read_value (p, end, &mode) ||
97
2.02k
              !_fuzzing_read_f32_value (p, end, &minimum) ||
98
2.01k
              !_fuzzing_read_f32_value (p, end, &middle) ||
99
1.99k
              !_fuzzing_read_f32_value (p, end, &maximum))
100
70
            return false;
101
102
1.97k
          if (!axis_count)
103
277
            continue;
104
105
409k
          for (unsigned axis_index = 0; axis_index < axis_count; axis_index++)
106
408k
          {
107
408k
            if (axes[axis_index].tag != tag)
108
407k
              continue;
109
110
760
            float value = axes[axis_index].default_value;
111
760
            if (mode == HB_FUZZING_AXIS_PIN_TO_DEFAULT)
112
483
              value = axes[axis_index].default_value;
113
277
            else if (mode == HB_FUZZING_AXIS_SET_RANGE)
114
255
            {
115
255
              if (!std::isnan (middle))
116
213
                value = middle;
117
42
              else if (!std::isnan (minimum))
118
22
                value = minimum;
119
20
              else if (!std::isnan (maximum))
120
15
                value = maximum;
121
255
            }
122
22
            else
123
22
              return false;
124
125
738
            bool updated = false;
126
738
            for (auto &variation : *variations)
127
1.10k
              if (variation.tag == tag)
128
570
              {
129
570
                variation.value = value;
130
570
                updated = true;
131
570
                break;
132
570
              }
133
738
            if (!updated)
134
168
              variations->push_back ({tag, value});
135
738
            break;
136
760
          }
137
1.70k
        }
138
92
        break;
139
184
      }
140
141
527
      case HB_FUZZING_OP_SET_FLAGS:
142
527
      {
143
527
        uint32_t ignored;
144
527
        if (!_fuzzing_read_u32_value (p, end, &ignored))
145
3
          return false;
146
524
        break;
147
527
      }
148
149
524
      case HB_FUZZING_OP_KEEP_EVERYTHING:
150
340
        break;
151
152
269
      case HB_FUZZING_OP_SET_CLEAR:
153
621
      case HB_FUZZING_OP_SET_INVERT:
154
621
      {
155
621
        uint8_t ignored;
156
621
        if (!_fuzzing_read_value (p, end, &ignored))
157
3
          return false;
158
618
        break;
159
621
      }
160
161
618
      case HB_FUZZING_OP_SET_ADD_RANGES:
162
92
      case HB_FUZZING_OP_SET_DEL_RANGES:
163
92
      {
164
92
        uint8_t ignored_set_type;
165
92
        uint32_t count;
166
92
        if (!_fuzzing_read_value (p, end, &ignored_set_type) ||
167
91
            !_fuzzing_read_u32_value (p, end, &count))
168
5
          return false;
169
623
        for (uint32_t i = 0; i < count; i++)
170
581
        {
171
581
          uint32_t ignored_start, ignored_end;
172
581
          if (!_fuzzing_read_u32_value (p, end, &ignored_start) ||
173
553
              !_fuzzing_read_u32_value (p, end, &ignored_end))
174
45
            return false;
175
581
        }
176
42
        break;
177
87
      }
178
179
43
      default:
180
43
        return false;
181
2.67k
    }
182
2.67k
  }
183
184
449
  return true;
185
738
}
186
187
static bool
188
_fuzzing_extract_extended_ops (const uint8_t *data,
189
                               size_t size,
190
                               size_t *font_len,
191
                               const uint8_t **ops,
192
                               size_t *ops_len)
193
18.2k
{
194
18.2k
  if (size < sizeof (_fuzzing_extended_magic) + 4)
195
314
    return false;
196
197
17.9k
  size_t magic_offset = size - sizeof (_fuzzing_extended_magic);
198
17.9k
  if (0 != memcmp (data + magic_offset, _fuzzing_extended_magic, sizeof (_fuzzing_extended_magic)))
199
16.8k
    return false;
200
201
1.03k
  size_t ops_len_offset = magic_offset - 4;
202
1.03k
  uint32_t parsed_ops_len = _fuzzing_read_u32_le (data + ops_len_offset);
203
1.03k
  if (parsed_ops_len > ops_len_offset)
204
297
    return false;
205
206
738
  *font_len = ops_len_offset - parsed_ops_len;
207
738
  *ops = data + *font_len;
208
738
  *ops_len = parsed_ops_len;
209
738
  return true;
210
1.03k
}
211
212
static _fuzzing_shape_input_status_t
213
_fuzzing_prepare_shape_input (const uint8_t *data,
214
                              size_t size,
215
                              int x_scale,
216
                              int y_scale,
217
                              _fuzzing_shape_input_t *input)
218
18.2k
{
219
18.2k
  size_t font_len = 0;
220
18.2k
  const uint8_t *ops = nullptr;
221
18.2k
  size_t ops_len = 0;
222
18.2k
  bool is_extended = _fuzzing_extract_extended_ops (data, size, &font_len, &ops, &ops_len);
223
224
18.2k
  input->blob = hb_blob_create ((const char *) data,
225
18.2k
                                is_extended ? font_len : size,
226
18.2k
                                HB_MEMORY_MODE_READONLY,
227
18.2k
                                nullptr, nullptr);
228
18.2k
  input->face = hb_face_create (input->blob, 0);
229
18.2k
  input->font = hb_font_create (input->face);
230
18.2k
  hb_font_set_scale (input->font, x_scale, y_scale);
231
232
18.2k
  if (is_extended)
233
738
  {
234
738
    if (!_fuzzing_apply_extended_shape_ops (input->face, &input->text, &input->variations, ops, ops_len))
235
289
      return HB_FUZZING_SHAPE_INPUT_MALFORMED;
236
237
449
    hb_font_set_variations (input->font,
238
449
                            input->variations.empty () ? nullptr : input->variations.data (),
239
449
                            input->variations.size ());
240
449
    return HB_FUZZING_SHAPE_INPUT_EXTENDED;
241
738
  }
242
243
17.4k
  unsigned num_coords = 0;
244
17.4k
  if (size)
245
17.4k
    num_coords = data[size - 1];
246
17.4k
  num_coords = hb_ot_var_get_axis_count (input->face) > num_coords ? num_coords : hb_ot_var_get_axis_count (input->face);
247
17.4k
  int *coords = (int *) calloc (num_coords, sizeof (int));
248
17.4k
  if (size > num_coords + 1)
249
44.2k
    for (unsigned i = 0; i < num_coords; ++i)
250
26.7k
      coords[i] = ((int) data[size - num_coords + i - 1] - 128) * 10;
251
17.4k
  hb_font_set_var_coords_normalized (input->font, coords, num_coords);
252
17.4k
  free (coords);
253
254
17.4k
  uint32_t text32[16] = {0};
255
17.4k
  unsigned int len = sizeof (text32);
256
17.4k
  if (size < len)
257
1.31k
    len = size;
258
17.4k
  if (len)
259
17.4k
    memcpy (text32, data + size - len, len);
260
261
17.4k
  input->text.assign (text32, text32 + sizeof (text32) / sizeof (text32[0]));
262
17.4k
  return HB_FUZZING_SHAPE_INPUT_RAW;
263
18.2k
}
264