This CL adds some validation checks. Following CLs will add further checks.
This CL also changes pointer declarations from "type *variable" to "type* variable" to follow other OTS code. BUG=none TEST=compiled Review URL: https://codereview.appspot.com/6139064
This commit is contained in:
+66
-27
@@ -68,7 +68,7 @@ const uint32_t kCompressionTypeMask = 0xf;
|
||||
const uint32_t kCompressionTypeNone = 0;
|
||||
const uint32_t kCompressionTypeGzip = 1;
|
||||
const uint32_t kCompressionTypeLzma = 2;
|
||||
|
||||
const uint32_t kShortFlagsContinue = 3;
|
||||
|
||||
struct Point {
|
||||
int x;
|
||||
@@ -673,7 +673,7 @@ bool Woff2Uncompress(uint8_t *dst_buf, size_t dst_size,
|
||||
const uint8_t* src_buf, size_t src_size, uint32_t compression_type) {
|
||||
if (compression_type == kCompressionTypeGzip) {
|
||||
uLongf uncompressed_length = dst_size;
|
||||
int r = uncompress((Bytef *)dst_buf, &uncompressed_length,
|
||||
int r = uncompress(reinterpret_cast<Bytef *>(dst_buf), &uncompressed_length,
|
||||
src_buf, src_size);
|
||||
if (r != Z_OK || uncompressed_length != src_size) {
|
||||
return OTS_FAILURE();
|
||||
@@ -763,13 +763,22 @@ bool ReadShortDirectory(ots::Buffer *file, std::vector<Table> *tables,
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
} else {
|
||||
if ((flag_byte & 0x1f) >= (sizeof(known_tags) / sizeof(known_tags[0]))) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
tag = known_tags[flag_byte & 0x1f];
|
||||
}
|
||||
uint32_t flags = flag_byte >> 6;
|
||||
if (flags == 3) {
|
||||
if (flags == kShortFlagsContinue) {
|
||||
flags = last_compression_type | kWoff2FlagsContinueStream;
|
||||
} else {
|
||||
if (flags == kCompressionTypeNone ||
|
||||
flags == kCompressionTypeGzip ||
|
||||
flags == kCompressionTypeLzma) {
|
||||
last_compression_type = flags;
|
||||
} else {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
if ((flag_byte & 0x20) != 0) {
|
||||
flags |= kWoff2FlagsTransform;
|
||||
@@ -785,7 +794,7 @@ bool ReadShortDirectory(ots::Buffer *file, std::vector<Table> *tables,
|
||||
}
|
||||
}
|
||||
uint32_t src_length = transform_length;
|
||||
if ((flag_byte >> 6) == 1 | (flag_byte >> 6) == 2) {
|
||||
if ((flag_byte >> 6) == 1 || (flag_byte >> 6) == 2) {
|
||||
if (!ReadBase128(file, &src_length)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
@@ -809,61 +818,82 @@ size_t ComputeWOFF2FinalSize(const uint8_t *data, size_t length) {
|
||||
file.Skip(16);
|
||||
uint32_t total_length = 0;
|
||||
if (!file.ReadU32(&total_length)) {
|
||||
return OTS_FAILURE();
|
||||
return 0;
|
||||
}
|
||||
return total_length;
|
||||
}
|
||||
|
||||
bool ConvertWOFF2ToTTF(uint8_t* result, size_t result_length,
|
||||
const uint8_t* data, size_t length) {
|
||||
static const uint32_t kWoff2Signature = 0x774f4632; // "wOF2"
|
||||
ots::Buffer file(data, length);
|
||||
|
||||
uint32_t signature = 0;
|
||||
uint32_t flavor = 0;
|
||||
if (!file.ReadU32(&signature) || signature != 0x774f4632 ||
|
||||
if (!file.ReadU32(&signature) || signature != kWoff2Signature ||
|
||||
!file.ReadU32(&flavor)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
file.Skip(4);
|
||||
uint16_t num_tables = 0;
|
||||
if (!file.ReadU16(&num_tables)) {
|
||||
|
||||
// TODO(bashi): Should call IsValidVersionTag() here.
|
||||
|
||||
uint32_t reported_length = 0;
|
||||
if (!file.ReadU32(&reported_length) || length != reported_length) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
uint16_t num_tables = 0;
|
||||
if (!file.ReadU16(&num_tables) || !num_tables) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
// We don't care about these fields of the header:
|
||||
// uint16_t reserved
|
||||
// uint32_t total_sfnt_size
|
||||
// uint16_t major_version, minor_version
|
||||
// uint32_t meta_offset, meta_length, meta_orig_length
|
||||
// uint32_t priv_offset, priv_length
|
||||
if (!file.Skip(30)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
file.Skip(30);
|
||||
std::vector<Table> tables(num_tables);
|
||||
// Note: change below to ReadLongDirectory to enable long format.
|
||||
if (!ReadShortDirectory(&file, &tables, num_tables)) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
size_t src_offset = file.offset();
|
||||
size_t dst_offset = kSfntHeaderSize + kSfntEntrySize * num_tables;
|
||||
size_t uncompressed_sum = 0;
|
||||
for (int i = 0; i < num_tables; ++i) {
|
||||
uint64_t src_offset = file.offset();
|
||||
uint64_t dst_offset = kSfntHeaderSize +
|
||||
kSfntEntrySize * static_cast<uint64_t>(num_tables);
|
||||
uint64_t uncompressed_sum = 0;
|
||||
for (uint16_t i = 0; i < num_tables; ++i) {
|
||||
Table* table = &tables[i];
|
||||
table->src_offset = src_offset;
|
||||
if (src_offset + table->src_length < src_offset) {
|
||||
src_offset += table->src_length;
|
||||
if (src_offset > std::numeric_limits<uint32_t>::max()) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
src_offset += table->src_length;
|
||||
src_offset = Round4(src_offset); // TODO: reconsider
|
||||
table->dst_offset = dst_offset;
|
||||
if (dst_offset + table->dst_length < dst_offset) {
|
||||
dst_offset += table->dst_length;
|
||||
if (dst_offset > std::numeric_limits<uint32_t>::max()) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
dst_offset += table->dst_length;
|
||||
dst_offset = Round4(dst_offset);
|
||||
if ((table->flags & kCompressionTypeMask) != kCompressionTypeNone) {
|
||||
if (uncompressed_sum + table->src_length < uncompressed_sum) {
|
||||
uncompressed_sum += table->src_length;
|
||||
if (uncompressed_sum > std::numeric_limits<uint32_t>::max()) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
uncompressed_sum += table->src_length;
|
||||
}
|
||||
}
|
||||
// Enforce same 30M limit on uncompressed tables as OTS
|
||||
if (uncompressed_sum > 30 * 1024 * 1024) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (dst_offset > result_length) {
|
||||
if (src_offset > length || dst_offset > result_length) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
const uint32_t sfnt_header_and_table_directory_size = 12 + 16 * num_tables;
|
||||
if (sfnt_header_and_table_directory_size > result_length) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
@@ -879,7 +909,7 @@ bool ConvertWOFF2ToTTF(uint8_t *result, size_t result_length,
|
||||
offset = Store16(result, offset, output_search_range);
|
||||
offset = Store16(result, offset, max_pow2);
|
||||
offset = Store16(result, offset, (num_tables << 4) - output_search_range);
|
||||
for (int i = 0; i < num_tables; ++i) {
|
||||
for (uint16_t i = 0; i < num_tables; ++i) {
|
||||
const Table* table = &tables[i];
|
||||
offset = StoreU32(result, offset, table->tag);
|
||||
offset = StoreU32(result, offset, 0); // checksum, to fill in later
|
||||
@@ -888,7 +918,7 @@ bool ConvertWOFF2ToTTF(uint8_t *result, size_t result_length,
|
||||
}
|
||||
std::vector<uint8_t> uncompressed_buf;
|
||||
bool continue_valid = false;
|
||||
for (int i = 0; i < num_tables; ++i) {
|
||||
for (uint16_t i = 0; i < num_tables; ++i) {
|
||||
const Table* table = &tables[i];
|
||||
uint32_t flags = table->flags;
|
||||
const uint8_t* src_buf = data + table->src_offset;
|
||||
@@ -906,15 +936,15 @@ bool ConvertWOFF2ToTTF(uint8_t *result, size_t result_length,
|
||||
transform_buf = src_buf;
|
||||
continue_valid = false;
|
||||
} else if ((flags & kWoff2FlagsContinueStream) == 0) {
|
||||
size_t total_size = transform_length;
|
||||
for (int j = i + 1; j < num_tables; ++j) {
|
||||
uint64_t total_size = transform_length;
|
||||
for (uint16_t j = i + 1; j < num_tables; ++j) {
|
||||
if ((tables[j].flags & kWoff2FlagsContinueStream) == 0) {
|
||||
break;
|
||||
}
|
||||
if (total_size + tables[j].transform_length < total_size) {
|
||||
total_size += tables[j].transform_length;
|
||||
if (total_size > std::numeric_limits<uint32_t>::max()) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
total_size += tables[j].transform_length;
|
||||
}
|
||||
uncompressed_buf.resize(total_size);
|
||||
if (!Woff2Uncompress(&uncompressed_buf[0], total_size,
|
||||
@@ -923,12 +953,18 @@ bool ConvertWOFF2ToTTF(uint8_t *result, size_t result_length,
|
||||
}
|
||||
transform_buf = &uncompressed_buf[0];
|
||||
continue_valid = true;
|
||||
} else {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
|
||||
if ((flags & kWoff2FlagsTransform) == 0) {
|
||||
if (transform_length != table->dst_length) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
if (static_cast<uint64_t>(table->dst_offset + transform_length) >
|
||||
result_length) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
std::memcpy(result + table->dst_offset, transform_buf,
|
||||
transform_length);
|
||||
} else {
|
||||
@@ -939,6 +975,9 @@ bool ConvertWOFF2ToTTF(uint8_t *result, size_t result_length,
|
||||
}
|
||||
if (continue_valid) {
|
||||
transform_buf += transform_length;
|
||||
if (transform_buf > &uncompressed_buf[uncompressed_buf.size()]) {
|
||||
return OTS_FAILURE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user