/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 | | |