Search code examples
ctestingdecoderflacfuzzing

Decoding data with libFLAC using C


My purpose is to decode flac data for testing purposes. I'm going to create a small stub that has a function that takes data and size as input arguments. There is no need to make anykind of output file because I'm only going to make test for decoding. I have read some examples and api documentation from libflac page (https://xiph.org/flac/api/).

Now this is giving me :ERROR: initializing decoder: (null) because FLAC__stream_decoder_init_stream is commented. Reason it is commented is that I don't know how to properly use it and get decoding work. Any advice and comments that could help me to get decoding work?

#include <stdio.h>
#include <stdlib.h>
#include "share/compat.h"
#include "FLAC/stream_decoder.h"

static void error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *data);
static FLAC__StreamDecoderWriteStatus write_callback(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 * const buffer[], void *data);
static FLAC__StreamDecoderReadStatus read_callback(const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *data);

extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
  FLAC__bool ok = true;
  FLAC__StreamDecoder *decoder = 0;
  FLAC__StreamDecoderInitStatus init_status;

  // init decoder
  if((decoder = FLAC__stream_decoder_new()) == NULL) {
        fprintf(stderr, "ERROR: allocating decoder\n");
        return 1;
    }

  (void)FLAC__stream_decoder_set_md5_checking(decoder, true);

  init_status = FLAC__stream_decoder_init_stream (  decoder, read_callback, /*seek_callback*/ NULL, /*tell_callback*/ NULL, /*length_callback*/ NULL, /*eof_callback*/ NULL, write_callback, /*metadata_callback*/ NULL, error_callback, data);

  if(init_status != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
    fprintf(stderr, "ERROR: initializing decoder: %s\n", FLAC__StreamDecoderInitStatusString[init_status]);
    ok = false;
  }

  if(ok) {
    ok = FLAC__stream_decoder_process_until_end_of_stream(decoder);
    fprintf(stderr, "decoding: %s\n", ok? "succeeded" : "FAILED");
    fprintf(stderr, "   state: %s\n", FLAC__StreamDecoderStateString[FLAC__stream_decoder_get_state(decoder)]);
  }

  FLAC__stream_decoder_delete(decoder);

  return 0;
}

void error_callback(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *data)
{
  (void)decoder, (void)data;
  fprintf(stderr, "Got error callback: %s\n", FLAC__StreamDecoderErrorStatusString[status]);
}

Update: After adding callback functions I get this error:

flac_fuzzer.c:23:16: error: no matching function for call to 'FLAC__stream_decoder_init_stream'
init_status = FLAC__stream_decoder_init_stream (        decoder,
              ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
include/FLAC/stream_decoder.h:1073:40: note: candidate function not viable: no known conversion from 'const uint8_t *'
(aka 'const unsigned char *') to 'void *' for 10th argument; take the address of the argument with &
FLAC_API FLAC__StreamDecoderInitStatus FLAC__stream_decoder_init_stream(
                                       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Solution

  • You typically need to implement a event processing framework, similar to what is done with ffmpeg where the flac library hooks into the event processing using the callbacks. What you have implemented so far looks like the (minimal) initialization part of the flow graph, but you are missing all the processing elements.

    Shamefully just looking at what somebody else have done here and here you will need to implement the callback functions with signatures like

    write callback example:

     static FLAC__StreamDecoderWriteStatus flac_write_music_cb(
                                       const FLAC__StreamDecoder *decoder,
                                       const FLAC__Frame *frame,
                                       const FLAC__int32 *const buffer[],
                                       void *client_data)
    

    error callback example:

     static void flac_error_music_cb(
                    const FLAC__StreamDecoder *decoder,
                    FLAC__StreamDecoderErrorStatus status,
                    void *client_data)
    

    You should also look at how the flac data is read, and implement the supporting function as as well.