Commit message: None
CVE: CVE-2016-0836
CWE: CWE-119
Vulnerability description: Stack-based buffer overflow in decoder/impeg2d_vld.c in mediaserver in Android 6.x before 2016-04-01 allows remote attackers to execute arbitrary code or cause a denial of service (memory corruption) via a crafted media file, aka internal bug 25812590.

Project: Android

UWORD16 impeg2d_dec_ac_coeff_zero(stream_t *ps_stream, UWORD16* pu2_sym_len, UWORD16* pu2_sym_val)
{
    UWORD16 u2_offset,u2_decoded_value;
    UWORD8  u1_shift;
    UWORD32 u4_bits_read;

    u4_bits_read = (UWORD16)impeg2d_bit_stream_nxt(ps_stream,MPEG2_AC_COEFF_MAX_LEN);

 if ((UWORD16)u4_bits_read >= 0x0800)
 {
        u2_offset = (UWORD16)u4_bits_read >> 11;
 }
 else if ((UWORD16)u4_bits_read >= 0x40)
 {
        u2_offset = 31 + ((UWORD16)u4_bits_read >> 6);
 }
 else if ((UWORD16)u4_bits_read >= 0x20)
 {
        u2_offset = 64;
 }
 else
 {
        u2_offset      = 63;
        u4_bits_read    = (UWORD16)u4_bits_read - 0x10;
 }
 /*-----------------------------------------------------------------------
     * The table gOffset contains both the offset for the group to which the
     * Vld code belongs in the Ac Coeff Table and the no of bits with which
     * the BitsRead should be shifted
     *-----------------------------------------------------------------------*/
    u2_offset = gau2_impeg2d_offset_zero[u2_offset];
    u1_shift  = u2_offset & 0xF;

 /*-----------------------------------------------------------------------
     * Depending upon the vld code, we index exactly to that particular
     * Vld codes value in the Ac Coeff Table.
     * (Offset >> 4)       gives the offset for the group in the AcCoeffTable.
     * (BitsRead >> shift) gives the offset within its group
     *-----------------------------------------------------------------------*/
     u2_offset = (u2_offset >> 4) + ((UWORD16)u4_bits_read >> u1_shift);
 /*-----------------------------------------------------------------------
     * DecodedValue has the Run, Level and the number of bits used by Vld code
     *-----------------------------------------------------------------------*/
    u2_decoded_value = gau2_impeg2d_dct_coeff_zero[u2_offset];
 if(u2_decoded_value == END_OF_BLOCK)
 {
 *pu2_sym_len = 2;
 *pu2_sym_val = EOB_CODE_VALUE;
 }
 else if(u2_decoded_value == ESCAPE_CODE)
 {
 *pu2_sym_len     = u2_decoded_value & 0x1F;
 *pu2_sym_val = ESC_CODE_VALUE;
 }
 else
 {
 *pu2_sym_len = u2_decoded_value & 0x1F;
 *pu2_sym_val = u2_decoded_value >> 5;
 }
 return(u2_decoded_value);
}

Project: Android

WORD16 impeg2d_dec_vld_symbol(stream_t *ps_stream,const WORD16 ai2_code_table[][2],  UWORD16 u2_max_len)
{
  UWORD16 u2_data;
  WORD16  u2_end = 0;
  UWORD16 u2_org_max_len = u2_max_len;
  UWORD16 u2_i_bit;

 /* Get the maximum number of bits needed to decode a symbol */
  u2_data = impeg2d_bit_stream_nxt(ps_stream,u2_max_len);
 do
 {
    u2_max_len--;
 /* Read one bit at a time from the variable to decode the huffman code */
    u2_i_bit = (UWORD8)((u2_data >> u2_max_len) & 0x1);

 /* Get the next node pointer or the symbol from the tree */
    u2_end = ai2_code_table[u2_end][u2_i_bit];
 }while(u2_end > 0);

 /* Flush the appropriate number of bits from the ps_stream */
  impeg2d_bit_stream_flush(ps_stream,(UWORD8)(u2_org_max_len - u2_max_len));
 return(u2_end);
}

Project: Android

WORD16 impeg2d_fast_dec_vld_symbol(stream_t *ps_stream,
 const WORD16  ai2_code_table[][2],
 const UWORD16 au2_indexTable[][2],
                     UWORD16 u2_max_len)
{
    UWORD16 u2_cur_code;
    UWORD16 u2_num_bits;
    UWORD16 u2_vld_offset;
    UWORD16 u2_start_len;
    WORD16  u2_value;
    UWORD16 u2_len;
    UWORD16 u2_huffCode;

    u2_start_len  = au2_indexTable[0][0];
    u2_vld_offset = 0;
    u2_huffCode  = impeg2d_bit_stream_nxt(ps_stream,u2_max_len);
 do
 {
        u2_cur_code = u2_huffCode >> (u2_max_len - u2_start_len);
        u2_num_bits = ai2_code_table[u2_cur_code + u2_vld_offset][0];
 if(u2_num_bits == 0)
 {
            u2_huffCode  &= ((1 << (u2_max_len - u2_start_len)) - 1);
            u2_max_len    -= u2_start_len;
            u2_start_len   = au2_indexTable[ai2_code_table[u2_cur_code + u2_vld_offset][1]][0];
            u2_vld_offset  = au2_indexTable[ai2_code_table[u2_cur_code + u2_vld_offset][1]][1];
 }
 else
 {
            u2_value = ai2_code_table[u2_cur_code + u2_vld_offset][1];
            u2_len   = u2_num_bits;
 }
 }while(u2_num_bits == 0);
    impeg2d_bit_stream_flush(ps_stream,u2_len);
 return(u2_value);
}

Project: Android

UWORD8 impeg2d_inv_quant_mpeg1(WORD16 *pi2_blk,
                              UWORD8 *pu1_weighting_matrix,
                              UWORD8 u1_quant_scale,
                              WORD32 u4_intra_flag,
                              WORD32 i4_num_coeffs,
                              WORD16 *pi2_coeffs,
                              UWORD8 *pu1_pos,
 const UWORD8 *pu1_scan,
                              UWORD16 *pu2_def_dc_pred,
                              UWORD16 u2_intra_dc_precision)
{
    UWORD16 i4_pos;

    WORD32  i4_iter;

 /* Inverse Quantize the predicted DC value for intra MB*/
 if(u4_intra_flag == 1)
 {
 /**************************************************************************/
 /* Decode the DC coefficient in case of Intra block and also update       */
 /* DC predictor value of the corresponding color component                */
 /**************************************************************************/
 {
            pi2_coeffs[0] += *pu2_def_dc_pred;
 *pu2_def_dc_pred      = pi2_coeffs[0];
            pi2_coeffs[0] <<= (3 - u2_intra_dc_precision);
            pi2_coeffs[0] = CLIP_S12(pi2_coeffs[0]);
 }

        pi2_blk[pu1_scan[0]] = pi2_coeffs[0];
 }
 /************************************************************************/
 /* Inverse quantization of other DCT coefficients                       */
 /************************************************************************/
 for(i4_iter = u4_intra_flag; i4_iter < i4_num_coeffs; i4_iter++)
 {

        WORD16 sign;
        WORD32 temp, temp1;

 /* Position is the inverse scan of the index stored */
        i4_pos      = pu1_pos[i4_iter];
        pi2_blk[i4_pos] = pi2_coeffs[i4_iter];

        sign = SIGN(pi2_blk[i4_pos]);
        temp = ABS(pi2_blk[i4_pos] << 1);

 /* pi2_coeffs has only non-zero elements. So no need to check
         * if the coeff is non-zero.
         */
        temp = temp + (1 * !u4_intra_flag);

        temp = temp * pu1_weighting_matrix[i4_pos] * u1_quant_scale;

        temp = temp >> 5;

        temp1 = temp | 1;

        temp1 = (temp1 > temp) ? (temp1 - temp) : (temp - temp1);

        temp = temp - temp1;

 if(temp < 0)
 {
            temp = 0;
 }

        temp = temp * sign;

        temp = CLIP_S12(temp);

        pi2_blk[i4_pos] = temp;
 }

 /*return value is used in the case of mpeg2 for mismatch control*/
 return (0);
} /* End of inv_quant() */

Project: Android

IMPEG2D_ERROR_CODES_T impeg2d_vld_inv_quant_mpeg2(
 void *pv_dec, /* Decoder State */
                             WORD16       *pi2_out_addr, /*!< Address where decoded symbols will be stored */
 const UWORD8 *pu1_scan, /*!< Scan table to be used */
                             UWORD16      u2_intra_flag, /*!< Intra Macroblock or not */
                             UWORD16      u2_colr_comp, /*!< 0 - Luma,1 - U comp, 2 - V comp */
                             UWORD16      u2_d_picture        /*!< D Picture or not */
 )
{
    UWORD8  *pu1_weighting_matrix;
    WORD32 u4_sum_is_even;
 dec_state_t *ps_dec = (dec_state_t *)pv_dec;
    IMPEG2D_ERROR_CODES_T e_error = (IMPEG2D_ERROR_CODES_T)IVD_ERROR_NONE;

    WORD16  pi2_coeffs[NUM_COEFFS];
    UWORD8  pi4_pos[NUM_COEFFS];
    WORD32  i4_num_coeffs;

 /* Perform VLD on the stream to get the coefficients and their positions */
    e_error = impeg2d_vld_decode(ps_dec, pi2_coeffs, pu1_scan, pi4_pos, u2_intra_flag,
                                 u2_colr_comp, u2_d_picture, ps_dec->u2_intra_vlc_format,
                                 ps_dec->u2_is_mpeg2, &i4_num_coeffs);
 if ((IMPEG2D_ERROR_CODES_T)IVD_ERROR_NONE != e_error)
 {
 return e_error;
 }

 /* For YUV420 format,Select the weighting matrix according to Table 7.5 */
    pu1_weighting_matrix = (u2_intra_flag == 1) ? ps_dec->au1_intra_quant_matrix:
                    ps_dec->au1_inter_quant_matrix;

 /*mismatch control for mpeg2*/
 /* Check if the block has only one non-zero coeff which is DC  */
    ps_dec->i4_last_value_one = 0;

    IMPEG2D_IQNT_INP_STATISTICS(pi2_out_addr, ps_dec->u4_non_zero_cols, ps_dec->u4_non_zero_rows);

 /* Inverse Quantize the Output of VLD */
    PROFILE_DISABLE_INVQUANT_IF0

 {
 /* Clear output matrix */
        PROFILE_DISABLE_MEMSET_RESBUF_IF0
 if (1 != (ps_dec->u4_non_zero_cols | ps_dec->u4_non_zero_rows))
 {
            ps_dec->pf_memset_16bit_8x8_linear_block (pi2_out_addr);
 }

        u4_sum_is_even  = impeg2d_inv_quant_mpeg2(pi2_out_addr, pu1_weighting_matrix,
                                                 ps_dec->u1_quant_scale, u2_intra_flag,
                                                 i4_num_coeffs, pi2_coeffs,
                                                 pi4_pos, pu1_scan,
 &ps_dec->u2_def_dc_pred[u2_colr_comp],
                                                 ps_dec->u2_intra_dc_precision);

 if (0 != pi2_out_addr[0])
 {
 /* The first coeff might've become non-zero due to intra_dc_decision
             * value. So, check here after inverse quantization.
             */
            ps_dec->u4_non_zero_cols  |= 0x1;
            ps_dec->u4_non_zero_rows  |= 0x1;
 }

 if (1 == (ps_dec->u4_non_zero_cols | ps_dec->u4_non_zero_rows))
 {
            ps_dec->i4_last_value_one = 1 - (pi2_out_addr[0] & 1);
 }
 else
 {
 /*toggle last bit if sum is even ,else retain it as it is*/
            pi2_out_addr[63] ^= (u4_sum_is_even & 1);

 if (0 != pi2_out_addr[63])
 {
                ps_dec->u4_non_zero_cols  |= 0x80;
                ps_dec->u4_non_zero_rows  |= 0x80;
 }
 }
 }

 return e_error;
}