/src/libheif/fuzzing/encoder_fuzzer.cc
Line | Count | Source |
1 | | /* |
2 | | * HEIF codec. |
3 | | * Copyright (c) 2018 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 <string.h> |
23 | | #include <stdlib.h> |
24 | | |
25 | | #include <memory> |
26 | | |
27 | | #include "libheif/heif.h" |
28 | | |
29 | | static void generate_plane(int width, int height, uint8_t* output, int stride) |
30 | 11.5k | { |
31 | | // TODO(fancycode): Fill with random data. |
32 | 11.5k | if (width == stride) { |
33 | 531 | memset(output, 0, width * height); |
34 | 531 | } |
35 | 11.0k | else { |
36 | 1.18M | for (int y = 0; y < height; y++) { |
37 | 1.17M | memset(output, 0, width); |
38 | 1.17M | output += stride; |
39 | 1.17M | } |
40 | 11.0k | } |
41 | 11.5k | } |
42 | | |
43 | | static size_t create_image(const uint8_t* data, size_t size, struct heif_image** image) |
44 | 3.92k | { |
45 | 3.92k | if (size < 2) { |
46 | 72 | return 0; |
47 | 72 | } |
48 | | |
49 | 3.85k | int width = data[0] + 16; |
50 | 3.85k | int height = data[1] + 16; |
51 | 3.85k | data += 2; |
52 | 3.85k | size -= 2; |
53 | | // TODO(fancycode): Get colorspace/chroma from fuzzing input. |
54 | 3.85k | heif_colorspace colorspace = heif_colorspace_YCbCr; |
55 | 3.85k | heif_chroma chroma = heif_chroma_420; |
56 | | |
57 | 3.85k | struct heif_error err = heif_image_create(width, height, colorspace, chroma, image); |
58 | 3.85k | if (err.code != heif_error_Ok) { |
59 | 0 | return 0; |
60 | 0 | } |
61 | | |
62 | 3.85k | int chroma_width = (width+1)/2; |
63 | 3.85k | int chroma_height = (height+1)/2; |
64 | | |
65 | 3.85k | err = heif_image_add_plane(*image, heif_channel_Y, width, height, 8); |
66 | 3.85k | assert(err.code == heif_error_Ok); |
67 | 3.85k | err = heif_image_add_plane(*image, heif_channel_Cb, chroma_width, chroma_height, 8); |
68 | 3.85k | assert(err.code == heif_error_Ok); |
69 | 3.85k | err = heif_image_add_plane(*image, heif_channel_Cr, chroma_width, chroma_height, 8); |
70 | 3.85k | assert(err.code == heif_error_Ok); |
71 | | |
72 | 3.85k | int stride; |
73 | 3.85k | uint8_t* plane; |
74 | | |
75 | 3.85k | plane = heif_image_get_plane(*image, heif_channel_Y, &stride); |
76 | 3.85k | generate_plane(width, height, plane, stride); |
77 | | |
78 | 3.85k | plane = heif_image_get_plane(*image, heif_channel_Cb, &stride); |
79 | 3.85k | generate_plane(chroma_width, chroma_height, plane, stride); |
80 | | |
81 | 3.85k | plane = heif_image_get_plane(*image, heif_channel_Cr, &stride); |
82 | 3.85k | generate_plane(chroma_width, chroma_height, plane, stride); |
83 | | |
84 | 3.85k | return 2; |
85 | 3.85k | } |
86 | | |
87 | | class MemoryWriter |
88 | | { |
89 | | public: |
90 | 3.85k | MemoryWriter() : data_(nullptr), size_(0), capacity_(0) |
91 | 3.85k | {} |
92 | | |
93 | | ~MemoryWriter() |
94 | 3.85k | { |
95 | 3.85k | free(data_); |
96 | 3.85k | } |
97 | | |
98 | | const uint8_t* data() const |
99 | 0 | { return data_; } |
100 | | |
101 | | size_t size() const |
102 | 3.85k | { return size_; } |
103 | | |
104 | | void write(const void* data, size_t size) |
105 | 3.85k | { |
106 | 3.85k | if (capacity_ - size_ < size) { |
107 | 3.85k | size_t new_capacity = capacity_ + size; |
108 | 3.85k | uint8_t* new_data = static_cast<uint8_t*>(malloc(new_capacity)); |
109 | 3.85k | assert(new_data); |
110 | 3.85k | if (data_) { |
111 | 0 | memcpy(new_data, data_, size_); |
112 | 0 | free(data_); |
113 | 0 | } |
114 | 3.85k | data_ = new_data; |
115 | 3.85k | capacity_ = new_capacity; |
116 | 3.85k | } |
117 | 3.85k | memcpy(&data_[size_], data, size); |
118 | 3.85k | size_ += size; |
119 | 3.85k | } |
120 | | |
121 | | public: |
122 | | uint8_t* data_; |
123 | | size_t size_; |
124 | | size_t capacity_; |
125 | | }; |
126 | | |
127 | | static struct heif_error writer_write(struct heif_context* ctx, const void* data, size_t size, void* userdata) |
128 | 3.85k | { |
129 | 3.85k | MemoryWriter* writer = static_cast<MemoryWriter*>(userdata); |
130 | 3.85k | writer->write(data, size); |
131 | 3.85k | struct heif_error err{heif_error_Ok, heif_suberror_Unspecified, nullptr}; |
132 | 3.85k | return err; |
133 | 3.85k | } |
134 | | |
135 | | extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) |
136 | 3.95k | { |
137 | 3.95k | struct heif_error err; |
138 | 3.95k | std::shared_ptr<heif_context> context(heif_context_alloc(), |
139 | 3.95k | [](heif_context* c) { heif_context_free(c); }); |
140 | 3.95k | assert(context); |
141 | | |
142 | 3.95k | if (size < 2) { |
143 | 2 | return 0; |
144 | 2 | } |
145 | | |
146 | 3.95k | int quality = (data[0] & 0x7F) % 101; |
147 | 3.95k | bool lossless = (data[0] & 0x80); |
148 | 3.95k | uint8_t format_uint8 = ((data[1] & 0xf0) >> 4); |
149 | 3.95k | uint8_t encoder_idx = data[1] & 0x0f; |
150 | | |
151 | 3.95k | if (format_uint8==0 || format_uint8 > 10) { |
152 | 15 | return 0; |
153 | 15 | } |
154 | 3.93k | heif_compression_format format = (heif_compression_format)format_uint8; |
155 | | |
156 | 3.93k | data += 2; |
157 | 3.93k | size -= 2; |
158 | | |
159 | 3.93k | static const size_t kMaxEncoders = 5; |
160 | 3.93k | const heif_encoder_descriptor* encoder_descriptors[kMaxEncoders]; |
161 | 3.93k | int count = heif_get_encoder_descriptors(format, |
162 | 3.93k | nullptr, |
163 | 3.93k | encoder_descriptors, kMaxEncoders); |
164 | 3.93k | assert(count >= 0); |
165 | 3.93k | if (count == 0) { |
166 | 1 | return 0; |
167 | 1 | } |
168 | | |
169 | 3.93k | if (encoder_idx >= count) { |
170 | 8 | return 0; |
171 | 8 | } |
172 | | |
173 | 3.92k | heif_encoder* encoder; |
174 | 3.92k | err = heif_context_get_encoder(context.get(), encoder_descriptors[encoder_idx], &encoder); |
175 | 3.92k | if (err.code != heif_error_Ok) { |
176 | 0 | return 0; |
177 | 0 | } |
178 | | |
179 | 3.92k | heif_encoder_set_lossy_quality(encoder, quality); |
180 | 3.92k | heif_encoder_set_lossless(encoder, lossless); |
181 | | |
182 | 3.92k | struct heif_image* image = nullptr; |
183 | 3.92k | size_t read = create_image(data, size, &image); |
184 | 3.92k | assert(read <= size); |
185 | 3.92k | if (!read) { |
186 | 72 | heif_image_release(image); |
187 | 72 | heif_encoder_release(encoder); |
188 | 72 | return 0; |
189 | 72 | } |
190 | | |
191 | 3.85k | data += read; |
192 | 3.85k | size -= read; |
193 | | |
194 | 3.85k | struct heif_image_handle* img; |
195 | 3.85k | err = heif_context_encode_image(context.get(), image, encoder, nullptr, &img); |
196 | 3.85k | heif_image_release(image); |
197 | 3.85k | heif_encoder_release(encoder); |
198 | 3.85k | heif_image_handle_release(img); |
199 | 3.85k | if (err.code != heif_error_Ok) { |
200 | 4 | return 0; |
201 | 4 | } |
202 | | |
203 | 3.85k | MemoryWriter writer; |
204 | 3.85k | struct heif_writer w; |
205 | 3.85k | w.writer_api_version = 1; |
206 | 3.85k | w.write = writer_write; |
207 | 3.85k | heif_context_write(context.get(), &w, &writer); |
208 | 3.85k | assert(writer.size() > 0); |
209 | 3.85k | return 0; |
210 | 3.85k | } |