Commit message: None
CVE: CVE-2016-2505
CWE: CWE-119
Vulnerability description: mpeg2ts/ATSParser.cpp in libstagefright in mediaserver in Android 6.x before 2016-07-01 does not validate a certain section length, which allows remote attackers to execute arbitrary code or cause a denial of service (memory corruption) via a crafted media file, aka internal bug 28333006.

Project: Android

ATSParser::Program::Program(
 ATSParser *parser, unsigned programNumber, unsigned programMapPID,
 int64_t lastRecoveredPTS)
 : mParser(parser),
      mProgramNumber(programNumber),
      mProgramMapPID(programMapPID),
      mFirstPTSValid(false),
      mFirstPTS(0),
      mLastRecoveredPTS(lastRecoveredPTS) {
    ALOGV("new program number %u", programNumber);
}

Project: Android

sp<MediaSource> ATSParser::Stream::getSource(SourceType type) {
 switch (type) {
 case VIDEO:
 {
 if (isVideo()) {
 return mSource;
 }
 break;
 }

 case AUDIO:
 {
 if (isAudio()) {
 return mSource;
 }
 break;
 }

 case META:
 {
 if (isMeta()) {
 return mSource;
 }
 break;
 }

 default:
 break;
 }

 return NULL;
}

Project: Android

bool ATSParser::Stream::isAudio() const {
 switch (mStreamType) {
 case STREAMTYPE_MPEG1_AUDIO:
 case STREAMTYPE_MPEG2_AUDIO:
 case STREAMTYPE_MPEG2_AUDIO_ADTS:
 case STREAMTYPE_LPCM_AC3:
 case STREAMTYPE_AC3:
 return true;

 default:
 return false;
 }
}

Project: Android

status_t ATSParser::Stream::parse(
 unsigned continuity_counter,
 unsigned payload_unit_start_indicator, ABitReader *br,
 SyncEvent *event) {
 if (mQueue == NULL) {
 return OK;
 }

 if (mExpectedContinuityCounter >= 0
 && (unsigned)mExpectedContinuityCounter != continuity_counter) {
        ALOGI("discontinuity on stream pid 0x%04x", mElementaryPID);

        mPayloadStarted = false;
        mBuffer->setRange(0, 0);
        mExpectedContinuityCounter = -1;

#if 0

 if (mStreamType == STREAMTYPE_H264) {
            ALOGI("clearing video queue");
            mQueue->clear(true /* clearFormat */);
 }
#endif

 if (!payload_unit_start_indicator) {
 return OK;
 }
 }

    mExpectedContinuityCounter = (continuity_counter + 1) & 0x0f;

 if (payload_unit_start_indicator) {
 off64_t offset = (event != NULL) ? event->getOffset() : 0;
 if (mPayloadStarted) {

 status_t err = flush(event);

 if (err != OK) {
                ALOGW("Error (%08x) happened while flushing; we simply discard "
 "the PES packet and continue.", err);
 }
 }

        mPayloadStarted = true;
        mPesStartOffset = offset;
 }

 if (!mPayloadStarted) {
 return OK;
 }

 size_t payloadSizeBits = br->numBitsLeft();
 if (payloadSizeBits % 8 != 0u) {
        ALOGE("Wrong value");
 return BAD_VALUE;
 }

 size_t neededSize = mBuffer->size() + payloadSizeBits / 8;
 if (mBuffer->capacity() < neededSize) {
        neededSize = (neededSize + 65535) & ~65535;

        ALOGI("resizing buffer to %zu bytes", neededSize);

        sp<ABuffer> newBuffer = new ABuffer(neededSize);
        memcpy(newBuffer->data(), mBuffer->data(), mBuffer->size());
        newBuffer->setRange(0, mBuffer->size());
        mBuffer = newBuffer;
 }

    memcpy(mBuffer->data() + mBuffer->size(), br->data(), payloadSizeBits / 8);
    mBuffer->setRange(0, mBuffer->size() + payloadSizeBits / 8);

 return OK;
}

Project: Android

status_t ATSParser::Stream::parsePES(ABitReader *br, SyncEvent *event) {
 unsigned packet_startcode_prefix = br->getBits(24);

    ALOGV("packet_startcode_prefix = 0x%08x", packet_startcode_prefix);

 if (packet_startcode_prefix != 1) {
        ALOGV("Supposedly payload_unit_start=1 unit does not start "
 "with startcode.");

 return ERROR_MALFORMED;
 }

 unsigned stream_id = br->getBits(8);
    ALOGV("stream_id = 0x%02x", stream_id);

 unsigned PES_packet_length = br->getBits(16);
    ALOGV("PES_packet_length = %u", PES_packet_length);

 if (stream_id != 0xbc // program_stream_map
 && stream_id != 0xbe // padding_stream
 && stream_id != 0xbf // private_stream_2
 && stream_id != 0xf0 // ECM
 && stream_id != 0xf1 // EMM
 && stream_id != 0xff // program_stream_directory
 && stream_id != 0xf2 // DSMCC
 && stream_id != 0xf8) { // H.222.1 type E
 if (br->getBits(2) != 2u) {
 return ERROR_MALFORMED;
 }

        MY_LOGV("PES_scrambling_control = %u", br->getBits(2));
        MY_LOGV("PES_priority = %u", br->getBits(1));
        MY_LOGV("data_alignment_indicator = %u", br->getBits(1));
        MY_LOGV("copyright = %u", br->getBits(1));
        MY_LOGV("original_or_copy = %u", br->getBits(1));

 unsigned PTS_DTS_flags = br->getBits(2);
        ALOGV("PTS_DTS_flags = %u", PTS_DTS_flags);

 unsigned ESCR_flag = br->getBits(1);
        ALOGV("ESCR_flag = %u", ESCR_flag);

 unsigned ES_rate_flag = br->getBits(1);
        ALOGV("ES_rate_flag = %u", ES_rate_flag);

 unsigned DSM_trick_mode_flag = br->getBits(1);
        ALOGV("DSM_trick_mode_flag = %u", DSM_trick_mode_flag);

 unsigned additional_copy_info_flag = br->getBits(1);
        ALOGV("additional_copy_info_flag = %u", additional_copy_info_flag);

        MY_LOGV("PES_CRC_flag = %u", br->getBits(1));
        MY_LOGV("PES_extension_flag = %u", br->getBits(1));

 unsigned PES_header_data_length = br->getBits(8);
        ALOGV("PES_header_data_length = %u", PES_header_data_length);

 unsigned optional_bytes_remaining = PES_header_data_length;

 uint64_t PTS = 0, DTS = 0;

 if (PTS_DTS_flags == 2 || PTS_DTS_flags == 3) {
 if (optional_bytes_remaining < 5u) {
 return ERROR_MALFORMED;
 }

 if (br->getBits(4) != PTS_DTS_flags) {
 return ERROR_MALFORMED;
 }
            PTS = ((uint64_t)br->getBits(3)) << 30;
 if (br->getBits(1) != 1u) {
 return ERROR_MALFORMED;
 }
            PTS |= ((uint64_t)br->getBits(15)) << 15;
 if (br->getBits(1) != 1u) {
 return ERROR_MALFORMED;
 }
            PTS |= br->getBits(15);
 if (br->getBits(1) != 1u) {
 return ERROR_MALFORMED;
 }

            ALOGV("PTS = 0x%016" PRIx64 " (%.2f)", PTS, PTS / 90000.0);

            optional_bytes_remaining -= 5;

 if (PTS_DTS_flags == 3) {
 if (optional_bytes_remaining < 5u) {
 return ERROR_MALFORMED;
 }

 if (br->getBits(4) != 1u) {
 return ERROR_MALFORMED;
 }

                DTS = ((uint64_t)br->getBits(3)) << 30;
 if (br->getBits(1) != 1u) {
 return ERROR_MALFORMED;
 }
                DTS |= ((uint64_t)br->getBits(15)) << 15;
 if (br->getBits(1) != 1u) {
 return ERROR_MALFORMED;
 }
                DTS |= br->getBits(15);
 if (br->getBits(1) != 1u) {
 return ERROR_MALFORMED;
 }

                ALOGV("DTS = %" PRIu64, DTS);

                optional_bytes_remaining -= 5;
 }
 }

 if (ESCR_flag) {
 if (optional_bytes_remaining < 6u) {
 return ERROR_MALFORMED;
 }

            br->getBits(2);

 uint64_t ESCR = ((uint64_t)br->getBits(3)) << 30;
 if (br->getBits(1) != 1u) {
 return ERROR_MALFORMED;
 }
            ESCR |= ((uint64_t)br->getBits(15)) << 15;
 if (br->getBits(1) != 1u) {
 return ERROR_MALFORMED;
 }
            ESCR |= br->getBits(15);
 if (br->getBits(1) != 1u) {
 return ERROR_MALFORMED;
 }

            ALOGV("ESCR = %" PRIu64, ESCR);
            MY_LOGV("ESCR_extension = %u", br->getBits(9));

 if (br->getBits(1) != 1u) {
 return ERROR_MALFORMED;
 }

            optional_bytes_remaining -= 6;
 }

 if (ES_rate_flag) {
 if (optional_bytes_remaining < 3u) {
 return ERROR_MALFORMED;
 }

 if (br->getBits(1) != 1u) {
 return ERROR_MALFORMED;
 }
            MY_LOGV("ES_rate = %u", br->getBits(22));
 if (br->getBits(1) != 1u) {
 return ERROR_MALFORMED;
 }

            optional_bytes_remaining -= 3;
 }

        br->skipBits(optional_bytes_remaining * 8);


 if (PES_packet_length != 0) {
 if (PES_packet_length < PES_header_data_length + 3) {
 return ERROR_MALFORMED;
 }

 unsigned dataLength =
                PES_packet_length - 3 - PES_header_data_length;

 if (br->numBitsLeft() < dataLength * 8) {
                ALOGE("PES packet does not carry enough data to contain "
 "payload. (numBitsLeft = %zu, required = %u)",
                     br->numBitsLeft(), dataLength * 8);

 return ERROR_MALFORMED;
 }

            onPayloadData(
                    PTS_DTS_flags, PTS, DTS, br->data(), dataLength, event);

            br->skipBits(dataLength * 8);
 } else {
            onPayloadData(
                    PTS_DTS_flags, PTS, DTS,
                    br->data(), br->numBitsLeft() / 8, event);

 size_t payloadSizeBits = br->numBitsLeft();
 if (payloadSizeBits % 8 != 0u) {
 return ERROR_MALFORMED;
 }

            ALOGV("There's %zu bytes of payload.", payloadSizeBits / 8);
 }
 } else if (stream_id == 0xbe) { // padding_stream
 if (PES_packet_length == 0u) {
 return ERROR_MALFORMED;
 }
        br->skipBits(PES_packet_length * 8);
 } else {
 if (PES_packet_length == 0u) {
 return ERROR_MALFORMED;
 }
        br->skipBits(PES_packet_length * 8);
 }

 return OK;
}

Project: Android

 uint32_t parserFlags() const {
 return mParser->mFlags;
 }

Project: Android

 void setPID(unsigned pid) { mElementaryPID = pid; }

Project: Android

void ATSParser::PSISection::setSkipBytes(uint8_t skip) {
    mSkipBytes = skip;
}

Project: Android

void ATSParser::signalDiscontinuity(
 DiscontinuityType type, const sp<AMessage> &extra) {
 int64_t mediaTimeUs;
 if ((type & DISCONTINUITY_TIME) && extra != NULL) {
 if (extra->findInt64(IStreamListener::kKeyMediaTimeUs, &mediaTimeUs)) {
            mAbsoluteTimeAnchorUs = mediaTimeUs;
 }
 if ((mFlags & TS_TIMESTAMPS_ARE_ABSOLUTE)
 && extra->findInt64(
 IStreamListener::kKeyRecentMediaTimeUs, &mediaTimeUs)) {
 if (mAbsoluteTimeAnchorUs >= 0ll) {
                mediaTimeUs -= mAbsoluteTimeAnchorUs;
 }
 if (mTimeOffsetValid) {
                mediaTimeUs -= mTimeOffsetUs;
 }
            mLastRecoveredPTS = (mediaTimeUs * 9) / 100;
 }
 } else if (type == DISCONTINUITY_ABSOLUTE_TIME) {
 int64_t timeUs;
 if (!extra->findInt64("timeUs", &timeUs)) {
            ALOGE("timeUs not found");
 return;
 }

 if (!mPrograms.empty()) {
            ALOGE("mPrograms is not empty");
 return;
 }
        mAbsoluteTimeAnchorUs = timeUs;
 return;
 } else if (type == DISCONTINUITY_TIME_OFFSET) {
 int64_t offset;
 if (!extra->findInt64("offset", &offset)) {
            ALOGE("offset not found");
 return;
 }

        mTimeOffsetValid = true;
        mTimeOffsetUs = offset;
 return;
 }

 for (size_t i = 0; i < mPrograms.size(); ++i) {
        mPrograms.editItemAt(i)->signalDiscontinuity(type, extra);
 }
}

Project: Android

void ATSParser::updatePCR(
 unsigned /* PID */, uint64_t PCR, size_t byteOffsetFromStart) {
    ALOGV("PCR 0x%016" PRIx64 " @ %zu", PCR, byteOffsetFromStart);

 if (mNumPCRs == 2) {
        mPCR[0] = mPCR[1];
        mPCRBytes[0] = mPCRBytes[1];
        mSystemTimeUs[0] = mSystemTimeUs[1];
        mNumPCRs = 1;
 }

    mPCR[mNumPCRs] = PCR;
    mPCRBytes[mNumPCRs] = byteOffsetFromStart;
    mSystemTimeUs[mNumPCRs] = ALooper::GetNowUs();

 ++mNumPCRs;

 if (mNumPCRs == 2) {
 double transportRate =
 (mPCRBytes[1] - mPCRBytes[0]) * 27E6 / (mPCR[1] - mPCR[0]);

        ALOGV("transportRate = %.2f bytes/sec", transportRate);
 }
}