xref: /aosp_15_r20/external/flac/oss-fuzz/metadata.cc (revision 600f14f40d737144c998e2ec7a483122d3776fbc)
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