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