Commit message: None
CVE: CVE-2016-3830
CWE: CWE-20
Vulnerability description: codecs/aacdec/SoftAAC2.cpp in libstagefright in mediaserver in Android 4.x before 4.4.4, 5.0.x before 5.0.2, 5.1.x before 5.1.1, and 6.x before 2016-08-01 allows remote attackers to cause a denial of service (device hang or reboot) via crafted ADTS data, aka internal bug 29153599.

Project: Android

SoftAAC2::SoftAAC2(
 const char *name,
 const OMX_CALLBACKTYPE *callbacks,
        OMX_PTR appData,
        OMX_COMPONENTTYPE **component)
 : SimpleSoftOMXComponent(name, callbacks, appData, component),
      mAACDecoder(NULL),
      mStreamInfo(NULL),
      mIsADTS(false),
      mInputBufferCount(0),
      mOutputBufferCount(0),
      mSignalledError(false),
      mLastInHeader(NULL),
      mOutputPortSettingsChange(NONE) {
    initPorts();
    CHECK_EQ(initDecoder(), (status_t)OK);
}

Project: Android

void SoftAAC2::configureDownmix() const {
 char value[PROPERTY_VALUE_MAX];
 if (!(property_get("media.aac_51_output_enabled", value, NULL)
 && (!strcmp(value, "1") || !strcasecmp(value, "true")))) {
        ALOGI("limiting to stereo output");
        aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, 2);
 }
}

Project: Android

android::SoftOMXComponent *createSoftOMXComponent(
 const char *name, const OMX_CALLBACKTYPE *callbacks,
        OMX_PTR appData, OMX_COMPONENTTYPE **component) {
 return new android::SoftAAC2(name, callbacks, appData, component);
}

Project: Android

void SoftAAC2::drainDecoder() {
 while (mOutputDelayCompensated > 0) {
        INT_PCM tmpOutBuffer[2048 * MAX_CHANNEL_COUNT];

        mDrcWrap.submitStreamData(mStreamInfo);
        mDrcWrap.update();

        AAC_DECODER_ERROR decoderErr =
            aacDecoder_DecodeFrame(mAACDecoder,
                                   tmpOutBuffer,
 2048 * MAX_CHANNEL_COUNT,
                                   AACDEC_FLUSH);
 if (decoderErr != AAC_DEC_OK) {
            ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr);
 }

 int32_t tmpOutBufferSamples = mStreamInfo->frameSize * mStreamInfo->numChannels;
 if (tmpOutBufferSamples > mOutputDelayCompensated) {
            tmpOutBufferSamples = mOutputDelayCompensated;
 }
        outputDelayRingBufferPutSamples(tmpOutBuffer, tmpOutBufferSamples);

        mOutputDelayCompensated -= tmpOutBufferSamples;
 }
}

Project: Android

void SoftAAC2::initPorts() {
    OMX_PARAM_PORTDEFINITIONTYPE def;
 InitOMXParams(&def);

    def.nPortIndex = 0;
    def.eDir = OMX_DirInput;
    def.nBufferCountMin = kNumInputBuffers;
    def.nBufferCountActual = def.nBufferCountMin;
    def.nBufferSize = 8192;
    def.bEnabled = OMX_TRUE;
    def.bPopulated = OMX_FALSE;
    def.eDomain = OMX_PortDomainAudio;
    def.bBuffersContiguous = OMX_FALSE;
    def.nBufferAlignment = 1;

    def.format.audio.cMIMEType = const_cast<char *>("audio/aac");
    def.format.audio.pNativeRender = NULL;
    def.format.audio.bFlagErrorConcealment = OMX_FALSE;
    def.format.audio.eEncoding = OMX_AUDIO_CodingAAC;

    addPort(def);

    def.nPortIndex = 1;
    def.eDir = OMX_DirOutput;
    def.nBufferCountMin = kNumOutputBuffers;
    def.nBufferCountActual = def.nBufferCountMin;
    def.nBufferSize = 4096 * MAX_CHANNEL_COUNT;
    def.bEnabled = OMX_TRUE;
    def.bPopulated = OMX_FALSE;
    def.eDomain = OMX_PortDomainAudio;
    def.bBuffersContiguous = OMX_FALSE;
    def.nBufferAlignment = 2;

    def.format.audio.cMIMEType = const_cast<char *>("audio/raw");
    def.format.audio.pNativeRender = NULL;
    def.format.audio.bFlagErrorConcealment = OMX_FALSE;
    def.format.audio.eEncoding = OMX_AUDIO_CodingPCM;

    addPort(def);
}

Project: Android

OMX_ERRORTYPE SoftAAC2::internalGetParameter(
        OMX_INDEXTYPE index, OMX_PTR params) {
 switch (index) {
 case OMX_IndexParamAudioAac:
 {
            OMX_AUDIO_PARAM_AACPROFILETYPE *aacParams =
 (OMX_AUDIO_PARAM_AACPROFILETYPE *)params;

 if (!isValidOMXParam(aacParams)) {
 return OMX_ErrorBadParameter;
 }

 if (aacParams->nPortIndex != 0) {
 return OMX_ErrorUndefined;
 }

            aacParams->nBitRate = 0;
            aacParams->nAudioBandWidth = 0;
            aacParams->nAACtools = 0;
            aacParams->nAACERtools = 0;
            aacParams->eAACProfile = OMX_AUDIO_AACObjectMain;

            aacParams->eAACStreamFormat =
                mIsADTS
 ? OMX_AUDIO_AACStreamFormatMP4ADTS
 : OMX_AUDIO_AACStreamFormatMP4FF;

            aacParams->eChannelMode = OMX_AUDIO_ChannelModeStereo;

 if (!isConfigured()) {
                aacParams->nChannels = 1;
                aacParams->nSampleRate = 44100;
                aacParams->nFrameLength = 0;
 } else {
                aacParams->nChannels = mStreamInfo->numChannels;
                aacParams->nSampleRate = mStreamInfo->sampleRate;
                aacParams->nFrameLength = mStreamInfo->frameSize;
 }

 return OMX_ErrorNone;
 }

 case OMX_IndexParamAudioPcm:
 {
            OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
 (OMX_AUDIO_PARAM_PCMMODETYPE *)params;

 if (!isValidOMXParam(pcmParams)) {
 return OMX_ErrorBadParameter;
 }

 if (pcmParams->nPortIndex != 1) {
 return OMX_ErrorUndefined;
 }

            pcmParams->eNumData = OMX_NumericalDataSigned;
            pcmParams->eEndian = OMX_EndianBig;
            pcmParams->bInterleaved = OMX_TRUE;
            pcmParams->nBitPerSample = 16;
            pcmParams->ePCMMode = OMX_AUDIO_PCMModeLinear;
            pcmParams->eChannelMapping[0] = OMX_AUDIO_ChannelLF;
            pcmParams->eChannelMapping[1] = OMX_AUDIO_ChannelRF;
            pcmParams->eChannelMapping[2] = OMX_AUDIO_ChannelCF;
            pcmParams->eChannelMapping[3] = OMX_AUDIO_ChannelLFE;
            pcmParams->eChannelMapping[4] = OMX_AUDIO_ChannelLS;
            pcmParams->eChannelMapping[5] = OMX_AUDIO_ChannelRS;

 if (!isConfigured()) {
                pcmParams->nChannels = 1;
                pcmParams->nSamplingRate = 44100;
 } else {
                pcmParams->nChannels = mStreamInfo->numChannels;
                pcmParams->nSamplingRate = mStreamInfo->sampleRate;
 }

 return OMX_ErrorNone;
 }

 default:
 return SimpleSoftOMXComponent::internalGetParameter(index, params);
 }
}

Project: Android

OMX_ERRORTYPE SoftAAC2::internalSetParameter(
        OMX_INDEXTYPE index, const OMX_PTR params) {
 switch ((int)index) {
 case OMX_IndexParamStandardComponentRole:
 {
 const OMX_PARAM_COMPONENTROLETYPE *roleParams =
 (const OMX_PARAM_COMPONENTROLETYPE *)params;

 if (!isValidOMXParam(roleParams)) {
 return OMX_ErrorBadParameter;
 }

 if (strncmp((const char *)roleParams->cRole,
 "audio_decoder.aac",
                        OMX_MAX_STRINGNAME_SIZE - 1)) {
 return OMX_ErrorUndefined;
 }

 return OMX_ErrorNone;
 }

 case OMX_IndexParamAudioAac:
 {
 const OMX_AUDIO_PARAM_AACPROFILETYPE *aacParams =
 (const OMX_AUDIO_PARAM_AACPROFILETYPE *)params;

 if (!isValidOMXParam(aacParams)) {
 return OMX_ErrorBadParameter;
 }

 if (aacParams->nPortIndex != 0) {
 return OMX_ErrorUndefined;
 }

 if (aacParams->eAACStreamFormat == OMX_AUDIO_AACStreamFormatMP4FF) {
                mIsADTS = false;
 } else if (aacParams->eAACStreamFormat
 == OMX_AUDIO_AACStreamFormatMP4ADTS) {
                mIsADTS = true;
 } else {
 return OMX_ErrorUndefined;
 }

 return OMX_ErrorNone;
 }

 case OMX_IndexParamAudioAndroidAacPresentation:
 {
 const OMX_AUDIO_PARAM_ANDROID_AACPRESENTATIONTYPE *aacPresParams =
 (const OMX_AUDIO_PARAM_ANDROID_AACPRESENTATIONTYPE *)params;

 if (!isValidOMXParam(aacPresParams)) {
 return OMX_ErrorBadParameter;
 }

 if (aacPresParams->nMaxOutputChannels >= 0) {
 int max;
 if (aacPresParams->nMaxOutputChannels >= 8) { max = 8; }
 else if (aacPresParams->nMaxOutputChannels >= 6) { max = 6; }
 else if (aacPresParams->nMaxOutputChannels >= 2) { max = 2; }
 else {
                    max = aacPresParams->nMaxOutputChannels;
 }
                ALOGV("set nMaxOutputChannels=%d", max);
                aacDecoder_SetParam(mAACDecoder, AAC_PCM_MAX_OUTPUT_CHANNELS, max);
 }
 bool updateDrcWrapper = false;
 if (aacPresParams->nDrcBoost >= 0) {
                ALOGV("set nDrcBoost=%d", aacPresParams->nDrcBoost);
                mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_BOOST_FACTOR,
                        aacPresParams->nDrcBoost);
                updateDrcWrapper = true;
 }
 if (aacPresParams->nDrcCut >= 0) {
                ALOGV("set nDrcCut=%d", aacPresParams->nDrcCut);
                mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_ATT_FACTOR, aacPresParams->nDrcCut);
                updateDrcWrapper = true;
 }
 if (aacPresParams->nHeavyCompression >= 0) {
                ALOGV("set nHeavyCompression=%d", aacPresParams->nHeavyCompression);
                mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_HEAVY,
                        aacPresParams->nHeavyCompression);
                updateDrcWrapper = true;
 }
 if (aacPresParams->nTargetReferenceLevel >= 0) {
                ALOGV("set nTargetReferenceLevel=%d", aacPresParams->nTargetReferenceLevel);
                mDrcWrap.setParam(DRC_PRES_MODE_WRAP_DESIRED_TARGET,
                        aacPresParams->nTargetReferenceLevel);
                updateDrcWrapper = true;
 }
 if (aacPresParams->nEncodedTargetLevel >= 0) {
                ALOGV("set nEncodedTargetLevel=%d", aacPresParams->nEncodedTargetLevel);
                mDrcWrap.setParam(DRC_PRES_MODE_WRAP_ENCODER_TARGET,
                        aacPresParams->nEncodedTargetLevel);
                updateDrcWrapper = true;
 }
 if (aacPresParams->nPCMLimiterEnable >= 0) {
                aacDecoder_SetParam(mAACDecoder, AAC_PCM_LIMITER_ENABLE,
 (aacPresParams->nPCMLimiterEnable != 0));
 }
 if (updateDrcWrapper) {
                mDrcWrap.update();
 }

 return OMX_ErrorNone;
 }

 case OMX_IndexParamAudioPcm:
 {
 const OMX_AUDIO_PARAM_PCMMODETYPE *pcmParams =
 (OMX_AUDIO_PARAM_PCMMODETYPE *)params;

 if (!isValidOMXParam(pcmParams)) {
 return OMX_ErrorBadParameter;
 }

 if (pcmParams->nPortIndex != 1) {
 return OMX_ErrorUndefined;
 }

 return OMX_ErrorNone;
 }

 default:
 return SimpleSoftOMXComponent::internalSetParameter(index, params);
 }
}

Project: Android

bool SoftAAC2::isConfigured() const {
 return mInputBufferCount > 0;
}

Project: Android

void SoftAAC2::onPortFlushCompleted(OMX_U32 portIndex) {
 if (portIndex == 0) {
        drainDecoder();
        mBufferTimestamps.clear();
        mBufferSizes.clear();
        mDecodedSizes.clear();
        mLastInHeader = NULL;
        mEndOfInput = false;
 } else {
 int avail;
 while ((avail = outputDelayRingBufferSamplesAvailable()) > 0) {
 if (avail > mStreamInfo->frameSize * mStreamInfo->numChannels) {
                avail = mStreamInfo->frameSize * mStreamInfo->numChannels;
 }
 int32_t ns = outputDelayRingBufferGetSamples(0, avail);
 if (ns != avail) {
                ALOGW("not a complete frame of samples available");
 break;
 }
            mOutputBufferCount++;
 }
        mOutputDelayRingBufferReadPos = mOutputDelayRingBufferWritePos;
        mEndOfOutput = false;
 }
}

Project: Android

void SoftAAC2::onReset() {
    drainDecoder();
    mInputBufferCount = 0;
    mOutputBufferCount = 0;
    mOutputDelayCompensated = 0;
    mOutputDelayRingBufferWritePos = 0;
    mOutputDelayRingBufferReadPos = 0;
    mOutputDelayRingBufferFilled = 0;
    mEndOfInput = false;
    mEndOfOutput = false;
    mBufferTimestamps.clear();
    mBufferSizes.clear();
    mDecodedSizes.clear();
    mLastInHeader = NULL;

    mStreamInfo->sampleRate = 0; // TODO: mStreamInfo is read only

    mSignalledError = false;
    mOutputPortSettingsChange = NONE;
}

Project: Android

bool SoftAAC2::outputDelayRingBufferPutSamples(INT_PCM *samples, int32_t numSamples) {
 if (numSamples == 0) {
 return true;
 }
 if (outputDelayRingBufferSpaceLeft() < numSamples) {
        ALOGE("RING BUFFER WOULD OVERFLOW");
 return false;
 }
 if (mOutputDelayRingBufferWritePos + numSamples <= mOutputDelayRingBufferSize
 && (mOutputDelayRingBufferReadPos <= mOutputDelayRingBufferWritePos
 || mOutputDelayRingBufferReadPos > mOutputDelayRingBufferWritePos + numSamples)) {
 for (int32_t i = 0; i < numSamples; i++) {
            mOutputDelayRingBuffer[mOutputDelayRingBufferWritePos++] = samples[i];
 }

 if (mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferSize) {
            mOutputDelayRingBufferWritePos -= mOutputDelayRingBufferSize;
 }
 } else {
        ALOGV("slow SoftAAC2::outputDelayRingBufferPutSamples()");

 for (int32_t i = 0; i < numSamples; i++) {
            mOutputDelayRingBuffer[mOutputDelayRingBufferWritePos] = samples[i];
            mOutputDelayRingBufferWritePos++;
 if (mOutputDelayRingBufferWritePos >= mOutputDelayRingBufferSize) {
                mOutputDelayRingBufferWritePos -= mOutputDelayRingBufferSize;
 }
 }
 }
    mOutputDelayRingBufferFilled += numSamples;
 return true;
}

Project: Android

int32_t SoftAAC2::outputDelayRingBufferSamplesAvailable() {
 return mOutputDelayRingBufferFilled;
}

Project: Android

int32_t SoftAAC2::outputDelayRingBufferSpaceLeft() {
 return mOutputDelayRingBufferSize - outputDelayRingBufferSamplesAvailable();
}

Project: Android

SoftAAC2::~SoftAAC2() {
    aacDecoder_Close(mAACDecoder);
 delete[] mOutputDelayRingBuffer;
}