Commit message: None
CVE: CVE-2016-3827
CWE: CWE-172
Vulnerability description: codecs/hevcdec/SoftHEVC.cpp in libstagefright in mediaserver in Android 6.0.1 before 2016-08-01 mishandles decoder errors, which allows remote attackers to cause a denial of service (device hang or reboot) via a crafted media file, aka internal bug 28816956.

Project: Android

SoftHEVC::SoftHEVC(
 const char *name,
 const OMX_CALLBACKTYPE *callbacks,
        OMX_PTR appData,
        OMX_COMPONENTTYPE **component)
 : SoftVideoDecoderOMXComponent(name, componentName, codingType,
            kProfileLevels, ARRAY_SIZE(kProfileLevels),
 320 /* width */, 240 /* height */, callbacks,
            appData, component),
      mCodecCtx(NULL),
      mFlushOutBuffer(NULL),
      mOmxColorFormat(OMX_COLOR_FormatYUV420Planar),
      mIvColorFormat(IV_YUV_420P),
      mChangingResolution(false),
      mSignalledError(false),
      mStride(mWidth) {
 const size_t kMinCompressionRatio = 4 /* compressionRatio (for Level 4+) */;
 const size_t kMaxOutputBufferSize = 2048 * 2048 * 3 / 2;
    initPorts(
            kNumBuffers, max(kMaxOutputBufferSize / kMinCompressionRatio, (size_t)INPUT_BUF_SIZE),
            kNumBuffers, CODEC_MIME_TYPE, kMinCompressionRatio);
}

Project: Android

android::SoftOMXComponent *createSoftOMXComponent(const char *name,
 const OMX_CALLBACKTYPE *callbacks, OMX_PTR appData,
        OMX_COMPONENTTYPE **component) {
    android::SoftHEVC *codec = new android::SoftHEVC(name, callbacks, appData, component);
 if (codec->init() != android::OK) {
        android::sp<android::SoftOMXComponent> release = codec;
 return NULL;
 }
 return codec;
}

Project: Android

status_t SoftHEVC::deInitDecoder() {
 size_t i;
    IV_API_CALL_STATUS_T status;

 if (mCodecCtx) {
 ivdext_delete_ip_t s_delete_ip;
 ivdext_delete_op_t s_delete_op;

        s_delete_ip.s_ivd_delete_ip_t.u4_size = sizeof(ivdext_delete_ip_t);
        s_delete_ip.s_ivd_delete_ip_t.e_cmd = IVD_CMD_DELETE;

        s_delete_op.s_ivd_delete_op_t.u4_size = sizeof(ivdext_delete_op_t);

        status = ivdec_api_function(mCodecCtx, (void *)&s_delete_ip, (void *)&s_delete_op);
 if (status != IV_SUCCESS) {
            ALOGE("Error in delete: 0x%x",
                    s_delete_op.s_ivd_delete_op_t.u4_error_code);
 return UNKNOWN_ERROR;
 }
 }


    mChangingResolution = false;

 return OK;
}

Project: Android

status_t SoftHEVC::init() {
 return initDecoder();
}

Project: Android

static void ivd_aligned_free(void *ctxt, void *buf) {
    UNUSED(ctxt);
    free(buf);
 return;
}

Project: Android

static void *ivd_aligned_malloc(void *ctxt, WORD32 alignment, WORD32 size) {
    UNUSED(ctxt);
 return memalign(alignment, size);
}

Project: Android

void SoftHEVC::logVersion() {
 ivd_ctl_getversioninfo_ip_t s_ctl_ip;
 ivd_ctl_getversioninfo_op_t s_ctl_op;
    UWORD8 au1_buf[512];
    IV_API_CALL_STATUS_T status;

    s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL;
    s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_GETVERSION;
    s_ctl_ip.u4_size = sizeof(ivd_ctl_getversioninfo_ip_t);
    s_ctl_op.u4_size = sizeof(ivd_ctl_getversioninfo_op_t);
    s_ctl_ip.pv_version_buffer = au1_buf;
    s_ctl_ip.u4_version_buffer_size = sizeof(au1_buf);

    status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip,
 (void *)&s_ctl_op);

 if (status != IV_SUCCESS) {
        ALOGE("Error in getting version number: 0x%x",
                s_ctl_op.u4_error_code);
 } else {
        ALOGV("Ittiam decoder version number: %s",
 (char *)s_ctl_ip.pv_version_buffer);
 }
 return;
}

Project: Android

void SoftHEVC::onPortFlushCompleted(OMX_U32 portIndex) {
 /* Once the output buffers are flushed, ignore any buffers that are held in decoder */
 if (kOutputPortIndex == portIndex) {
        setFlushMode();

 /* Allocate a picture buffer to flushed data */
 uint32_t displayStride = outputBufferWidth();
 uint32_t displayHeight = outputBufferHeight();

 uint32_t bufferSize = displayStride * displayHeight * 3 / 2;
        mFlushOutBuffer = (uint8_t *)memalign(128, bufferSize);
 if (NULL == mFlushOutBuffer) {
            ALOGE("Could not allocate flushOutputBuffer of size %zu", bufferSize);
 return;
 }

 while (true) {
 ivd_video_decode_ip_t s_dec_ip;
 ivd_video_decode_op_t s_dec_op;
            IV_API_CALL_STATUS_T status;
 size_t sizeY, sizeUV;

            setDecodeArgs(&s_dec_ip, &s_dec_op, NULL, NULL, 0);

            status = ivdec_api_function(mCodecCtx, (void *)&s_dec_ip,
 (void *)&s_dec_op);
 if (0 == s_dec_op.u4_output_present) {
                resetPlugin();
 break;
 }
 }

 if (mFlushOutBuffer) {
            free(mFlushOutBuffer);
            mFlushOutBuffer = NULL;
 }

 }
}

Project: Android

void SoftHEVC::onReset() {
    ALOGV("onReset called");
 SoftVideoDecoderOMXComponent::onReset();

    mSignalledError = false;
    resetDecoder();
    resetPlugin();
}

Project: Android

status_t SoftHEVC::resetDecoder() {
 ivd_ctl_reset_ip_t s_ctl_ip;
 ivd_ctl_reset_op_t s_ctl_op;
    IV_API_CALL_STATUS_T status;

    s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL;
    s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_RESET;
    s_ctl_ip.u4_size = sizeof(ivd_ctl_reset_ip_t);
    s_ctl_op.u4_size = sizeof(ivd_ctl_reset_op_t);

    status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip,
 (void *)&s_ctl_op);
 if (IV_SUCCESS != status) {
        ALOGE("Error in reset: 0x%x", s_ctl_op.u4_error_code);
 return UNKNOWN_ERROR;
 }
    mSignalledError = false;

 /* Set number of cores/threads to be used by the codec */
    setNumCores();

    mStride = 0;
 return OK;
}

Project: Android

status_t SoftHEVC::resetPlugin() {
    mIsInFlush = false;
    mReceivedEOS = false;
    memset(mTimeStamps, 0, sizeof(mTimeStamps));
    memset(mTimeStampsValid, 0, sizeof(mTimeStampsValid));

 /* Initialize both start and end times */
    gettimeofday(&mTimeStart, NULL);
    gettimeofday(&mTimeEnd, NULL);

 return OK;
}

Project: Android

status_t SoftHEVC::setFlushMode() {
    IV_API_CALL_STATUS_T status;
 ivd_ctl_flush_ip_t s_video_flush_ip;
 ivd_ctl_flush_op_t s_video_flush_op;

    s_video_flush_ip.e_cmd = IVD_CMD_VIDEO_CTL;
    s_video_flush_ip.e_sub_cmd = IVD_CMD_CTL_FLUSH;
    s_video_flush_ip.u4_size = sizeof(ivd_ctl_flush_ip_t);
    s_video_flush_op.u4_size = sizeof(ivd_ctl_flush_op_t);
    ALOGV("Set the decoder in flush mode ");

 /* Set the decoder in Flush mode, subsequent decode() calls will flush */
    status = ivdec_api_function(mCodecCtx, (void *)&s_video_flush_ip,
 (void *)&s_video_flush_op);

 if (status != IV_SUCCESS) {
        ALOGE("Error in setting the decoder in flush mode: (%d) 0x%x", status,
                s_video_flush_op.u4_error_code);
 return UNKNOWN_ERROR;
 }

    mIsInFlush = true;
 return OK;
}

Project: Android

status_t SoftHEVC::setParams(size_t stride) {
 ivd_ctl_set_config_ip_t s_ctl_ip;
 ivd_ctl_set_config_op_t s_ctl_op;
    IV_API_CALL_STATUS_T status;
    s_ctl_ip.u4_disp_wd = (UWORD32)stride;
    s_ctl_ip.e_frm_skip_mode = IVD_SKIP_NONE;

    s_ctl_ip.e_frm_out_mode = IVD_DISPLAY_FRAME_OUT;
    s_ctl_ip.e_vid_dec_mode = IVD_DECODE_FRAME;
    s_ctl_ip.e_cmd = IVD_CMD_VIDEO_CTL;
    s_ctl_ip.e_sub_cmd = IVD_CMD_CTL_SETPARAMS;
    s_ctl_ip.u4_size = sizeof(ivd_ctl_set_config_ip_t);
    s_ctl_op.u4_size = sizeof(ivd_ctl_set_config_op_t);

    ALOGV("Set the run-time (dynamic) parameters stride = %zu", stride);
    status = ivdec_api_function(mCodecCtx, (void *)&s_ctl_ip,
 (void *)&s_ctl_op);

 if (status != IV_SUCCESS) {
        ALOGE("Error in setting the run-time parameters: 0x%x",
                s_ctl_op.u4_error_code);

 return UNKNOWN_ERROR;
 }
 return OK;
}

Project: Android

SoftHEVC::~SoftHEVC() {
    ALOGV("In SoftHEVC::~SoftHEVC");
    CHECK_EQ(deInitDecoder(), (status_t)OK);
}