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:
Kenichi Ishibashi
2012-05-02 09:05:08 +09:00
parent dcecdd883a
commit 029e5f75f0
+66 -27
View File
@@ -68,7 +68,7 @@ const uint32_t kCompressionTypeMask = 0xf;
const uint32_t kCompressionTypeNone = 0; const uint32_t kCompressionTypeNone = 0;
const uint32_t kCompressionTypeGzip = 1; const uint32_t kCompressionTypeGzip = 1;
const uint32_t kCompressionTypeLzma = 2; const uint32_t kCompressionTypeLzma = 2;
const uint32_t kShortFlagsContinue = 3;
struct Point { struct Point {
int x; 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) { const uint8_t* src_buf, size_t src_size, uint32_t compression_type) {
if (compression_type == kCompressionTypeGzip) { if (compression_type == kCompressionTypeGzip) {
uLongf uncompressed_length = dst_size; 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); src_buf, src_size);
if (r != Z_OK || uncompressed_length != src_size) { if (r != Z_OK || uncompressed_length != src_size) {
return OTS_FAILURE(); return OTS_FAILURE();
@@ -763,13 +763,22 @@ bool ReadShortDirectory(ots::Buffer *file, std::vector<Table> *tables,
return OTS_FAILURE(); return OTS_FAILURE();
} }
} else { } else {
if ((flag_byte & 0x1f) >= (sizeof(known_tags) / sizeof(known_tags[0]))) {
return OTS_FAILURE();
}
tag = known_tags[flag_byte & 0x1f]; tag = known_tags[flag_byte & 0x1f];
} }
uint32_t flags = flag_byte >> 6; uint32_t flags = flag_byte >> 6;
if (flags == 3) { if (flags == kShortFlagsContinue) {
flags = last_compression_type | kWoff2FlagsContinueStream; flags = last_compression_type | kWoff2FlagsContinueStream;
} else { } else {
if (flags == kCompressionTypeNone ||
flags == kCompressionTypeGzip ||
flags == kCompressionTypeLzma) {
last_compression_type = flags; last_compression_type = flags;
} else {
return OTS_FAILURE();
}
} }
if ((flag_byte & 0x20) != 0) { if ((flag_byte & 0x20) != 0) {
flags |= kWoff2FlagsTransform; flags |= kWoff2FlagsTransform;
@@ -785,7 +794,7 @@ bool ReadShortDirectory(ots::Buffer *file, std::vector<Table> *tables,
} }
} }
uint32_t src_length = transform_length; 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)) { if (!ReadBase128(file, &src_length)) {
return OTS_FAILURE(); return OTS_FAILURE();
} }
@@ -809,61 +818,82 @@ size_t ComputeWOFF2FinalSize(const uint8_t *data, size_t length) {
file.Skip(16); file.Skip(16);
uint32_t total_length = 0; uint32_t total_length = 0;
if (!file.ReadU32(&total_length)) { if (!file.ReadU32(&total_length)) {
return OTS_FAILURE(); return 0;
} }
return total_length; return total_length;
} }
bool ConvertWOFF2ToTTF(uint8_t* result, size_t result_length, bool ConvertWOFF2ToTTF(uint8_t* result, size_t result_length,
const uint8_t* data, size_t length) { const uint8_t* data, size_t length) {
static const uint32_t kWoff2Signature = 0x774f4632; // "wOF2"
ots::Buffer file(data, length); ots::Buffer file(data, length);
uint32_t signature = 0; uint32_t signature = 0;
uint32_t flavor = 0; uint32_t flavor = 0;
if (!file.ReadU32(&signature) || signature != 0x774f4632 || if (!file.ReadU32(&signature) || signature != kWoff2Signature ||
!file.ReadU32(&flavor)) { !file.ReadU32(&flavor)) {
return OTS_FAILURE(); return OTS_FAILURE();
} }
file.Skip(4);
uint16_t num_tables = 0; // TODO(bashi): Should call IsValidVersionTag() here.
if (!file.ReadU16(&num_tables)) {
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(); return OTS_FAILURE();
} }
file.Skip(30);
std::vector<Table> tables(num_tables); std::vector<Table> tables(num_tables);
// Note: change below to ReadLongDirectory to enable long format. // Note: change below to ReadLongDirectory to enable long format.
if (!ReadShortDirectory(&file, &tables, num_tables)) { if (!ReadShortDirectory(&file, &tables, num_tables)) {
return OTS_FAILURE(); return OTS_FAILURE();
} }
size_t src_offset = file.offset(); uint64_t src_offset = file.offset();
size_t dst_offset = kSfntHeaderSize + kSfntEntrySize * num_tables; uint64_t dst_offset = kSfntHeaderSize +
size_t uncompressed_sum = 0; kSfntEntrySize * static_cast<uint64_t>(num_tables);
for (int i = 0; i < num_tables; ++i) { uint64_t uncompressed_sum = 0;
for (uint16_t i = 0; i < num_tables; ++i) {
Table* table = &tables[i]; Table* table = &tables[i];
table->src_offset = src_offset; 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(); return OTS_FAILURE();
} }
src_offset += table->src_length;
src_offset = Round4(src_offset); // TODO: reconsider src_offset = Round4(src_offset); // TODO: reconsider
table->dst_offset = dst_offset; 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(); return OTS_FAILURE();
} }
dst_offset += table->dst_length;
dst_offset = Round4(dst_offset); dst_offset = Round4(dst_offset);
if ((table->flags & kCompressionTypeMask) != kCompressionTypeNone) { 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(); return OTS_FAILURE();
} }
uncompressed_sum += table->src_length;
} }
} }
// Enforce same 30M limit on uncompressed tables as OTS // Enforce same 30M limit on uncompressed tables as OTS
if (uncompressed_sum > 30 * 1024 * 1024) { if (uncompressed_sum > 30 * 1024 * 1024) {
return OTS_FAILURE(); 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(); 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, output_search_range);
offset = Store16(result, offset, max_pow2); offset = Store16(result, offset, max_pow2);
offset = Store16(result, offset, (num_tables << 4) - output_search_range); 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]; const Table* table = &tables[i];
offset = StoreU32(result, offset, table->tag); offset = StoreU32(result, offset, table->tag);
offset = StoreU32(result, offset, 0); // checksum, to fill in later 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; std::vector<uint8_t> uncompressed_buf;
bool continue_valid = false; 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]; const Table* table = &tables[i];
uint32_t flags = table->flags; uint32_t flags = table->flags;
const uint8_t* src_buf = data + table->src_offset; 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; transform_buf = src_buf;
continue_valid = false; continue_valid = false;
} else if ((flags & kWoff2FlagsContinueStream) == 0) { } else if ((flags & kWoff2FlagsContinueStream) == 0) {
size_t total_size = transform_length; uint64_t total_size = transform_length;
for (int j = i + 1; j < num_tables; ++j) { for (uint16_t j = i + 1; j < num_tables; ++j) {
if ((tables[j].flags & kWoff2FlagsContinueStream) == 0) { if ((tables[j].flags & kWoff2FlagsContinueStream) == 0) {
break; 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(); return OTS_FAILURE();
} }
total_size += tables[j].transform_length;
} }
uncompressed_buf.resize(total_size); uncompressed_buf.resize(total_size);
if (!Woff2Uncompress(&uncompressed_buf[0], 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]; transform_buf = &uncompressed_buf[0];
continue_valid = true; continue_valid = true;
} else {
return OTS_FAILURE();
} }
if ((flags & kWoff2FlagsTransform) == 0) { if ((flags & kWoff2FlagsTransform) == 0) {
if (transform_length != table->dst_length) { if (transform_length != table->dst_length) {
return OTS_FAILURE(); 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, std::memcpy(result + table->dst_offset, transform_buf,
transform_length); transform_length);
} else { } else {
@@ -939,6 +975,9 @@ bool ConvertWOFF2ToTTF(uint8_t *result, size_t result_length,
} }
if (continue_valid) { if (continue_valid) {
transform_buf += transform_length; transform_buf += transform_length;
if (transform_buf > &uncompressed_buf[uncompressed_buf.size()]) {
return OTS_FAILURE();
}
} }
} }