Project: flac (xiph/flac), language: c++
Vulnerability instance: flac.cve-2020-22219
Sanitizer: address

================= Bug Report (1/1) ==================
## Source: GitHub Issue
## URL: https://github.com/xiph/flac/issues/215
## Description:
Issue: xiph/flac#215
Title: wild-addr-write found by fuzz
State: closed
Created by: ltx2018
Created at: 2020-06-02 03:09:10+00:00

Issue Body:
we found wild-addr-write by fuzzing flac-master:
```
==217==ERROR: AddressSanitizer: SEGV on unknown address 0xb6029a2c (pc 0x0822a2ae bp 0xffeb31e8 sp 0xffeb30a0 T0)
==217==The signal is caused by a WRITE memory access.
SCARINESS: 30 (wild-addr-write)
    #0 0x822a2ad in FLAC__bitwriter_write_raw_uint32_nocheck /src/flac/src/libFLAC/bitwriter.c
    #1 0x8229a42 in FLAC__bitwriter_write_raw_uint32 /src/flac/src/libFLAC/bitwriter.c:369:9
    #2 0x8218ec3 in FLAC__frame_add_header /src/flac/src/libFLAC/stream_encoder_framing.c:227:6
    #3 0x820557b in process_subframes_ /src/flac/src/libFLAC/stream_encoder.c:3365:7
    #4 0x81d940f in process_frame_ /src/flac/src/libFLAC/stream_encoder.c:3096:6
    #5 0x81f3770 in FLAC__stream_encoder_process_interleaved /src/flac/src/libFLAC/stream_encoder.c:2298:9
    #6 0x81bfa80 in FLAC::Encoder::Stream::process_interleaved(int const*, unsigned int) /src/flac/src/libFLAC++/stream_encoder.cpp:370:29
    #7 0x81ac167 in LLVMFuzzerTestOneInput /src/flac-fuzzers/fuzzer_encoder.cpp:141:46
    #8 0x80ac766 in fuzzer::Fuzzer::ExecuteCallback(unsigned char const*, unsigned int) /src/llvm/projects/compiler-rt/lib/fuzzer/FuzzerLoop.cpp:556:15
    #9 0x8098c13 in fuzzer::RunOneTest(fuzzer::Fuzzer*, char const*, unsigned int) /src/llvm/projects/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:292:6
    #10 0x809e318 in fuzzer::FuzzerDriver(int*, char***, int (*)(unsigned char const*, unsigned int)) /src/llvm/projects/compiler-rt/lib/fuzzer/FuzzerDriver.cpp:774:9
    #11 0x80c3167 in main /src/llvm/projects/compiler-rt/lib/fuzzer/FuzzerMain.cpp:19:10
    #12 0xf7539636 in __libc_start_main (/lib32/libc.so.6+0x18636)
    #13 0x8073c38 in _start (/out/flac/fuzzer_encoder+0x8073c38)
```

here is my debug info:
bw->buffer was realloc here
```
bitwriter_grow_ (bw=0xf5a00a90, bits_to_add=62914562) at bitwriter.c:128
128		if(new_buffer == 0)
(gdb) n
130		bw->buffer = new_buffer;
(gdb) l
125		FLAC__ASSERT(new_capacity >= bw->words + ((bw->bits + bits_to_add + FLAC__BITS_PER_WORD - 1) / FLAC__BITS_PER_WORD));
126
127		new_buffer = safe_realloc_mul_2op_(bw->buffer, sizeof(bwword), /*times*/new_capacity);
128		if(new_buffer == 0)
129			return false;
130		bw->buffer = new_buffer;
131		bw->capacity = new_capacity;
132		return true;
133	}
134
(gdb) p new_buffer
$1 = (bwword *) 0x7abd7800
(gdb) p new_capacity
$2 = 250956800
```


later, bw->buffer was freed but it's value NOT set to 0
```
156	static inline void *safe_realloc_(void *ptr, size_t size)
157	{
158		void *oldptr = ptr;
159		void *newptr = realloc(ptr, size);
160		if(size > 0 && newptr == 0)
161			free(oldptr);
162		return newptr;
(gdb) n
159		void *newptr = realloc(ptr, size);
(gdb) n
160		if(size > 0 && newptr == 0)
(gdb) p newptr
$4 = (void *) 0x0
(gdb) p size
$5 = 1006448640
(gdb) n
161			free(oldptr);
(gdb) p oldptr
$6 = (void *) 0x7abd7800
(gdb) n
162		return newptr;
(gdb) n
safe_realloc_mul_2op_ (ptr=0x7abd7800, size1=4, size2=251612160) at ../../include/share/alloc.h:206
206	}
(gdb) n
bitwriter_grow_ (bw=0xf5a00a90, bits_to_add=20971521) at bitwriter.c:128
128		if(new_buffer == 0)
(gdb) l
123		FLAC__ASSERT(0 == (new_capacity - bw->capacity) % FLAC__BITWRITER_DEFAULT_INCREMENT);
124		FLAC__ASSERT(new_capacity > bw->capacity);
125		FLAC__ASSERT(new_capacity >= bw->words + ((bw->bits + bits_to_add + FLAC__BITS_PER_WORD - 1) / FLAC__BITS_PER_WORD));
126
127		new_buffer = safe_realloc_mul_2op_(bw->buffer, sizeof(bwword), /*times*/new_capacity);
128		if(new_buffer == 0)
129			return false;
130		bw->buffer = new_buffer;
131		bw->capacity = new_capacity;
132		return true;
(gdb) p bw->buffer
$7 = (bwword *) 0x7abd7800
(gdb) p bw->capacity
$8 = 250956800
(gdb) n
129			return false
```



Comments:
Comment by ltx2018 on 2020-06-02 03:11:03+00:00:
#214  a way to fix

---

Comment by erikd on 2020-06-02 03:34:15+00:00:
How can this be triggered?

---

Comment by ltx2018 on 2020-06-02 07:34:08+00:00:
> How can this be triggered?

i just run fuzzer_encoder(build by oss-fuzz) locally, here is my crash-corpus
[crash-fuzzer_encoder.zip](https://github.com/xiph/flac/files/4715489/crash-fuzzer_encoder.zip)


---

Comment by ltx2018 on 2020-06-02 07:35:29+00:00:
> How can this be triggered?

by the way, ARCHITECTURE is i386

---

Comment by attritionorg on 2020-06-03 16:46:23+00:00:
How it is triggered is important for many reasons, not just debugging. It helps us understand if this can legitimately be triggered by a user and if so, represents a security risk.

---

Comment by ltx2018 on 2020-06-04 06:12:18+00:00:
> How it is triggered is important for many reasons, not just debugging. It helps us understand if this can legitimately be triggered by a user and if so, represents a security risk.

Yeah, you can run `fuzzer_encoder crash-fuzzer_encoder` to reproduce(source code is oss-fuzz/fuzz-encoder.cc), as i have already mentioned before.

---

Comment by erikd on 2020-06-04 07:36:22+00:00:
This is only triggered by submitting a specially crafted input to the *encoder* . It would be a far bigger issue if this was in the decoder.

---

Comment by petterreinholdtsen on 2020-06-04 11:17:46+00:00:
[Erik de Castro Lopo]
> This is only triggered by submitting a specially crafted input to the
> *encoder* . It would be a far bigger issue if this was in the decoder.

Absolutely.  But given these web streaming days, even encoding might
provide an avenue into the system. :)

-- 
Happy hacking
Petter Reinholdtsen


---

Comment by attritionorg on 2020-06-13 17:37:10+00:00:
Is there potential for more than a crash / denial of service?

---

Comment by ktmf01 on 2021-11-30 18:34:41+00:00:
@ltx2018  I've tried to reproduce this, but I seem to be bumping into [this problem with clang/LLVM](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=945073), which prevents me from building fuzzer_encoder on a 32-bit system.

Anyway, I have a hunch about what might be triggering the problem. Would you mind checking whether PR #252 solves this problem? I know #214 should already fix the problem, but I think #252 might be fixing the root cause. Please let me know.

---

Comment by ktmf01 on 2022-01-11 20:59:28+00:00:
@ltx2018 Okay, I finally got this one to trigger. #252 didn't fix it, neither did #251.

---

Comment by ktmf01 on 2022-04-12 17:42:33+00:00:
I believe this is fixed with the merge of #273. @ltx2018 could you check?

---

Comment by ktmf01 on 2022-08-20 14:05:18+00:00:
This is fixed with the merge of #419 

---

Comment by cebarks on 2023-08-28 20:50:40+00:00:
FYI, this flaw is being tracked by CVE-2020-22219.

---

Comment by ktmf01 on 2023-09-03 17:51:22+00:00:
I think the analysis of CVE-2020-22219 is wrong. The vulnerability described in CVE-2020-22219 is in libFLAC. However, this vulnerability is only exploitable when the program calling the library does not fulfill a key requirement of the library API: the calling program has to supply values that are out-of-bounds. This requirement is mentioned [here](https://xiph.org/flac/api/group__flac__stream__encoder.html#ga7453fc29d7e86b499f23b1adfba98da1), [here](https://xiph.org/flac/api/group__flac__stream__encoder.html#ga87b9c361292da5c5928a8fb5fda7c423) and [here](https://xiph.org/flac/api/group__flac__stream__encoder.html#ga6e31c221f7e23345267c52f53c046c24).

This means that there is no way to exploit the vulnerability through the flac command line tool that is provided by the project: it does not violate this requirement, it only supplies values that are within bounds.

So, the vulnerability is only exploitable with a program calling libFLAC that is does not properly check whether inputs are bounded or not. That seems a pretty important omission in the current analysis.

---

Comment by ktmf01 on 2023-09-06 06:05:52+00:00:
On second thought, there is another way to exploit this, but that is very unreliable: when running out of memory. As FLAC uses only a tiny amount of memory and the failing realloc only allocates a small amount of memory, this is probably hard to turn into an exploit.

In short, this buffer overflow only happens after an unhandled memory allocation failure.

---

Comment by mlichvar on 2023-09-11 13:14:11+00:00:
Doesn't the attacker control how much memory will the encoder need? With flac it causes a double free, but I guess other applications might do something with the encoder after the reallocation failed.

---

Comment by ktmf01 on 2023-09-11 13:31:46+00:00:
No, the encoding parameters bound how much memory is used, so an attacker cannot let libFLAC use an arbitrary amount of memory, except when the program allows for supplying invalid data. The problem here was that invalid input made the encoder go beyond those bounds. That is why #273 adds an explicit check that input is valid, #252 added a limit to the bitwriter size (which was increased in #378) and #419 fixed handling of the allocation failure.

Attachments:
https://github.com/xiph/flac/files/4715489/crash-fuzzer_encoder.zip

Build script:
#!/bin/bash -eu
# Minimized build script with only core build commands
set -eu
export ASAN_OPTIONS="detect_leaks=0"
    export CXXFLAGS="$CXXFLAGS -DASAN"
    export CXXFLAGS="$CXXFLAGS -DMSAN"
export CFLAGS="$CFLAGS -D_FORTIFY_SOURCE=0"
export CXXFLAGS="$CXXFLAGS -D_FORTIFY_SOURCE=0 -D_GLIBCXX_DEBUG"
mkdir -p $SRC/libogg-install
cd $SRC/ogg
./autogen.sh
./configure --disable-crc --disable-shared --prefix="$SRC/libogg-install"
make -j$(nproc)
make install -j$(nproc)
cd $SRC/flac/
    LD_LIBRARY_PATH="$SRC/libogg-install/lib" ./configure --with-ogg="$SRC/libogg-install" --enable-static --disable-shared --disable-oggtest --disable-examples --disable-programs --disable-xmms-plugin --disable-asm-optimizations --disable-sse --enable-oss-fuzzers
    LD_LIBRARY_PATH="$SRC/libogg-install/lib" ./configure --with-ogg="$SRC/libogg-install" --enable-static --disable-shared --disable-oggtest --disable-examples --disable-programs --disable-xmms-plugin --enable-oss-fuzzers

Dockerfile:
FROM hwiwonlee/secb.base:latest
RUN apt-get update && apt-get install -y make autoconf automake libtool libtool-bin pkg-config gettext sudo default-jdk
RUN git clone https://github.com/xiph/flac flac
RUN git -C flac checkout 35303458f01eaf36e5d4b9beafa0a1c2d626d0d6
WORKDIR $SRC/flac
COPY build.sh $SRC/

Additional files:
filename: fuzzer_exo.cpp
content: // Copyright 2020 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <assert.h>
#include <string>

#include "include/flac_parser.h"

#include <jni.h>

// #include <android/log.h>

#include <cassert>
#include <cstdlib>
#include <cstring>

#include "fuzzer_common.h"

#define LOG_TAG "FLACParser"

#define LITERAL_TO_STRING_INTERNAL(x) #x
#define LITERAL_TO_STRING(x) LITERAL_TO_STRING_INTERNAL(x)

#define CHECK(x) if (!(x)) return 0;

const int endian = 1;
#define isBigEndian() (*(reinterpret_cast<const char *>(&endian)) == 0)

// The FLAC parser calls our C++ static callbacks using C calling conventions,
// inside FLAC__stream_decoder_process_until_end_of_metadata
// and FLAC__stream_decoder_process_single.
// We immediately then call our corresponding C++ instance methods
// with the same parameter list, but discard redundant information.

FLAC__StreamDecoderReadStatus FLACParser::read_callback(
    const FLAC__StreamDecoder * /* decoder */, FLAC__byte buffer[],
    size_t *bytes, void *client_data) {
  return reinterpret_cast<FLACParser *>(client_data)
      ->readCallback(buffer, bytes);
}

FLAC__StreamDecoderSeekStatus FLACParser::seek_callback(
    const FLAC__StreamDecoder * /* decoder */,
    FLAC__uint64 absolute_byte_offset, void *client_data) {
  return reinterpret_cast<FLACParser *>(client_data)
      ->seekCallback(absolute_byte_offset);
}

FLAC__StreamDecoderTellStatus FLACParser::tell_callback(
    const FLAC__StreamDecoder * /* decoder */,
    FLAC__uint64 *absolute_byte_offset, void *client_data) {
  return reinterpret_cast<FLACParser *>(client_data)
      ->tellCallback(absolute_byte_offset);
}

FLAC__StreamDecoderLengthStatus FLACParser::length_callback(
    const FLAC__StreamDecoder * /* decoder */, FLAC__uint64 *stream_length,
    void *client_data) {
  return reinterpret_cast<FLACParser *>(client_data)
      ->lengthCallback(stream_length);
}

FLAC__bool FLACParser::eof_callback(const FLAC__StreamDecoder * /* decoder */,
                                    void *client_data) {
  return reinterpret_cast<FLACParser *>(client_data)->eofCallback();
}

FLAC__StreamDecoderWriteStatus FLACParser::write_callback(
    const FLAC__StreamDecoder * /* decoder */, const FLAC__Frame *frame,
    const FLAC__int32 *const buffer[], void *client_data) {
  return reinterpret_cast<FLACParser *>(client_data)
      ->writeCallback(frame, buffer);
}

void FLACParser::metadata_callback(const FLAC__StreamDecoder * /* decoder */,
                                   const FLAC__StreamMetadata *metadata,
                                   void *client_data) {
  reinterpret_cast<FLACParser *>(client_data)->metadataCallback(metadata);
}

void FLACParser::error_callback(const FLAC__StreamDecoder * /* decoder */,
                                FLAC__StreamDecoderErrorStatus status,
                                void *client_data) {
  reinterpret_cast<FLACParser *>(client_data)->errorCallback(status);
}

// These are the corresponding callbacks with C++ calling conventions

FLAC__StreamDecoderReadStatus FLACParser::readCallback(FLAC__byte buffer[],
                                                       size_t *bytes) {
  size_t requested = *bytes;
  ssize_t actual = mDataSource->readAt(mCurrentPos, buffer, requested);
  if (0 > actual) {
    *bytes = 0;
    return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
  } else if (0 == actual) {
    *bytes = 0;
    mEOF = true;
    return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
  } else {
    assert(actual <= requested);
    *bytes = actual;
    mCurrentPos += actual;
    return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
  }
}

FLAC__StreamDecoderSeekStatus FLACParser::seekCallback(
    FLAC__uint64 absolute_byte_offset) {
  mCurrentPos = absolute_byte_offset;
  mEOF = false;
  return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
}

FLAC__StreamDecoderTellStatus FLACParser::tellCallback(
    FLAC__uint64 *absolute_byte_offset) {
  *absolute_byte_offset = mCurrentPos;
  return FLAC__STREAM_DECODER_TELL_STATUS_OK;
}

FLAC__StreamDecoderLengthStatus FLACParser::lengthCallback(
    FLAC__uint64 *stream_length) {
  return FLAC__STREAM_DECODER_LENGTH_STATUS_UNSUPPORTED;
}

FLAC__bool FLACParser::eofCallback() { return mEOF; }

FLAC__StreamDecoderWriteStatus FLACParser::writeCallback(
    const FLAC__Frame *frame, const FLAC__int32 *const buffer[]) {
  if (mWriteRequested) {
    mWriteRequested = false;
    // FLAC parser doesn't free or realloc buffer until next frame or finish
    mWriteHeader = frame->header;
    mWriteBuffer = buffer;
    mWriteCompleted = true;
    return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
  } else {
    return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
  }
}

void FLACParser::metadataCallback(const FLAC__StreamMetadata *metadata) {
  switch (metadata->type) {
    case FLAC__METADATA_TYPE_STREAMINFO:
      if (!mStreamInfoValid) {
        mStreamInfo = metadata->data.stream_info;
        mStreamInfoValid = true;
      } else {
        break;
      }
      break;
    case FLAC__METADATA_TYPE_SEEKTABLE:
      mSeekTable = &metadata->data.seek_table;
      break;
    case FLAC__METADATA_TYPE_VORBIS_COMMENT:
      if (!mVorbisCommentsValid) {
        FLAC__StreamMetadata_VorbisComment vorbisComment =
            metadata->data.vorbis_comment;
        for (FLAC__uint32 i = 0; i < vorbisComment.num_comments; ++i) {
          FLAC__StreamMetadata_VorbisComment_Entry vorbisCommentEntry =
              vorbisComment.comments[i];
          if (vorbisCommentEntry.entry != NULL) {
            std::string comment(
                reinterpret_cast<char *>(vorbisCommentEntry.entry),
                vorbisCommentEntry.length);
            mVorbisComments.push_back(comment);
          }
        }
        mVorbisCommentsValid = true;
      } else {
        break;
      }
      break;
    case FLAC__METADATA_TYPE_PICTURE: {
      const FLAC__StreamMetadata_Picture *parsedPicture =
          &metadata->data.picture;
      FlacPicture picture;
      picture.mimeType.assign(std::string(parsedPicture->mime_type));
      picture.description.assign(
          std::string((char *)parsedPicture->description));
      picture.data.assign(parsedPicture->data,
                          parsedPicture->data + parsedPicture->data_length);
      picture.width = parsedPicture->width;
      picture.height = parsedPicture->height;
      picture.depth = parsedPicture->depth;
      picture.colors = parsedPicture->colors;
      picture.type = parsedPicture->type;
      mPictures.push_back(picture);
      mPicturesValid = true;
      break;
    }
    default:
      break;
  }
}

void FLACParser::errorCallback(FLAC__StreamDecoderErrorStatus status) {
  mErrorStatus = status;
}

// Copy samples from FLAC native 32-bit non-interleaved to
// correct bit-depth (non-zero padded), interleaved.
// These are candidates for optimization if needed.
static void copyToByteArrayBigEndian(int8_t *dst, const int *const *src,
                                     unsigned bytesPerSample, unsigned nSamples,
                                     unsigned nChannels) {
  for (unsigned i = 0; i < nSamples; ++i) {
    for (unsigned c = 0; c < nChannels; ++c) {
      // point to the first byte of the source address
      // and then skip the first few bytes (most significant bytes)
      // depending on the bit depth
      const int8_t *byteSrc =
          reinterpret_cast<const int8_t *>(&src[c][i]) + 4 - bytesPerSample;
      memcpy(dst, byteSrc, bytesPerSample);
      dst = dst + bytesPerSample;
    }
  }
}

static void copyToByteArrayLittleEndian(int8_t *dst, const int *const *src,
                                        unsigned bytesPerSample,
                                        unsigned nSamples, unsigned nChannels) {
  for (unsigned i = 0; i < nSamples; ++i) {
    for (unsigned c = 0; c < nChannels; ++c) {
      // with little endian, the most significant bytes will be at the end
      // copy the bytes in little endian will remove the most significant byte
      // so we are good here.
      memcpy(dst, &(src[c][i]), bytesPerSample);
      dst = dst + bytesPerSample;
    }
  }
}

static void copyTrespass(int8_t * /* dst */, const int *const * /* src */,
                         unsigned /* bytesPerSample */, unsigned /* nSamples */,
                         unsigned /* nChannels */) {
  ;
}

// FLACParser

FLACParser::FLACParser(DataSource *source)
    : mDataSource(source),
      mCopy(copyTrespass),
      mDecoder(NULL),
      mCurrentPos(0LL),
      mEOF(false),
      mStreamInfoValid(false),
      mSeekTable(NULL),
      firstFrameOffset(0LL),
      mVorbisCommentsValid(false),
      mPicturesValid(false),
      mWriteRequested(false),
      mWriteCompleted(false),
      mWriteBuffer(NULL),
      mErrorStatus((FLAC__StreamDecoderErrorStatus)-1) {
  memset(&mStreamInfo, 0, sizeof(mStreamInfo));
  memset(&mWriteHeader, 0, sizeof(mWriteHeader));
}

FLACParser::~FLACParser() {
  if (mDecoder != NULL) {
    FLAC__stream_decoder_delete(mDecoder);
    mDecoder = NULL;
  }
}

bool FLACParser::init() {
  // setup libFLAC parser
  mDecoder = FLAC__stream_decoder_new();
  if (mDecoder == NULL) {
    // The new should succeed, since probably all it does is a malloc
    // that always succeeds in Android.  But to avoid dependence on the
    // libFLAC internals, we check and log here.
    return false;
  }
  FLAC__stream_decoder_set_md5_checking(mDecoder, false);
  FLAC__stream_decoder_set_metadata_ignore_all(mDecoder);
  FLAC__stream_decoder_set_metadata_respond(mDecoder,
                                            FLAC__METADATA_TYPE_STREAMINFO);
  FLAC__stream_decoder_set_metadata_respond(mDecoder,
                                            FLAC__METADATA_TYPE_SEEKTABLE);
  FLAC__stream_decoder_set_metadata_respond(mDecoder,
                                            FLAC__METADATA_TYPE_VORBIS_COMMENT);
  FLAC__stream_decoder_set_metadata_respond(mDecoder,
                                            FLAC__METADATA_TYPE_PICTURE);
  FLAC__StreamDecoderInitStatus initStatus;
  initStatus = FLAC__stream_decoder_init_stream(
      mDecoder, read_callback, seek_callback, tell_callback, length_callback,
      eof_callback, write_callback, metadata_callback, error_callback,
      reinterpret_cast<void *>(this));
  if (initStatus != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
    // A failure here probably indicates a programming error and so is
    // unlikely to happen. But we check and log here similarly to above.
    return false;
  }
  return true;
}

bool FLACParser::decodeMetadata() {
  // parse all metadata
  if (!FLAC__stream_decoder_process_until_end_of_metadata(mDecoder)) {
    return false;
  }
  // store first frame offset
  FLAC__stream_decoder_get_decode_position(mDecoder, &firstFrameOffset);

  if (mStreamInfoValid) {
    // check channel count
    if (getChannels() == 0 || getChannels() > 8) {
      return false;
    }
    // check bit depth
    switch (getBitsPerSample()) {
      case 8:
      case 16:
      case 24:
      case 32:
        break;
      default:
        return false;
    }
    // configure the appropriate copy function based on device endianness.
    if (isBigEndian()) {
      mCopy = copyToByteArrayBigEndian;
    } else {
      mCopy = copyToByteArrayLittleEndian;
    }
  } else {
    return false;
  }
  return true;
}

size_t FLACParser::readBuffer(void *output, size_t output_size) {
  mWriteRequested = true;
  mWriteCompleted = false;

  if (!FLAC__stream_decoder_process_single(mDecoder)) {
    return -1;
  }
  if (!mWriteCompleted) {
    if (FLAC__stream_decoder_get_state(mDecoder) !=
        FLAC__STREAM_DECODER_END_OF_STREAM) {
    }
    return -1;
  }

  // verify that block header keeps the promises made by STREAMINFO
  unsigned blocksize = mWriteHeader.blocksize;
  if (blocksize == 0 || blocksize > getMaxBlockSize()) {
    return -1;
  }
  if (mWriteHeader.sample_rate != getSampleRate() ||
      mWriteHeader.channels != getChannels() ||
      mWriteHeader.bits_per_sample != getBitsPerSample()) {
    return -1;
  }

  unsigned bytesPerSample = getBitsPerSample() >> 3;
  size_t bufferSize = blocksize * getChannels() * bytesPerSample;
  if (bufferSize > output_size) {
    return -1;
  }

  // copy PCM from FLAC write buffer to our media buffer, with interleaving.
  (*mCopy)(reinterpret_cast<int8_t *>(output), mWriteBuffer, bytesPerSample,
           blocksize, getChannels());

  // fill in buffer metadata
  CHECK(mWriteHeader.number_type == FLAC__FRAME_NUMBER_TYPE_SAMPLE_NUMBER);

  return bufferSize;
}

bool FLACParser::getSeekPositions(int64_t timeUs,
                                  std::array<int64_t, 4> &result) {
  if (!mSeekTable) {
    return false;
  }

  unsigned sampleRate = getSampleRate();
  int64_t totalSamples = getTotalSamples();
  int64_t targetSampleNumber = (timeUs * sampleRate) / 1000000LL;
  if (targetSampleNumber >= totalSamples) {
    targetSampleNumber = totalSamples - 1;
  }

  FLAC__StreamMetadata_SeekPoint* points = mSeekTable->points;
  unsigned length = mSeekTable->num_points;

  for (unsigned i = length; i != 0; i--) {
    int64_t sampleNumber = points[i - 1].sample_number;
    if (sampleNumber == -1) {  // placeholder
      continue;
    }
    if (sampleNumber <= targetSampleNumber) {
      result[0] = (sampleNumber * 1000000LL) / sampleRate;
      result[1] = firstFrameOffset + points[i - 1].stream_offset;
      if (sampleNumber == targetSampleNumber || i >= length ||
          points[i].sample_number == -1) {  // placeholder
        // exact seek, or no following non-placeholder seek point
        result[2] = result[0];
        result[3] = result[1];
      } else {
        result[2] = (points[i].sample_number * 1000000LL) / sampleRate;
        result[3] = firstFrameOffset + points[i].stream_offset;
      }
      return true;
    }
  }
  result[0] = 0;
  result[1] = firstFrameOffset;
  result[2] = 0;
  result[3] = firstFrameOffset;
  return true;
}

namespace {

  class FuzzDataSource : public DataSource {
    const uint8_t *data_;
    size_t size_;

   public:
    FuzzDataSource(const uint8_t *data, size_t size) {
      data_ = data;
      size_ = size;
    }

    ssize_t readAt(off64_t offset, void *const data, size_t size) {
      if (offset > size_)
        return -1;
      size_t remaining = size_ - offset;
      if (remaining < size)
        size = remaining;
      memcpy(data, data_ + offset, size);
      return size;
    }
  };

}  // namespace

// Fuzz FLAC format and instrument the result as exoplayer JNI would:
// https://github.com/google/ExoPlayer/blob/release-v2/extensions/flac/src/main/jni/
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
  FuzzDataSource source(data, size);
  FLACParser parser(&source);

  // Early parsing
  if (!parser.init() || !parser.decodeMetadata())
    return 0;

  auto streamInfo = parser.getStreamInfo();

  // Similar implementation than ExoPlayer
  int buffer_size = streamInfo.max_blocksize * streamInfo.channels * 2;
  assert(buffer_size >= 0);  // Not expected
  auto buffer = new uint8_t[buffer_size];

  while (parser.readBuffer(buffer, buffer_size) < ((size_t)-1));
  delete[] buffer;

  return 0;
}