Commit message: None
CVE: CVE-2017-0588
CWE: CWE-119
Vulnerability description: A remote code execution vulnerability in id3/ID3.cpp in libstagefright in Mediaserver could enable an attacker using a specially crafted file to cause memory corruption during media file and data processing. This issue is rated as Critical due to the possibility of remote code execution within the context of the Mediaserver process. Product: Android. Versions: 4.4.4, 5.0.2, 5.1.1, 6.0, 6.0.1, 7.0, 7.1.1, 7.1.2. Android ID: A-34618607.

Project: Android

ID3::ID3(const uint8_t *data, size_t size, bool ignoreV1)
 : mIsValid(false),
      mData(NULL),
      mSize(0),
      mFirstFrameOffset(0),
      mVersion(ID3_UNKNOWN),
      mRawSize(0) {
    sp<MemorySource> source = new (std::nothrow) MemorySource(data, size);

 if (source == NULL)
 return;

    mIsValid = parseV2(source, 0);

 if (!mIsValid && !ignoreV1) {
        mIsValid = parseV1(source);
 }
}

Project: Android

static size_t StringSize(const uint8_t *start, size_t limit, uint8_t encoding) {

 if (encoding == 0x00 || encoding == 0x03) {
 return strnlen((const char *)start, limit) + 1;
 }

 size_t n = 0;
 while ((n+1 < limit) && (start[n] != '\0' || start[n + 1] != '\0')) {
        n += 2;
 }
    n += 2;
 return n;
}

Project: Android

ID3::getAlbumArt(size_t *length, String8 *mime) const {
 *length = 0;
    mime->setTo("");

 Iterator it(
 *this,
 (mVersion == ID3_V2_3 || mVersion == ID3_V2_4) ? "APIC" : "PIC");

 while (!it.done()) {
 size_t size;
 const uint8_t *data = it.getData(&size);
 if (!data) {
 return NULL;
 }

 if (mVersion == ID3_V2_3 || mVersion == ID3_V2_4) {
 uint8_t encoding = data[0];
 size_t consumed = 1;

 size_t mimeLen = StringSize(&data[consumed], size - consumed, 0x00);
 if (mimeLen > size - consumed) {
                ALOGW("bogus album art size: mime");
 return NULL;
 }
            mime->setTo((const char *)&data[consumed]);
            consumed += mimeLen;

#if 0
 uint8_t picType = data[consumed];
 if (picType != 0x03) {
                it.next();
 continue;
 }
#endif

            consumed++;
 if (consumed >= size) {
                ALOGW("bogus album art size: pic type");
 return NULL;
 }

 size_t descLen = StringSize(&data[consumed], size - consumed, encoding);
            consumed += descLen;

 if (consumed >= size) {
                ALOGW("bogus album art size: description");
 return NULL;
 }

 *length = size - consumed;

 return &data[consumed];
 } else {
 uint8_t encoding = data[0];

 if (size <= 5) {
 return NULL;
 }

 if (!memcmp(&data[1], "PNG", 3)) {
                mime->setTo("image/png");
 } else if (!memcmp(&data[1], "JPG", 3)) {
                mime->setTo("image/jpeg");
 } else if (!memcmp(&data[1], "-->", 3)) {
                mime->setTo("text/plain");
 } else {
 return NULL;
 }

#if 0
 uint8_t picType = data[4];
 if (picType != 0x03) {
                it.next();
 continue;
 }
#endif

 size_t descLen = StringSize(&data[5], size - 5, encoding);
 if (descLen > size - 5) {
 return NULL;
 }

 *length = size - 5 - descLen;

 return &data[5 + descLen];
 }
 }

 return NULL;
}

Project: Android

void ID3::Iterator::getstring(String8 *id, bool otherdata) const {
    id->setTo("");

 const uint8_t *frameData = mFrameData;
 if (frameData == NULL) {
 return;
 }

 uint8_t encoding = *frameData;

 if (mParent.mVersion == ID3_V1 || mParent.mVersion == ID3_V1_1) {
 if (mOffset == 126 || mOffset == 127) {
 char tmp[16];
            sprintf(tmp, "%d", (int)*frameData);

            id->setTo(tmp);
 return;
 }

        id->setTo((const char*)frameData, mFrameSize);
 return;
 }

 if (mFrameSize < getHeaderLength() + 1) {
 return;
 }
 size_t n = mFrameSize - getHeaderLength() - 1;
 if (otherdata) {
        frameData += 4;
 int32_t i = n - 4;
 while(--i >= 0 && *++frameData != 0) ;
 int skipped = (frameData - mFrameData);
 if (skipped >= (int)n) {
 return;
 }
        n -= skipped;
 }

 if (n <= 0) {
 return;
 }

 if (encoding == 0x00) {
        id->setTo((const char*)frameData + 1, n);
 } else if (encoding == 0x03) {
        id->setTo((const char *)(frameData + 1), n);
 } else if (encoding == 0x02) {
 int len = n / 2;
 const char16_t *framedata = (const char16_t *) (frameData + 1);
 char16_t *framedatacopy = NULL;
#if BYTE_ORDER == LITTLE_ENDIAN
 if (len > 0) {
            framedatacopy = new (std::nothrow) char16_t[len];
 if (framedatacopy == NULL) {
 return;
 }
 for (int i = 0; i < len; i++) {
                framedatacopy[i] = bswap_16(framedata[i]);
 }
            framedata = framedatacopy;
 }
#endif
        id->setTo(framedata, len);
 if (framedatacopy != NULL) {
 delete[] framedatacopy;
 }
 } else if (encoding == 0x01) {
 int len = n / 2;
 const char16_t *framedata = (const char16_t *) (frameData + 1);
 char16_t *framedatacopy = NULL;
 if (*framedata == 0xfffe) {
 if (len <= 1) {
 return; // nothing after the marker
 }
            framedatacopy = new (std::nothrow) char16_t[len];
 if (framedatacopy == NULL) {
 return;
 }
 for (int i = 0; i < len; i++) {
                framedatacopy[i] = bswap_16(framedata[i]);
 }
            framedata = framedatacopy;
            framedata++;
            len--;
 } else if (*framedata == 0xfeff) {
 if (len <= 1) {
 return; // nothing after the marker
 }
            framedata++;
            len--;
 }

 bool eightBit = true;
 for (int i = 0; i < len; i++) {
 if (framedata[i] > 0xff) {
                eightBit = false;
 break;
 }
 }
 if (eightBit) {
 char *frame8 = new (std::nothrow) char[len];
 if (frame8 != NULL) {
 for (int i = 0; i < len; i++) {
                    frame8[i] = framedata[i];
 }
                id->setTo(frame8, len);
 delete [] frame8;
 } else {
                id->setTo(framedata, len);
 }
 } else {
            id->setTo(framedata, len);
 }

 if (framedatacopy != NULL) {
 delete[] framedatacopy;
 }
 }
}