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 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();
}
}
}