Commit message: None
CVE: CVE-2016-0808
CWE: CWE-19
Vulnerability description: Integer overflow in the getCoverageFormat12 function in CmapCoverage.cpp in the Minikin library in Android 5.x before 5.1.1 LMY49G and 6.x before 2016-02-01 allows attackers to cause a denial of service (continuous rebooting) via an application that triggers loading of a crafted TTF font, aka internal bug 25645298.

Project: Android

bool CmapCoverage::getCoverage(SparseBitSet& coverage, const uint8_t* cmap_data, size_t cmap_size) {
 vector<uint32_t> coverageVec;
 const size_t kHeaderSize = 4;
 const size_t kNumTablesOffset = 2;
 const size_t kTableSize = 8;
 const size_t kPlatformIdOffset = 0;
 const size_t kEncodingIdOffset = 2;
 const size_t kOffsetOffset = 4;
 const int kMicrosoftPlatformId = 3;
 const int kUnicodeBmpEncodingId = 1;
 const int kUnicodeUcs4EncodingId = 10;
 if (kHeaderSize > cmap_size) {
 return false;
 }
 int numTables = readU16(cmap_data, kNumTablesOffset);
 if (kHeaderSize + numTables * kTableSize > cmap_size) {
 return false;
 }
 int bestTable = -1;
 for (int i = 0; i < numTables; i++) {
 uint16_t platformId = readU16(cmap_data, kHeaderSize + i * kTableSize + kPlatformIdOffset);
 uint16_t encodingId = readU16(cmap_data, kHeaderSize + i * kTableSize + kEncodingIdOffset);
 if (platformId == kMicrosoftPlatformId && encodingId == kUnicodeUcs4EncodingId) {
            bestTable = i;
 break;
 } else if (platformId == kMicrosoftPlatformId && encodingId == kUnicodeBmpEncodingId) {
            bestTable = i;
 }
 }
#ifdef VERBOSE_DEBUG
    ALOGD("best table = %d\n", bestTable);
#endif
 if (bestTable < 0) {
 return false;
 }
 uint32_t offset = readU32(cmap_data, kHeaderSize + bestTable * kTableSize + kOffsetOffset);
 if (offset + 2 > cmap_size) {
 return false;
 }
 uint16_t format = readU16(cmap_data, offset);
 bool success = false;
 const uint8_t* tableData = cmap_data + offset;
 const size_t tableSize = cmap_size - offset;
 if (format == 4) {
        success = getCoverageFormat4(coverageVec, tableData, tableSize);
 } else if (format == 12) {
        success = getCoverageFormat12(coverageVec, tableData, tableSize);
 }
 if (success) {
        coverage.initFromRanges(&coverageVec.front(), coverageVec.size() >> 1);
 }
#ifdef VERBOSE_DEBUG
 for (size_t i = 0; i < coverageVec.size(); i += 2) {
        ALOGD("%x:%x\n", coverageVec[i], coverageVec[i + 1]);
 }
    ALOGD("success = %d", success);
#endif
 return success;
}

Project: Android

static bool getCoverageFormat4(vector<uint32_t>& coverage, const uint8_t* data, size_t size) {
 const size_t kSegCountOffset = 6;
 const size_t kEndCountOffset = 14;
 const size_t kHeaderSize = 16;
 const size_t kSegmentSize = 8; // total size of array elements for one segment
 if (kEndCountOffset > size) {
 return false;
 }
 size_t segCount = readU16(data, kSegCountOffset) >> 1;
 if (kHeaderSize + segCount * kSegmentSize > size) {
 return false;
 }
 for (size_t i = 0; i < segCount; i++) {
 int end = readU16(data, kEndCountOffset + 2 * i);
 int start = readU16(data, kHeaderSize + 2 * (segCount + i));
 int rangeOffset = readU16(data, kHeaderSize + 2 * (3 * segCount + i));
 if (rangeOffset == 0) {
 int delta = readU16(data, kHeaderSize + 2 * (2 * segCount + i));
 if (((end + delta) & 0xffff) > end - start) {
                addRange(coverage, start, end + 1);
 } else {
 for (int j = start; j < end + 1; j++) {
 if (((j + delta) & 0xffff) != 0) {
                        addRange(coverage, j, j + 1);
 }
 }
 }
 } else {
 for (int j = start; j < end + 1; j++) {
 uint32_t actualRangeOffset = kHeaderSize + 6 * segCount + rangeOffset +
 (i + j - start) * 2;
 if (actualRangeOffset + 2 > size) {
 continue;
 }
 int glyphId = readU16(data, actualRangeOffset);
 if (glyphId != 0) {
                    addRange(coverage, j, j + 1);
 }
 }
 }
 }
 return true;
}