Coverage Report

Created: 2026-06-13 06:33

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
37.0k
  {
28
37.0k
    hb_font_destroy (font);
29
37.0k
    hb_face_destroy (face);
30
37.0k
    hb_blob_destroy (blob);
31
37.0k
  }
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
327
{
41
327
  variations->clear ();
42
327
  unsigned axis_count = hb_ot_var_get_axis_count (face);
43
327
  std::vector<hb_ot_var_axis_info_t> axes;
44
327
  if (axis_count)
45
129
  {
46
129
    axes.resize (axis_count);
47
129
    (void) hb_ot_var_get_axis_infos (face, 0, &axis_count, axes.data ());
48
129
  }
49
50
327
  const uint8_t *p = ops;
51
327
  const uint8_t *end = ops + ops_len;
52
18.3k
  while (p < end)
53
18.1k
  {
54
18.1k
    uint8_t op;
55
18.1k
    if (!_fuzzing_read_value (p, end, &op))
56
0
      return false;
57
58
18.1k
    switch (op)
59
18.1k
    {
60
64
      case HB_FUZZING_OP_TEXT_ADD:
61
103
      case HB_FUZZING_OP_TEXT_DEL:
62
103
      {
63
103
        uint32_t count;
64
103
        if (!_fuzzing_read_u32_value (p, end, &count))
65
2
          return false;
66
67
2.39k
        for (uint32_t i = 0; i < count; i++)
68
2.32k
        {
69
2.32k
          uint32_t cp;
70
2.32k
          if (!_fuzzing_read_u32_value (p, end, &cp))
71
34
            return false;
72
2.29k
          if (op == HB_FUZZING_OP_TEXT_ADD)
73
1.06k
            text->push_back (cp);
74
1.22k
          else
75
1.22k
            text->erase (std::remove (text->begin (), text->end (), cp), text->end ());
76
2.29k
        }
77
67
        break;
78
101
      }
79
80
16.2k
      case HB_FUZZING_OP_AXIS_PIN_ALL_TO_DEFAULT:
81
16.2k
        variations->clear ();
82
16.2k
        break;
83
84
172
      case HB_FUZZING_OP_AXIS_SET:
85
172
      {
86
172
        uint32_t count;
87
172
        if (!_fuzzing_read_u32_value (p, end, &count))
88
1
          return false;
89
90
2.88k
        for (uint32_t i = 0; i < count; i++)
91
2.76k
        {
92
2.76k
          uint32_t tag;
93
2.76k
          uint8_t mode;
94
2.76k
          float minimum, middle, maximum;
95
2.76k
          if (!_fuzzing_read_u32_value (p, end, &tag) ||
96
2.75k
              !_fuzzing_read_value (p, end, &mode) ||
97
2.75k
              !_fuzzing_read_f32_value (p, end, &minimum) ||
98
2.74k
              !_fuzzing_read_f32_value (p, end, &middle) ||
99
2.73k
              !_fuzzing_read_f32_value (p, end, &maximum))
100
41
            return false;
101
102
2.72k
          if (!axis_count)
103
387
            continue;
104
105
49.5k
          for (unsigned axis_index = 0; axis_index < axis_count; axis_index++)
106
47.9k
          {
107
47.9k
            if (axes[axis_index].tag != tag)
108
47.2k
              continue;
109
110
782
            float value = axes[axis_index].default_value;
111
782
            if (mode == HB_FUZZING_AXIS_PIN_TO_DEFAULT)
112
456
              value = axes[axis_index].default_value;
113
326
            else if (mode == HB_FUZZING_AXIS_SET_RANGE)
114
309
            {
115
309
              if (!std::isnan (middle))
116
289
                value = middle;
117
20
              else if (!std::isnan (minimum))
118
7
                value = minimum;
119
13
              else if (!std::isnan (maximum))
120
10
                value = maximum;
121
309
            }
122
17
            else
123
17
              return false;
124
125
765
            bool updated = false;
126
765
            for (auto &variation : *variations)
127
917
              if (variation.tag == tag)
128
621
              {
129
621
                variation.value = value;
130
621
                updated = true;
131
621
                break;
132
621
              }
133
765
            if (!updated)
134
144
              variations->push_back ({tag, value});
135
765
            break;
136
782
          }
137
2.33k
        }
138
113
        break;
139
171
      }
140
141
316
      case HB_FUZZING_OP_SET_FLAGS:
142
316
      {
143
316
        uint32_t ignored;
144
316
        if (!_fuzzing_read_u32_value (p, end, &ignored))
145
4
          return false;
146
312
        break;
147
316
      }
148
149
590
      case HB_FUZZING_OP_KEEP_EVERYTHING:
150
590
        break;
151
152
276
      case HB_FUZZING_OP_SET_CLEAR:
153
606
      case HB_FUZZING_OP_SET_INVERT:
154
606
      {
155
606
        uint8_t ignored;
156
606
        if (!_fuzzing_read_value (p, end, &ignored))
157
2
          return false;
158
604
        break;
159
606
      }
160
161
604
      case HB_FUZZING_OP_SET_ADD_RANGES:
162
36
      case HB_FUZZING_OP_SET_DEL_RANGES:
163
36
      {
164
36
        uint8_t ignored_set_type;
165
36
        uint32_t count;
166
36
        if (!_fuzzing_read_value (p, end, &ignored_set_type) ||
167
35
            !_fuzzing_read_u32_value (p, end, &count))
168
3
          return false;
169
260
        for (uint32_t i = 0; i < count; i++)
170
247
        {
171
247
          uint32_t ignored_start, ignored_end;
172
247
          if (!_fuzzing_read_u32_value (p, end, &ignored_start) ||
173
234
              !_fuzzing_read_u32_value (p, end, &ignored_end))
174
20
            return false;
175
247
        }
176
13
        break;
177
33
      }
178
179
69
      default:
180
69
        return false;
181
18.1k
    }
182
18.1k
  }
183
184
134
  return true;
185
327
}
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
37.0k
{
194
37.0k
  if (size < sizeof (_fuzzing_extended_magic) + 4)
195
1.93k
    return false;
196
197
35.1k
  size_t magic_offset = size - sizeof (_fuzzing_extended_magic);
198
35.1k
  if (0 != memcmp (data + magic_offset, _fuzzing_extended_magic, sizeof (_fuzzing_extended_magic)))
199
34.8k
    return false;
200
201
351
  size_t ops_len_offset = magic_offset - 4;
202
351
  uint32_t parsed_ops_len = _fuzzing_read_u32_le (data + ops_len_offset);
203
351
  if (parsed_ops_len > ops_len_offset)
204
24
    return false;
205
206
327
  *font_len = ops_len_offset - parsed_ops_len;
207
327
  *ops = data + *font_len;
208
327
  *ops_len = parsed_ops_len;
209
327
  return true;
210
351
}
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
37.0k
{
219
37.0k
  size_t font_len = 0;
220
37.0k
  const uint8_t *ops = nullptr;
221
37.0k
  size_t ops_len = 0;
222
37.0k
  bool is_extended = _fuzzing_extract_extended_ops (data, size, &font_len, &ops, &ops_len);
223
224
37.0k
  input->blob = hb_blob_create ((const char *) data,
225
37.0k
                                is_extended ? font_len : size,
226
37.0k
                                HB_MEMORY_MODE_READONLY,
227
37.0k
                                nullptr, nullptr);
228
37.0k
  input->face = hb_face_create (input->blob, 0);
229
37.0k
  input->font = hb_font_create (input->face);
230
37.0k
  hb_font_set_scale (input->font, x_scale, y_scale);
231
232
37.0k
  if (is_extended)
233
327
  {
234
327
    if (!_fuzzing_apply_extended_shape_ops (input->face, &input->text, &input->variations, ops, ops_len))
235
193
      return HB_FUZZING_SHAPE_INPUT_MALFORMED;
236
237
134
    hb_font_set_variations (input->font,
238
134
                            input->variations.empty () ? nullptr : input->variations.data (),
239
134
                            input->variations.size ());
240
134
    return HB_FUZZING_SHAPE_INPUT_EXTENDED;
241
327
  }
242
243
36.7k
  unsigned num_coords = 0;
244
36.7k
  if (size)
245
36.7k
    num_coords = data[size - 1];
246
36.7k
  num_coords = hb_ot_var_get_axis_count (input->face) > num_coords ? num_coords : hb_ot_var_get_axis_count (input->face);
247
36.7k
  int *coords = (int *) calloc (num_coords, sizeof (int));
248
36.7k
  if (size > num_coords + 1)
249
41.9k
    for (unsigned i = 0; i < num_coords; ++i)
250
5.23k
      coords[i] = ((int) data[size - num_coords + i - 1] - 128) * 10;
251
36.7k
  hb_font_set_var_coords_normalized (input->font, coords, num_coords);
252
36.7k
  free (coords);
253
254
36.7k
  uint32_t text32[16] = {0};
255
36.7k
  unsigned int len = sizeof (text32);
256
36.7k
  if (size < len)
257
6.49k
    len = size;
258
36.7k
  if (len)
259
36.7k
    memcpy (text32, data + size - len, len);
260
261
36.7k
  input->text.assign (text32, text32 + sizeof (text32) / sizeof (text32[0]));
262
36.7k
  return HB_FUZZING_SHAPE_INPUT_RAW;
263
37.0k
}
264