Commit message: None
CVE: CVE-2015-3864
CWE: CWE-189
Vulnerability description: Integer underflow in the MPEG4Extractor::parseChunk function in MPEG4Extractor.cpp in libstagefright in mediaserver in Android before 5.1.1 LMY48M allows remote attackers to execute arbitrary code via crafted MPEG-4 data, aka internal bug 23034759.  NOTE: this vulnerability exists because of an incomplete fix for CVE-2015-3824.

Project: Android

static bool AdjustChannelsAndRate(uint32_t fourcc, uint32_t *channels, uint32_t *rate) {
 if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_NB, FourCC2MIME(fourcc))) {
 *channels = 1;
 *rate = 8000;
 return true;
 } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, FourCC2MIME(fourcc))) {
 *channels = 1;
 *rate = 16000;
 return true;
 }
 return false;
}

Project: Android

static const char *FourCC2MIME(uint32_t fourcc) {
 switch (fourcc) {
 case FOURCC('m', 'p', '4', 'a'):
 return MEDIA_MIMETYPE_AUDIO_AAC;

 case FOURCC('s', 'a', 'm', 'r'):
 return MEDIA_MIMETYPE_AUDIO_AMR_NB;

 case FOURCC('s', 'a', 'w', 'b'):
 return MEDIA_MIMETYPE_AUDIO_AMR_WB;

 case FOURCC('m', 'p', '4', 'v'):
 return MEDIA_MIMETYPE_VIDEO_MPEG4;

 case FOURCC('s', '2', '6', '3'):
 case FOURCC('h', '2', '6', '3'):
 case FOURCC('H', '2', '6', '3'):
 return MEDIA_MIMETYPE_VIDEO_H263;

 case FOURCC('a', 'v', 'c', '1'):
 return MEDIA_MIMETYPE_VIDEO_AVC;

 case FOURCC('h', 'v', 'c', '1'):
 case FOURCC('h', 'e', 'v', '1'):
 return MEDIA_MIMETYPE_VIDEO_HEVC;
 default:
            CHECK(!"should not be here.");
 return NULL;
 }
}

Project: Android

void MPEG4DataSource::clearCache() {
 if (mCache) {
        free(mCache);
        mCache = NULL;
 }

    mCachedOffset = 0;
    mCachedSize = 0;
}

Project: Android

char* MPEG4Extractor::getDrmTrackInfo(size_t trackID, int *len) {
 if (mFirstSINF == NULL) {
 return NULL;
 }

    SINF *sinf = mFirstSINF;
 while (sinf && (trackID != sinf->trackID)) {
        sinf = sinf->next;
 }

 if (sinf == NULL) {
 return NULL;
 }

 *len = sinf->len;
 return sinf->IPMPData;
}

Project: Android

void MPEG4Extractor::parseID3v2MetaData(off64_t offset) {
    ID3 id3(mDataSource, true /* ignorev1 */, offset);

 if (id3.isValid()) {
 struct Map {
 int key;
 const char *tag1;
 const char *tag2;
 };
 static const Map kMap[] = {
 { kKeyAlbum, "TALB", "TAL" },
 { kKeyArtist, "TPE1", "TP1" },
 { kKeyAlbumArtist, "TPE2", "TP2" },
 { kKeyComposer, "TCOM", "TCM" },
 { kKeyGenre, "TCON", "TCO" },
 { kKeyTitle, "TIT2", "TT2" },
 { kKeyYear, "TYE", "TYER" },
 { kKeyAuthor, "TXT", "TEXT" },
 { kKeyCDTrackNumber, "TRK", "TRCK" },
 { kKeyDiscNumber, "TPA", "TPOS" },
 { kKeyCompilation, "TCP", "TCMP" },
 };
 static const size_t kNumMapEntries = sizeof(kMap) / sizeof(kMap[0]);

 for (size_t i = 0; i < kNumMapEntries; ++i) {
 if (!mFileMetaData->hasData(kMap[i].key)) {
                ID3::Iterator *it = new ID3::Iterator(id3, kMap[i].tag1);
 if (it->done()) {
 delete it;
                    it = new ID3::Iterator(id3, kMap[i].tag2);
 }

 if (it->done()) {
 delete it;
 continue;
 }

 String8 s;
                it->getString(&s);
 delete it;

                mFileMetaData->setCString(kMap[i].key, s);
 }
 }

 size_t dataSize;
 String8 mime;
 const void *data = id3.getAlbumArt(&dataSize, &mime);

 if (data) {
            mFileMetaData->setData(kKeyAlbumArt, MetaData::TYPE_NONE, data, dataSize);
            mFileMetaData->setCString(kKeyAlbumArtMIME, mime.string());
 }
 }
}

Project: Android

status_t MPEG4Extractor::updateAudioTrackInfoFromESDS_MPEG4Audio(
 const void *esds_data, size_t esds_size) {
    ESDS esds(esds_data, esds_size);

 uint8_t objectTypeIndication;
 if (esds.getObjectTypeIndication(&objectTypeIndication) != OK) {
 return ERROR_MALFORMED;
 }

 if (objectTypeIndication == 0xe1) {
        mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_QCELP);
 return OK;
 }

 if (objectTypeIndication  == 0x6b) {
        ALOGE("MP3 track in MP4/3GPP file is not supported");
 return ERROR_UNSUPPORTED;
 }

 const uint8_t *csd;
 size_t csd_size;
 if (esds.getCodecSpecificInfo(
 (const void **)&csd, &csd_size) != OK) {
 return ERROR_MALFORMED;
 }

#if 0
    printf("ESD of size %d\n", csd_size);
    hexdump(csd, csd_size);
#endif

 if (csd_size == 0) {

 return OK;
 }

 if (csd_size < 2) {
 return ERROR_MALFORMED;
 }

 static uint32_t kSamplingRate[] = {
 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
 16000, 12000, 11025, 8000, 7350
 };

 ABitReader br(csd, csd_size);
 uint32_t objectType = br.getBits(5);

 if (objectType == 31) { // AAC-ELD => additional 6 bits
        objectType = 32 + br.getBits(6);
 }

    mLastTrack->meta->setInt32(kKeyAACAOT, objectType);

 uint32_t freqIndex = br.getBits(4);

 int32_t sampleRate = 0;
 int32_t numChannels = 0;
 if (freqIndex == 15) {
 if (csd_size < 5) {
 return ERROR_MALFORMED;
 }
        sampleRate = br.getBits(24);
        numChannels = br.getBits(4);
 } else {
        numChannels = br.getBits(4);

 if (freqIndex == 13 || freqIndex == 14) {
 return ERROR_MALFORMED;
 }

        sampleRate = kSamplingRate[freqIndex];
 }

 if (objectType == AOT_SBR || objectType == AOT_PS) {//SBR specific config per 14496-3 table 1.13
 uint32_t extFreqIndex = br.getBits(4);
 int32_t extSampleRate;
 if (extFreqIndex == 15) {
 if (csd_size < 8) {
 return ERROR_MALFORMED;
 }
            extSampleRate = br.getBits(24);
 } else {
 if (extFreqIndex == 13 || extFreqIndex == 14) {
 return ERROR_MALFORMED;
 }
            extSampleRate = kSamplingRate[extFreqIndex];
 }
 }

 switch (numChannels) {
 case 0:
 case 1:// FC
 case 2:// FL FR
 case 3:// FC, FL FR
 case 4:// FC, FL FR, RC
 case 5:// FC, FL FR, SL SR
 case 6:// FC, FL FR, SL SR, LFE
 break;
 case 11:// FC, FL FR, SL SR, RC, LFE
            numChannels = 7;
 break;
 case 7: // FC, FCL FCR, FL FR, SL SR, LFE
 case 12:// FC, FL  FR,  SL SR, RL RR, LFE
 case 14:// FC, FL  FR,  SL SR, LFE, FHL FHR
            numChannels = 8;
 break;
 default:
 return ERROR_UNSUPPORTED;
 }

 {
 if (objectType == AOT_SBR || objectType == AOT_PS) {
            objectType = br.getBits(5);

 if (objectType == AOT_ESCAPE) {
                objectType = 32 + br.getBits(6);
 }
 }
 if (objectType == AOT_AAC_LC || objectType == AOT_ER_AAC_LC ||
                objectType == AOT_ER_AAC_LD || objectType == AOT_ER_AAC_SCAL ||
                objectType == AOT_ER_BSAC) {
 const int32_t frameLengthFlag = br.getBits(1);

 const int32_t dependsOnCoreCoder = br.getBits(1);

 if (dependsOnCoreCoder ) {
 const int32_t coreCoderDelay = br.getBits(14);
 }

 int32_t extensionFlag = -1;
 if (br.numBitsLeft() > 0) {
                extensionFlag = br.getBits(1);
 } else {
 switch (objectType) {
 case AOT_AAC_LC:
                    extensionFlag = 0;
 break;
 case AOT_ER_AAC_LC:
 case AOT_ER_AAC_SCAL:
 case AOT_ER_BSAC:
 case AOT_ER_AAC_LD:
                    extensionFlag = 1;
 break;
 default:
                    TRESPASS();
 break;
 }
                ALOGW("csd missing extension flag; assuming %d for object type %u.",
                        extensionFlag, objectType);
 }

 if (numChannels == 0) {
 int32_t channelsEffectiveNum = 0;
 int32_t channelsNum = 0;
 const int32_t ElementInstanceTag = br.getBits(4);
 const int32_t Profile = br.getBits(2);
 const int32_t SamplingFrequencyIndex = br.getBits(4);
 const int32_t NumFrontChannelElements = br.getBits(4);
 const int32_t NumSideChannelElements = br.getBits(4);
 const int32_t NumBackChannelElements = br.getBits(4);
 const int32_t NumLfeChannelElements = br.getBits(2);
 const int32_t NumAssocDataElements = br.getBits(3);
 const int32_t NumValidCcElements = br.getBits(4);

 const int32_t MonoMixdownPresent = br.getBits(1);
 if (MonoMixdownPresent != 0) {
 const int32_t MonoMixdownElementNumber = br.getBits(4);
 }

 const int32_t StereoMixdownPresent = br.getBits(1);
 if (StereoMixdownPresent != 0) {
 const int32_t StereoMixdownElementNumber = br.getBits(4);
 }

 const int32_t MatrixMixdownIndexPresent = br.getBits(1);
 if (MatrixMixdownIndexPresent != 0) {
 const int32_t MatrixMixdownIndex = br.getBits(2);
 const int32_t PseudoSurroundEnable = br.getBits(1);
 }

 int i;
 for (i=0; i < NumFrontChannelElements; i++) {
 const int32_t FrontElementIsCpe = br.getBits(1);
 const int32_t FrontElementTagSelect = br.getBits(4);
                    channelsNum += FrontElementIsCpe ? 2 : 1;
 }

 for (i=0; i < NumSideChannelElements; i++) {
 const int32_t SideElementIsCpe = br.getBits(1);
 const int32_t SideElementTagSelect = br.getBits(4);
                    channelsNum += SideElementIsCpe ? 2 : 1;
 }

 for (i=0; i < NumBackChannelElements; i++) {
 const int32_t BackElementIsCpe = br.getBits(1);
 const int32_t BackElementTagSelect = br.getBits(4);
                    channelsNum += BackElementIsCpe ? 2 : 1;
 }
                channelsEffectiveNum = channelsNum;

 for (i=0; i < NumLfeChannelElements; i++) {
 const int32_t LfeElementTagSelect = br.getBits(4);
                    channelsNum += 1;
 }
                ALOGV("mpeg4 audio channelsNum = %d", channelsNum);
                ALOGV("mpeg4 audio channelsEffectiveNum = %d", channelsEffectiveNum);
                numChannels = channelsNum;
 }
 }
 }

 if (numChannels == 0) {
 return ERROR_UNSUPPORTED;
 }

 int32_t prevSampleRate;
    CHECK(mLastTrack->meta->findInt32(kKeySampleRate, &prevSampleRate));

 if (prevSampleRate != sampleRate) {
        ALOGV("mpeg4 audio sample rate different from previous setting. "
 "was: %d, now: %d", prevSampleRate, sampleRate);
 }

    mLastTrack->meta->setInt32(kKeySampleRate, sampleRate);

 int32_t prevChannelCount;
    CHECK(mLastTrack->meta->findInt32(kKeyChannelCount, &prevChannelCount));

 if (prevChannelCount != numChannels) {
        ALOGV("mpeg4 audio channel count different from previous setting. "
 "was: %d, now: %d", prevChannelCount, numChannels);
 }

    mLastTrack->meta->setInt32(kKeyChannelCount, numChannels);

 return OK;
}