2022-04-20 16:16:46 +03:00
|
|
|
/* fuzzer_encoder_v2
|
|
|
|
* Copyright (C) 2022 Xiph.Org Foundation
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
*
|
|
|
|
* - Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
*
|
|
|
|
* - Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* - Neither the name of the Xiph.org Foundation nor the names of its
|
|
|
|
* contributors may be used to endorse or promote products derived from
|
|
|
|
* this software without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR
|
|
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
|
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
|
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <cstdlib>
|
|
|
|
#include <cstring> /* for memcpy */
|
|
|
|
#include "FLAC/stream_encoder.h"
|
|
|
|
#include "FLAC/metadata.h"
|
|
|
|
extern "C" {
|
|
|
|
#include "share/private.h"
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This C++ fuzzer uses the FLAC and not FLAC++ because the latter lacks a few
|
|
|
|
* hidden functions like FLAC__stream_encoder_disable_constant_subframes. It
|
|
|
|
* is still processed by a C++ compiler because that's what oss-fuzz expects */
|
|
|
|
|
|
|
|
|
|
|
|
static FLAC__StreamEncoderWriteStatus write_callback(const FLAC__StreamEncoder *encoder, const FLAC__byte buffer[], size_t bytes, uint32_t samples, uint32_t current_frame, void *client_data)
|
|
|
|
{
|
|
|
|
(void)encoder, (void)buffer, (void)bytes, (void)samples, (void)current_frame, (void)client_data;
|
|
|
|
return FLAC__STREAM_ENCODER_WRITE_STATUS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)
|
|
|
|
{
|
|
|
|
FLAC__bool encoder_valid = true;
|
|
|
|
FLAC__StreamEncoder *encoder = 0;
|
|
|
|
FLAC__StreamMetadata *metadata[16] = {NULL};
|
|
|
|
unsigned num_metadata = 0;
|
|
|
|
FLAC__StreamMetadata_VorbisComment_Entry VorbisCommentField;
|
|
|
|
|
|
|
|
unsigned sample_rate, channels, bps;
|
|
|
|
uint64_t samples_estimate;
|
|
|
|
unsigned compression_level, input_data_width, blocksize, max_lpc_order, qlp_coeff_precision, min_residual_partition_order, max_residual_partition_order, metadata_mask;
|
|
|
|
FLAC__bool ogg, interleaved;
|
|
|
|
|
|
|
|
FLAC__bool data_bools[24];
|
|
|
|
|
|
|
|
/* allocate the encoder */
|
|
|
|
if((encoder = FLAC__stream_encoder_new()) == NULL) {
|
|
|
|
fprintf(stderr, "ERROR: allocating encoder\n");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Use first 20 byte for configuration */
|
|
|
|
if(size < 20){
|
|
|
|
FLAC__stream_encoder_delete(encoder);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* First 3 byte for sample rate, 4th byte for channels, 5th byte for bps */
|
|
|
|
sample_rate = ((unsigned)data[0] << 16) + ((unsigned)data[1] << 8) + data[2];
|
|
|
|
channels = data[3];
|
|
|
|
bps = data[4];
|
|
|
|
|
|
|
|
/* Number of samples estimate, format accepts 36-bit max */
|
|
|
|
samples_estimate = ((uint64_t)data[5] << 32) + ((unsigned)data[6] << 24) + ((unsigned)data[7] << 16) + ((unsigned)data[8] << 8) + data[9];
|
|
|
|
|
|
|
|
compression_level = data[10]&0b1111;
|
|
|
|
input_data_width = 1 + (data[10]&0b1111000)%3; /* Slight bias away from 32-bit (4-byte) */
|
|
|
|
blocksize = ((unsigned)data[11] << 8) + (unsigned)data[12];
|
|
|
|
max_lpc_order = data[13];
|
|
|
|
qlp_coeff_precision = data[14];
|
|
|
|
min_residual_partition_order = data[15] & 0b1111;
|
|
|
|
max_residual_partition_order = data[15] & 0b11110000;
|
|
|
|
metadata_mask = (unsigned)data[16];
|
|
|
|
|
|
|
|
/* data[17] is spare */
|
|
|
|
|
|
|
|
/* Get array of bools from configuration */
|
|
|
|
for(int i = 0; i < 16; i++)
|
|
|
|
data_bools[i] = data[18+i/8] & (1 << (i % 8));
|
|
|
|
|
|
|
|
ogg = data_bools[0];
|
|
|
|
interleaved = data_bools[1];
|
|
|
|
|
|
|
|
/* Set input and process parameters */
|
|
|
|
encoder_valid &= FLAC__stream_encoder_set_verify(encoder, data_bools[2]);
|
|
|
|
encoder_valid &= FLAC__stream_encoder_set_channels(encoder, channels);
|
|
|
|
encoder_valid &= FLAC__stream_encoder_set_bits_per_sample(encoder, bps);
|
|
|
|
encoder_valid &= FLAC__stream_encoder_set_sample_rate(encoder, sample_rate);
|
|
|
|
encoder_valid &= FLAC__stream_encoder_set_total_samples_estimate(encoder, samples_estimate);
|
|
|
|
|
|
|
|
/* Set compression related parameters */
|
|
|
|
encoder_valid &= FLAC__stream_encoder_set_compression_level(encoder, compression_level);
|
|
|
|
if(data_bools[3]){
|
|
|
|
/* Bias towards regular compression levels */
|
|
|
|
encoder_valid &= FLAC__stream_encoder_set_streamable_subset(encoder, data_bools[4]);
|
|
|
|
encoder_valid &= FLAC__stream_encoder_set_blocksize(encoder, blocksize);
|
|
|
|
encoder_valid &= FLAC__stream_encoder_set_max_lpc_order(encoder, max_lpc_order);
|
|
|
|
encoder_valid &= FLAC__stream_encoder_set_qlp_coeff_precision(encoder, qlp_coeff_precision);
|
|
|
|
encoder_valid &= FLAC__stream_encoder_set_min_residual_partition_order(encoder, min_residual_partition_order);
|
|
|
|
encoder_valid &= FLAC__stream_encoder_set_max_residual_partition_order(encoder, max_residual_partition_order);
|
|
|
|
|
2022-05-07 21:00:06 +03:00
|
|
|
if(size < (1 << 18)) {
|
|
|
|
/* The following three options are **slow**, and when combined with a large input
|
|
|
|
* make fuzzers timeout, so only enable them on reasonably sized inputs. 2^17 is taken
|
|
|
|
* because that is the maximum blocksize * 4 (32-bit input)
|
|
|
|
*/
|
|
|
|
encoder_valid &= FLAC__stream_encoder_set_do_qlp_coeff_prec_search(encoder, data_bools[5]);
|
|
|
|
encoder_valid &= FLAC__stream_encoder_set_do_escape_coding(encoder, data_bools[6]);
|
|
|
|
encoder_valid &= FLAC__stream_encoder_set_do_exhaustive_model_search(encoder, data_bools[7]);
|
|
|
|
}
|
2022-04-20 16:16:46 +03:00
|
|
|
encoder_valid &= FLAC__stream_encoder_set_do_mid_side_stereo(encoder, data_bools[8]);
|
|
|
|
encoder_valid &= FLAC__stream_encoder_set_loose_mid_side_stereo(encoder, data_bools[9]);
|
|
|
|
|
|
|
|
encoder_valid &= FLAC__stream_encoder_disable_constant_subframes(encoder, data_bools[10]);
|
|
|
|
encoder_valid &= FLAC__stream_encoder_disable_fixed_subframes(encoder, data_bools[11]);
|
|
|
|
encoder_valid &= FLAC__stream_encoder_disable_verbatim_subframes(encoder, data_bools[12]);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* data_bools[13..15] are spare */
|
|
|
|
|
|
|
|
/* add metadata */
|
|
|
|
if(encoder_valid && (metadata_mask & 1)) {
|
|
|
|
if((metadata[num_metadata] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_STREAMINFO)) == NULL)
|
|
|
|
encoder_valid = false;
|
|
|
|
else
|
|
|
|
num_metadata++;
|
|
|
|
}
|
|
|
|
if(encoder_valid && (metadata_mask & 2) && size > 21){
|
|
|
|
if((metadata[num_metadata] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_PADDING)) == NULL)
|
|
|
|
encoder_valid = false;
|
|
|
|
else {
|
|
|
|
metadata[num_metadata++]->length = (((unsigned)data[20]) << 8) + (unsigned)(data[21]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(encoder_valid && (metadata_mask & 4) && size > 20){
|
|
|
|
FLAC__byte * application_data = (FLAC__byte *)malloc(size-20);
|
|
|
|
if(0 != application_data && ((metadata[num_metadata] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_APPLICATION)) == NULL))
|
|
|
|
encoder_valid = false;
|
|
|
|
else {
|
|
|
|
memcpy(application_data,data+20,size-20);
|
|
|
|
FLAC__metadata_object_application_set_data(metadata[num_metadata++], application_data, size-20, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(encoder_valid && (metadata_mask & 8) > 25){
|
|
|
|
if((metadata[num_metadata] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_SEEKTABLE)) == NULL)
|
|
|
|
encoder_valid = false;
|
|
|
|
else {
|
|
|
|
unsigned seekpoint_spacing = ((unsigned)data[22] << 8) + data[23];
|
|
|
|
unsigned total_samples_for_seekpoints = ((unsigned)data[24] << 8) + data[25];
|
|
|
|
FLAC__metadata_object_seektable_template_append_spaced_points_by_samples(metadata[num_metadata++], seekpoint_spacing, total_samples_for_seekpoints);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(encoder_valid && (metadata_mask & 16)){
|
|
|
|
if((metadata[num_metadata] = FLAC__metadata_object_new(FLAC__METADATA_TYPE_VORBIS_COMMENT)) == NULL)
|
|
|
|
encoder_valid = false;
|
|
|
|
else {
|
|
|
|
/* Append a vorbis comment */
|
|
|
|
if(!FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&VorbisCommentField, "COMMENTARY", "Nothing to 🤔 report"))
|
|
|
|
encoder_valid = false;
|
|
|
|
else {
|
|
|
|
FLAC__metadata_object_vorbiscomment_append_comment(metadata[num_metadata], VorbisCommentField, false);
|
|
|
|
|
|
|
|
/* Insert a vorbis comment at the first index */
|
|
|
|
if(!FLAC__metadata_object_vorbiscomment_entry_from_name_value_pair(&VorbisCommentField, "COMMENTARY", "Still nothing to report 🤔🤣"))
|
|
|
|
encoder_valid = false;
|
|
|
|
else
|
|
|
|
FLAC__metadata_object_vorbiscomment_insert_comment(metadata[num_metadata++], 0, VorbisCommentField, false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(num_metadata && encoder_valid)
|
|
|
|
encoder_valid = FLAC__stream_encoder_set_metadata(encoder, metadata, num_metadata);
|
|
|
|
|
|
|
|
/* initialize encoder */
|
|
|
|
if(encoder_valid) {
|
|
|
|
FLAC__StreamEncoderInitStatus init_status;
|
|
|
|
if(ogg)
|
|
|
|
init_status = FLAC__stream_encoder_init_ogg_stream(encoder, NULL, write_callback, NULL, NULL, NULL, NULL);
|
|
|
|
else
|
|
|
|
init_status = FLAC__stream_encoder_init_stream(encoder, write_callback, NULL, NULL, NULL, NULL);
|
|
|
|
if(init_status != FLAC__STREAM_ENCODER_INIT_STATUS_OK) {
|
|
|
|
encoder_valid = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* send samples to encoder */
|
|
|
|
if(encoder_valid && size > (input_data_width*channels+26)) {
|
|
|
|
unsigned samples = (size - 26)/input_data_width/channels;
|
|
|
|
const uint8_t * pcm_data = data + 26;
|
|
|
|
int32_t * data_as_int32 = (int32_t *)malloc(4*samples*channels);
|
|
|
|
if(0 != data_as_int32){
|
|
|
|
for(unsigned i = 0; i < samples*channels; i++)
|
|
|
|
if(input_data_width == 1)
|
|
|
|
data_as_int32[i] = (int32_t)pcm_data[i] - 0x80;
|
|
|
|
else if(input_data_width == 2)
|
|
|
|
data_as_int32[i] = (((int32_t)pcm_data[i*2] << 8) + pcm_data[i*2+1]) - 0x8000;
|
|
|
|
else if(input_data_width == 3)
|
|
|
|
data_as_int32[i] = (((int32_t)pcm_data[i*3] << 16) + ((int32_t)pcm_data[i*3+1] << 8) + pcm_data[i*3+2]) - 0x800000;
|
|
|
|
else if(input_data_width == 4)
|
|
|
|
data_as_int32[i] = (((int64_t)pcm_data[i*4] << 24) + ((int32_t)pcm_data[i*4+1] << 16) + ((int32_t)pcm_data[i*4+2] << 8) + pcm_data[i*4+3]) - 0x80000000;
|
|
|
|
|
|
|
|
/* feed samples to encoder */
|
|
|
|
if(interleaved)
|
|
|
|
encoder_valid = FLAC__stream_encoder_process_interleaved(encoder, data_as_int32, samples);
|
|
|
|
else {
|
|
|
|
encoder_valid = FLAC__stream_encoder_process(encoder, (const int32_t*[]){data_as_int32,
|
|
|
|
data_as_int32+samples,
|
|
|
|
data_as_int32+samples*2,
|
|
|
|
data_as_int32+samples*3,
|
|
|
|
data_as_int32+samples*4, data_as_int32+samples*5, data_as_int32+samples*6, data_as_int32+samples*7}, samples);
|
|
|
|
}
|
|
|
|
free(data_as_int32);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
encoder_valid = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-05-10 20:59:53 +03:00
|
|
|
if(FLAC__stream_encoder_get_state(encoder) != FLAC__STREAM_ENCODER_OK &&
|
|
|
|
FLAC__stream_encoder_get_state(encoder) != FLAC__STREAM_ENCODER_UNINITIALIZED &&
|
|
|
|
FLAC__stream_encoder_get_state(encoder) != FLAC__STREAM_ENCODER_CLIENT_ERROR){
|
|
|
|
fprintf(stderr,"-----\nERROR: stream encoder returned %s\n-----\n",FLAC__stream_encoder_get_resolved_state_string(encoder));
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
|
2022-04-20 16:16:46 +03:00
|
|
|
FLAC__stream_encoder_finish(encoder);
|
|
|
|
|
|
|
|
/* now that encoding is finished, the metadata can be freed */
|
|
|
|
for(unsigned i = 0; i < 16; i++)
|
|
|
|
if(0 != metadata[i])
|
|
|
|
FLAC__metadata_object_delete(metadata[i]);
|
|
|
|
|
|
|
|
FLAC__stream_encoder_delete(encoder);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|