Coverage Report

Created: 2026-05-30 06:06

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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], &region_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
}