/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 | 35.0k | { |
33 | 35.0k | struct heif_image* image = nullptr; |
34 | 35.0k | struct heif_error err; |
35 | | |
36 | 35.0k | bool primary = heif_image_handle_is_primary_image(handle); |
37 | 35.0k | (void) primary; |
38 | 35.0k | int width = heif_image_handle_get_width(handle); |
39 | 35.0k | int height = heif_image_handle_get_height(handle); |
40 | 35.0k | (void)width; (void)height; |
41 | 35.0k | assert(width >= 0); |
42 | 35.0k | assert(height >= 0); |
43 | 35.0k | int metadata_count = heif_image_handle_get_number_of_metadata_blocks(handle, nullptr); |
44 | 35.0k | assert(metadata_count >= 0); |
45 | 35.0k | assert(static_cast<size_t>(metadata_count) < filesize / sizeof(heif_item_id)); |
46 | 35.0k | heif_item_id* metadata_ids = static_cast<heif_item_id*>(malloc(metadata_count * sizeof(heif_item_id))); |
47 | 35.0k | assert(metadata_ids); |
48 | 35.0k | int metadata_ids_count = heif_image_handle_get_list_of_metadata_block_IDs(handle, nullptr, metadata_ids, |
49 | 35.0k | metadata_count); |
50 | 35.0k | assert(metadata_count == metadata_ids_count); |
51 | 35.0k | (void)metadata_ids_count; |
52 | 41.5k | for (int i = 0; i < metadata_count; i++) { |
53 | 6.41k | heif_image_handle_get_metadata_type(handle, metadata_ids[i]); |
54 | 6.41k | heif_image_handle_get_metadata_content_type(handle, metadata_ids[i]); |
55 | 6.41k | 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 | 6.41k | uint8_t* metadata_data = static_cast<uint8_t*>(malloc(metadata_size)); |
62 | 6.41k | assert(metadata_data); |
63 | 6.41k | heif_image_handle_get_metadata(handle, metadata_ids[i], metadata_data); |
64 | 6.41k | free(metadata_data); |
65 | 6.41k | } |
66 | 35.0k | free(metadata_ids); |
67 | | |
68 | 35.0k | err = heif_decode_image(handle, &image, kFuzzColorSpace, kFuzzChroma, nullptr); |
69 | 35.0k | if (err.code != heif_error_Ok) { |
70 | 27.3k | heif_image_release(image); |
71 | 27.3k | return; |
72 | 27.3k | } |
73 | | |
74 | 35.0k | assert(heif_image_get_colorspace(image) == kFuzzColorSpace); |
75 | 7.78k | assert(heif_image_get_chroma_format(image) == kFuzzChroma); |
76 | | |
77 | | // TODO(fancycode): Should we also check the planes? |
78 | | |
79 | 7.78k | heif_image_release(image); |
80 | 7.78k | } |
81 | | |
82 | | static int clip_int(size_t size) |
83 | 71.7k | { |
84 | 71.7k | return size > INT_MAX ? INT_MAX : static_cast<int>(size); |
85 | 71.7k | } |
86 | | |
87 | | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) |
88 | 23.9k | { |
89 | 23.9k | struct heif_context* ctx; |
90 | 23.9k | struct heif_error err; |
91 | 23.9k | struct heif_image_handle* primary_handle = nullptr; |
92 | 23.9k | int images_count; |
93 | 23.9k | heif_item_id* image_IDs = NULL; |
94 | 23.9k | bool explicit_init = size == 0 || data[size - 1] & 1; |
95 | | |
96 | 23.9k | if (explicit_init) { |
97 | 8.14k | heif_init(nullptr); |
98 | 8.14k | } |
99 | | |
100 | 23.9k | heif_check_filetype(data, clip_int(size)); |
101 | 23.9k | heif_main_brand(data, clip_int(size)); |
102 | 23.9k | heif_get_file_mime_type(data, clip_int(size)); |
103 | | |
104 | 23.9k | ctx = heif_context_alloc(); |
105 | 23.9k | assert(ctx); |
106 | | |
107 | 23.9k | auto* limits = heif_context_get_security_limits(ctx); |
108 | 23.9k | limits->max_total_memory = UINT64_C(2) * 1024 * 1024 * 1024; |
109 | 23.9k | limits->max_memory_block_size = 128 * 1024 * 1024; // 128 MB |
110 | | |
111 | | // Also bound the image size itself. libheif's memory accounting only covers its own |
112 | | // allocations, but the codec libraries allocate their frame buffers before libheif |
113 | | // sees anything (dav1d needs ~12.5 bytes/pixel for a frame header alone). Under MSan, |
114 | | // where every allocation costs about three times its size in RSS, a 56 Mpixel AV1 frame |
115 | | // header was enough to exceed libFuzzer's 2560 MB limit. |
116 | 23.9k | limits->max_image_size_pixels = 16 * 1024 * 1024; // 16 Mpixel |
117 | | |
118 | 23.9k | err = heif_context_read_from_memory(ctx, data, size, nullptr); |
119 | 23.9k | if (err.code != heif_error_Ok) { |
120 | | // Not a valid HEIF file passed (which is most likely while fuzzing). |
121 | 6.39k | goto quit; |
122 | 6.39k | } |
123 | | |
124 | 17.5k | err = heif_context_get_primary_image_handle(ctx, &primary_handle); |
125 | 17.5k | if (err.code == heif_error_Ok) { |
126 | 13.0k | assert(heif_image_handle_is_primary_image(primary_handle)); |
127 | 13.0k | TestDecodeImage(ctx, primary_handle, size); |
128 | 13.0k | heif_image_handle_release(primary_handle); |
129 | 13.0k | primary_handle = nullptr; |
130 | 13.0k | } |
131 | | |
132 | 17.5k | images_count = heif_context_get_number_of_top_level_images(ctx); |
133 | 17.5k | if (!images_count) { |
134 | | // File doesn't contain any images. |
135 | 63 | goto quit; |
136 | 63 | } |
137 | | |
138 | 17.4k | image_IDs = (heif_item_id*) malloc(images_count * sizeof(heif_item_id)); |
139 | 17.4k | assert(image_IDs); |
140 | 17.4k | images_count = heif_context_get_list_of_top_level_image_IDs(ctx, image_IDs, images_count); |
141 | 17.4k | if (!images_count) { |
142 | | // Could not get list of image ids. |
143 | 0 | goto quit; |
144 | 0 | } |
145 | | |
146 | 47.2k | for (int i = 0; i < images_count; ++i) { |
147 | 29.7k | struct heif_image_handle* image_handle = nullptr; |
148 | 29.7k | err = heif_context_get_image_handle(ctx, image_IDs[i], &image_handle); |
149 | 29.7k | if (err.code != heif_error_Ok) { |
150 | 7.69k | heif_image_handle_release(image_handle); |
151 | | // Ignore, we are only interested in crashes here. |
152 | 7.69k | continue; |
153 | 7.69k | } |
154 | | |
155 | 22.0k | TestDecodeImage(ctx, image_handle, size); |
156 | | |
157 | 22.0k | int num_thumbnails = heif_image_handle_get_number_of_thumbnails(image_handle); |
158 | 22.4k | for (int t = 0; t < num_thumbnails; ++t) { |
159 | 317 | struct heif_image_handle* thumbnail_handle = nullptr; |
160 | 317 | heif_image_handle_get_thumbnail(image_handle, t, &thumbnail_handle); |
161 | 317 | if (thumbnail_handle) { |
162 | 3 | TestDecodeImage(ctx, thumbnail_handle, size); |
163 | 3 | heif_image_handle_release(thumbnail_handle); |
164 | 3 | } |
165 | 317 | } |
166 | | |
167 | 22.0k | heif_image_handle_release(image_handle); |
168 | 22.0k | } |
169 | | |
170 | 23.9k | quit: |
171 | 23.9k | heif_image_handle_release(primary_handle); |
172 | 23.9k | heif_context_free(ctx); |
173 | 23.9k | free(image_IDs); |
174 | | |
175 | 23.9k | if (explicit_init) { |
176 | 8.14k | heif_deinit(); |
177 | 8.14k | } |
178 | | |
179 | 23.9k | return 0; |
180 | 17.4k | } |