Commit message: None
CVE: CVE-2016-3870
CWE: CWE-264
Vulnerability description: omx/SimpleSoftOMXComponent.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, 6.x before 2016-09-01, and 7.0 before 2016-09-01 does not prevent input-port changes, which allows attackers to gain privileges via a crafted application, aka internal bug 29421804.

Project: Android

SimpleSoftOMXComponent::SimpleSoftOMXComponent(
 const char *name,
 const OMX_CALLBACKTYPE *callbacks,
        OMX_PTR appData,
        OMX_COMPONENTTYPE **component)
 : SoftOMXComponent(name, callbacks, appData, component),
      mLooper(new ALooper),
      mHandler(new AHandlerReflector<SimpleSoftOMXComponent>(this)),
      mState(OMX_StateLoaded),
      mTargetState(OMX_StateLoaded) {
    mLooper->setName(name);
    mLooper->registerHandler(mHandler);

    mLooper->start(
 false, // runOnCallingThread
 false, // canCallJava
            ANDROID_PRIORITY_FOREGROUND);
}

Project: Android

void SimpleSoftOMXComponent::addPort(const OMX_PARAM_PORTDEFINITIONTYPE &def) {
    CHECK_EQ(def.nPortIndex, mPorts.size());

    mPorts.push();
 PortInfo *info = &mPorts.editItemAt(mPorts.size() - 1);
    info->mDef = def;
    info->mTransition = PortInfo::NONE;
}

Project: Android

OMX_ERRORTYPE SimpleSoftOMXComponent::allocateBuffer(
        OMX_BUFFERHEADERTYPE **header,
        OMX_U32 portIndex,
        OMX_PTR appPrivate,
        OMX_U32 size) {
    OMX_U8 *ptr = new OMX_U8[size];

    OMX_ERRORTYPE err =
        useBuffer(header, portIndex, appPrivate, size, ptr);

 if (err != OMX_ErrorNone) {
 delete[] ptr;
        ptr = NULL;

 return err;
 }

    CHECK((*header)->pPlatformPrivate == NULL);
 (*header)->pPlatformPrivate = ptr;

 return OMX_ErrorNone;
}

Project: Android

void SimpleSoftOMXComponent::checkTransitions() {
 if (mState != mTargetState) {
 bool transitionComplete = true;

 if (mState == OMX_StateLoaded) {
            CHECK_EQ((int)mTargetState, (int)OMX_StateIdle);

 for (size_t i = 0; i < mPorts.size(); ++i) {
 const PortInfo &port = mPorts.itemAt(i);
 if (port.mDef.bEnabled == OMX_FALSE) {
 continue;
 }

 if (port.mDef.bPopulated == OMX_FALSE) {
                    transitionComplete = false;
 break;
 }
 }
 } else if (mTargetState == OMX_StateLoaded) {
            CHECK_EQ((int)mState, (int)OMX_StateIdle);

 for (size_t i = 0; i < mPorts.size(); ++i) {
 const PortInfo &port = mPorts.itemAt(i);
 if (port.mDef.bEnabled == OMX_FALSE) {
 continue;
 }

 size_t n = port.mBuffers.size();

 if (n > 0) {
                    CHECK_LE(n, port.mDef.nBufferCountActual);

 if (n == port.mDef.nBufferCountActual) {
                        CHECK_EQ((int)port.mDef.bPopulated, (int)OMX_TRUE);
 } else {
                        CHECK_EQ((int)port.mDef.bPopulated, (int)OMX_FALSE);
 }

                    transitionComplete = false;
 break;
 }
 }
 }

 if (transitionComplete) {
            mState = mTargetState;

 if (mState == OMX_StateLoaded) {
                onReset();
 }

            notify(OMX_EventCmdComplete, OMX_CommandStateSet, mState, NULL);
 }
 }

 for (size_t i = 0; i < mPorts.size(); ++i) {
 PortInfo *port = &mPorts.editItemAt(i);

 if (port->mTransition == PortInfo::DISABLING) {
 if (port->mBuffers.empty()) {
                ALOGV("Port %zu now disabled.", i);

                port->mTransition = PortInfo::NONE;
                notify(OMX_EventCmdComplete, OMX_CommandPortDisable, i, NULL);

                onPortEnableCompleted(i, false /* enabled */);
 }
 } else if (port->mTransition == PortInfo::ENABLING) {
 if (port->mDef.bPopulated == OMX_TRUE) {
                ALOGV("Port %zu now enabled.", i);

                port->mTransition = PortInfo::NONE;
                port->mDef.bEnabled = OMX_TRUE;
                notify(OMX_EventCmdComplete, OMX_CommandPortEnable, i, NULL);

                onPortEnableCompleted(i, true /* enabled */);
 }
 }
 }
}

Project: Android

SimpleSoftOMXComponent::PortInfo *SimpleSoftOMXComponent::editPortInfo(
        OMX_U32 portIndex) {
    CHECK_LT(portIndex, mPorts.size());
 return &mPorts.editItemAt(portIndex);
}

Project: Android

OMX_ERRORTYPE SimpleSoftOMXComponent::fillThisBuffer(
        OMX_BUFFERHEADERTYPE *buffer) {
    sp<AMessage> msg = new AMessage(kWhatFillThisBuffer, mHandler);
    msg->setPointer("header", buffer);
    msg->post();

 return OMX_ErrorNone;
}

Project: Android

OMX_ERRORTYPE SimpleSoftOMXComponent::getParameter(
        OMX_INDEXTYPE index, OMX_PTR params) {
 Mutex::Autolock autoLock(mLock);
 return internalGetParameter(index, params);
}

Project: Android

SimpleSoftOMXComponent::getPortQueue(OMX_U32 portIndex) {
    CHECK_LT(portIndex, mPorts.size());
 return mPorts.editItemAt(portIndex).mQueue;
}

Project: Android

OMX_ERRORTYPE SimpleSoftOMXComponent::getState(OMX_STATETYPE *state) {
 Mutex::Autolock autoLock(mLock);

 *state = mState;

 return OMX_ErrorNone;
}

Project: Android

OMX_ERRORTYPE SimpleSoftOMXComponent::internalGetParameter(
        OMX_INDEXTYPE index, OMX_PTR params) {
 switch (index) {
 case OMX_IndexParamPortDefinition:
 {
            OMX_PARAM_PORTDEFINITIONTYPE *defParams =
 (OMX_PARAM_PORTDEFINITIONTYPE *)params;

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

 if (defParams->nPortIndex >= mPorts.size()
 || defParams->nSize
 != sizeof(OMX_PARAM_PORTDEFINITIONTYPE)) {
 return OMX_ErrorUndefined;
 }

 const PortInfo *port =
 &mPorts.itemAt(defParams->nPortIndex);

            memcpy(defParams, &port->mDef, sizeof(port->mDef));

 return OMX_ErrorNone;
 }

 default:
 return OMX_ErrorUnsupportedIndex;
 }
}

Project: Android

OMX_ERRORTYPE SimpleSoftOMXComponent::internalSetParameter(
        OMX_INDEXTYPE index, const OMX_PTR params) {
 switch (index) {
 case OMX_IndexParamPortDefinition:
 {
            OMX_PARAM_PORTDEFINITIONTYPE *defParams =
 (OMX_PARAM_PORTDEFINITIONTYPE *)params;

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

 if (defParams->nPortIndex >= mPorts.size()) {
 return OMX_ErrorBadPortIndex;
 }
 if (defParams->nSize != sizeof(OMX_PARAM_PORTDEFINITIONTYPE)) {
 return OMX_ErrorUnsupportedSetting;
 }

 PortInfo *port =
 &mPorts.editItemAt(defParams->nPortIndex);

 if (defParams->nBufferSize > port->mDef.nBufferSize) {
                port->mDef.nBufferSize = defParams->nBufferSize;
 }

 if (defParams->nBufferCountActual < port->mDef.nBufferCountMin) {
                ALOGW("component requires at least %u buffers (%u requested)",
                        port->mDef.nBufferCountMin, defParams->nBufferCountActual);
 return OMX_ErrorUnsupportedSetting;
 }

            port->mDef.nBufferCountActual = defParams->nBufferCountActual;
 return OMX_ErrorNone;
 }

 default:
 return OMX_ErrorUnsupportedIndex;
 }
}

Project: Android

bool SimpleSoftOMXComponent::isSetParameterAllowed(
        OMX_INDEXTYPE index, const OMX_PTR params) const {
 if (mState == OMX_StateLoaded) {
 return true;
 }

    OMX_U32 portIndex;

 switch (index) {
 case OMX_IndexParamPortDefinition:
 {
            portIndex = ((OMX_PARAM_PORTDEFINITIONTYPE *)params)->nPortIndex;
 break;
 }

 case OMX_IndexParamAudioPcm:
 {
            portIndex = ((OMX_AUDIO_PARAM_PCMMODETYPE *)params)->nPortIndex;
 break;
 }

 case OMX_IndexParamAudioAac:
 {
            portIndex = ((OMX_AUDIO_PARAM_AACPROFILETYPE *)params)->nPortIndex;
 break;
 }

 default:
 return false;
 }

    CHECK(portIndex < mPorts.size());

 return !mPorts.itemAt(portIndex).mDef.bEnabled;
}

Project: Android

void SimpleSoftOMXComponent::onChangeState(OMX_STATETYPE state) {
    CHECK_EQ((int)mState, (int)mTargetState);

 switch (mState) {
 case OMX_StateLoaded:
            CHECK_EQ((int)state, (int)OMX_StateIdle);
 break;
 case OMX_StateIdle:
            CHECK(state == OMX_StateLoaded || state == OMX_StateExecuting);
 break;
 case OMX_StateExecuting:
 {
            CHECK_EQ((int)state, (int)OMX_StateIdle);

 for (size_t i = 0; i < mPorts.size(); ++i) {
                onPortFlush(i, false /* sendFlushComplete */);
 }

            mState = OMX_StateIdle;
            notify(OMX_EventCmdComplete, OMX_CommandStateSet, state, NULL);
 break;
 }

 default:
            TRESPASS();
 }

    mTargetState = state;

    checkTransitions();
}

Project: Android

void SimpleSoftOMXComponent::onMessageReceived(const sp<AMessage> &msg) {
 Mutex::Autolock autoLock(mLock);
 uint32_t msgType = msg->what();
    ALOGV("msgType = %d", msgType);
 switch (msgType) {
 case kWhatSendCommand:
 {
 int32_t cmd, param;
            CHECK(msg->findInt32("cmd", &cmd));
            CHECK(msg->findInt32("param", &param));

            onSendCommand((OMX_COMMANDTYPE)cmd, (OMX_U32)param);
 break;
 }

 case kWhatEmptyThisBuffer:
 case kWhatFillThisBuffer:
 {
            OMX_BUFFERHEADERTYPE *header;
            CHECK(msg->findPointer("header", (void **)&header));

            CHECK(mState == OMX_StateExecuting && mTargetState == mState);

 bool found = false;
 size_t portIndex = (kWhatEmptyThisBuffer == msgType)?
                    header->nInputPortIndex: header->nOutputPortIndex;
 PortInfo *port = &mPorts.editItemAt(portIndex);

 for (size_t j = 0; j < port->mBuffers.size(); ++j) {
 BufferInfo *buffer = &port->mBuffers.editItemAt(j);

 if (buffer->mHeader == header) {
                    CHECK(!buffer->mOwnedByUs);

                    buffer->mOwnedByUs = true;

                    CHECK((msgType == kWhatEmptyThisBuffer
 && port->mDef.eDir == OMX_DirInput)
 || (port->mDef.eDir == OMX_DirOutput));

                    port->mQueue.push_back(buffer);
                    onQueueFilled(portIndex);

                    found = true;
 break;
 }
 }

            CHECK(found);
 break;
 }

 default:
            TRESPASS();
 break;
 }
}

Project: Android

void SimpleSoftOMXComponent::onPortEnableCompleted(
        OMX_U32 portIndex __unused, bool enabled __unused) {
}

Project: Android

void SimpleSoftOMXComponent::onPortFlush(
        OMX_U32 portIndex, bool sendFlushComplete) {
 if (portIndex == OMX_ALL) {
 for (size_t i = 0; i < mPorts.size(); ++i) {
            onPortFlush(i, sendFlushComplete);
 }

 if (sendFlushComplete) {
            notify(OMX_EventCmdComplete, OMX_CommandFlush, OMX_ALL, NULL);
 }

 return;
 }

    CHECK_LT(portIndex, mPorts.size());

 PortInfo *port = &mPorts.editItemAt(portIndex);
 if (port->mTransition != PortInfo::NONE) {
        notify(OMX_EventError, OMX_ErrorUndefined, 0, 0);
 }

 for (size_t i = 0; i < port->mBuffers.size(); ++i) {
 BufferInfo *buffer = &port->mBuffers.editItemAt(i);

 if (!buffer->mOwnedByUs) {
 continue;
 }

        buffer->mHeader->nFilledLen = 0;
        buffer->mHeader->nOffset = 0;
        buffer->mHeader->nFlags = 0;

        buffer->mOwnedByUs = false;

 if (port->mDef.eDir == OMX_DirInput) {
            notifyEmptyBufferDone(buffer->mHeader);
 } else {
            CHECK_EQ(port->mDef.eDir, OMX_DirOutput);

            notifyFillBufferDone(buffer->mHeader);
 }
 }

    port->mQueue.clear();

 if (sendFlushComplete) {
        notify(OMX_EventCmdComplete, OMX_CommandFlush, portIndex, NULL);

        onPortFlushCompleted(portIndex);
 }
}

Project: Android

void SimpleSoftOMXComponent::onQueueFilled(OMX_U32 portIndex __unused) {
}

Project: Android

void SimpleSoftOMXComponent::onReset() {
}

Project: Android

void SimpleSoftOMXComponent::onSendCommand(
        OMX_COMMANDTYPE cmd, OMX_U32 param) {
 switch (cmd) {
 case OMX_CommandStateSet:
 {
            onChangeState((OMX_STATETYPE)param);
 break;
 }

 case OMX_CommandPortEnable:
 case OMX_CommandPortDisable:
 {
            onPortEnable(param, cmd == OMX_CommandPortEnable);
 break;
 }

 case OMX_CommandFlush:
 {
            onPortFlush(param, true /* sendFlushComplete */);
 break;
 }

 default:
            TRESPASS();
 break;
 }
}

Project: Android

void SimpleSoftOMXComponent::prepareForDestruction() {

    mLooper->unregisterHandler(mHandler->id());
    mLooper->stop();
}

Project: Android

OMX_ERRORTYPE SimpleSoftOMXComponent::sendCommand(
        OMX_COMMANDTYPE cmd, OMX_U32 param, OMX_PTR data) {
    CHECK(data == NULL);

    sp<AMessage> msg = new AMessage(kWhatSendCommand, mHandler);
    msg->setInt32("cmd", cmd);
    msg->setInt32("param", param);
    msg->post();

 return OMX_ErrorNone;
}

Project: Android

OMX_ERRORTYPE SimpleSoftOMXComponent::setParameter(
        OMX_INDEXTYPE index, const OMX_PTR params) {
 Mutex::Autolock autoLock(mLock);

    CHECK(isSetParameterAllowed(index, params));

 return internalSetParameter(index, params);
}