1 /* fuzzer_metadata
2 * Copyright (C) 2022-2023 Xiph.Org Foundation
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * - Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * - Neither the name of the Xiph.org Foundation nor the names of its
16 * contributors may be used to endorse or promote products derived from
17 * this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
24 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
25 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
26 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
27 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
28 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include <cstdlib>
33 #include <cstdio>
34 #include <cstring> /* for memcpy */
35 #include <unistd.h>
36 #include "FLAC++/metadata.h"
37 #include "common.h"
38
39 #define CONFIG_LENGTH 2
40
41 #define min(x,y) (x<y?x:y)
42
43 static void run_tests_with_level_0_interface(char filename[]);
44 static void run_tests_with_level_1_interface(char filename[], bool readonly, bool preservestats, const uint8_t *data, size_t size);
45 static void run_tests_with_level_2_interface(char filename[], bool ogg, bool use_padding, const uint8_t *data, size_t size);
46
LLVMFuzzerTestOneInput(const uint8_t * data,size_t size)47 extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
48 {
49 uint8_t command_length;
50 char filename[] = "/tmp/tmpXXXXXX.flac";
51 FLAC__bool init_bools[4];
52
53 /* Use first byte for configuration, leave at least one byte of input */
54 if(size < 1 + CONFIG_LENGTH){
55 return 0;
56 }
57
58 /* First 4 bits for configuration bools, next 4 for length of command section */
59 for(int i = 0; i < 4; i++)
60 init_bools[i] = data[i/8] & (1 << (i % 8));
61
62 command_length = data[0] >> 4;
63
64 if(0)//data[1] < 128) /* Use MSB as on/off */
65 alloc_check_threshold = data[1];
66 else
67 alloc_check_threshold = INT32_MAX;
68 alloc_check_counter = 0;
69
70
71 /* Leave at least one byte as input */
72 if(command_length >= size - 1 - CONFIG_LENGTH)
73 command_length = size - 1 - CONFIG_LENGTH;
74
75 /* Dump input to file */
76 {
77 int file_to_fuzz = mkstemps(filename, 5);
78
79 if (file_to_fuzz < 0)
80 abort();
81 write(file_to_fuzz,data+CONFIG_LENGTH+command_length,size-CONFIG_LENGTH-command_length);
82 close(file_to_fuzz);
83 }
84
85 run_tests_with_level_0_interface(filename);
86 run_tests_with_level_1_interface(filename, init_bools[1], init_bools[2], data+CONFIG_LENGTH, command_length/2);
87
88 /* Dump input to file, to start fresh for level 2 */
89 if(!init_bools[1]){
90 FILE * file_to_fuzz = fopen(filename,"w");
91 fwrite(data+CONFIG_LENGTH+command_length,1,size-CONFIG_LENGTH-command_length,file_to_fuzz);
92 fclose(file_to_fuzz);
93 }
94
95 run_tests_with_level_2_interface(filename, init_bools[0], init_bools[3], data+command_length/2+CONFIG_LENGTH, command_length/2);
96
97 remove(filename);
98
99 return 0;
100 }
101
run_tests_with_level_0_interface(char filename[])102 static void run_tests_with_level_0_interface(char filename[]) {
103 FLAC::Metadata::StreamInfo streaminfo;
104 FLAC::Metadata::VorbisComment vorbis_comment;
105 FLAC::Metadata::CueSheet cue_sheet;
106 FLAC::Metadata::Picture picture;
107
108 FLAC::Metadata::get_streaminfo(filename,streaminfo);
109 FLAC::Metadata::get_tags(filename,vorbis_comment);
110 FLAC::Metadata::get_cuesheet(filename,cue_sheet);
111 FLAC::Metadata::get_picture(filename,picture, (FLAC__StreamMetadata_Picture_Type)(1), NULL, NULL, -1, -1, -1, -1);
112 }
113
run_tests_with_level_1_interface(char filename[],bool readonly,bool preservestats,const uint8_t * data,size_t size)114 static void run_tests_with_level_1_interface(char filename[], bool readonly, bool preservestats, const uint8_t *data, size_t size) {
115 FLAC::Metadata::SimpleIterator iterator;
116 FLAC::Metadata::Prototype *metadata_block = nullptr;
117 uint8_t id[4] = {0};
118
119 if(!iterator.is_valid())
120 return;
121
122 if(!iterator.init(filename,readonly,preservestats))
123 return;
124
125 for(size_t i = 0; i < size && iterator.status() == FLAC__METADATA_SIMPLE_ITERATOR_STATUS_OK; i++) {
126 switch(data[i] & 7) {
127 case 0:
128 iterator.get_block_type();
129 iterator.get_block_offset();
130 iterator.get_block_length();
131 iterator.get_application_id(id);
132 break;
133 case 1:
134 iterator.next();
135 break;
136 case 2:
137 iterator.prev();
138 break;
139 case 3:
140 iterator.delete_block(data[i] & 8);
141 break;
142 case 4:
143 if(metadata_block != 0) {
144 delete metadata_block;
145 metadata_block = nullptr;
146 }
147 metadata_block = iterator.get_block();
148 break;
149 case 5:
150 if(metadata_block != 0)
151 iterator.set_block(metadata_block,data[i] & 8);
152 break;
153 case 6:
154 if(metadata_block != 0)
155 iterator.insert_block_after(metadata_block, data[i] & 8);
156 break;
157 case 7:
158 iterator.status();
159 iterator.is_last();
160 iterator.is_writable();
161 break;
162 }
163 }
164 if(metadata_block != 0) {
165 delete metadata_block;
166 metadata_block = nullptr;
167 }
168
169
170 }
171
172
run_tests_with_level_2_interface(char filename[],bool ogg,bool use_padding,const uint8_t * data,size_t size)173 static void run_tests_with_level_2_interface(char filename[], bool ogg, bool use_padding, const uint8_t *data, size_t size) {
174 FLAC::Metadata::Chain chain;
175 FLAC::Metadata::Iterator iterator;
176 FLAC::Metadata::Prototype *metadata_block_get = nullptr;
177 FLAC::Metadata::Prototype *metadata_block_transfer = nullptr;
178 FLAC::Metadata::Prototype *metadata_block_put = nullptr;
179
180 if(!chain.is_valid())
181 return;
182
183 if(!chain.read(filename, ogg))
184 return;
185
186 iterator.init(chain);
187
188 for(size_t i = 0; i < size; i++) {
189 switch(data[i] & 15) {
190 case 0:
191 iterator.get_block_type();
192 break;
193 case 1:
194 iterator.next();
195 break;
196 case 2:
197 iterator.prev();
198 break;
199 case 3:
200 iterator.delete_block(data[i] & 16);
201 break;
202 case 4:
203 metadata_block_get = iterator.get_block();
204 if(metadata_block_get != 0 && metadata_block_get->is_valid()) {
205 if(metadata_block_transfer != 0 && metadata_block_transfer->is_valid()) {
206 if(metadata_block_transfer != metadata_block_get) {
207 delete metadata_block_transfer;
208 metadata_block_transfer = nullptr;
209 metadata_block_transfer = FLAC::Metadata::clone(metadata_block_get);
210 }
211 }
212 else {
213 metadata_block_transfer = FLAC::Metadata::clone(metadata_block_get);
214 }
215 }
216 delete metadata_block_get;
217 break;
218 case 5:
219 if(metadata_block_transfer != 0 && metadata_block_transfer->is_valid()) {
220 metadata_block_put = FLAC::Metadata::clone(metadata_block_transfer);
221 if(metadata_block_put != 0 && metadata_block_put->is_valid()) {
222 if(!iterator.insert_block_before(metadata_block_put))
223 delete metadata_block_put;
224 }
225 else
226 if(metadata_block_put != 0)
227 delete metadata_block_put;
228 }
229 break;
230 case 6:
231 if(metadata_block_transfer != 0 && metadata_block_transfer->is_valid()) {
232 metadata_block_put = FLAC::Metadata::clone(metadata_block_transfer);
233 if(metadata_block_put != 0 && metadata_block_put->is_valid()) {
234 if(!iterator.insert_block_after(metadata_block_put))
235 delete metadata_block_put;
236 }
237 else
238 if(metadata_block_put != 0)
239 delete metadata_block_put;
240 }
241 break;
242 case 7:
243 if(metadata_block_transfer != 0 && metadata_block_transfer->is_valid()) {
244 metadata_block_put = FLAC::Metadata::clone(metadata_block_transfer);
245 if(metadata_block_put != 0 && metadata_block_put->is_valid()) {
246 if(!iterator.set_block(metadata_block_put))
247 delete metadata_block_put;
248 }
249 else
250 if(metadata_block_put != 0)
251 delete metadata_block_put;
252 }
253 break;
254 case 8: /* Examine block */
255 if(metadata_block_transfer != 0 && metadata_block_transfer->is_valid()) {
256 switch(metadata_block_transfer->get_type()) {
257 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
258 {
259 uint32_t num_comments;
260 ::FLAC__StreamMetadata_VorbisComment_Entry entry;
261 FLAC::Metadata::VorbisComment::Entry entry_cpp;
262 FLAC::Metadata::VorbisComment * vorbiscomment = dynamic_cast<FLAC::Metadata::VorbisComment *>(metadata_block_transfer);
263 const ::FLAC__StreamMetadata * metadata_c = *metadata_block_transfer;
264 if(vorbiscomment == 0)
265 abort();
266 vorbiscomment->get_vendor_string();
267 num_comments = vorbiscomment->get_num_comments();
268 if(num_comments > 0) {
269 entry = metadata_c->data.vorbis_comment.comments[min(data[i]>>4,num_comments-1)];
270 if(entry.entry == 0)
271 abort();
272 if(vorbiscomment->get_comment(min(data[i]>>4,num_comments-1)).is_valid()) {
273 entry_cpp = vorbiscomment->get_comment(min(data[i]>>4,num_comments-1));
274 if(entry_cpp.is_valid() && entry_cpp.get_field() == 0)
275 abort();
276 vorbiscomment->find_entry_from(0,"TEST");
277 }
278 }
279
280 }
281 break;
282 case FLAC__METADATA_TYPE_CUESHEET:
283 {
284 uint32_t num_tracks, num_indices;
285 FLAC::Metadata::CueSheet * cuesheet = dynamic_cast<FLAC::Metadata::CueSheet *>(metadata_block_transfer);
286 if(cuesheet == 0 || !cuesheet->is_legal())
287 break;
288 cuesheet->is_legal(true); /* check CDDA subset */
289 cuesheet->calculate_cddb_id();
290 cuesheet->get_media_catalog_number();
291 cuesheet->get_lead_in();
292 cuesheet->get_is_cd();
293 num_tracks = cuesheet->get_num_tracks();
294 if(num_tracks > 0) {
295 FLAC::Metadata::CueSheet::Track track = cuesheet->get_track(min(data[i]>>4,num_tracks-1));
296 track.get_offset();
297 track.get_number();
298 track.get_isrc();
299 track.get_pre_emphasis();
300 num_indices = track.get_num_indices();
301 if(num_indices > 0) {
302 FLAC__StreamMetadata_CueSheet_Index index = track.get_index(min(data[i]>>4,num_indices-1));
303 (void)index;
304 }
305 }
306 }
307 break;
308 case FLAC__METADATA_TYPE_PICTURE:
309 {
310 char * violation = nullptr;
311 FLAC::Metadata::Picture * picture = dynamic_cast<FLAC::Metadata::Picture *>(metadata_block_transfer);
312 if(picture == 0 || !picture->is_legal((const char **)&violation))
313 break;
314 picture->get_data();
315 }
316 break;
317 default:
318 break;
319 }
320
321 }
322 break;
323 case 9: /* Replace or add in block */
324 if(metadata_block_transfer != 0 && metadata_block_transfer->is_valid()) {
325 switch(metadata_block_transfer->get_type()) {
326 case FLAC__METADATA_TYPE_SEEKTABLE:
327 {
328 uint32_t num_seekpoints;
329 FLAC__StreamMetadata_SeekPoint seekpoint;
330 FLAC::Metadata::SeekTable * seektable = dynamic_cast<FLAC::Metadata::SeekTable *>(metadata_block_transfer);
331 if(seektable == 0)
332 break;
333 if(seektable->is_valid() && seektable->is_legal()) {
334 num_seekpoints = seektable->get_num_points();
335 if(num_seekpoints > 0) {
336 seekpoint = seektable->get_point(min(data[i]>>5,num_seekpoints-1));
337 seektable->set_point(0,seekpoint);
338 seektable->insert_point(min(data[i]>>5,num_seekpoints-1),seekpoint);
339 }
340 seektable->template_append_placeholders(4);
341 seektable->template_append_point(111111);
342 seektable->template_append_points((FLAC__uint64[]){222222, 333333, 444444}, 3);
343 seektable->template_append_spaced_points(data[i]>>5, 1234567);
344 seektable->template_append_spaced_points_by_samples(data[i]>>5, 2468000);
345 seektable->template_sort(data[i] & 16);
346 }
347 }
348 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
349 {
350 uint32_t num_comments;
351 FLAC::Metadata::VorbisComment::Entry entry;
352 FLAC::Metadata::VorbisComment * vorbiscomment = dynamic_cast<FLAC::Metadata::VorbisComment *>(metadata_block_transfer);
353 if(vorbiscomment == 0)
354 break;
355 num_comments = vorbiscomment->get_num_comments();
356 if(num_comments > 0 && entry.is_valid()) {
357 if(vorbiscomment->get_comment(min(data[i]>>5,num_comments-1)).is_valid()) {
358 entry = vorbiscomment->get_comment(min(data[i]>>5,num_comments-1));
359 if(entry.is_valid()) {
360 vorbiscomment->replace_comment(entry,data[i] & 16);
361 vorbiscomment->set_comment(0,entry);
362 vorbiscomment->append_comment(entry);
363 vorbiscomment->insert_comment(0,entry);
364 }
365 }
366 }
367 }
368 break;
369 case FLAC__METADATA_TYPE_CUESHEET:
370 {
371 uint32_t num_tracks, num_indices;
372 FLAC::Metadata::CueSheet * cuesheet = dynamic_cast<FLAC::Metadata::CueSheet *>(metadata_block_transfer);
373 if(cuesheet == 0 || !cuesheet->is_legal())
374 break;
375 num_tracks = cuesheet->get_num_tracks();
376 if(num_tracks > 0) {
377 FLAC::Metadata::CueSheet::Track track = cuesheet->get_track(min(data[i]>>4,num_tracks-1));
378 num_indices = track.get_num_indices();
379 if(num_indices > 0) {
380 FLAC__StreamMetadata_CueSheet_Index index = track.get_index(min(data[i]>>4,num_indices-1));
381 track.set_index(0,index);
382 cuesheet->insert_index(0,0,index);
383 cuesheet->insert_blank_index(0,0);
384 }
385 cuesheet->insert_blank_track(0);
386 cuesheet->insert_track(0,track);
387 cuesheet->resize_indices(min(data[i]>>4,num_tracks-1),data[i]>>4);
388 }
389 }
390 break;
391 case FLAC__METADATA_TYPE_PICTURE:
392 {
393 FLAC::Metadata::Picture * picture = dynamic_cast<FLAC::Metadata::Picture *>(metadata_block_transfer);
394 const char testtext[] = "TEST";
395 if(picture == 0 || !picture->is_legal(NULL))
396 break;
397 picture->set_description((FLAC__byte *)&testtext);
398 picture->set_mime_type((const char *)&testtext);
399 picture->set_data((FLAC__byte *)&testtext,4);
400 }
401 break;
402 default:
403 break;
404 }
405
406 }
407 break;
408 case 10: /* Delete from block */
409 if(metadata_block_transfer != 0 && metadata_block_transfer->is_valid()) {
410 switch(metadata_block_transfer->get_type()) {
411 case FLAC__METADATA_TYPE_SEEKTABLE:
412 {
413 uint32_t num_seekpoints;
414 FLAC::Metadata::SeekTable * seektable = dynamic_cast<FLAC::Metadata::SeekTable *>(metadata_block_transfer);
415 if(seektable == 0)
416 break;
417 if(seektable->is_valid() && seektable->is_legal()) {
418 num_seekpoints = seektable->get_num_points();
419 if(num_seekpoints > 0)
420 seektable->delete_point(min(data[i]>>4,num_seekpoints-1));
421 }
422 }
423 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
424 {
425 uint32_t num_comments;
426 FLAC::Metadata::VorbisComment * vorbiscomment = dynamic_cast<FLAC::Metadata::VorbisComment *>(metadata_block_transfer);
427 if(vorbiscomment == 0)
428 break;
429 num_comments = vorbiscomment->get_num_comments();
430 if(num_comments > 0)
431 vorbiscomment->delete_comment(min(data[i]>>4,num_comments-1));
432 vorbiscomment->remove_entry_matching("TEST");
433 vorbiscomment->remove_entries_matching("TEST");
434 }
435 break;
436 case FLAC__METADATA_TYPE_CUESHEET:
437 {
438 uint32_t num_tracks;
439 FLAC::Metadata::CueSheet * cuesheet = dynamic_cast<FLAC::Metadata::CueSheet *>(metadata_block_transfer);
440 if(cuesheet == 0 || !cuesheet->is_legal())
441 break;
442 num_tracks = cuesheet->get_num_tracks();
443 if(num_tracks > 0) {
444 FLAC::Metadata::CueSheet::Track track = cuesheet->get_track(min(data[i]>>4,num_tracks-1));
445 if(track.get_num_indices() > 0)
446 cuesheet->delete_index(min(data[i]>>4,num_tracks-1),0);
447 cuesheet->delete_track(0);
448 }
449 }
450 break;
451 default:
452 break;
453 }
454
455 }
456 break;
457 case 11: /* Resize block */
458 if(metadata_block_transfer != 0 && metadata_block_transfer->is_valid()) {
459 switch(metadata_block_transfer->get_type()) {
460 case FLAC__METADATA_TYPE_PADDING:
461 {
462 FLAC::Metadata::Padding * padding = dynamic_cast<FLAC::Metadata::Padding *>(metadata_block_transfer);
463 if(padding == 0)
464 break;
465 padding->set_length(data[i]>>4);
466 }
467 break;
468 case FLAC__METADATA_TYPE_SEEKTABLE:
469 {
470 FLAC::Metadata::SeekTable * seektable = dynamic_cast<FLAC::Metadata::SeekTable *>(metadata_block_transfer);
471 if(seektable == 0)
472 break;
473 seektable->resize_points(data[i]>>4);
474 }
475 break;
476 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
477 {
478 FLAC::Metadata::VorbisComment * vorbiscomment = dynamic_cast<FLAC::Metadata::VorbisComment *>(metadata_block_transfer);
479 if(vorbiscomment == 0)
480 break;
481 vorbiscomment->resize_comments(data[i]>>4);
482 }
483 break;
484 case FLAC__METADATA_TYPE_CUESHEET:
485 {
486 uint32_t num_tracks;
487 FLAC::Metadata::CueSheet * cuesheet = dynamic_cast<FLAC::Metadata::CueSheet *>(metadata_block_transfer);
488 if(cuesheet == 0 || !cuesheet->is_legal())
489 break;
490 num_tracks = cuesheet->get_num_tracks();
491 if(num_tracks > 0) {
492 cuesheet->resize_indices(min(data[i]>>4,num_tracks-1),data[i]>>4);
493 }
494 cuesheet->resize_tracks(data[i]<<4);
495 }
496 break;
497 default:
498 break;
499 }
500
501 }
502 break;
503 case 12: /* Prototype functions */
504 if(metadata_block_transfer != 0 && metadata_block_transfer->is_valid()) {
505 const ::FLAC__StreamMetadata * metadata_compare = *metadata_block_transfer;
506 metadata_block_transfer->get_is_last();
507 metadata_block_transfer->get_length();
508 metadata_block_transfer->set_is_last(data[i] & 16);
509 FLAC__metadata_object_is_equal(metadata_compare, metadata_compare);
510 }
511 break;
512 }
513 }
514 if(metadata_block_transfer != 0) {
515 delete metadata_block_transfer;
516 metadata_block_transfer = nullptr;
517 }
518
519 chain.status();
520 chain.sort_padding();
521 chain.merge_padding();
522
523 chain.check_if_tempfile_needed(!use_padding);
524 chain.write(use_padding);
525
526 }
527