Bug 1396026 - Update OTS to accept Awami Nastaliq. r=froydnj,jfkthame

MozReview-Commit-ID: EvF3YDhuwNn

--HG--
extra : rebase_source : 20046aa4f2695f923574bf00207f06a113d1f246
This commit is contained in:
Kevin Hsieh 2017-09-07 10:40:59 -07:00
Родитель 9a59011a99
Коммит a0a376f2f1
8 изменённых файлов: 186 добавлений и 107 удалений

Просмотреть файл

@ -2,7 +2,7 @@ This is the Sanitiser for OpenType project, from http://code.google.com/p/ots/.
Our reference repository is https://github.com/khaledhosny/ots/. Our reference repository is https://github.com/khaledhosny/ots/.
Current revision: e2d4b5daba24e746a48d240e90d92fe09a20b681 (5.2.0) Current revision: f87b4556191e4132ef5c47365762eb88ace97fc3 (6.0.0)
Upstream files included: LICENSE, src/, include/, tests/*.cc Upstream files included: LICENSE, src/, include/, tests/*.cc

Просмотреть файл

@ -10,25 +10,23 @@ diff --git a/gfx/ots/src/glat.cc b/gfx/ots/src/glat.cc
#include <list> #include <list>
namespace ots { namespace ots {
@@ -201,13 +201,15 @@ bool OpenTypeGLAT_v3::Parse(const uint8_t* data, size_t length, @@ -201,14 +201,15 @@ bool OpenTypeGLAT_v3::Parse(const uint8_t* data, size_t length,
return DropGraphite("Illegal nested compression"); return DropGraphite("Illegal nested compression");
} }
std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE, 0); std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE);
- int ret = LZ4_decompress_safe( - int ret = LZ4_decompress_safe_partial(
- reinterpret_cast<const char*>(data + table.offset()),
- reinterpret_cast<char*>(decompressed.data()),
- table.remaining(),
- decompressed.size());
- if (ret < 0) {
- return DropGraphite("Decompression failed with error code %d", ret);
+ size_t outputSize = 0; + size_t outputSize = 0;
+ if (!mozilla::Compression::LZ4::decompress( + bool ret = mozilla::Compression::LZ4::decompressPartial(
+ reinterpret_cast<const char*>(data + table.offset()), reinterpret_cast<const char*>(data + table.offset()),
+ table.remaining(), - reinterpret_cast<char*>(decompressed.data()),
+ reinterpret_cast<char*>(decompressed.data()), table.remaining(), // input buffer size (input size + padding)
+ decompressed.size(), + reinterpret_cast<char*>(decompressed.data()),
+ &outputSize) || decompressed.size(), // target output size
+ outputSize != (this->compHead & FULL_SIZE)) { - decompressed.size()); // output buffer size
- if (ret != decompressed.size()) {
- return DropGraphite("Decompression failed with error code %d", ret);
+ &outputSize); // return output size
+ if (!ret || outputSize != decompressed.size()) {
+ return DropGraphite("Decompression failed"); + return DropGraphite("Decompression failed");
} }
return this->Parse(decompressed.data(), decompressed.size(), true); return this->Parse(decompressed.data(), decompressed.size(), true);
@ -45,25 +43,23 @@ diff --git a/gfx/ots/src/silf.cc b/gfx/ots/src/silf.cc
#include <cmath> #include <cmath>
namespace ots { namespace ots {
@@ -39,13 +39,15 @@ bool OpenTypeSILF::Parse(const uint8_t* data, size_t length, @@ -39,14 +39,15 @@ bool OpenTypeSILF::Parse(const uint8_t* data, size_t length,
return DropGraphite("Illegal nested compression"); return DropGraphite("Illegal nested compression");
} }
std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE, 0); std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE);
- int ret = LZ4_decompress_safe( - int ret = LZ4_decompress_safe_partial(
- reinterpret_cast<const char*>(data + table.offset()),
- reinterpret_cast<char*>(decompressed.data()),
- table.remaining(),
- decompressed.size());
- if (ret < 0) {
- return DropGraphite("Decompression failed with error code %d", ret);
+ size_t outputSize = 0; + size_t outputSize = 0;
+ if (!mozilla::Compression::LZ4::decompress( + bool ret = mozilla::Compression::LZ4::decompressPartial(
+ reinterpret_cast<const char*>(data + table.offset()), reinterpret_cast<const char*>(data + table.offset()),
+ table.remaining(), - reinterpret_cast<char*>(decompressed.data()),
+ reinterpret_cast<char*>(decompressed.data()), table.remaining(), // input buffer size (input size + padding)
+ decompressed.size(), + reinterpret_cast<char*>(decompressed.data()),
+ &outputSize) || decompressed.size(), // target output size
+ outputSize != (this->compHead & FULL_SIZE)) { - decompressed.size()); // output buffer size
- if (ret != decompressed.size()) {
- return DropGraphite("Decompression failed with error code %d", ret);
+ &outputSize); // return output size
+ if (!ret || outputSize != decompressed.size()) {
+ return DropGraphite("Decompression failed"); + return DropGraphite("Decompression failed");
} }
return this->Parse(decompressed.data(), decompressed.size(), true); return this->Parse(decompressed.data(), decompressed.size(), true);

Просмотреть файл

@ -200,15 +200,15 @@ bool OpenTypeGLAT_v3::Parse(const uint8_t* data, size_t length,
if (prevent_decompression) { if (prevent_decompression) {
return DropGraphite("Illegal nested compression"); return DropGraphite("Illegal nested compression");
} }
std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE, 0); std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE);
size_t outputSize = 0; size_t outputSize = 0;
if (!mozilla::Compression::LZ4::decompress( bool ret = mozilla::Compression::LZ4::decompressPartial(
reinterpret_cast<const char*>(data + table.offset()), reinterpret_cast<const char*>(data + table.offset()),
table.remaining(), table.remaining(), // input buffer size (input size + padding)
reinterpret_cast<char*>(decompressed.data()), reinterpret_cast<char*>(decompressed.data()),
decompressed.size(), decompressed.size(), // target output size
&outputSize) || &outputSize); // return output size
outputSize != (this->compHead & FULL_SIZE)) { if (!ret || outputSize != decompressed.size()) {
return DropGraphite("Decompression failed"); return DropGraphite("Decompression failed");
} }
return this->Parse(decompressed.data(), decompressed.size(), true); return this->Parse(decompressed.data(), decompressed.size(), true);
@ -388,7 +388,7 @@ GlatEntry::ParsePart(Buffer& table) {
} }
//this->attributes.resize(this->num); //this->attributes.resize(this->num);
for (unsigned i = 0; i < this->num; ++i) { for (int i = 0; i < this->num; ++i) {
this->attributes.emplace_back(); this->attributes.emplace_back();
if (!table.ReadS16(&this->attributes[i])) { if (!table.ReadS16(&this->attributes[i])) {
return parent->Error("GlatEntry: Failed to read attribute %u", i); return parent->Error("GlatEntry: Failed to read attribute %u", i);

Просмотреть файл

@ -311,15 +311,15 @@ bool ParseClassDefFormat2(const ots::Font *font,
// Skip format field. // Skip format field.
if (!subtable.Skip(2)) { if (!subtable.Skip(2)) {
return OTS_FAILURE_MSG("Failed to skip format of class defintion header"); return OTS_FAILURE_MSG("Failed to read class definition format");
} }
uint16_t range_count = 0; uint16_t range_count = 0;
if (!subtable.ReadU16(&range_count)) { if (!subtable.ReadU16(&range_count)) {
return OTS_FAILURE_MSG("Failed to read range count in class definition"); return OTS_FAILURE_MSG("Failed to read classRangeCount");
} }
if (range_count > num_glyphs) { if (range_count > num_glyphs) {
return OTS_FAILURE_MSG("bad range count: %u", range_count); return OTS_FAILURE_MSG("classRangeCount > glyph count: %u > %u", range_count, num_glyphs);
} }
uint16_t last_end = 0; uint16_t last_end = 0;
@ -330,13 +330,16 @@ bool ParseClassDefFormat2(const ots::Font *font,
if (!subtable.ReadU16(&start) || if (!subtable.ReadU16(&start) ||
!subtable.ReadU16(&end) || !subtable.ReadU16(&end) ||
!subtable.ReadU16(&class_value)) { !subtable.ReadU16(&class_value)) {
return OTS_FAILURE_MSG("Failed to read class definition reange %d", i); return OTS_FAILURE_MSG("Failed to read ClassRangeRecord %d", i);
} }
if (start > end || (last_end && start <= last_end)) { if (start > end) {
return OTS_FAILURE_MSG("glyph range is overlapping.in range %d", i); return OTS_FAILURE_MSG("ClassRangeRecord %d, start > end: %u > %u", i, start, end);
}
if (last_end && start <= last_end) {
return OTS_FAILURE_MSG("ClassRangeRecord %d start overlaps with end of the previous one: %u <= %u", i, start, last_end);
} }
if (class_value > num_classes) { if (class_value > num_classes) {
return OTS_FAILURE_MSG("bad class value: %u", class_value); return OTS_FAILURE_MSG("ClassRangeRecord %d class > number of classes: %u > %u", i, class_value, num_classes);
} }
last_end = end; last_end = end;
} }

Просмотреть файл

@ -38,15 +38,15 @@ bool OpenTypeSILF::Parse(const uint8_t* data, size_t length,
if (prevent_decompression) { if (prevent_decompression) {
return DropGraphite("Illegal nested compression"); return DropGraphite("Illegal nested compression");
} }
std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE, 0); std::vector<uint8_t> decompressed(this->compHead & FULL_SIZE);
size_t outputSize = 0; size_t outputSize = 0;
if (!mozilla::Compression::LZ4::decompress( bool ret = mozilla::Compression::LZ4::decompressPartial(
reinterpret_cast<const char*>(data + table.offset()), reinterpret_cast<const char*>(data + table.offset()),
table.remaining(), table.remaining(), // input buffer size (input size + padding)
reinterpret_cast<char*>(decompressed.data()), reinterpret_cast<char*>(decompressed.data()),
decompressed.size(), decompressed.size(), // target output size
&outputSize) || &outputSize); // return output size
outputSize != (this->compHead & FULL_SIZE)) { if (!ret || outputSize != decompressed.size()) {
return DropGraphite("Decompression failed"); return DropGraphite("Decompression failed");
} }
return this->Parse(decompressed.data(), decompressed.size(), true); return this->Parse(decompressed.data(), decompressed.size(), true);
@ -255,8 +255,13 @@ bool OpenTypeSILF::SILSub::ParsePart(Buffer& table) {
} }
} }
if (!table.ReadU16(&this->lbGID) || this->lbGID > this->maxGlyphID) { if (!table.ReadU16(&this->lbGID)) {
return parent->Error("SILSub: Failed to read valid lbGID"); return parent->Error("SILSub: Failed to read lbGID");
}
if (this->lbGID > this->maxGlyphID) {
parent->Warning("SILSub: lbGID %u outside range 0..%u, replaced with 0",
this->lbGID, this->maxGlyphID);
this->lbGID = 0;
} }
if (parent->version >> 16 >= 3 && if (parent->version >> 16 >= 3 &&
@ -280,19 +285,27 @@ bool OpenTypeSILF::SILSub::ParsePart(Buffer& table) {
if (!table.ReadU16(&this->numPseudo)) { if (!table.ReadU16(&this->numPseudo)) {
return parent->Error("SILSub: Failed to read numPseudo"); return parent->Error("SILSub: Failed to read numPseudo");
} }
if (!table.ReadU16(&this->searchPseudo) || this->searchPseudo !=
(this->numPseudo == 0 ? 0 : // protect against log2(0) // The following three fields are deprecated and ignored. We fix them up here
(unsigned)std::pow(2, std::floor(std::log2(this->numPseudo))))) { // just for internal consistency, but the Graphite engine doesn't care.
return parent->Error("SILSub: Failed to read valid searchPseudo"); if (!table.ReadU16(&this->searchPseudo) ||
!table.ReadU16(&this->pseudoSelector) ||
!table.ReadU16(&this->pseudoShift)) {
return parent->Error("SILSub: Failed to read searchPseudo..pseudoShift");
} }
if (!table.ReadU16(&this->pseudoSelector) || this->pseudoSelector != if (this->numPseudo == 0) {
(this->numPseudo == 0 ? 0 : // protect against log2(0) if (this->searchPseudo != 0 || this->pseudoSelector != 0 || this->pseudoShift != 0) {
(unsigned)std::floor(std::log2(this->numPseudo)))) { this->searchPseudo = this->pseudoSelector = this->pseudoShift = 0;
return parent->Error("SILSub: Failed to read valid pseudoSelector"); }
} } else {
if (!table.ReadU16(&this->pseudoShift) || unsigned floorLog2 = std::floor(std::log2(this->numPseudo));
this->pseudoShift != this->numPseudo - this->searchPseudo) { if (this->searchPseudo != 6 * (unsigned)std::pow(2, floorLog2) ||
return parent->Error("SILSub: Failed to read valid pseudoShift"); this->pseudoSelector != floorLog2 ||
this->pseudoShift != 6 * this->numPseudo - this->searchPseudo) {
this->searchPseudo = 6 * (unsigned)std::pow(2, floorLog2);
this->pseudoSelector = floorLog2;
this->pseudoShift = 6 * this->numPseudo - this->searchPseudo;
}
} }
//this->pMaps.resize(this->numPseudo, parent); //this->pMaps.resize(this->numPseudo, parent);
@ -541,19 +554,26 @@ LookupClass::ParsePart(Buffer& table) {
if (!table.ReadU16(&this->numIDs)) { if (!table.ReadU16(&this->numIDs)) {
return parent->Error("LookupClass: Failed to read numIDs"); return parent->Error("LookupClass: Failed to read numIDs");
} }
if (!table.ReadU16(&this->searchRange) || this->searchRange != if (!table.ReadU16(&this->searchRange) ||
(this->numIDs == 0 ? 0 : // protect against log2(0) !table.ReadU16(&this->entrySelector) ||
(unsigned)std::pow(2, std::floor(std::log2(this->numIDs))))) { !table.ReadU16(&this->rangeShift)) {
return parent->Error("LookupClass: Failed to read valid searchRange"); return parent->Error("LookupClass: Failed to read searchRange..rangeShift");
} }
if (!table.ReadU16(&this->entrySelector) || this->entrySelector != if (this->numIDs == 0) {
(this->numIDs == 0 ? 0 : // protect against log2(0) if (this->searchRange != 0 || this->entrySelector != 0 || this->rangeShift != 0) {
(unsigned)std::floor(std::log2(this->numIDs)))) { parent->Warning("LookupClass: Correcting binary-search header for zero-length LookupPair list");
return parent->Error("LookupClass: Failed to read valid entrySelector"); this->searchRange = this->entrySelector = this->rangeShift = 0;
} }
if (!table.ReadU16(&this->rangeShift) || } else {
this->rangeShift != this->numIDs - this->searchRange) { unsigned floorLog2 = std::floor(std::log2(this->numIDs));
return parent->Error("LookupClass: Failed to read valid rangeShift"); if (this->searchRange != (unsigned)std::pow(2, floorLog2) ||
this->entrySelector != floorLog2 ||
this->rangeShift != this->numIDs - this->searchRange) {
parent->Warning("LookupClass: Correcting binary-search header for LookupPair list");
this->searchRange = (unsigned)std::pow(2, floorLog2);
this->entrySelector = floorLog2;
this->rangeShift = this->numIDs - this->searchRange;
}
} }
//this->lookups.resize(this->numIDs, parent); //this->lookups.resize(this->numIDs, parent);
@ -661,19 +681,27 @@ SILPass::ParsePart(Buffer& table, const size_t SILSub_init_offset,
if (!table.ReadU16(&this->numRange)) { if (!table.ReadU16(&this->numRange)) {
return parent->Error("SILPass: Failed to read numRange"); return parent->Error("SILPass: Failed to read numRange");
} }
if (!table.ReadU16(&this->searchRange) || this->searchRange !=
(this->numRange == 0 ? 0 : // protect against log2(0) // The following three fields are deprecated and ignored. We fix them up here
(unsigned)std::pow(2, std::floor(std::log2(this->numRange))))) { // just for internal consistency, but the Graphite engine doesn't care.
return parent->Error("SILPass: Failed to read valid searchRange"); if (!table.ReadU16(&this->searchRange) ||
!table.ReadU16(&this->entrySelector) ||
!table.ReadU16(&this->rangeShift)) {
return parent->Error("SILPass: Failed to read searchRange..rangeShift");
} }
if (!table.ReadU16(&this->entrySelector) || this->entrySelector != if (this->numRange == 0) {
(this->numRange == 0 ? 0 : // protect against log2(0) if (this->searchRange != 0 || this->entrySelector != 0 || this->rangeShift != 0) {
(unsigned)std::floor(std::log2(this->numRange)))) { this->searchRange = this->entrySelector = this->rangeShift = 0;
return parent->Error("SILPass: Failed to read valid entrySelector"); }
} } else {
if (!table.ReadU16(&this->rangeShift) || unsigned floorLog2 = std::floor(std::log2(this->numRange));
this->rangeShift != this->numRange - this->searchRange) { if (this->searchRange != 6 * (unsigned)std::pow(2, floorLog2) ||
return parent->Error("SILPass: Failed to read valid rangeShift"); this->entrySelector != floorLog2 ||
this->rangeShift != 6 * this->numRange - this->searchRange) {
this->searchRange = 6 * (unsigned)std::pow(2, floorLog2);
this->entrySelector = floorLog2;
this->rangeShift = 6 * this->numRange - this->searchRange;
}
} }
//this->ranges.resize(this->numRange, parent); //this->ranges.resize(this->numRange, parent);

Просмотреть файл

@ -22,19 +22,27 @@ bool OpenTypeSILL::Parse(const uint8_t* data, size_t length) {
if (!table.ReadU16(&this->numLangs)) { if (!table.ReadU16(&this->numLangs)) {
return Drop("Failed to read numLangs"); return Drop("Failed to read numLangs");
} }
if (!table.ReadU16(&this->searchRange) || this->searchRange !=
(this->numLangs == 0 ? 0 : // protect against log2(0) // The following three fields are deprecated and ignored. We fix them up here
(unsigned)std::pow(2, std::floor(std::log2(this->numLangs))))) { // just for internal consistency, but the Graphite engine doesn't care.
return Drop("Failed to read valid searchRange"); if (!table.ReadU16(&this->searchRange) ||
!table.ReadU16(&this->entrySelector) ||
!table.ReadU16(&this->rangeShift)) {
return Drop("Failed to read searchRange..rangeShift");
} }
if (!table.ReadU16(&this->entrySelector) || this->entrySelector != if (this->numLangs == 0) {
(this->numLangs == 0 ? 0 : // protect against log2(0) if (this->searchRange != 0 || this->entrySelector != 0 || this->rangeShift != 0) {
(unsigned)std::floor(std::log2(this->numLangs)))) { this->searchRange = this->entrySelector = this->rangeShift = 0;
return Drop("Failed to read valid entrySelector"); }
} } else {
if (!table.ReadU16(&this->rangeShift) || unsigned floorLog2 = std::floor(std::log2(this->numLangs));
this->rangeShift != this->numLangs - this->searchRange) { if (this->searchRange != (unsigned)std::pow(2, floorLog2) ||
return Drop("Failed to read valid rangeShift"); this->entrySelector != floorLog2 ||
this->rangeShift != this->numLangs - this->searchRange) {
this->searchRange = (unsigned)std::pow(2, floorLog2);
this->entrySelector = floorLog2;
this->rangeShift = this->numLangs - this->searchRange;
}
} }
std::unordered_set<size_t> unverified; std::unordered_set<size_t> unverified;

Просмотреть файл

@ -81,3 +81,24 @@ LZ4::decompress(const char* aSource, size_t aInputSize, char* aDest,
return false; return false;
} }
bool
LZ4::decompressPartial(const char* aSource, size_t aInputSize, char* aDest,
size_t aMaxOutputSize, size_t* aOutputSize)
{
CheckedInt<int> maxOutputSizeChecked = aMaxOutputSize;
MOZ_ASSERT(maxOutputSizeChecked.isValid());
CheckedInt<int> inputSizeChecked = aInputSize;
MOZ_ASSERT(inputSizeChecked.isValid());
int ret = LZ4_decompress_safe_partial(aSource, aDest,
inputSizeChecked.value(),
maxOutputSizeChecked.value(),
maxOutputSizeChecked.value());
if (ret >= 0) {
*aOutputSize = ret;
return true;
}
*aOutputSize = 0;
return false;
}

Просмотреть файл

@ -96,6 +96,29 @@ public:
decompress(const char* aSource, size_t aInputSize, char* aDest, decompress(const char* aSource, size_t aInputSize, char* aDest,
size_t aMaxOutputSize, size_t* aOutputSize); size_t aMaxOutputSize, size_t* aOutputSize);
/**
* If the source stream is malformed, the function will stop decoding
* and return false.
*
* This function never writes beyond aDest + aMaxOutputSize, and is
* therefore protected against malicious data packets. It also ignores
* unconsumed input upon reaching aMaxOutputSize and can therefore be used
* for partial decompression.
*
* Note: Destination buffer must be already allocated. This version is
* slightly slower than the decompress without the aMaxOutputSize.
*
* @param aInputSize is the length of the input compressed data
* @param aMaxOutputSize is the size of the destination buffer (which must be
* already allocated)
* @param aOutputSize the actual number of bytes decoded in the destination
* buffer (necessarily <= aMaxOutputSize)
* @return true on success, false on failure
*/
static MFBT_API MOZ_MUST_USE bool
decompressPartial(const char* aSource, size_t aInputSize, char* aDest,
size_t aMaxOutputSize, size_t* aOutputSize);
/* /*
* Provides the maximum size that LZ4 may output in a "worst case" * Provides the maximum size that LZ4 may output in a "worst case"
* scenario (input data not compressible) primarily useful for memory * scenario (input data not compressible) primarily useful for memory