478 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			478 lines
		
	
	
		
			15 KiB
		
	
	
	
		
			C++
		
	
	
	
| // 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>
 | |
| 
 | |
| #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];
 | |
|   int runs = 0;
 | |
|   while (parser.readBuffer(buffer, buffer_size) >= buffer_size) {
 | |
|     runs++;
 | |
|     continue;
 | |
|   }
 | |
|   delete[] buffer;
 | |
| 
 | |
|   return 0;
 | |
| }
 |