Commit message: None
CVE: CVE-2017-0818
CWE: CWE-772
Vulnerability description: A vulnerability in the Android media framework (n/a). Product: Android. Versions: 7.0, 7.1.1, 7.1.2, 8.0. Android ID: A-63581671.

Project: Android

 explicit MyOpusExtractor(const sp<DataSource> &source)
 : MyOggExtractor(source, MEDIA_MIMETYPE_AUDIO_OPUS, /*numHeaders*/ 2, kOpusSeekPreRollUs),
          mChannelCount(0),
          mCodecDelay(0),
          mStartGranulePosition(-1) {
 }

Project: Android

status_t MyOggExtractor::_readNextPacket(MediaBuffer **out, bool calcVorbisTimestamp) {
 *out = NULL;

 MediaBuffer *buffer = NULL;
 int64_t timeUs = -1;

 for (;;) {
 size_t i;
 size_t packetSize = 0;
 bool gotFullPacket = false;
 for (i = mNextLaceIndex; i < mCurrentPage.mNumSegments; ++i) {
 uint8_t lace = mCurrentPage.mLace[i];

            packetSize += lace;

 if (lace < 255) {
                gotFullPacket = true;
 ++i;
 break;
 }
 }

 if (mNextLaceIndex < mCurrentPage.mNumSegments) {
 off64_t dataOffset = mOffset + 27 + mCurrentPage.mNumSegments;
 for (size_t j = 0; j < mNextLaceIndex; ++j) {
                dataOffset += mCurrentPage.mLace[j];
 }

 size_t fullSize = packetSize;
 if (buffer != NULL) {
                fullSize += buffer->range_length();
 }
 if (fullSize > 16 * 1024 * 1024) { // arbitrary limit of 16 MB packet size
 if (buffer != NULL) {
                    buffer->release();
 }
                ALOGE("b/36592202");
 return ERROR_MALFORMED;
 }
 MediaBuffer *tmp = new (std::nothrow) MediaBuffer(fullSize);
 if (tmp == NULL) {
 if (buffer != NULL) {
                    buffer->release();
 }
                ALOGE("b/36592202");
 return ERROR_MALFORMED;
 }
 if (buffer != NULL) {
                memcpy(tmp->data(), buffer->data(), buffer->range_length());
                tmp->set_range(0, buffer->range_length());
                buffer->release();
 } else {
                tmp->set_range(0, 0);
 }
            buffer = tmp;

 ssize_t n = mSource->readAt(
                    dataOffset,
 (uint8_t *)buffer->data() + buffer->range_length(),
                    packetSize);

 if (n < (ssize_t)packetSize) {
                buffer->release();
                ALOGV("failed to read %zu bytes at %#016llx, got %zd bytes",
                        packetSize, (long long)dataOffset, n);
 return ERROR_IO;
 }

            buffer->set_range(0, fullSize);

            mNextLaceIndex = i;

 if (gotFullPacket) {

 if (mFirstPacketInPage) {
                    buffer->meta_data()->setInt32(
                            kKeyValidSamples, mCurrentPageSamples);
                    mFirstPacketInPage = false;
 }

 if (calcVorbisTimestamp) {
 int32_t curBlockSize = getPacketBlockSize(buffer);
 if (mCurrentPage.mPrevPacketSize < 0) {
                        mCurrentPage.mPrevPacketSize = curBlockSize;
                        mCurrentPage.mPrevPacketPos =
                                mCurrentPage.mGranulePosition - mCurrentPageSamples;
                        timeUs = mCurrentPage.mPrevPacketPos * 1000000ll / mVi.rate;
 } else {
 int32_t actualBlockSize =
 (curBlockSize + mCurrentPage.mPrevPacketSize) / 2;
                        timeUs = mCurrentPage.mPrevPacketPos * 1000000ll / mVi.rate;
                        mCurrentPage.mPrevPacketPos += actualBlockSize / 2;
                        mCurrentPage.mPrevPacketSize = curBlockSize;
 }
                    buffer->meta_data()->setInt64(kKeyTime, timeUs);
 }
 *out = buffer;

 return OK;
 }

 }

        CHECK_EQ(mNextLaceIndex, mCurrentPage.mNumSegments);

        mOffset += mCurrentPageSize;
 ssize_t n = readPage(mOffset, &mCurrentPage);

 if (n <= 0) {
 if (buffer) {
                buffer->release();
                buffer = NULL;
 }

            ALOGV("readPage returned %zd", n);

 return n < 0 ? n : (status_t)ERROR_END_OF_STREAM;
 }

 if (mCurrentPage.mGranulePosition >= mPrevGranulePosition) {
            mCurrentPageSamples =
                    mCurrentPage.mGranulePosition - mPrevGranulePosition;
 } else {
            mCurrentPageSamples = 0;
 }
        mFirstPacketInPage = true;

        mPrevGranulePosition = mCurrentPage.mGranulePosition;

        mCurrentPageSize = n;
        mNextLaceIndex = 0;

 if (buffer != NULL) {
 if ((mCurrentPage.mFlags & 1) == 0) {

 if (timeUs >= 0) {
                    buffer->meta_data()->setInt64(kKeyTime, timeUs);
 }

                buffer->meta_data()->setInt32(
                        kKeyValidSamples, mCurrentPageSamples);
                mFirstPacketInPage = false;

 *out = buffer;

 return OK;
 }
 }
 }
}

Project: Android

static void extractAlbumArt(
 const sp<MetaData> &fileMeta, const void *data, size_t size) {
    ALOGV("extractAlbumArt from '%s'", (const char *)data);

    sp<ABuffer> flacBuffer = decodeBase64(AString((const char *)data, size));
 if (flacBuffer == NULL) {
        ALOGE("malformed base64 encoded data.");
 return;
 }

 size_t flacSize = flacBuffer->size();
 uint8_t *flac = flacBuffer->data();
    ALOGV("got flac of size %zu", flacSize);

 uint32_t picType;
 uint32_t typeLen;
 uint32_t descLen;
 uint32_t dataLen;
 char type[128];

 if (flacSize < 8) {
 return;
 }

    picType = U32_AT(flac);

 if (picType != 3) {
 return;
 }

    typeLen = U32_AT(&flac[4]);
 if (typeLen > sizeof(type) - 1) {
 return;
 }

 if (flacSize - 8 < typeLen) {
 return;
 }

    memcpy(type, &flac[8], typeLen);
    type[typeLen] = '\0';

    ALOGV("picType = %d, type = '%s'", picType, type);

 if (!strcmp(type, "-->")) {
 return;
 }

 if (flacSize < 32 || flacSize - 32 < typeLen) {
 return;
 }

    descLen = U32_AT(&flac[8 + typeLen]);
 if (flacSize - 32 - typeLen < descLen) {
 return;
 }

    dataLen = U32_AT(&flac[8 + typeLen + 4 + descLen + 16]);

 if (flacSize - 32 - typeLen - descLen < dataLen) {
 return;
 }

    ALOGV("got image data, %zu trailing bytes",
         flacSize - 32 - typeLen - descLen - dataLen);

    fileMeta->setData(
            kKeyAlbumArt, 0, &flac[8 + typeLen + 4 + descLen + 20], dataLen);

    fileMeta->setCString(kKeyAlbumArtMIME, type);
}

Project: Android

uint32_t MyOpusExtractor::getNumSamplesInPacket(MediaBuffer *buffer) const {
 if (buffer == NULL || buffer->range_length() < 1) {
 return 0;
 }

 uint8_t *data = (uint8_t *)buffer->data() + buffer->range_offset();
 uint8_t toc = data[0];
 uint8_t config = (toc >> 3) & 0x1f;
 uint32_t frameSizesUs[] = {
 10000, 20000, 40000, 60000, // 0...3
 10000, 20000, 40000, 60000, // 4...7
 10000, 20000, 40000, 60000, // 8...11
 10000, 20000, // 12...13
 10000, 20000, // 14...15
 2500, 5000, 10000, 20000, // 16...19
 2500, 5000, 10000, 20000, // 20...23
 2500, 5000, 10000, 20000, // 24...27
 2500, 5000, 10000, 20000 // 28...31
 };
 uint32_t frameSizeUs = frameSizesUs[config];

 uint32_t numFrames;
 uint8_t c = toc & 3;
 switch (c) {
 case 0:
        numFrames = 1;
 break;
 case 1:
 case 2:
        numFrames = 2;
 break;
 case 3:
 if (buffer->range_length() < 3) {
            numFrames = 0;
 } else {
            numFrames = data[2] & 0x3f;
 }
 break;
 default:
        TRESPASS();
 }

 uint32_t numSamples = frameSizeUs * numFrames * kOpusSampleRate / 1000000;
 return numSamples;
}

Project: Android

status_t MyVorbisExtractor::verifyHeader(
 MediaBuffer *buffer, uint8_t type) {
 const uint8_t *data =
 (const uint8_t *)buffer->data() + buffer->range_offset();

 size_t size = buffer->range_length();

 if (size < 7 || data[0] != type || memcmp(&data[1], "vorbis", 6)) {
 return ERROR_MALFORMED;
 }

    ogg_buffer buf;
    buf.data = (uint8_t *)data;
    buf.size = size;
    buf.refcount = 1;
    buf.ptr.owner = NULL;

    ogg_reference ref;
    ref.buffer = &buf;
    ref.begin = 0;
    ref.length = size;
    ref.next = NULL;

    oggpack_buffer bits;
    oggpack_readinit(&bits, &ref);

 if (oggpack_read(&bits, 8) != type) {
 return ERROR_MALFORMED;
 }
 for (size_t i = 0; i < 6; ++i) {
        oggpack_read(&bits, 8); // skip 'vorbis'
 }

 switch (type) {
 case 1:
 {
 if (0 != _vorbis_unpack_info(&mVi, &bits)) {
 return ERROR_MALFORMED;
 }

            mMeta->setData(kKeyVorbisInfo, 0, data, size);
            mMeta->setInt32(kKeySampleRate, mVi.rate);
            mMeta->setInt32(kKeyChannelCount, mVi.channels);

            ALOGV("lower-bitrate = %ld", mVi.bitrate_lower);
            ALOGV("upper-bitrate = %ld", mVi.bitrate_upper);
            ALOGV("nominal-bitrate = %ld", mVi.bitrate_nominal);
            ALOGV("window-bitrate = %ld", mVi.bitrate_window);
            ALOGV("blocksizes: %d/%d",
                    vorbis_info_blocksize(&mVi, 0),
                    vorbis_info_blocksize(&mVi, 1)
 );

 off64_t size;
 if (mSource->getSize(&size) == OK) {
 uint64_t bps = approxBitrate();
 if (bps != 0) {
                    mMeta->setInt64(kKeyDuration, size * 8000000ll / bps);
 }
 }
 break;
 }

 case 3:
 {
 if (0 != _vorbis_unpack_comment(&mVc, &bits)) {
 return ERROR_MALFORMED;
 }

            parseFileMetaData();
 break;
 }

 case 5:
 {
 if (0 != _vorbis_unpack_books(&mVi, &bits)) {
 return ERROR_MALFORMED;
 }

            mMeta->setData(kKeyVorbisBooks, 0, data, size);
 break;
 }
 }

 return OK;
}

Project: Android

status_t MyOpusExtractor::verifyOpusComments(MediaBuffer *buffer) {
 int32_t commentSize = buffer->range_length() + 1;
    sp<ABuffer> aBuf = new ABuffer(commentSize);
 if (aBuf->capacity() <= buffer->range_length()) {
 return ERROR_MALFORMED;
 }

 uint8_t* commentData = aBuf->data();
    memcpy(commentData,
 (uint8_t *)buffer->data() + buffer->range_offset(),
            buffer->range_length());

    ogg_buffer buf;
    buf.data = commentData;
    buf.size = commentSize;
    buf.refcount = 1;
    buf.ptr.owner = NULL;

    ogg_reference ref;
    ref.buffer = &buf;
    ref.begin = 0;
    ref.length = commentSize;
    ref.next = NULL;

    oggpack_buffer bits;
    oggpack_readinit(&bits, &ref);

 const char *OpusTags = "OpusTags";
 const int32_t headerLen = strlen(OpusTags);
 int32_t framingBitOffset = headerLen;
 for (int i = 0; i < headerLen; ++i) {
 char chr = oggpack_read(&bits, 8);
 if (chr != OpusTags[i]) {
 return ERROR_MALFORMED;
 }
 }

 int32_t vendorLen = oggpack_read(&bits, 32);
    framingBitOffset += 4;
 if (vendorLen < 0 || vendorLen > commentSize - 8) {
 return ERROR_MALFORMED;
 }
    framingBitOffset += vendorLen;
 for (int i = 0; i < vendorLen; ++i) {
        oggpack_read(&bits, 8);
 }

 int32_t n = oggpack_read(&bits, 32);
    framingBitOffset += 4;
 if (n < 0 || n > ((commentSize - oggpack_bytes(&bits)) >> 2)) {
 return ERROR_MALFORMED;
 }
 for (int i = 0; i < n; ++i) {
 int32_t len = oggpack_read(&bits, 32);
        framingBitOffset += 4;
 if (len  < 0 || len  > (commentSize - oggpack_bytes(&bits))) {
 return ERROR_MALFORMED;
 }
        framingBitOffset += len;
 for (int j = 0; j < len; ++j) {
            oggpack_read(&bits, 8);
 }
 }
 if (framingBitOffset < 0 || framingBitOffset >= commentSize) {
 return ERROR_MALFORMED;
 }
    commentData[framingBitOffset] = 1;

    buf.data = commentData + headerLen;
    buf.size = commentSize - headerLen;
    buf.refcount = 1;
    buf.ptr.owner = NULL;

    ref.buffer = &buf;
    ref.begin = 0;
    ref.length = commentSize - headerLen;
    ref.next = NULL;

    oggpack_readinit(&bits, &ref);
 int err = _vorbis_unpack_comment(&mVc, &bits);
 if (0 != err) {
 return ERROR_MALFORMED;
 }

    parseFileMetaData();
 return OK;
}