/src/libheif/fuzzing/api_fuzzer.cc
Line | Count | Source |
1 | | /* |
2 | | * HEIF codec. |
3 | | * Copyright (c) 2026 struktur AG |
4 | | * |
5 | | * This file is part of libheif. |
6 | | * |
7 | | * libheif is free software: you can redistribute it and/or modify |
8 | | * it under the terms of the GNU Lesser General Public License as |
9 | | * published by the Free Software Foundation, either version 3 of |
10 | | * the License, or (at your option) any later version. |
11 | | * |
12 | | * libheif is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | * GNU Lesser General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU Lesser General Public License |
18 | | * along with libheif. If not, see <http://www.gnu.org/licenses/>. |
19 | | */ |
20 | | |
21 | | /* |
22 | | * Companion to file_fuzzer that exercises API surfaces beyond the basic |
23 | | * decode-and-iterate path: image sequences, region items, depth and |
24 | | * auxiliary images, tile-by-tile grid decode, item-level data access, |
25 | | * entity groups, color profiles, and item properties. |
26 | | */ |
27 | | |
28 | | #include <cstdint> |
29 | | #include <cstddef> |
30 | | #include <cstdlib> |
31 | | |
32 | | #include "libheif/heif.h" |
33 | | #include "libheif/heif_sequences.h" |
34 | | #include "libheif/heif_regions.h" |
35 | | #include "libheif/heif_aux_images.h" |
36 | | #include "libheif/heif_tiling.h" |
37 | | #include "libheif/heif_properties.h" |
38 | | #include "libheif/heif_entity_groups.h" |
39 | | #include "libheif/heif_items.h" |
40 | | #include "libheif/heif_color.h" |
41 | | |
42 | | static void TestSequences(struct heif_context* ctx) |
43 | 13.2k | { |
44 | 13.2k | if (!heif_context_has_sequence(ctx)) { |
45 | 13.2k | return; |
46 | 13.2k | } |
47 | | |
48 | 0 | int ntracks = heif_context_number_of_sequence_tracks(ctx); |
49 | 0 | if (ntracks <= 0 || ntracks > 16) { |
50 | 0 | return; |
51 | 0 | } |
52 | | |
53 | 0 | uint32_t* track_ids = static_cast<uint32_t*>(malloc(ntracks * sizeof(uint32_t))); |
54 | 0 | if (!track_ids) return; |
55 | 0 | heif_context_get_track_ids(ctx, track_ids); |
56 | |
|
57 | 0 | for (int t = 0; t < ntracks && t < 4; t++) { |
58 | 0 | struct heif_track* track = heif_context_get_track(ctx, track_ids[t]); |
59 | 0 | if (!track) continue; |
60 | | |
61 | 0 | heif_track_get_track_handler_type(track); |
62 | |
|
63 | 0 | uint16_t tw = 0, th = 0; |
64 | 0 | heif_track_get_image_resolution(track, &tw, &th); |
65 | |
|
66 | 0 | for (int f = 0; f < 8; f++) { |
67 | 0 | struct heif_image* img = nullptr; |
68 | 0 | struct heif_error err = heif_track_decode_next_image(track, &img, |
69 | 0 | heif_colorspace_YCbCr, heif_chroma_420, nullptr); |
70 | 0 | if (err.code != heif_error_Ok || !img) break; |
71 | 0 | int stride; |
72 | 0 | heif_image_get_plane_readonly(img, heif_channel_Y, &stride); |
73 | 0 | heif_image_release(img); |
74 | 0 | } |
75 | 0 | heif_track_release(track); |
76 | 0 | } |
77 | 0 | free(track_ids); |
78 | 0 | } |
79 | | |
80 | | static void TestRegions(struct heif_context* ctx, const struct heif_image_handle* handle) |
81 | 13.1k | { |
82 | 13.1k | int nregion_items = heif_image_handle_get_number_of_region_items(handle); |
83 | 13.1k | if (nregion_items <= 0 || nregion_items > 32) { |
84 | 13.1k | return; |
85 | 13.1k | } |
86 | | |
87 | 0 | heif_item_id* region_ids = static_cast<heif_item_id*>(malloc(nregion_items * sizeof(heif_item_id))); |
88 | 0 | if (!region_ids) return; |
89 | 0 | heif_image_handle_get_list_of_region_item_ids(handle, region_ids, nregion_items); |
90 | |
|
91 | 0 | for (int r = 0; r < nregion_items && r < 8; r++) { |
92 | 0 | struct heif_region_item* region_item = nullptr; |
93 | 0 | struct heif_error err = heif_context_get_region_item(ctx, region_ids[r], ®ion_item); |
94 | 0 | if (err.code != heif_error_Ok || !region_item) continue; |
95 | | |
96 | 0 | uint32_t ref_w = 0, ref_h = 0; |
97 | 0 | heif_region_item_get_reference_size(region_item, &ref_w, &ref_h); |
98 | |
|
99 | 0 | int nregions = heif_region_item_get_number_of_regions(region_item); |
100 | 0 | if (nregions > 0 && nregions < 64) { |
101 | 0 | struct heif_region** regions = static_cast<struct heif_region**>(malloc(nregions * sizeof(void*))); |
102 | 0 | if (regions) { |
103 | 0 | int got = heif_region_item_get_list_of_regions(region_item, regions, nregions); |
104 | 0 | for (int i = 0; i < got && i < 16; i++) { |
105 | 0 | enum heif_region_type rtype = heif_region_get_type(regions[i]); |
106 | 0 | (void)rtype; |
107 | |
|
108 | 0 | int npts = heif_region_get_polygon_num_points(regions[i]); |
109 | 0 | if (npts > 0 && npts < 1024) { |
110 | 0 | int32_t* pts = static_cast<int32_t*>(malloc(2 * npts * sizeof(int32_t))); |
111 | 0 | if (pts) { |
112 | 0 | heif_region_get_polygon_points(regions[i], pts); |
113 | 0 | free(pts); |
114 | 0 | } |
115 | 0 | } |
116 | |
|
117 | 0 | size_t mask_len = heif_region_get_inline_mask_data_len(regions[i]); |
118 | 0 | if (mask_len > 0 && mask_len < 1024 * 1024) { |
119 | 0 | uint8_t* mask_data = static_cast<uint8_t*>(malloc(mask_len)); |
120 | 0 | if (mask_data) { |
121 | 0 | int32_t mx, my; |
122 | 0 | uint32_t mw, mh; |
123 | 0 | heif_region_get_inline_mask_data(regions[i], &mx, &my, &mw, &mh, mask_data); |
124 | 0 | free(mask_data); |
125 | 0 | } |
126 | 0 | } |
127 | 0 | } |
128 | 0 | heif_region_release_many(regions, got); |
129 | 0 | free(regions); |
130 | 0 | } |
131 | 0 | } |
132 | 0 | heif_region_item_release(region_item); |
133 | 0 | } |
134 | 0 | free(region_ids); |
135 | 0 | } |
136 | | |
137 | | static void TestDepthAux(const struct heif_image_handle* handle) |
138 | 13.1k | { |
139 | 13.1k | if (heif_image_handle_has_depth_image(handle)) { |
140 | 0 | int ndepth = heif_image_handle_get_number_of_depth_images(handle); |
141 | 0 | if (ndepth > 0 && ndepth < 8) { |
142 | 0 | heif_item_id depth_ids[8]; |
143 | 0 | heif_image_handle_get_list_of_depth_image_IDs(handle, depth_ids, ndepth); |
144 | 0 | for (int i = 0; i < ndepth && i < 2; i++) { |
145 | 0 | struct heif_image_handle* depth_handle = nullptr; |
146 | 0 | struct heif_error err = heif_image_handle_get_depth_image_handle(handle, depth_ids[i], &depth_handle); |
147 | 0 | if (err.code == heif_error_Ok && depth_handle) { |
148 | 0 | struct heif_image* img = nullptr; |
149 | 0 | heif_decode_image(depth_handle, &img, |
150 | 0 | heif_colorspace_monochrome, heif_chroma_monochrome, nullptr); |
151 | 0 | if (img) heif_image_release(img); |
152 | 0 | heif_image_handle_release(depth_handle); |
153 | 0 | } |
154 | 0 | } |
155 | |
|
156 | 0 | const struct heif_depth_representation_info* depth_info = nullptr; |
157 | 0 | heif_image_handle_get_depth_image_representation_info(handle, depth_ids[0], &depth_info); |
158 | 0 | if (depth_info) { |
159 | 0 | heif_depth_representation_info_free(depth_info); |
160 | 0 | } |
161 | 0 | } |
162 | 0 | } |
163 | | |
164 | 13.1k | int naux = heif_image_handle_get_number_of_auxiliary_images(handle, 0); |
165 | 13.1k | if (naux > 0 && naux < 16) { |
166 | 6.09k | heif_item_id aux_ids[16]; |
167 | 6.09k | heif_image_handle_get_list_of_auxiliary_image_IDs(handle, 0, aux_ids, naux); |
168 | 12.1k | for (int i = 0; i < naux && i < 4; i++) { |
169 | 6.09k | struct heif_image_handle* aux_handle = nullptr; |
170 | 6.09k | struct heif_error err = heif_image_handle_get_auxiliary_image_handle(handle, aux_ids[i], &aux_handle); |
171 | 6.09k | if (err.code == heif_error_Ok && aux_handle) { |
172 | 6.09k | const char* aux_type = nullptr; |
173 | 6.09k | heif_image_handle_get_auxiliary_type(aux_handle, &aux_type); |
174 | 6.09k | if (aux_type) heif_image_handle_release_auxiliary_type(aux_handle, &aux_type); |
175 | | |
176 | 6.09k | struct heif_image* img = nullptr; |
177 | 6.09k | heif_decode_image(aux_handle, &img, |
178 | 6.09k | heif_colorspace_YCbCr, heif_chroma_420, nullptr); |
179 | 6.09k | if (img) heif_image_release(img); |
180 | 6.09k | heif_image_handle_release(aux_handle); |
181 | 6.09k | } |
182 | 6.09k | } |
183 | 6.09k | } |
184 | 13.1k | } |
185 | | |
186 | | static void TestGridTiles(const struct heif_image_handle* handle) |
187 | 13.1k | { |
188 | 13.1k | struct heif_image_tiling tiling = {}; |
189 | 13.1k | struct heif_error err = heif_image_handle_get_image_tiling(handle, 1, &tiling); |
190 | 13.1k | if (err.code != heif_error_Ok) { |
191 | 0 | return; |
192 | 0 | } |
193 | 13.1k | if (tiling.num_columns == 0 || tiling.num_rows == 0) { |
194 | 0 | return; |
195 | 0 | } |
196 | 13.1k | if (tiling.num_columns > 8 || tiling.num_rows > 8) { |
197 | 0 | return; |
198 | 0 | } |
199 | | |
200 | 13.1k | int decoded = 0; |
201 | 26.3k | for (uint32_t y = 0; y < tiling.num_rows && decoded < 4; y++) { |
202 | 26.3k | for (uint32_t x = 0; x < tiling.num_columns && decoded < 4; x++) { |
203 | 13.1k | struct heif_image* tile_img = nullptr; |
204 | 13.1k | err = heif_image_handle_decode_image_tile(handle, &tile_img, |
205 | 13.1k | heif_colorspace_YCbCr, heif_chroma_420, nullptr, x, y); |
206 | 13.1k | if (err.code == heif_error_Ok && tile_img) { |
207 | 476 | int stride; |
208 | 476 | heif_image_get_plane_readonly(tile_img, heif_channel_Y, &stride); |
209 | 476 | heif_image_release(tile_img); |
210 | 476 | } |
211 | 13.1k | decoded++; |
212 | 13.1k | } |
213 | 13.1k | } |
214 | 13.1k | } |
215 | | |
216 | | static void TestProperties(struct heif_context* ctx, const struct heif_image_handle* handle) |
217 | 13.1k | { |
218 | 13.1k | heif_item_id item_id = heif_image_handle_get_item_id(handle); |
219 | | |
220 | 13.1k | heif_property_id trans_props[16]; |
221 | 13.1k | int ntrans = heif_item_get_transformation_properties(ctx, item_id, trans_props, 16); |
222 | 13.1k | (void)ntrans; |
223 | | |
224 | 13.1k | size_t icc_size = heif_image_handle_get_raw_color_profile_size(handle); |
225 | 13.1k | if (icc_size > 0 && icc_size < 2 * 1024 * 1024) { |
226 | 185 | uint8_t* icc_data = static_cast<uint8_t*>(malloc(icc_size)); |
227 | 185 | if (icc_data) { |
228 | 185 | heif_image_handle_get_raw_color_profile(handle, icc_data); |
229 | 185 | free(icc_data); |
230 | 185 | } |
231 | 185 | } |
232 | | |
233 | 13.1k | struct heif_color_profile_nclx* nclx = nullptr; |
234 | 13.1k | heif_image_handle_get_nclx_color_profile(handle, &nclx); |
235 | 13.1k | if (nclx) heif_nclx_color_profile_free(nclx); |
236 | 13.1k | } |
237 | | |
238 | | static void TestEntityGroups(struct heif_context* ctx) |
239 | 13.2k | { |
240 | 13.2k | int num_groups = 0; |
241 | 13.2k | struct heif_entity_group* groups = heif_context_get_entity_groups(ctx, 0, 0, &num_groups); |
242 | 13.2k | if (groups) { |
243 | 0 | heif_entity_groups_release(groups, num_groups); |
244 | 0 | } |
245 | 13.2k | } |
246 | | |
247 | | static void TestItems(struct heif_context* ctx) |
248 | 13.2k | { |
249 | 13.2k | int nItems = heif_context_get_number_of_items(ctx); |
250 | 13.2k | if (nItems <= 0 || nItems > 128) { |
251 | 6 | return; |
252 | 6 | } |
253 | | |
254 | 13.2k | heif_item_id* item_ids = static_cast<heif_item_id*>(malloc(nItems * sizeof(heif_item_id))); |
255 | 13.2k | if (!item_ids) return; |
256 | 13.2k | heif_context_get_list_of_item_IDs(ctx, item_ids, nItems); |
257 | | |
258 | 32.7k | for (int i = 0; i < nItems && i < 16; i++) { |
259 | 19.5k | uint32_t item_type = heif_item_get_item_type(ctx, item_ids[i]); |
260 | 19.5k | (void)item_type; |
261 | 19.5k | int hidden = heif_item_is_item_hidden(ctx, item_ids[i]); |
262 | 19.5k | (void)hidden; |
263 | | |
264 | 19.5k | const char* mime_type = heif_item_get_mime_item_content_type(ctx, item_ids[i]); |
265 | 19.5k | (void)mime_type; |
266 | 19.5k | const char* uri_type = heif_item_get_uri_item_uri_type(ctx, item_ids[i]); |
267 | 19.5k | (void)uri_type; |
268 | 19.5k | const char* item_name = heif_item_get_item_name(ctx, item_ids[i]); |
269 | 19.5k | (void)item_name; |
270 | | |
271 | 19.5k | enum heif_metadata_compression compression; |
272 | 19.5k | uint8_t* item_data = nullptr; |
273 | 19.5k | size_t item_data_size = 0; |
274 | 19.5k | struct heif_error err = heif_item_get_item_data(ctx, item_ids[i], &compression, |
275 | 19.5k | &item_data, &item_data_size); |
276 | 19.5k | if (err.code == heif_error_Ok && item_data) { |
277 | 19.5k | heif_release_item_data(ctx, &item_data); |
278 | 19.5k | } |
279 | | |
280 | 19.5k | char* ext_lang = nullptr; |
281 | 19.5k | heif_item_get_property_extended_language(ctx, item_ids[i], &ext_lang); |
282 | 19.5k | if (ext_lang) heif_string_release(ext_lang); |
283 | 19.5k | } |
284 | 13.2k | free(item_ids); |
285 | 13.2k | } |
286 | | |
287 | | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) |
288 | 20.7k | { |
289 | 20.7k | if (size < 12 || size > 512 * 1024) { |
290 | 10 | return 0; |
291 | 10 | } |
292 | | |
293 | 20.7k | struct heif_context* ctx = heif_context_alloc(); |
294 | 20.7k | if (!ctx) return 0; |
295 | | |
296 | 20.7k | auto* limits = heif_context_get_security_limits(ctx); |
297 | 20.7k | limits->max_memory_block_size = 32 * 1024 * 1024; |
298 | 20.7k | limits->max_total_memory = 128 * 1024 * 1024; |
299 | 20.7k | limits->max_image_size_pixels = 512 * 512; |
300 | | |
301 | 20.7k | struct heif_error err = heif_context_read_from_memory(ctx, data, size, nullptr); |
302 | 20.7k | if (err.code != heif_error_Ok) { |
303 | 7.49k | goto quit; |
304 | 7.49k | } |
305 | | |
306 | 13.2k | TestSequences(ctx); |
307 | 13.2k | TestEntityGroups(ctx); |
308 | 13.2k | TestItems(ctx); |
309 | | |
310 | 13.2k | { |
311 | 13.2k | struct heif_image_handle* primary = nullptr; |
312 | 13.2k | err = heif_context_get_primary_image_handle(ctx, &primary); |
313 | 13.2k | if (err.code == heif_error_Ok && primary) { |
314 | 13.1k | TestRegions(ctx, primary); |
315 | 13.1k | TestDepthAux(primary); |
316 | 13.1k | TestGridTiles(primary); |
317 | 13.1k | TestProperties(ctx, primary); |
318 | 13.1k | heif_image_handle_release(primary); |
319 | 13.1k | } |
320 | 13.2k | } |
321 | | |
322 | 20.7k | quit: |
323 | 20.7k | heif_context_free(ctx); |
324 | 20.7k | return 0; |
325 | 13.2k | } |