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