Reject fonts with invalid ranges in cmap

A corrupt or malicious font may have a negative size in its cmap
range, which in turn could lead to memory corruption. This patch
detects the case and rejects the font, and also includes an assertion
in the sparse bit set implementation if we missed any such case.

External issue:
https://code.google.com/p/android/issues/detail?id=192618

Bug: 26413177
Change-Id: Icc0c80e4ef389abba0964495b89aa0fae3e9f4b2
This commit is contained in:
Raph Levien
2016-01-06 14:31:23 -08:00
parent 1fd1c39021
commit f5d2fa97bb
2 changed files with 27 additions and 16 deletions

View File

@@ -50,7 +50,7 @@ static void addRange(vector<uint32_t> &coverage, uint32_t start, uint32_t end) {
} }
} }
// Get the coverage information out of a Format 12 subtable, storing it in the coverage vector // Get the coverage information out of a Format 4 subtable, storing it in the coverage vector
static bool getCoverageFormat4(vector<uint32_t>& coverage, const uint8_t* data, size_t size) { static bool getCoverageFormat4(vector<uint32_t>& coverage, const uint8_t* data, size_t size) {
const size_t kSegCountOffset = 6; const size_t kSegCountOffset = 6;
const size_t kEndCountOffset = 14; const size_t kEndCountOffset = 14;
@@ -64,28 +64,32 @@ static bool getCoverageFormat4(vector<uint32_t>& coverage, const uint8_t* data,
return false; return false;
} }
for (size_t i = 0; i < segCount; i++) { for (size_t i = 0; i < segCount; i++) {
int end = readU16(data, kEndCountOffset + 2 * i); uint32_t end = readU16(data, kEndCountOffset + 2 * i);
int start = readU16(data, kHeaderSize + 2 * (segCount + i)); uint32_t start = readU16(data, kHeaderSize + 2 * (segCount + i));
int rangeOffset = readU16(data, kHeaderSize + 2 * (3 * segCount + i)); if (end < start) {
// invalid segment range: size must be positive
return false;
}
uint32_t rangeOffset = readU16(data, kHeaderSize + 2 * (3 * segCount + i));
if (rangeOffset == 0) { if (rangeOffset == 0) {
int delta = readU16(data, kHeaderSize + 2 * (2 * segCount + i)); uint32_t delta = readU16(data, kHeaderSize + 2 * (2 * segCount + i));
if (((end + delta) & 0xffff) > end - start) { if (((end + delta) & 0xffff) > end - start) {
addRange(coverage, start, end + 1); addRange(coverage, start, end + 1);
} else { } else {
for (int j = start; j < end + 1; j++) { for (uint32_t j = start; j < end + 1; j++) {
if (((j + delta) & 0xffff) != 0) { if (((j + delta) & 0xffff) != 0) {
addRange(coverage, j, j + 1); addRange(coverage, j, j + 1);
} }
} }
} }
} else { } else {
for (int j = start; j < end + 1; j++) { for (uint32_t j = start; j < end + 1; j++) {
uint32_t actualRangeOffset = kHeaderSize + 6 * segCount + rangeOffset + uint32_t actualRangeOffset = kHeaderSize + 6 * segCount + rangeOffset +
(i + j - start) * 2; (i + j - start) * 2;
if (actualRangeOffset + 2 > size) { if (actualRangeOffset + 2 > size) {
return false; return false;
} }
int glyphId = readU16(data, actualRangeOffset); uint32_t glyphId = readU16(data, actualRangeOffset);
if (glyphId != 0) { if (glyphId != 0) {
addRange(coverage, j, j + 1); addRange(coverage, j, j + 1);
} }
@@ -115,6 +119,10 @@ static bool getCoverageFormat12(vector<uint32_t>& coverage, const uint8_t* data,
uint32_t groupOffset = kFirstGroupOffset + i * kGroupSize; uint32_t groupOffset = kFirstGroupOffset + i * kGroupSize;
uint32_t start = readU32(data, groupOffset + kStartCharCodeOffset); uint32_t start = readU32(data, groupOffset + kStartCharCodeOffset);
uint32_t end = readU32(data, groupOffset + kEndCharCodeOffset); uint32_t end = readU32(data, groupOffset + kEndCharCodeOffset);
if (end < start) {
// invalid group range: size must be positive
return false;
}
addRange(coverage, start, end + 1); // file is inclusive, vector is exclusive addRange(coverage, start, end + 1); // file is inclusive, vector is exclusive
} }
return true; return true;
@@ -128,18 +136,19 @@ bool CmapCoverage::getCoverage(SparseBitSet& coverage, const uint8_t* cmap_data,
const size_t kPlatformIdOffset = 0; const size_t kPlatformIdOffset = 0;
const size_t kEncodingIdOffset = 2; const size_t kEncodingIdOffset = 2;
const size_t kOffsetOffset = 4; const size_t kOffsetOffset = 4;
const int kMicrosoftPlatformId = 3; const uint16_t kMicrosoftPlatformId = 3;
const int kUnicodeBmpEncodingId = 1; const uint16_t kUnicodeBmpEncodingId = 1;
const int kUnicodeUcs4EncodingId = 10; const uint16_t kUnicodeUcs4EncodingId = 10;
const uint32_t kNoTable = UINT32_MAX;
if (kHeaderSize > cmap_size) { if (kHeaderSize > cmap_size) {
return false; return false;
} }
int numTables = readU16(cmap_data, kNumTablesOffset); uint32_t numTables = readU16(cmap_data, kNumTablesOffset);
if (kHeaderSize + numTables * kTableSize > cmap_size) { if (kHeaderSize + numTables * kTableSize > cmap_size) {
return false; return false;
} }
int bestTable = -1; uint32_t bestTable = kNoTable;
for (int i = 0; i < numTables; i++) { for (uint32_t i = 0; i < numTables; i++) {
uint16_t platformId = readU16(cmap_data, kHeaderSize + i * kTableSize + kPlatformIdOffset); uint16_t platformId = readU16(cmap_data, kHeaderSize + i * kTableSize + kPlatformIdOffset);
uint16_t encodingId = readU16(cmap_data, kHeaderSize + i * kTableSize + kEncodingIdOffset); uint16_t encodingId = readU16(cmap_data, kHeaderSize + i * kTableSize + kEncodingIdOffset);
if (platformId == kMicrosoftPlatformId && encodingId == kUnicodeUcs4EncodingId) { if (platformId == kMicrosoftPlatformId && encodingId == kUnicodeUcs4EncodingId) {
@@ -152,11 +161,11 @@ bool CmapCoverage::getCoverage(SparseBitSet& coverage, const uint8_t* cmap_data,
#ifdef PRINTF_DEBUG #ifdef PRINTF_DEBUG
printf("best table = %d\n", bestTable); printf("best table = %d\n", bestTable);
#endif #endif
if (bestTable < 0) { if (bestTable == kNoTable) {
return false; return false;
} }
uint32_t offset = readU32(cmap_data, kHeaderSize + bestTable * kTableSize + kOffsetOffset); uint32_t offset = readU32(cmap_data, kHeaderSize + bestTable * kTableSize + kOffsetOffset);
if (offset + 2 > cmap_size) { if (offset > cmap_size - 2) {
return false; return false;
} }
uint16_t format = readU16(cmap_data, offset); uint16_t format = readU16(cmap_data, offset);

View File

@@ -14,6 +14,7 @@
* limitations under the License. * limitations under the License.
*/ */
#include <cutils/log.h>
#include <stddef.h> #include <stddef.h>
#include <string.h> #include <string.h>
#include <minikin/SparseBitSet.h> #include <minikin/SparseBitSet.h>
@@ -71,6 +72,7 @@ void SparseBitSet::initFromRanges(const uint32_t* ranges, size_t nRanges) {
for (size_t i = 0; i < nRanges; i++) { for (size_t i = 0; i < nRanges; i++) {
uint32_t start = ranges[i * 2]; uint32_t start = ranges[i * 2];
uint32_t end = ranges[i * 2 + 1]; uint32_t end = ranges[i * 2 + 1];
LOG_ALWAYS_FATAL_IF(end < start); // make sure range size is nonnegative
uint32_t startPage = start >> kLogValuesPerPage; uint32_t startPage = start >> kLogValuesPerPage;
uint32_t endPage = (end - 1) >> kLogValuesPerPage; uint32_t endPage = (end - 1) >> kLogValuesPerPage;
if (startPage >= nonzeroPageEnd) { if (startPage >= nonzeroPageEnd) {