Commit message: None
CVE: CVE-2017-0813
CWE: CWE-772
Vulnerability description: A denial of service vulnerability in the Android media framework (libstagefright). Product: Android. Versions: 7.0, 7.1.1, 7.1.2. Android ID: A-36531046.

Project: Android

SampleTable::SampleTable(const sp<DataSource> &source)
 : mDataSource(source),
      mChunkOffsetOffset(-1),
      mChunkOffsetType(0),
      mNumChunkOffsets(0),
      mSampleToChunkOffset(-1),
      mNumSampleToChunkOffsets(0),
      mSampleSizeOffset(-1),
      mSampleSizeFieldSize(0),
      mDefaultSampleSize(0),
      mNumSampleSizes(0),
      mHasTimeToSample(false),
      mTimeToSampleCount(0),
      mTimeToSample(NULL),
      mSampleTimeEntries(NULL),
      mCompositionTimeDeltaEntries(NULL),
      mNumCompositionTimeDeltaEntries(0),
      mCompositionDeltaLookup(new CompositionDeltaLookup),
      mSyncSampleOffset(-1),
      mNumSyncSamples(0),
      mSyncSamples(NULL),
      mLastSyncSampleIndex(0),
      mSampleToChunkEntries(NULL),
      mTotalSize(0) {
    mSampleIterator = new SampleIterator(this);
}

Project: Android

int32_t SampleTable::CompositionDeltaLookup::getCompositionTimeOffset(
 uint32_t sampleIndex) {
 Mutex::Autolock autolock(mLock);

 if (mDeltaEntries == NULL) {
 return 0;
 }

 if (sampleIndex < mCurrentEntrySampleIndex) {
        mCurrentDeltaEntry = 0;
        mCurrentEntrySampleIndex = 0;
 }

 while (mCurrentDeltaEntry < mNumDeltaEntries) {
 uint32_t sampleCount = mDeltaEntries[2 * mCurrentDeltaEntry];
 if (sampleIndex < mCurrentEntrySampleIndex + sampleCount) {
 return mDeltaEntries[2 * mCurrentDeltaEntry + 1];
 }

        mCurrentEntrySampleIndex += sampleCount;
 ++mCurrentDeltaEntry;
 }

 return 0;
}

Project: Android

int32_t SampleTable::getCompositionTimeOffset(uint32_t sampleIndex) {
 return mCompositionDeltaLookup->getCompositionTimeOffset(sampleIndex);

 }

Project: Android

bool SampleTable::isValid() const {
 return mChunkOffsetOffset >= 0
 && mSampleToChunkOffset >= 0
 && mSampleSizeOffset >= 0
 && mHasTimeToSample;
}

Project: Android

status_t SampleTable::setChunkOffsetParams(
 uint32_t type, off64_t data_offset, size_t data_size) {
 if (mChunkOffsetOffset >= 0) {
 return ERROR_MALFORMED;
 }

    CHECK(type == kChunkOffsetType32 || type == kChunkOffsetType64);

    mChunkOffsetOffset = data_offset;
    mChunkOffsetType = type;

 if (data_size < 8) {
 return ERROR_MALFORMED;
 }

 uint8_t header[8];
 if (mDataSource->readAt(
                data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
 return ERROR_IO;
 }

 if (U32_AT(header) != 0) {
 return ERROR_MALFORMED;
 }

    mNumChunkOffsets = U32_AT(&header[4]);

 if (mChunkOffsetType == kChunkOffsetType32) {
 if ((data_size - 8) / 4 < mNumChunkOffsets) {
 return ERROR_MALFORMED;
 }
 } else {
 if ((data_size - 8) / 8 < mNumChunkOffsets) {
 return ERROR_MALFORMED;
 }
 }

 return OK;
}

Project: Android

status_t SampleTable::setCompositionTimeToSampleParams(
 off64_t data_offset, size_t data_size) {
    ALOGI("There are reordered frames present.");

 if (mCompositionTimeDeltaEntries != NULL || data_size < 8) {
 return ERROR_MALFORMED;
 }

 uint8_t header[8];
 if (mDataSource->readAt(
                data_offset, header, sizeof(header))
 < (ssize_t)sizeof(header)) {
 return ERROR_IO;
 }

 uint32_t flags = U32_AT(header);
 uint32_t version = flags >> 24;
    flags &= 0xffffff;

 if ((version != 0 && version != 1) || flags != 0) {
 return ERROR_MALFORMED;
 }

 size_t numEntries = U32_AT(&header[4]);

 if (((SIZE_MAX / 8) - 1 < numEntries) || (data_size != (numEntries + 1) * 8)) {
 return ERROR_MALFORMED;
 }

    mNumCompositionTimeDeltaEntries = numEntries;
 uint64_t allocSize = (uint64_t)numEntries * 2 * sizeof(int32_t);
 if (allocSize > kMaxTotalSize) {
        ALOGE("Composition-time-to-sample table size too large.");
 return ERROR_OUT_OF_RANGE;
 }

    mTotalSize += allocSize;
 if (mTotalSize > kMaxTotalSize) {
        ALOGE("Composition-time-to-sample table would make sample table too large.\n"
 "    Requested composition-time-to-sample table size = %llu\n"
 "    Eventual sample table size >= %llu\n"
 "    Allowed sample table size = %llu\n",
 (unsigned long long)allocSize,
 (unsigned long long)mTotalSize,
 (unsigned long long)kMaxTotalSize);
 return ERROR_OUT_OF_RANGE;
 }

    mCompositionTimeDeltaEntries = new (std::nothrow) int32_t[2 * numEntries];
 if (!mCompositionTimeDeltaEntries) {
        ALOGE("Cannot allocate composition-time-to-sample table with %llu "
 "entries.", (unsigned long long)numEntries);
 return ERROR_OUT_OF_RANGE;
 }

 if (mDataSource->readAt(data_offset + 8, mCompositionTimeDeltaEntries,
 (size_t)allocSize) < (ssize_t)allocSize) {
 delete[] mCompositionTimeDeltaEntries;
        mCompositionTimeDeltaEntries = NULL;

 return ERROR_IO;
 }

 for (size_t i = 0; i < 2 * numEntries; ++i) {
        mCompositionTimeDeltaEntries[i] = ntohl(mCompositionTimeDeltaEntries[i]);
 }

    mCompositionDeltaLookup->setEntries(
            mCompositionTimeDeltaEntries, mNumCompositionTimeDeltaEntries);

 return OK;
}

Project: Android

void SampleTable::CompositionDeltaLookup::setEntries(
 const int32_t *deltaEntries, size_t numDeltaEntries) {
 Mutex::Autolock autolock(mLock);

    mDeltaEntries = deltaEntries;
    mNumDeltaEntries = numDeltaEntries;
    mCurrentDeltaEntry = 0;
    mCurrentEntrySampleIndex = 0;
}

Project: Android

status_t SampleTable::setSampleSizeParams(
 uint32_t type, off64_t data_offset, size_t data_size) {
 if (mSampleSizeOffset >= 0) {
 return ERROR_MALFORMED;
 }

    CHECK(type == kSampleSizeType32 || type == kSampleSizeTypeCompact);

    mSampleSizeOffset = data_offset;

 if (data_size < 12) {
 return ERROR_MALFORMED;
 }

 uint8_t header[12];
 if (mDataSource->readAt(
                data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
 return ERROR_IO;
 }

 if (U32_AT(header) != 0) {
 return ERROR_MALFORMED;
 }

    mDefaultSampleSize = U32_AT(&header[4]);
    mNumSampleSizes = U32_AT(&header[8]);
 if (mNumSampleSizes > (UINT32_MAX - 12) / 16) {
        ALOGE("b/23247055, mNumSampleSizes(%u)", mNumSampleSizes);
 return ERROR_MALFORMED;
 }

 if (type == kSampleSizeType32) {
        mSampleSizeFieldSize = 32;

 if (mDefaultSampleSize != 0) {
 return OK;
 }

 if (data_size < 12 + mNumSampleSizes * 4) {
 return ERROR_MALFORMED;
 }
 } else {
 if ((mDefaultSampleSize & 0xffffff00) != 0) {
 return ERROR_MALFORMED;
 }

        mSampleSizeFieldSize = mDefaultSampleSize & 0xff;
        mDefaultSampleSize = 0;

 if (mSampleSizeFieldSize != 4 && mSampleSizeFieldSize != 8
 && mSampleSizeFieldSize != 16) {
 return ERROR_MALFORMED;
 }

 if (data_size < 12 + (mNumSampleSizes * mSampleSizeFieldSize + 4) / 8) {
 return ERROR_MALFORMED;
 }
 }

 return OK;
}

Project: Android

status_t SampleTable::setSampleToChunkParams(
 off64_t data_offset, size_t data_size) {
 if (mSampleToChunkOffset >= 0) {
 return ERROR_MALFORMED;
 }

    mSampleToChunkOffset = data_offset;

 if (data_size < 8) {
 return ERROR_MALFORMED;
 }

 uint8_t header[8];
 if (mDataSource->readAt(
                data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
 return ERROR_IO;
 }

 if (U32_AT(header) != 0) {
 return ERROR_MALFORMED;
 }

    mNumSampleToChunkOffsets = U32_AT(&header[4]);

 if ((data_size - 8) / sizeof(SampleToChunkEntry) < mNumSampleToChunkOffsets) {
 return ERROR_MALFORMED;
 }

 if ((uint64_t)kMaxTotalSize / sizeof(SampleToChunkEntry) <=
 (uint64_t)mNumSampleToChunkOffsets) {
        ALOGE("Sample-to-chunk table size too large.");
 return ERROR_OUT_OF_RANGE;
 }

    mTotalSize += (uint64_t)mNumSampleToChunkOffsets *
 sizeof(SampleToChunkEntry);
 if (mTotalSize > kMaxTotalSize) {
        ALOGE("Sample-to-chunk table size would make sample table too large.\n"
 "    Requested sample-to-chunk table size = %llu\n"
 "    Eventual sample table size >= %llu\n"
 "    Allowed sample table size = %llu\n",
 (unsigned long long)mNumSampleToChunkOffsets *
 sizeof(SampleToChunkEntry),
 (unsigned long long)mTotalSize,
 (unsigned long long)kMaxTotalSize);
 return ERROR_OUT_OF_RANGE;
 }

    mSampleToChunkEntries =
 new (std::nothrow) SampleToChunkEntry[mNumSampleToChunkOffsets];
 if (!mSampleToChunkEntries) {
        ALOGE("Cannot allocate sample-to-chunk table with %llu entries.",
 (unsigned long long)mNumSampleToChunkOffsets);
 return ERROR_OUT_OF_RANGE;
 }

 if (mNumSampleToChunkOffsets == 0) {
 return OK;
 }

 if ((off64_t)(kMaxOffset - 8 -
 ((mNumSampleToChunkOffsets - 1) * sizeof(SampleToChunkEntry)))
 < mSampleToChunkOffset) {
 return ERROR_MALFORMED;
 }

 for (uint32_t i = 0; i < mNumSampleToChunkOffsets; ++i) {
 uint8_t buffer[sizeof(SampleToChunkEntry)];

 if (mDataSource->readAt(
                    mSampleToChunkOffset + 8 + i * sizeof(SampleToChunkEntry),
                    buffer,
 sizeof(buffer))
 != (ssize_t)sizeof(buffer)) {
 return ERROR_IO;
 }
 if (U32_AT(buffer) < 1) {
            ALOGE("b/23534160");
 return ERROR_OUT_OF_RANGE;
 }

        mSampleToChunkEntries[i].startChunk = U32_AT(buffer) - 1;
        mSampleToChunkEntries[i].samplesPerChunk = U32_AT(&buffer[4]);
        mSampleToChunkEntries[i].chunkDesc = U32_AT(&buffer[8]);
 }

 return OK;
}

Project: Android

SampleTable::~SampleTable() {
 delete[] mSampleToChunkEntries;
    mSampleToChunkEntries = NULL;

 delete[] mSyncSamples;
    mSyncSamples = NULL;

 delete[] mTimeToSample;
    mTimeToSample = NULL;

 delete mCompositionDeltaLookup;
    mCompositionDeltaLookup = NULL;

 delete[] mCompositionTimeDeltaEntries;
    mCompositionTimeDeltaEntries = NULL;

 delete[] mSampleTimeEntries;
    mSampleTimeEntries = NULL;

 delete mSampleIterator;
    mSampleIterator = NULL;
}