/src/libheif/fuzzing/file_fuzzer.cc
Line | Count | Source |
1 | | /* |
2 | | * HEIF codec. |
3 | | * Copyright (c) 2017 struktur AG, Joachim Bauch <bauch@struktur.de> |
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 | | #include <assert.h> |
22 | | #include <limits.h> |
23 | | #include <stdlib.h> |
24 | | |
25 | | #include "libheif/heif.h" |
26 | | |
27 | | static const enum heif_colorspace kFuzzColorSpace = heif_colorspace_YCbCr; |
28 | | static const enum heif_chroma kFuzzChroma = heif_chroma_420; |
29 | | |
30 | | static void TestDecodeImage(struct heif_context* ctx, |
31 | | const struct heif_image_handle* handle, size_t filesize) |
32 | 3.91k | { |
33 | 3.91k | struct heif_image* image = nullptr; |
34 | 3.91k | struct heif_error err; |
35 | | |
36 | 3.91k | bool primary = heif_image_handle_is_primary_image(handle); |
37 | 3.91k | (void) primary; |
38 | 3.91k | int width = heif_image_handle_get_width(handle); |
39 | 3.91k | int height = heif_image_handle_get_height(handle); |
40 | 3.91k | (void)width; (void)height; |
41 | 3.91k | assert(width >= 0); |
42 | 3.91k | assert(height >= 0); |
43 | 3.91k | int metadata_count = heif_image_handle_get_number_of_metadata_blocks(handle, nullptr); |
44 | 3.91k | assert(metadata_count >= 0); |
45 | 3.91k | assert(static_cast<size_t>(metadata_count) < filesize / sizeof(heif_item_id)); |
46 | 3.91k | heif_item_id* metadata_ids = static_cast<heif_item_id*>(malloc(metadata_count * sizeof(heif_item_id))); |
47 | 3.91k | assert(metadata_ids); |
48 | 3.91k | int metadata_ids_count = heif_image_handle_get_list_of_metadata_block_IDs(handle, nullptr, metadata_ids, |
49 | 3.91k | metadata_count); |
50 | 3.91k | assert(metadata_count == metadata_ids_count); |
51 | 3.91k | (void)metadata_ids_count; |
52 | 3.92k | for (int i = 0; i < metadata_count; i++) { |
53 | 13 | heif_image_handle_get_metadata_type(handle, metadata_ids[i]); |
54 | 13 | heif_image_handle_get_metadata_content_type(handle, metadata_ids[i]); |
55 | 13 | size_t metadata_size = heif_image_handle_get_metadata_size(handle, metadata_ids[i]); |
56 | | |
57 | | // This assertion is invalid. Metadata can in fact be larger than the file if there are several |
58 | | // overlapping iloc extents. Does not make much sense, but it is technically valid. |
59 | | //assert(metadata_size < filesize); |
60 | | |
61 | 13 | uint8_t* metadata_data = static_cast<uint8_t*>(malloc(metadata_size)); |
62 | 13 | assert(metadata_data); |
63 | 13 | heif_image_handle_get_metadata(handle, metadata_ids[i], metadata_data); |
64 | 13 | free(metadata_data); |
65 | 13 | } |
66 | 3.91k | free(metadata_ids); |
67 | | |
68 | 3.91k | err = heif_decode_image(handle, &image, kFuzzColorSpace, kFuzzChroma, nullptr); |
69 | 3.91k | if (err.code != heif_error_Ok) { |
70 | 3.59k | heif_image_release(image); |
71 | 3.59k | return; |
72 | 3.59k | } |
73 | | |
74 | 3.91k | assert(heif_image_get_colorspace(image) == kFuzzColorSpace); |
75 | 319 | assert(heif_image_get_chroma_format(image) == kFuzzChroma); |
76 | | |
77 | | // TODO(fancycode): Should we also check the planes? |
78 | | |
79 | 319 | heif_image_release(image); |
80 | 319 | } |
81 | | |
82 | | static int clip_int(size_t size) |
83 | 37.1k | { |
84 | 37.1k | return size > INT_MAX ? INT_MAX : static_cast<int>(size); |
85 | 37.1k | } |
86 | | |
87 | | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) |
88 | 12.3k | { |
89 | 12.3k | struct heif_context* ctx; |
90 | 12.3k | struct heif_error err; |
91 | 12.3k | struct heif_image_handle* primary_handle = nullptr; |
92 | 12.3k | int images_count; |
93 | 12.3k | heif_item_id* image_IDs = NULL; |
94 | 12.3k | bool explicit_init = size == 0 || data[size - 1] & 1; |
95 | | |
96 | 12.3k | if (explicit_init) { |
97 | 4.65k | heif_init(nullptr); |
98 | 4.65k | } |
99 | | |
100 | 12.3k | heif_check_filetype(data, clip_int(size)); |
101 | 12.3k | heif_main_brand(data, clip_int(size)); |
102 | 12.3k | heif_get_file_mime_type(data, clip_int(size)); |
103 | | |
104 | 12.3k | ctx = heif_context_alloc(); |
105 | 12.3k | heif_context_get_security_limits(ctx)->max_total_memory = UINT64_C(2) * 1024 * 1024 * 1024; |
106 | 12.3k | assert(ctx); |
107 | | |
108 | 12.3k | auto* limits = heif_context_get_security_limits(ctx); |
109 | 12.3k | limits->max_memory_block_size = 128 * 1024 * 1024; // 128 MB |
110 | | |
111 | 12.3k | err = heif_context_read_from_memory(ctx, data, size, nullptr); |
112 | 12.3k | if (err.code != heif_error_Ok) { |
113 | | // Not a valid HEIF file passed (which is most likely while fuzzing). |
114 | 9.66k | goto quit; |
115 | 9.66k | } |
116 | | |
117 | 2.71k | err = heif_context_get_primary_image_handle(ctx, &primary_handle); |
118 | 2.71k | if (err.code == heif_error_Ok) { |
119 | 1.25k | assert(heif_image_handle_is_primary_image(primary_handle)); |
120 | 1.25k | TestDecodeImage(ctx, primary_handle, size); |
121 | 1.25k | heif_image_handle_release(primary_handle); |
122 | 1.25k | primary_handle = nullptr; |
123 | 1.25k | } |
124 | | |
125 | 2.71k | images_count = heif_context_get_number_of_top_level_images(ctx); |
126 | 2.71k | if (!images_count) { |
127 | | // File doesn't contain any images. |
128 | 243 | goto quit; |
129 | 243 | } |
130 | | |
131 | 2.46k | image_IDs = (heif_item_id*) malloc(images_count * sizeof(heif_item_id)); |
132 | 2.46k | assert(image_IDs); |
133 | 2.46k | images_count = heif_context_get_list_of_top_level_image_IDs(ctx, image_IDs, images_count); |
134 | 2.46k | if (!images_count) { |
135 | | // Could not get list of image ids. |
136 | 0 | goto quit; |
137 | 0 | } |
138 | | |
139 | 8.00k | for (int i = 0; i < images_count; ++i) { |
140 | 5.53k | struct heif_image_handle* image_handle = nullptr; |
141 | 5.53k | err = heif_context_get_image_handle(ctx, image_IDs[i], &image_handle); |
142 | 5.53k | if (err.code != heif_error_Ok) { |
143 | 2.88k | heif_image_handle_release(image_handle); |
144 | | // Ignore, we are only interested in crashes here. |
145 | 2.88k | continue; |
146 | 2.88k | } |
147 | | |
148 | 2.65k | TestDecodeImage(ctx, image_handle, size); |
149 | | |
150 | 2.65k | int num_thumbnails = heif_image_handle_get_number_of_thumbnails(image_handle); |
151 | 2.69k | for (int t = 0; t < num_thumbnails; ++t) { |
152 | 39 | struct heif_image_handle* thumbnail_handle = nullptr; |
153 | 39 | heif_image_handle_get_thumbnail(image_handle, t, &thumbnail_handle); |
154 | 39 | if (thumbnail_handle) { |
155 | 3 | TestDecodeImage(ctx, thumbnail_handle, size); |
156 | 3 | heif_image_handle_release(thumbnail_handle); |
157 | 3 | } |
158 | 39 | } |
159 | | |
160 | 2.65k | heif_image_handle_release(image_handle); |
161 | 2.65k | } |
162 | | |
163 | 12.3k | quit: |
164 | 12.3k | heif_image_handle_release(primary_handle); |
165 | 12.3k | heif_context_free(ctx); |
166 | 12.3k | free(image_IDs); |
167 | | |
168 | 12.3k | if (explicit_init) { |
169 | 4.65k | heif_deinit(); |
170 | 4.65k | } |
171 | | |
172 | 12.3k | return 0; |
173 | 2.46k | } |