Commit message: None
CVE: CVE-2016-2476
CWE: CWE-119
Vulnerability description: 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-06-01 does not validate OMX buffer sizes, which allows attackers to gain privileges via a crafted application, as demonstrated by obtaining Signature or SignatureOrSystem access, aka internal bug 27207275.

Project: Android

ACodec::ExecutingState::ExecutingState(ACodec *codec)
 : BaseState(codec),
      mActive(false) {
}

Project: Android

ACodec::ExecutingToIdleState::ExecutingToIdleState(ACodec *codec)
 : BaseState(codec),
      mComponentNowIdle(false) {
}

Project: Android

bool ACodec::allYourBuffersAreBelongToUs(
        OMX_U32 portIndex) {
 for (size_t i = 0; i < mBuffers[portIndex].size(); ++i) {
 BufferInfo *info = &mBuffers[portIndex].editItemAt(i);

 if (info->mStatus != BufferInfo::OWNED_BY_US
 && info->mStatus != BufferInfo::OWNED_BY_NATIVE_WINDOW) {
            ALOGV("[%s] Buffer %u on port %u still has status %d",
                    mComponentName.c_str(),
                    info->mBufferID, portIndex, info->mStatus);
 return false;
 }
 }

 return true;
}

Project: Android

bool ACodec::describeDefaultColorFormat(DescribeColorFormatParams &params) {
 MediaImage &image = params.sMediaImage;
    memset(&image, 0, sizeof(image));

    image.mType = MediaImage::MEDIA_IMAGE_TYPE_UNKNOWN;
    image.mNumPlanes = 0;

 const OMX_COLOR_FORMATTYPE fmt = params.eColorFormat;
    image.mWidth = params.nFrameWidth;
    image.mHeight = params.nFrameHeight;

 if (fmt != OMX_COLOR_FormatYUV420Planar &&
        fmt != OMX_COLOR_FormatYUV420PackedPlanar &&
        fmt != OMX_COLOR_FormatYUV420SemiPlanar &&
        fmt != OMX_COLOR_FormatYUV420PackedSemiPlanar &&
        fmt != HAL_PIXEL_FORMAT_YV12) {
        ALOGW("do not know color format 0x%x = %d", fmt, fmt);
 return false;
 }

 if (params.nStride != 0 && params.nSliceHeight == 0) {
        ALOGW("using sliceHeight=%u instead of what codec advertised (=0)",
                params.nFrameHeight);
        params.nSliceHeight = params.nFrameHeight;
 }

 if (params.nStride == 0 || params.nSliceHeight == 0) {
        ALOGW("cannot describe color format 0x%x = %d with stride=%u and sliceHeight=%u",
                fmt, fmt, params.nStride, params.nSliceHeight);
 return false;
 }

    image.mType = MediaImage::MEDIA_IMAGE_TYPE_YUV;
    image.mNumPlanes = 3;
    image.mBitDepth = 8;
    image.mPlane[image.Y].mOffset = 0;
    image.mPlane[image.Y].mColInc = 1;
    image.mPlane[image.Y].mRowInc = params.nStride;
    image.mPlane[image.Y].mHorizSubsampling = 1;
    image.mPlane[image.Y].mVertSubsampling = 1;

 switch ((int)fmt) {
 case HAL_PIXEL_FORMAT_YV12:
 if (params.bUsingNativeBuffers) {
 size_t ystride = align(params.nStride, 16);
 size_t cstride = align(params.nStride / 2, 16);
                image.mPlane[image.Y].mRowInc = ystride;

                image.mPlane[image.V].mOffset = ystride * params.nSliceHeight;
                image.mPlane[image.V].mColInc = 1;
                image.mPlane[image.V].mRowInc = cstride;
                image.mPlane[image.V].mHorizSubsampling = 2;
                image.mPlane[image.V].mVertSubsampling = 2;

                image.mPlane[image.U].mOffset = image.mPlane[image.V].mOffset
 + (cstride * params.nSliceHeight / 2);
                image.mPlane[image.U].mColInc = 1;
                image.mPlane[image.U].mRowInc = cstride;
                image.mPlane[image.U].mHorizSubsampling = 2;
                image.mPlane[image.U].mVertSubsampling = 2;
 break;
 } else {
 }

 case OMX_COLOR_FormatYUV420Planar:
 case OMX_COLOR_FormatYUV420PackedPlanar:
            image.mPlane[image.U].mOffset = params.nStride * params.nSliceHeight;
            image.mPlane[image.U].mColInc = 1;
            image.mPlane[image.U].mRowInc = params.nStride / 2;
            image.mPlane[image.U].mHorizSubsampling = 2;
            image.mPlane[image.U].mVertSubsampling = 2;

            image.mPlane[image.V].mOffset = image.mPlane[image.U].mOffset
 + (params.nStride * params.nSliceHeight / 4);
            image.mPlane[image.V].mColInc = 1;
            image.mPlane[image.V].mRowInc = params.nStride / 2;
            image.mPlane[image.V].mHorizSubsampling = 2;
            image.mPlane[image.V].mVertSubsampling = 2;
 break;

 case OMX_COLOR_FormatYUV420SemiPlanar:
 case OMX_COLOR_FormatYUV420PackedSemiPlanar:
            image.mPlane[image.U].mOffset = params.nStride * params.nSliceHeight;
            image.mPlane[image.U].mColInc = 2;
            image.mPlane[image.U].mRowInc = params.nStride;
            image.mPlane[image.U].mHorizSubsampling = 2;
            image.mPlane[image.U].mVertSubsampling = 2;

            image.mPlane[image.V].mOffset = image.mPlane[image.U].mOffset + 1;
            image.mPlane[image.V].mColInc = 2;
            image.mPlane[image.V].mRowInc = params.nStride;
            image.mPlane[image.V].mHorizSubsampling = 2;
            image.mPlane[image.V].mVertSubsampling = 2;
 break;

 default:
            TRESPASS();
 }
 return true;
}

Project: Android

void ACodec::dumpBuffers(OMX_U32 portIndex) {
    CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);
    ALOGI("[%s] %s port has %zu buffers:", mComponentName.c_str(),
            portIndex == kPortIndexInput ? "input" : "output", mBuffers[portIndex].size());
 for (size_t i = 0; i < mBuffers[portIndex].size(); ++i) {
 const BufferInfo &info = mBuffers[portIndex][i];
        ALOGI("  slot %2zu: #%8u %p/%p %s(%d) dequeued:%u",
                i, info.mBufferID, info.mGraphicBuffer.get(),
                info.mGraphicBuffer == NULL ? NULL : info.mGraphicBuffer->getNativeBuffer(),
                _asString(info.mStatus), info.mStatus, info.mDequeuedAt);
 }
}

Project: Android

ACodec::BufferInfo *ACodec::findBufferByID(
 uint32_t portIndex, IOMX::buffer_id bufferID, ssize_t *index) {
 for (size_t i = 0; i < mBuffers[portIndex].size(); ++i) {
 BufferInfo *info = &mBuffers[portIndex].editItemAt(i);

 if (info->mBufferID == bufferID) {
 if (index != NULL) {
 *index = i;
 }
 return info;
 }
 }

    ALOGE("Could not find buffer with ID %u", bufferID);
 return NULL;
}

Project: Android

void ACodec::initiateShutdown(bool keepComponentAllocated) {
    sp<AMessage> msg = new AMessage(kWhatShutdown, this);
    msg->setInt32("keepComponentAllocated", keepComponentAllocated);
    msg->post();
 if (!keepComponentAllocated) {
 (new AMessage(kWhatReleaseCodecInstance, this))->post(3000000);
 }
}

Project: Android

bool ACodec::LoadedState::onMessageReceived(const sp<AMessage> &msg) {
 bool handled = false;

 switch (msg->what()) {
 case ACodec::kWhatConfigureComponent:
 {
            onConfigureComponent(msg);
            handled = true;
 break;
 }

 case ACodec::kWhatCreateInputSurface:
 {
            onCreateInputSurface(msg);
            handled = true;
 break;
 }

 case ACodec::kWhatSetInputSurface:
 {
            onSetInputSurface(msg);
            handled = true;
 break;
 }

 case ACodec::kWhatStart:
 {
            onStart();
            handled = true;
 break;
 }

 case ACodec::kWhatShutdown:
 {
 int32_t keepComponentAllocated;
            CHECK(msg->findInt32(
 "keepComponentAllocated", &keepComponentAllocated));

            mCodec->mExplicitShutdown = true;
            onShutdown(keepComponentAllocated);

            handled = true;
 break;
 }

 case ACodec::kWhatFlush:
 {
            sp<AMessage> notify = mCodec->mNotify->dup();
            notify->setInt32("what", CodecBase::kWhatFlushCompleted);
            notify->post();

            handled = true;
 break;
 }

 default:
 return BaseState::onMessageReceived(msg);
 }

 return handled;
}

Project: Android

bool ACodec::ExecutingToIdleState::onMessageReceived(const sp<AMessage> &msg) {
 bool handled = false;

 switch (msg->what()) {
 case kWhatFlush:
 {
            ALOGW("Ignoring flush request in ExecutingToIdleState");
 break;
 }

 case kWhatShutdown:
 {

            handled = true;
 break;
 }

 default:
            handled = BaseState::onMessageReceived(msg);
 break;
 }

 return handled;
}

Project: Android

bool ACodec::IdleToLoadedState::onMessageReceived(const sp<AMessage> &msg) {
 bool handled = false;

 switch (msg->what()) {
 case kWhatShutdown:
 {

            handled = true;
 break;
 }

 case kWhatFlush:
 {
            ALOGE("Got flush request in IdleToLoadedState");
 break;
 }

 default:
            handled = BaseState::onMessageReceived(msg);
 break;
 }

 return handled;
}

Project: Android

bool ACodec::BaseState::onOMXEvent(
        OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
 if (event != OMX_EventError) {
        ALOGV("[%s] EVENT(%d, 0x%08x, 0x%08x)",
             mCodec->mComponentName.c_str(), event, data1, data2);

 return false;
 }

    ALOGE("[%s] ERROR(0x%08x)", mCodec->mComponentName.c_str(), data1);

    OMX_ERRORTYPE omxError = (OMX_ERRORTYPE)data1;
 if (!isOMXError(omxError)) {
        ALOGW("Invalid OMX error %#x", omxError);
        omxError = OMX_ErrorUndefined;
 }
    mCodec->signalError(omxError);

 return true;
}

Project: Android

bool ACodec::FlushingState::onOMXEvent(
        OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
    ALOGV("[%s] FlushingState onOMXEvent(%u,%d)",
            mCodec->mComponentName.c_str(), event, (OMX_S32)data1);

 switch (event) {
 case OMX_EventCmdComplete:
 {
 if (data1 != (OMX_U32)OMX_CommandFlush) {
                ALOGE("unexpected EventCmdComplete %s(%d) data2:%d in FlushingState",
                        asString((OMX_COMMANDTYPE)data1), data1, data2);
                mCodec->signalError(OMX_ErrorUndefined, FAILED_TRANSACTION);
 return true;
 }

 if (data2 == kPortIndexInput || data2 == kPortIndexOutput) {
 if (mFlushComplete[data2]) {
                    ALOGW("Flush already completed for %s port",
                            data2 == kPortIndexInput ? "input" : "output");
 return true;
 }
                mFlushComplete[data2] = true;

 if (mFlushComplete[kPortIndexInput] && mFlushComplete[kPortIndexOutput]) {
                    changeStateIfWeOwnAllBuffers();
 }
 } else if (data2 == OMX_ALL) {
 if (!mFlushComplete[kPortIndexInput] || !mFlushComplete[kPortIndexOutput]) {
                    ALOGW("received flush complete event for OMX_ALL before ports have been"
 "flushed (%d/%d)",
                            mFlushComplete[kPortIndexInput], mFlushComplete[kPortIndexOutput]);
 return false;
 }

                changeStateIfWeOwnAllBuffers();
 } else {
                ALOGW("data2 not OMX_ALL but %u in EventCmdComplete CommandFlush", data2);
 }

 return true;
 }

 case OMX_EventPortSettingsChanged:
 {
            sp<AMessage> msg = new AMessage(kWhatOMXMessage, mCodec);
            msg->setInt32("type", omx_message::EVENT);
            msg->setInt32("node", mCodec->mNode);
            msg->setInt32("event", event);
            msg->setInt32("data1", data1);
            msg->setInt32("data2", data2);

            ALOGV("[%s] Deferring OMX_EventPortSettingsChanged",
                 mCodec->mComponentName.c_str());

            mCodec->deferMessage(msg);

 return true;
 }

 default:
 return BaseState::onOMXEvent(event, data1, data2);
 }

 return true;
}

Project: Android

void ACodec::LoadedState::onSetInputSurface(
 const sp<AMessage> &msg) {
    ALOGV("onSetInputSurface");

    sp<AMessage> notify = mCodec->mNotify->dup();
    notify->setInt32("what", CodecBase::kWhatInputSurfaceAccepted);

    sp<RefBase> obj;
    CHECK(msg->findObject("input-surface", &obj));
    sp<PersistentSurface> surface = static_cast<PersistentSurface *>(obj.get());

 status_t err = mCodec->mOMX->setInputSurface(
            mCodec->mNode, kPortIndexInput, surface->getBufferConsumer(),
 &mCodec->mInputMetadataType);

 if (err == OK) {
        err = setupInputSurface();
 }

 if (err != OK) {
        ALOGE("[%s] onSetInputSurface returning error %d",
                mCodec->mComponentName.c_str(), err);
        notify->setInt32("err", err);
 }
    notify->post();
}

Project: Android

void ACodec::BaseState::postFillThisBuffer(BufferInfo *info) {
 if (mCodec->mPortEOS[kPortIndexInput]) {
 return;
 }

    CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_US);

    sp<AMessage> notify = mCodec->mNotify->dup();
    notify->setInt32("what", CodecBase::kWhatFillThisBuffer);
    notify->setInt32("buffer-id", info->mBufferID);

    info->mData->meta()->clear();
    notify->setBuffer("buffer", info->mData);

    sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, mCodec);
    reply->setInt32("buffer-id", info->mBufferID);

    notify->setMessage("reply", reply);

    notify->post();

    info->mStatus = BufferInfo::OWNED_BY_UPSTREAM;
}

Project: Android

status_t ACodec::setupErrorCorrectionParameters() {
    OMX_VIDEO_PARAM_ERRORCORRECTIONTYPE errorCorrectionType;
 InitOMXParams(&errorCorrectionType);
    errorCorrectionType.nPortIndex = kPortIndexOutput;

 status_t err = mOMX->getParameter(
            mNode, OMX_IndexParamVideoErrorCorrection,
 &errorCorrectionType, sizeof(errorCorrectionType));

 if (err != OK) {
 return OK; // Optional feature. Ignore this failure
 }

    errorCorrectionType.bEnableHEC = OMX_FALSE;
    errorCorrectionType.bEnableResync = OMX_TRUE;
    errorCorrectionType.nResynchMarkerSpacing = 256;
    errorCorrectionType.bEnableDataPartitioning = OMX_FALSE;
    errorCorrectionType.bEnableRVLC = OMX_FALSE;

 return mOMX->setParameter(
            mNode, OMX_IndexParamVideoErrorCorrection,
 &errorCorrectionType, sizeof(errorCorrectionType));
}

Project: Android

void ACodec::FlushingState::stateEntered() {
    ALOGV("[%s] Now Flushing", mCodec->mComponentName.c_str());

    mFlushComplete[kPortIndexInput] = mFlushComplete[kPortIndexOutput] = false;
}