Commit message: None
CVE: CVE-2016-3879
CWE: CWE-284
Vulnerability description: arm-wt-22k/lib_src/eas_mdls.c 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-09-01 allows remote attackers to cause a denial of service (NULL pointer dereference, and device hang or reboot) via a crafted media file, aka internal bug 29770686.

Project: Android

static EAS_RESULT Parse_data (SDLS_SYNTHESIZER_DATA *pDLSData, EAS_I32 pos, EAS_I32 size, S_WSMP_DATA *pWsmp, EAS_SAMPLE *pSample, EAS_U32 sampleLen)
{
    EAS_RESULT result;
    EAS_U8 convBuf[SAMPLE_CONVERT_CHUNK_SIZE];
    EAS_I32 count;
    EAS_I32 i;
    EAS_I8 *p;

 /* seek to start of chunk */
 if ((result = EAS_HWFileSeek(pDLSData->hwInstData, pDLSData->fileHandle, pos)) != EAS_SUCCESS)
 return result;

 /* 8-bit samples in an 8-bit synth, just copy the data, and flip bit 7 */
    p = pSample;
 if (pWsmp->bitsPerSample == 8)
 {
 if ((result = EAS_HWReadFile(pDLSData->hwInstData, pDLSData->fileHandle, pSample, size, &count)) != EAS_SUCCESS)
 return result;
 for (i = 0; i < size; i++)
 /*lint -e{734} convert from unsigned to signed audio */
 *p++ ^= 0x80;
 }

 /* 16-bit samples, need to convert to 8-bit or ADPCM */
 else
 {

 while (size)
 {
            EAS_I8 *pInput;

 /* for undithered conversion, we're just copying the 8-bit data */
 if (pDLSData->bigEndian)
                pInput = (EAS_I8*) convBuf;
 else
                pInput = (EAS_I8*) convBuf + 1;

 /* read a small chunk of data and convert it */
            count = (size < SAMPLE_CONVERT_CHUNK_SIZE ? size : SAMPLE_CONVERT_CHUNK_SIZE);
 if ((result = EAS_HWReadFile(pDLSData->hwInstData, pDLSData->fileHandle, convBuf, count, &count)) != EAS_SUCCESS)
 return result;
            size -= count;
 /*lint -e{704} use shift for performance */
            count = count >> 1;

 while (count--)
 {
 *p++ = *pInput;
                pInput += 2;
 }
 }
 }

 /* for looped samples, copy the last sample to the end */
 if (pWsmp->loopLength)
 {
 if (sampleLen < sizeof(EAS_SAMPLE)
 || (pWsmp->loopStart + pWsmp->loopLength) * sizeof(EAS_SAMPLE) > sampleLen - sizeof(EAS_SAMPLE))
 {
 return EAS_FAILURE;
 }

        pSample[pWsmp->loopStart + pWsmp->loopLength] = pSample[pWsmp->loopStart];
 }

 return EAS_SUCCESS;
}

Project: Android

static EAS_RESULT Parse_rgn (SDLS_SYNTHESIZER_DATA *pDLSData, EAS_I32 pos, EAS_I32 size, EAS_U16 artIndex)
{
    EAS_RESULT result;
    EAS_U32 temp;
    EAS_I32 chunkPos;
    EAS_I32 endChunk;
    EAS_I32 rgnhPos;
    EAS_I32 lartPos;
    EAS_I32 lartSize;
    EAS_I32 lar2Pos;
    EAS_I32 lar2Size;
    EAS_I32 wlnkPos;
    EAS_I32 wsmpPos;
    EAS_U32 waveIndex;
    S_DLS_ART_VALUES art;
    S_WSMP_DATA wsmp;
    S_WSMP_DATA *pWsmp;
    EAS_U16 regionIndex;

 /* seek to start of chunk */
 if ((result = EAS_HWFileSeek(pDLSData->hwInstData, pDLSData->fileHandle, pos)) != EAS_SUCCESS)
 return result;

 /* no chunks found yet */
    rgnhPos = lartPos = lartSize = lar2Pos = lar2Size = wsmpPos = wlnkPos = 0;
    regionIndex = (EAS_U16) pDLSData->regionCount;

 /* read to end of chunk */
    endChunk = pos + size;
 while (pos < endChunk)
 {
        chunkPos = pos;

 /* get the next chunk type */
 if ((result = NextChunk(pDLSData, &pos, &temp, &size)) != EAS_SUCCESS)
 return result;

 /* parse useful chunks */
 switch (temp)
 {
 case CHUNK_CDL:
 if ((result = Parse_cdl(pDLSData, size, &temp)) != EAS_SUCCESS)
 return result;

 /* if conditional chunk evaluates false, skip this list */
 if (!temp)
 return EAS_SUCCESS;
 break;

 case CHUNK_RGNH:
                rgnhPos = chunkPos + 8;
 break;

 case CHUNK_WLNK:
                wlnkPos = chunkPos + 8;
 break;

 case CHUNK_WSMP:
                wsmpPos = chunkPos + 8;
 break;

 case CHUNK_LART:
                lartPos = chunkPos + 12;
                lartSize = size;
 break;

 case CHUNK_LAR2:
                lar2Pos = chunkPos + 12;
                lar2Size = size;
 break;

 default:
 break;
 }
 }

 /* must have a rgnh chunk to be useful */
 if (!rgnhPos)
 {
 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "DLS rgn chunk has no rgnh chunk\n"); */ }
 return EAS_ERROR_UNRECOGNIZED_FORMAT;
 }

 /* must have a wlnk chunk to be useful */
 if (!wlnkPos)
 {
 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "DLS rgn chunk has no wlnk chunk\n"); */ }
 return EAS_ERROR_UNRECOGNIZED_FORMAT;
 }

 /* parse wlnk chunk */
 if ((result = Parse_wlnk(pDLSData, wlnkPos, &waveIndex)) != EAS_SUCCESS)
 return result;
 if (waveIndex >= pDLSData->waveCount)
 {
 return EAS_FAILURE;
 }
    pWsmp = &pDLSData->wsmpData[waveIndex];

 /* if there is any articulation data, parse it */
    EAS_HWMemCpy(&art, &defaultArt, sizeof(S_DLS_ART_VALUES));
 if (lartPos)
 {
 if ((result = Parse_lart(pDLSData, lartPos, lartSize, &art)) != EAS_SUCCESS)
 return result;
 }

 if (lar2Pos)
 {
 if ((result = Parse_lart(pDLSData, lar2Pos, lar2Size, &art)) != EAS_SUCCESS)
 return result;
 }

 /* if second pass, process region header */
 if (pDLSData->pDLS)
 {

 /* if local data was found convert it */
 if (art.values[PARAM_MODIFIED] == EAS_TRUE)
 {
 Convert_art(pDLSData, &art, (EAS_U16) pDLSData->artCount);
            artIndex = (EAS_U16) pDLSData->artCount;
 }

 /* parse region header */
 if ((result = Parse_rgnh(pDLSData, rgnhPos, &pDLSData->pDLS->pDLSRegions[regionIndex & REGION_INDEX_MASK])) != EAS_SUCCESS)
 return result;

 /* parse wsmp chunk, copying parameters from original first */
 if (wsmpPos)
 {
            EAS_HWMemCpy(&wsmp, pWsmp, sizeof(wsmp));
 if ((result = Parse_wsmp(pDLSData, wsmpPos, &wsmp)) != EAS_SUCCESS)
 return result;

            pWsmp = &wsmp;
 }

 Convert_rgn(pDLSData, regionIndex, artIndex, (EAS_U16) waveIndex, pWsmp);

 /* ensure loopStart and loopEnd fall in the range */
 if (pWsmp->loopLength != 0)
 {
            EAS_U32 sampleLen = pDLSData->pDLS->pDLSSampleLen[waveIndex];
 if (sampleLen < sizeof(EAS_SAMPLE)
 || (pWsmp->loopStart + pWsmp->loopLength) * sizeof(EAS_SAMPLE) > sampleLen - sizeof(EAS_SAMPLE))
 {
 return EAS_FAILURE;
 }
 }
 }

 /* if local articulation, bump count */
 if (art.values[PARAM_MODIFIED])
        pDLSData->artCount++;

 /* increment region count */
    pDLSData->regionCount++;
 return EAS_SUCCESS;
}

Project: Android

static EAS_RESULT Parse_wave (SDLS_SYNTHESIZER_DATA *pDLSData, EAS_I32 pos, EAS_U16 waveIndex)
{
    EAS_RESULT result;
    EAS_U32 temp;
    EAS_I32 size;
    EAS_I32 endChunk;
    EAS_I32 chunkPos;
    EAS_I32 wsmpPos = 0;
    EAS_I32 fmtPos = 0;
    EAS_I32 dataPos = 0;
    EAS_I32 dataSize = 0;
    S_WSMP_DATA *p;
 void *pSample;
    S_WSMP_DATA wsmp;

 /* seek to start of chunk */
    chunkPos = pos + 12;
 if ((result = EAS_HWFileSeek(pDLSData->hwInstData, pDLSData->fileHandle, pos)) != EAS_SUCCESS)
 return result;

 /* get the chunk type */
 if ((result = NextChunk(pDLSData, &pos, &temp, &size)) != EAS_SUCCESS)
 return result;

 /* make sure it is a wave chunk */
 if (temp != CHUNK_WAVE)
 {
 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Offset in ptbl does not point to wave chunk\n"); */ }
 return EAS_ERROR_FILE_FORMAT;
 }

 /* read to end of chunk */
    pos = chunkPos;
    endChunk = pos + size;
 while (pos < endChunk)
 {
        chunkPos = pos;

 /* get the chunk type */
 if ((result = NextChunk(pDLSData, &pos, &temp, &size)) != EAS_SUCCESS)
 return result;

 /* parse useful chunks */
 switch (temp)
 {
 case CHUNK_WSMP:
                wsmpPos = chunkPos + 8;
 break;

 case CHUNK_FMT:
                fmtPos = chunkPos + 8;
 break;

 case CHUNK_DATA:
                dataPos = chunkPos + 8;
                dataSize = size;
 break;

 default:
 break;
 }
 }

 if (dataSize < 0 || dataSize > MAX_DLS_WAVE_SIZE)
 {
 return EAS_ERROR_SOUND_LIBRARY;
 }

 /* for first pass, use temporary variable */
 if (pDLSData->pDLS == NULL)
        p = &wsmp;
 else
        p = &pDLSData->wsmpData[waveIndex];

 /* set the defaults */
    p->fineTune = 0;
    p->unityNote = 60;
    p->gain = 0;
    p->loopStart = 0;
    p->loopLength = 0;

 /* must have a fmt chunk */
 if (!fmtPos)
 {
 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "DLS wave chunk has no fmt chunk\n"); */ }
 return EAS_ERROR_UNRECOGNIZED_FORMAT;
 }

 /* must have a data chunk */
 if (!dataPos)
 {
 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "DLS wave chunk has no data chunk\n"); */ }
 return EAS_ERROR_UNRECOGNIZED_FORMAT;
 }

 /* parse the wsmp chunk */
 if (wsmpPos)
 {
 if ((result = Parse_wsmp(pDLSData, wsmpPos, p)) != EAS_SUCCESS)
 return result;
 }

 /* parse the fmt chunk */
 if ((result = Parse_fmt(pDLSData, fmtPos, p)) != EAS_SUCCESS)
 return result;

 /* calculate the size of the wavetable needed. We need only half
     * the memory for 16-bit samples when in 8-bit mode, and we need
     * double the memory for 8-bit samples in 16-bit mode. For
     * unlooped samples, we may use ADPCM. If so, we need only 1/4
     * the memory.
     *
     * We also need to add one for looped samples to allow for
     * the first sample to be copied to the end of the loop.
     */

 /* use ADPCM encode for unlooped 16-bit samples if ADPCM is enabled */
 /*lint -e{506} -e{774} groundwork for future version to support 8 & 16 bit */
 if (bitDepth == 8)
 {
 if (p->bitsPerSample == 8)
            size = dataSize;
 else
 /*lint -e{704} use shift for performance */
            size = dataSize >> 1;
 if (p->loopLength)
            size++;
 }

 else
 {
 if (p->bitsPerSample == 16)
            size = dataSize;
 else
 /*lint -e{703} use shift for performance */
            size = dataSize << 1;
 if (p->loopLength)
            size += 2;
 }

 /* for first pass, add size to wave pool size and return */
 if (pDLSData->pDLS == NULL)
 {
        pDLSData->wavePoolSize += (EAS_U32) size;
 return EAS_SUCCESS;
 }

 /* allocate memory and read in the sample data */
    pSample = (EAS_U8*)pDLSData->pDLS->pDLSSamples + pDLSData->wavePoolOffset;
    pDLSData->pDLS->pDLSSampleOffsets[waveIndex] = pDLSData->wavePoolOffset;
    pDLSData->pDLS->pDLSSampleLen[waveIndex] = (EAS_U32) size;
    pDLSData->wavePoolOffset += (EAS_U32) size;
 if (pDLSData->wavePoolOffset > pDLSData->wavePoolSize)
 {
 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Wave pool exceeded allocation\n"); */ }
 return EAS_ERROR_SOUND_LIBRARY;
 }

 if ((result = Parse_data(pDLSData, dataPos, dataSize, p, pSample, (EAS_U32)size)) != EAS_SUCCESS)
 return result;

 return EAS_SUCCESS;
}

Project: Android

static EAS_RESULT Parse_wsmp (SDLS_SYNTHESIZER_DATA *pDLSData, EAS_I32 pos, S_WSMP_DATA *p)
{
    EAS_RESULT result;
    EAS_U16 wtemp;
    EAS_U32 ltemp;
    EAS_U32 cbSize;

 /* seek to start of chunk */
 if ((result = EAS_HWFileSeek(pDLSData->hwInstData, pDLSData->fileHandle, pos)) != EAS_SUCCESS)
 return result;

 /* get structure size */
 if ((result = EAS_HWGetDWord(pDLSData->hwInstData, pDLSData->fileHandle, &cbSize, EAS_FALSE)) != EAS_SUCCESS)
 return result;

 /* get unity note */
 if ((result = EAS_HWGetWord(pDLSData->hwInstData, pDLSData->fileHandle, &wtemp, EAS_FALSE)) != EAS_SUCCESS)
 return result;
 if (wtemp <= 127)
        p->unityNote = (EAS_U8) wtemp;
 else
 {
        p->unityNote = 60;
 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Invalid unity note [%u] in DLS wsmp ignored, set to 60\n", wtemp); */ }
 }

 /* get fine tune */
 if ((result = EAS_HWGetWord(pDLSData->hwInstData, pDLSData->fileHandle, &p->fineTune, EAS_FALSE)) != EAS_SUCCESS)
 return result;

 /* get gain */
 if ((result = EAS_HWGetDWord(pDLSData->hwInstData, pDLSData->fileHandle, &p->gain, EAS_FALSE)) != EAS_SUCCESS)
 return result;
 if (p->gain > 0)
 {
 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Positive gain [%ld] in DLS wsmp ignored, set to 0dB\n", p->gain); */ }
        p->gain = 0;
 }

 /* option flags */
 if ((result = EAS_HWGetDWord(pDLSData->hwInstData, pDLSData->fileHandle, &ltemp, EAS_FALSE)) != EAS_SUCCESS)
 return result;

 /* sample loops */
 if ((result = EAS_HWGetDWord(pDLSData->hwInstData, pDLSData->fileHandle, &ltemp, EAS_FALSE)) != EAS_SUCCESS)
 return result;

 /* if looped sample, get loop data */
 if (ltemp)
 {

 if (ltemp > 1)
 { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "DLS sample with %lu loops, ignoring extra loops\n", ltemp); */ }

 /* skip ahead to loop data */
 if ((result = EAS_HWFileSeek(pDLSData->hwInstData, pDLSData->fileHandle, pos + (EAS_I32) cbSize)) != EAS_SUCCESS)
 return result;

 /* get structure size */
 if ((result = EAS_HWGetDWord(pDLSData->hwInstData, pDLSData->fileHandle, &ltemp, EAS_FALSE)) != EAS_SUCCESS)
 return result;

 /* get loop type */
 if ((result = EAS_HWGetDWord(pDLSData->hwInstData, pDLSData->fileHandle, &ltemp, EAS_FALSE)) != EAS_SUCCESS)
 return result;

 /* get loop start */
 if ((result = EAS_HWGetDWord(pDLSData->hwInstData, pDLSData->fileHandle, &p->loopStart, EAS_FALSE)) != EAS_SUCCESS)
 return result;

 /* get loop length */
 if ((result = EAS_HWGetDWord(pDLSData->hwInstData, pDLSData->fileHandle, &p->loopLength, EAS_FALSE)) != EAS_SUCCESS)
 return result;

 /* ensure no overflow */
 if (p->loopLength
 && ((p->loopStart > EAS_U32_MAX - p->loopLength)
 || (p->loopStart + p->loopLength > EAS_U32_MAX / sizeof(EAS_SAMPLE))))
 {
 return EAS_FAILURE;
 }
 }

 return EAS_SUCCESS;
}