Loading gfx/ots/README.mozilla +1 −1 Original line number Diff line number Diff line Loading @@ -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/. Current revision: a6533c4f6a7cb777153c78b78295e42f185ccd3d (8.1.4) Current revision: 2369c7c00de9e729c79d13e41e2c3601ed2f6c69 (8.2.1) Upstream files included: LICENSE, src/, include/, tests/*.cc Loading gfx/ots/src/cff.cc +19 −3 Original line number Diff line number Diff line Loading @@ -404,13 +404,17 @@ bool ParsePrivateDictData( std::vector<Operand> operands; bool cff2 = (out_cff->major == 2); bool blend_seen = false; uint32_t vsindex = 0; int32_t vsindex = 0; // Since a Private DICT for FDArray might not have a Local Subr (e.g. Hiragino // Kaku Gothic Std W8), we create an empty Local Subr here to match the size // of FDArray the size of |local_subrs_per_font|. // For CFF2, |vsindex_per_font| gets a similar treatment. if (type == DICT_DATA_FDARRAY) { out_cff->local_subrs_per_font.push_back(new ots::CFFIndex); if (cff2) { out_cff->vsindex_per_font.push_back(vsindex); } } while (dict.offset() < dict.length()) { Loading Loading @@ -527,9 +531,11 @@ bool ParsePrivateDictData( return OTS_FAILURE(); } vsindex = operands.back().first; if (vsindex >= out_cff->region_index_count.size()) { if (vsindex < 0 || vsindex >= (int32_t)out_cff->region_index_count.size()) { return OTS_FAILURE(); } out_cff->vsindex_per_font.back() = vsindex; break; } Loading @@ -540,7 +546,7 @@ bool ParsePrivateDictData( if (operands.size() < 1) { return OTS_FAILURE(); } if (vsindex >= out_cff->region_index_count.size()) { if (vsindex >= (int32_t)out_cff->region_index_count.size()) { return OTS_FAILURE(); } uint16_t k = out_cff->region_index_count.at(vsindex); Loading Loading @@ -625,6 +631,7 @@ bool ParseDictData(ots::Buffer& table, ots::Buffer& dict, bool have_charstrings = false; bool have_vstore = false; size_t charset_offset = 0; bool have_private = false; if (cff2) { // Parse VariationStore first, since it might be referenced in other places Loading @@ -640,6 +647,10 @@ bool ParseDictData(ots::Buffer& table, ots::Buffer& dict, const uint32_t op = operands.back().first; operands.pop_back(); if (op == 18 && type == DICT_DATA_FDARRAY) { have_private = true; } if (op == 24) { // vstore if (type != DICT_DATA_TOPLEVEL) { return OTS_FAILURE(); Loading @@ -661,6 +672,11 @@ bool ParseDictData(ots::Buffer& table, ots::Buffer& dict, } operands.clear(); dict.set_offset(dict_offset); if (type == DICT_DATA_FDARRAY && !have_private) { return OTS_FAILURE(); // CFF2 FD must have PrivateDICT entry (even if 0, 0) } } while (dict.offset() < dict.length()) { Loading gfx/ots/src/cff.h +5 −0 Original line number Diff line number Diff line Loading @@ -64,6 +64,11 @@ class OpenTypeCFF : public Table { // CFF2 VariationStore regionIndexCount. std::vector<uint16_t> region_index_count; // CFF2 vsindex: per FontDICT->PrivateDICT // default of 0 is stored for each font if not // explicitly set in Font's PrivateDICT. std::vector<int32_t> vsindex_per_font; protected: bool ValidateFDSelect(uint16_t num_glyphs); Loading gfx/ots/src/cff_charstring.cc +90 −88 Original line number Diff line number Diff line Loading @@ -36,10 +36,7 @@ bool ExecuteCharString(ots::OpenTypeCFF& cff, ots::Buffer *cff_table, ots::Buffer *char_string, std::stack<int32_t> *argument_stack, bool *out_found_endchar, bool *out_found_width, size_t *in_out_num_stems, bool cff2); ots::CharStringContext& cs_ctx); bool ArgumentStackOverflows(std::stack<int32_t> *argument_stack, bool cff2) { if ((cff2 && argument_stack->size() > ots::kMaxCFF2ArgumentStack) || Loading Loading @@ -266,8 +263,8 @@ bool ValidCFF2Operator(int32_t op) { // Executes |op| and updates |argument_stack|. Returns true if the execution // succeeds. If the |op| is kCallSubr or kCallGSubr, the function recursively // calls ExecuteCharString() function. The arguments other than |op| and // |argument_stack| are passed for that reason. // calls ExecuteCharString() function. The |cs_ctx| argument holds values that // need to persist through these calls (see CharStringContext for details) bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, int32_t op, size_t call_depth, Loading @@ -276,17 +273,11 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, ots::Buffer *cff_table, ots::Buffer *char_string, std::stack<int32_t> *argument_stack, bool *out_found_endchar, bool *in_out_found_width, size_t *in_out_num_stems, bool *in_out_have_blend, bool *in_out_have_visindex, int32_t *in_out_vsindex, bool cff2) { ots::CharStringContext& cs_ctx) { ots::Font* font = cff.GetFont(); const size_t stack_size = argument_stack->size(); if (cff2 && !ValidCFF2Operator(op)) { if (cs_ctx.cff2 && !ValidCFF2Operator(op)) { return OTS_FAILURE(); } Loading Loading @@ -349,62 +340,60 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, cff_table, &char_string_to_jump, argument_stack, out_found_endchar, in_out_found_width, in_out_num_stems, cff2); cs_ctx); } case ots::kReturn: return true; case ots::kEndChar: *out_found_endchar = true; *in_out_found_width = true; // just in case. cs_ctx.endchar_seen = true; cs_ctx.width_seen = true; // just in case. return true; case ots::kVSIndex: { if (!cff2) { if (!cs_ctx.cff2) { return OTS_FAILURE(); } if (stack_size != 1) { return OTS_FAILURE(); } if (*in_out_have_blend || *in_out_have_visindex) { if (cs_ctx.blend_seen || cs_ctx.vsindex_seen) { return OTS_FAILURE(); } if (argument_stack->top() >= cff.region_index_count.size()) { if (argument_stack->top() < 0 || argument_stack->top() >= (int32_t)cff.region_index_count.size()) { return OTS_FAILURE(); } *in_out_have_visindex = true; *in_out_vsindex = argument_stack->top(); cs_ctx.vsindex_seen = true; cs_ctx.vsindex = argument_stack->top(); while (!argument_stack->empty()) argument_stack->pop(); return true; } case ots::kBlend: { if (!cff2) { if (!cs_ctx.cff2) { return OTS_FAILURE(); } if (stack_size < 1) { return OTS_FAILURE(); } if (*in_out_vsindex >= cff.region_index_count.size()) { if (cs_ctx.vsindex >= (int32_t)cff.region_index_count.size()) { return OTS_FAILURE(); } uint16_t k = cff.region_index_count.at(*in_out_vsindex); uint16_t k = cff.region_index_count.at(cs_ctx.vsindex); uint16_t n = argument_stack->top(); if (stack_size < n * (k + 1) + 1) { return OTS_FAILURE(); } // Keep the 1st n operands on the stack for the next operator to use and // pop the rest. There can be multiple consecutive blend operator, so this // pop the rest. There can be multiple consecutive blend operators, so this // makes sure the operands of all of them are kept on the stack. while (argument_stack->size() > stack_size - ((n * k) + 1)) argument_stack->pop(); *in_out_have_blend = true; cs_ctx.blend_seen = true; return true; } Loading @@ -418,18 +407,18 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, } if ((stack_size % 2) == 0) { successful = true; } else if ((!(*in_out_found_width)) && (((stack_size - 1) % 2) == 0)) { } else if ((!(cs_ctx.width_seen)) && (((stack_size - 1) % 2) == 0)) { // The -1 is for "width" argument. For details, see Adobe Technical Note // #5177, page 16, note 4. successful = true; } (*in_out_num_stems) += (stack_size / 2); if ((*in_out_num_stems) > kMaxNumberOfStemHints) { cs_ctx.num_stems += (stack_size / 2); if ((cs_ctx.num_stems) > kMaxNumberOfStemHints) { return OTS_FAILURE(); } while (!argument_stack->empty()) argument_stack->pop(); *in_out_found_width = true; // always set true since "w" might be 0 byte. cs_ctx.width_seen = true; // always set true since "w" might be 0 byte. return successful ? true : OTS_FAILURE(); } Loading @@ -437,12 +426,12 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, bool successful = false; if (stack_size == 2) { successful = true; } else if ((!(*in_out_found_width)) && (stack_size - 1 == 2)) { } else if ((!(cs_ctx.width_seen)) && (stack_size - 1 == 2)) { successful = true; } while (!argument_stack->empty()) argument_stack->pop(); *in_out_found_width = true; cs_ctx.width_seen = true; return successful ? true : OTS_FAILURE(); } Loading @@ -451,12 +440,12 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, bool successful = false; if (stack_size == 1) { successful = true; } else if ((!(*in_out_found_width)) && (stack_size - 1 == 1)) { } else if ((!(cs_ctx.width_seen)) && (stack_size - 1 == 1)) { successful = true; } while (!argument_stack->empty()) argument_stack->pop(); *in_out_found_width = true; cs_ctx.width_seen = true; return successful ? true : OTS_FAILURE(); } Loading @@ -465,15 +454,15 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, bool successful = false; if (stack_size == 0) { successful = true; } else if ((!(*in_out_found_width)) && (stack_size == 1)) { } else if ((!(cs_ctx.width_seen)) && (stack_size == 1)) { // A number for "width" is found. successful = true; } else if ((!(*in_out_found_width)) || // in this case, any sizes are ok. } else if ((!(cs_ctx.width_seen)) || // in this case, any sizes are ok. ((stack_size % 2) == 0)) { // The numbers are vstem definition. // See Adobe Technical Note #5177, page 24, hintmask. (*in_out_num_stems) += (stack_size / 2); if ((*in_out_num_stems) > kMaxNumberOfStemHints) { cs_ctx.num_stems += (stack_size / 2); if ((cs_ctx.num_stems) > kMaxNumberOfStemHints) { return OTS_FAILURE(); } successful = true; Loading @@ -482,21 +471,21 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, return OTS_FAILURE(); } if ((*in_out_num_stems) == 0) { if ((cs_ctx.num_stems) == 0) { return OTS_FAILURE(); } const size_t mask_bytes = (*in_out_num_stems + 7) / 8; const size_t mask_bytes = (cs_ctx.num_stems + 7) / 8; if (!char_string->Skip(mask_bytes)) { return OTS_FAILURE(); } while (!argument_stack->empty()) argument_stack->pop(); *in_out_found_width = true; cs_ctx.width_seen = true; return true; } case ots::kRLineTo: if (!(*in_out_found_width)) { if (!(cs_ctx.width_seen)) { // The first stack-clearing operator should be one of hstem, hstemhm, // vstem, vstemhm, cntrmask, hintmask, hmoveto, vmoveto, rmoveto, or // endchar. For details, see Adobe Technical Note #5177, page 16, note 4. Loading @@ -514,7 +503,7 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, case ots::kHLineTo: case ots::kVLineTo: if (!(*in_out_found_width)) { if (!(cs_ctx.width_seen)) { return OTS_FAILURE(); } if (stack_size < 1) { Loading @@ -525,7 +514,7 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, return true; case ots::kRRCurveTo: if (!(*in_out_found_width)) { if (!(cs_ctx.width_seen)) { return OTS_FAILURE(); } if (stack_size < 6) { Loading @@ -539,7 +528,7 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, return true; case ots::kRCurveLine: if (!(*in_out_found_width)) { if (!(cs_ctx.width_seen)) { return OTS_FAILURE(); } if (stack_size < 8) { Loading @@ -553,7 +542,7 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, return true; case ots::kRLineCurve: if (!(*in_out_found_width)) { if (!(cs_ctx.width_seen)) { return OTS_FAILURE(); } if (stack_size < 8) { Loading @@ -567,7 +556,7 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, return true; case ots::kVVCurveTo: if (!(*in_out_found_width)) { if (!(cs_ctx.width_seen)) { return OTS_FAILURE(); } if (stack_size < 4) { Loading @@ -583,7 +572,7 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, case ots::kHHCurveTo: { bool successful = false; if (!(*in_out_found_width)) { if (!(cs_ctx.width_seen)) { return OTS_FAILURE(); } if (stack_size < 4) { Loading @@ -604,7 +593,7 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, case ots::kVHCurveTo: case ots::kHVCurveTo: { bool successful = false; if (!(*in_out_found_width)) { if (!(cs_ctx.width_seen)) { return OTS_FAILURE(); } if (stack_size < 4) { Loading Loading @@ -747,7 +736,7 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, argument_stack->pop(); argument_stack->push(dummy_result); argument_stack->push(dummy_result); if (ArgumentStackOverflows(argument_stack, cff2)) { if (ArgumentStackOverflows(argument_stack, cs_ctx.cff2)) { return OTS_FAILURE(); } // TODO(yusukes): Implement this. We should push a real value for all Loading @@ -767,7 +756,7 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, return true; case ots::kHFlex: if (!(*in_out_found_width)) { if (!(cs_ctx.width_seen)) { return OTS_FAILURE(); } if (stack_size != 7) { Loading @@ -778,7 +767,7 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, return true; case ots::kFlex: if (!(*in_out_found_width)) { if (!(cs_ctx.width_seen)) { return OTS_FAILURE(); } if (stack_size != 13) { Loading @@ -789,7 +778,7 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, return true; case ots::kHFlex1: if (!(*in_out_found_width)) { if (!(cs_ctx.width_seen)) { return OTS_FAILURE(); } if (stack_size != 9) { Loading @@ -800,7 +789,7 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, return true; case ots::kFlex1: if (!(*in_out_found_width)) { if (!(cs_ctx.width_seen)) { return OTS_FAILURE(); } if (stack_size != 11) { Loading @@ -816,6 +805,7 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, // Executes |char_string| and updates |argument_stack|. // // cff: parent OpenTypeCFF reference // call_depth: The current call depth. Initial value is zero. // global_subrs_index: Global subroutines. // local_subrs_index: Local subroutines for the current glyph. Loading @@ -823,10 +813,16 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, // char_string: A charstring we'll execute. |char_string| can be a main routine // in CharString INDEX, or a subroutine in GlobalSubr/LocalSubr. // argument_stack: The stack which an operator in |char_string| operates. // out_found_endchar: true is set if |char_string| contains 'endchar'. // in_out_found_width: true is set if |char_string| contains 'width' byte (which // is 0 or 1 byte.) // in_out_num_stems: total number of hstems and vstems processed so far. // cs_ctx: a CharStringContext holding values to persist across subrs, etc. // endchar_seen: true is set if |char_string| contains 'endchar'. // width_seen: true is set if |char_string| contains 'width' byte (which // is 0 or 1 byte) or if cff2 // num_stems: total number of hstems and vstems processed so far. // cff2: true if this is a CFF2 table // blend_seen: initially false; set to true if 'blend' operator encountered. // vsindex_seen: initially false; set to true if 'vsindex' encountered. // vsindex: initially = PrivateDICT's vsindex; may be changed by 'vsindex' // operator in CharString bool ExecuteCharString(ots::OpenTypeCFF& cff, size_t call_depth, const ots::CFFIndex& global_subrs_index, Loading @@ -834,17 +830,12 @@ bool ExecuteCharString(ots::OpenTypeCFF& cff, ots::Buffer *cff_table, ots::Buffer *char_string, std::stack<int32_t> *argument_stack, bool *out_found_endchar, bool *in_out_found_width, size_t *in_out_num_stems, bool cff2) { ots::CharStringContext& cs_ctx) { if (call_depth > kMaxSubrNesting) { return OTS_FAILURE(); } *out_found_endchar = false; cs_ctx.endchar_seen = false; bool in_out_have_blend = false, in_out_have_visindex = false; int32_t in_out_vsindex = 0; const size_t length = char_string->length(); while (char_string->offset() < length) { int32_t operator_or_operand = 0; Loading Loading @@ -873,7 +864,7 @@ bool ExecuteCharString(ots::OpenTypeCFF& cff, if (!is_operator) { argument_stack->push(operator_or_operand); if (ArgumentStackOverflows(argument_stack, cff2)) { if (ArgumentStackOverflows(argument_stack, cs_ctx.cff2)) { return OTS_FAILURE(); } continue; Loading @@ -888,16 +879,10 @@ bool ExecuteCharString(ots::OpenTypeCFF& cff, cff_table, char_string, argument_stack, out_found_endchar, in_out_found_width, in_out_num_stems, &in_out_have_blend, &in_out_have_visindex, &in_out_vsindex, cff2)) { cs_ctx)) { return OTS_FAILURE(); } if (*out_found_endchar) { if (cs_ctx.endchar_seen) { return true; } if (operator_or_operand == ots::kReturn) { Loading @@ -905,13 +890,13 @@ bool ExecuteCharString(ots::OpenTypeCFF& cff, } } // No endchar operator is found. if (cff2) // No endchar operator is found (CFF1 only) if (cs_ctx.cff2) return true; return OTS_FAILURE(); } // Selects a set of subroutings for |glyph_index| from |cff| and sets it on // Selects a set of subroutines for |glyph_index| from |cff| and sets it on // |out_local_subrs_to_use|. Returns true on success. bool SelectLocalSubr(const ots::OpenTypeCFF& cff, uint16_t glyph_index, // 0-origin Loading Loading @@ -960,7 +945,6 @@ bool ValidateCFFCharStrings( return OTS_FAILURE(); // no charstring. } bool cff2 = (cff.major == 2); // For each glyph, validate the corresponding charstring. for (unsigned i = 1; i < char_strings_index.offsets.size(); ++i) { // Prepare a Buffer object, |char_string|, which contains the charstring Loading Loading @@ -993,20 +977,38 @@ bool ValidateCFFCharStrings( // Check a charstring for the |i|-th glyph. std::stack<int32_t> argument_stack; bool found_endchar = false; // Context to store values that must persist across subrs, etc. CharStringContext cs_ctx; cs_ctx.cff2 = (cff.major == 2); // CFF2 CharString has no value for width, so we start with true here to // error out if width is found. bool found_width = cff2; size_t num_stems = 0; cs_ctx.width_seen = cs_ctx.cff2; // CFF2 CharStrings' default vsindex comes from the associated PrivateDICT if (cs_ctx.cff2) { const auto& iter = cff.fd_select.find(glyph_index); auto fd_index = 0; if (iter != cff.fd_select.end()) { fd_index = iter->second; } if (fd_index >= (int32_t)cff.vsindex_per_font.size()) { // shouldn't get this far with a font in this condition, but just in case return OTS_FAILURE(); // fd_index out-of-range } cs_ctx.vsindex = cff.vsindex_per_font.at(fd_index); } #ifdef DUMP_T2CHARSTRING fprintf(stderr, "\n---- CharString %*d ----\n", 5, glyph_index); #endif if (!ExecuteCharString(cff, 0 /* initial call_depth is zero */, global_subrs_index, *local_subrs_to_use, cff_table, &char_string, &argument_stack, &found_endchar, &found_width, &num_stems, cff2)) { cs_ctx)) { return OTS_FAILURE(); } if (!cff2 && !found_endchar) { if (!cs_ctx.cff2 && !cs_ctx.endchar_seen) { return OTS_FAILURE(); } } Loading gfx/ots/src/cff_charstring.h +13 −6 Original line number Diff line number Diff line Loading @@ -21,7 +21,7 @@ const size_t kMaxCFF2ArgumentStack = 513; // http://www.adobe.com/devnet/font/pdfs/5177.Type2.pdf // // The validation will fail if one of the following conditions is met: // 1. The code uses more than 48 values of argument stack. // 1. The code uses more than the max argument stack size (48 for CFF1; 513 for CFF2) // 2. The code uses deeply nested subroutine calls (more than 10 levels.) // 3. The code passes invalid number of operands to an operator. // 4. The code calls an undefined global or local subroutine. Loading @@ -29,12 +29,9 @@ const size_t kMaxCFF2ArgumentStack = 513; // an ordinary fonts, and could be dangerous: random, put, get, index, roll. // // Arguments: // cff: parent OpenTypeCFF reference // global_subrs_index: Global subroutines which could be called by a charstring // in |char_strings_index|. // fd_select: A map from glyph # to font #. // local_subrs_per_font: A list of Local Subrs associated with FDArrays. Can be // empty. // local_subrs: A Local Subrs associated with Top DICT. Can be NULL. // cff_table: A buffer which contains actual byte code of charstring, global // subroutines and local subroutines. bool ValidateCFFCharStrings( Loading Loading @@ -95,7 +92,17 @@ enum CharStringOperator { kFlex = (12 << 8) + 35, kHFlex1 = (12 << 8) + 36, kFlex1 = (12 << 8) + 37, // Operators that are undocumented, such as 'blend', will be rejected. // Operators that are undocumented will be rejected. }; struct CharStringContext { bool endchar_seen = false; bool width_seen = false; size_t num_stems = 0; bool cff2 = false; bool blend_seen = false; bool vsindex_seen = false; int32_t vsindex = 0; }; } // namespace ots Loading Loading
gfx/ots/README.mozilla +1 −1 Original line number Diff line number Diff line Loading @@ -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/. Current revision: a6533c4f6a7cb777153c78b78295e42f185ccd3d (8.1.4) Current revision: 2369c7c00de9e729c79d13e41e2c3601ed2f6c69 (8.2.1) Upstream files included: LICENSE, src/, include/, tests/*.cc Loading
gfx/ots/src/cff.cc +19 −3 Original line number Diff line number Diff line Loading @@ -404,13 +404,17 @@ bool ParsePrivateDictData( std::vector<Operand> operands; bool cff2 = (out_cff->major == 2); bool blend_seen = false; uint32_t vsindex = 0; int32_t vsindex = 0; // Since a Private DICT for FDArray might not have a Local Subr (e.g. Hiragino // Kaku Gothic Std W8), we create an empty Local Subr here to match the size // of FDArray the size of |local_subrs_per_font|. // For CFF2, |vsindex_per_font| gets a similar treatment. if (type == DICT_DATA_FDARRAY) { out_cff->local_subrs_per_font.push_back(new ots::CFFIndex); if (cff2) { out_cff->vsindex_per_font.push_back(vsindex); } } while (dict.offset() < dict.length()) { Loading Loading @@ -527,9 +531,11 @@ bool ParsePrivateDictData( return OTS_FAILURE(); } vsindex = operands.back().first; if (vsindex >= out_cff->region_index_count.size()) { if (vsindex < 0 || vsindex >= (int32_t)out_cff->region_index_count.size()) { return OTS_FAILURE(); } out_cff->vsindex_per_font.back() = vsindex; break; } Loading @@ -540,7 +546,7 @@ bool ParsePrivateDictData( if (operands.size() < 1) { return OTS_FAILURE(); } if (vsindex >= out_cff->region_index_count.size()) { if (vsindex >= (int32_t)out_cff->region_index_count.size()) { return OTS_FAILURE(); } uint16_t k = out_cff->region_index_count.at(vsindex); Loading Loading @@ -625,6 +631,7 @@ bool ParseDictData(ots::Buffer& table, ots::Buffer& dict, bool have_charstrings = false; bool have_vstore = false; size_t charset_offset = 0; bool have_private = false; if (cff2) { // Parse VariationStore first, since it might be referenced in other places Loading @@ -640,6 +647,10 @@ bool ParseDictData(ots::Buffer& table, ots::Buffer& dict, const uint32_t op = operands.back().first; operands.pop_back(); if (op == 18 && type == DICT_DATA_FDARRAY) { have_private = true; } if (op == 24) { // vstore if (type != DICT_DATA_TOPLEVEL) { return OTS_FAILURE(); Loading @@ -661,6 +672,11 @@ bool ParseDictData(ots::Buffer& table, ots::Buffer& dict, } operands.clear(); dict.set_offset(dict_offset); if (type == DICT_DATA_FDARRAY && !have_private) { return OTS_FAILURE(); // CFF2 FD must have PrivateDICT entry (even if 0, 0) } } while (dict.offset() < dict.length()) { Loading
gfx/ots/src/cff.h +5 −0 Original line number Diff line number Diff line Loading @@ -64,6 +64,11 @@ class OpenTypeCFF : public Table { // CFF2 VariationStore regionIndexCount. std::vector<uint16_t> region_index_count; // CFF2 vsindex: per FontDICT->PrivateDICT // default of 0 is stored for each font if not // explicitly set in Font's PrivateDICT. std::vector<int32_t> vsindex_per_font; protected: bool ValidateFDSelect(uint16_t num_glyphs); Loading
gfx/ots/src/cff_charstring.cc +90 −88 Original line number Diff line number Diff line Loading @@ -36,10 +36,7 @@ bool ExecuteCharString(ots::OpenTypeCFF& cff, ots::Buffer *cff_table, ots::Buffer *char_string, std::stack<int32_t> *argument_stack, bool *out_found_endchar, bool *out_found_width, size_t *in_out_num_stems, bool cff2); ots::CharStringContext& cs_ctx); bool ArgumentStackOverflows(std::stack<int32_t> *argument_stack, bool cff2) { if ((cff2 && argument_stack->size() > ots::kMaxCFF2ArgumentStack) || Loading Loading @@ -266,8 +263,8 @@ bool ValidCFF2Operator(int32_t op) { // Executes |op| and updates |argument_stack|. Returns true if the execution // succeeds. If the |op| is kCallSubr or kCallGSubr, the function recursively // calls ExecuteCharString() function. The arguments other than |op| and // |argument_stack| are passed for that reason. // calls ExecuteCharString() function. The |cs_ctx| argument holds values that // need to persist through these calls (see CharStringContext for details) bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, int32_t op, size_t call_depth, Loading @@ -276,17 +273,11 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, ots::Buffer *cff_table, ots::Buffer *char_string, std::stack<int32_t> *argument_stack, bool *out_found_endchar, bool *in_out_found_width, size_t *in_out_num_stems, bool *in_out_have_blend, bool *in_out_have_visindex, int32_t *in_out_vsindex, bool cff2) { ots::CharStringContext& cs_ctx) { ots::Font* font = cff.GetFont(); const size_t stack_size = argument_stack->size(); if (cff2 && !ValidCFF2Operator(op)) { if (cs_ctx.cff2 && !ValidCFF2Operator(op)) { return OTS_FAILURE(); } Loading Loading @@ -349,62 +340,60 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, cff_table, &char_string_to_jump, argument_stack, out_found_endchar, in_out_found_width, in_out_num_stems, cff2); cs_ctx); } case ots::kReturn: return true; case ots::kEndChar: *out_found_endchar = true; *in_out_found_width = true; // just in case. cs_ctx.endchar_seen = true; cs_ctx.width_seen = true; // just in case. return true; case ots::kVSIndex: { if (!cff2) { if (!cs_ctx.cff2) { return OTS_FAILURE(); } if (stack_size != 1) { return OTS_FAILURE(); } if (*in_out_have_blend || *in_out_have_visindex) { if (cs_ctx.blend_seen || cs_ctx.vsindex_seen) { return OTS_FAILURE(); } if (argument_stack->top() >= cff.region_index_count.size()) { if (argument_stack->top() < 0 || argument_stack->top() >= (int32_t)cff.region_index_count.size()) { return OTS_FAILURE(); } *in_out_have_visindex = true; *in_out_vsindex = argument_stack->top(); cs_ctx.vsindex_seen = true; cs_ctx.vsindex = argument_stack->top(); while (!argument_stack->empty()) argument_stack->pop(); return true; } case ots::kBlend: { if (!cff2) { if (!cs_ctx.cff2) { return OTS_FAILURE(); } if (stack_size < 1) { return OTS_FAILURE(); } if (*in_out_vsindex >= cff.region_index_count.size()) { if (cs_ctx.vsindex >= (int32_t)cff.region_index_count.size()) { return OTS_FAILURE(); } uint16_t k = cff.region_index_count.at(*in_out_vsindex); uint16_t k = cff.region_index_count.at(cs_ctx.vsindex); uint16_t n = argument_stack->top(); if (stack_size < n * (k + 1) + 1) { return OTS_FAILURE(); } // Keep the 1st n operands on the stack for the next operator to use and // pop the rest. There can be multiple consecutive blend operator, so this // pop the rest. There can be multiple consecutive blend operators, so this // makes sure the operands of all of them are kept on the stack. while (argument_stack->size() > stack_size - ((n * k) + 1)) argument_stack->pop(); *in_out_have_blend = true; cs_ctx.blend_seen = true; return true; } Loading @@ -418,18 +407,18 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, } if ((stack_size % 2) == 0) { successful = true; } else if ((!(*in_out_found_width)) && (((stack_size - 1) % 2) == 0)) { } else if ((!(cs_ctx.width_seen)) && (((stack_size - 1) % 2) == 0)) { // The -1 is for "width" argument. For details, see Adobe Technical Note // #5177, page 16, note 4. successful = true; } (*in_out_num_stems) += (stack_size / 2); if ((*in_out_num_stems) > kMaxNumberOfStemHints) { cs_ctx.num_stems += (stack_size / 2); if ((cs_ctx.num_stems) > kMaxNumberOfStemHints) { return OTS_FAILURE(); } while (!argument_stack->empty()) argument_stack->pop(); *in_out_found_width = true; // always set true since "w" might be 0 byte. cs_ctx.width_seen = true; // always set true since "w" might be 0 byte. return successful ? true : OTS_FAILURE(); } Loading @@ -437,12 +426,12 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, bool successful = false; if (stack_size == 2) { successful = true; } else if ((!(*in_out_found_width)) && (stack_size - 1 == 2)) { } else if ((!(cs_ctx.width_seen)) && (stack_size - 1 == 2)) { successful = true; } while (!argument_stack->empty()) argument_stack->pop(); *in_out_found_width = true; cs_ctx.width_seen = true; return successful ? true : OTS_FAILURE(); } Loading @@ -451,12 +440,12 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, bool successful = false; if (stack_size == 1) { successful = true; } else if ((!(*in_out_found_width)) && (stack_size - 1 == 1)) { } else if ((!(cs_ctx.width_seen)) && (stack_size - 1 == 1)) { successful = true; } while (!argument_stack->empty()) argument_stack->pop(); *in_out_found_width = true; cs_ctx.width_seen = true; return successful ? true : OTS_FAILURE(); } Loading @@ -465,15 +454,15 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, bool successful = false; if (stack_size == 0) { successful = true; } else if ((!(*in_out_found_width)) && (stack_size == 1)) { } else if ((!(cs_ctx.width_seen)) && (stack_size == 1)) { // A number for "width" is found. successful = true; } else if ((!(*in_out_found_width)) || // in this case, any sizes are ok. } else if ((!(cs_ctx.width_seen)) || // in this case, any sizes are ok. ((stack_size % 2) == 0)) { // The numbers are vstem definition. // See Adobe Technical Note #5177, page 24, hintmask. (*in_out_num_stems) += (stack_size / 2); if ((*in_out_num_stems) > kMaxNumberOfStemHints) { cs_ctx.num_stems += (stack_size / 2); if ((cs_ctx.num_stems) > kMaxNumberOfStemHints) { return OTS_FAILURE(); } successful = true; Loading @@ -482,21 +471,21 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, return OTS_FAILURE(); } if ((*in_out_num_stems) == 0) { if ((cs_ctx.num_stems) == 0) { return OTS_FAILURE(); } const size_t mask_bytes = (*in_out_num_stems + 7) / 8; const size_t mask_bytes = (cs_ctx.num_stems + 7) / 8; if (!char_string->Skip(mask_bytes)) { return OTS_FAILURE(); } while (!argument_stack->empty()) argument_stack->pop(); *in_out_found_width = true; cs_ctx.width_seen = true; return true; } case ots::kRLineTo: if (!(*in_out_found_width)) { if (!(cs_ctx.width_seen)) { // The first stack-clearing operator should be one of hstem, hstemhm, // vstem, vstemhm, cntrmask, hintmask, hmoveto, vmoveto, rmoveto, or // endchar. For details, see Adobe Technical Note #5177, page 16, note 4. Loading @@ -514,7 +503,7 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, case ots::kHLineTo: case ots::kVLineTo: if (!(*in_out_found_width)) { if (!(cs_ctx.width_seen)) { return OTS_FAILURE(); } if (stack_size < 1) { Loading @@ -525,7 +514,7 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, return true; case ots::kRRCurveTo: if (!(*in_out_found_width)) { if (!(cs_ctx.width_seen)) { return OTS_FAILURE(); } if (stack_size < 6) { Loading @@ -539,7 +528,7 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, return true; case ots::kRCurveLine: if (!(*in_out_found_width)) { if (!(cs_ctx.width_seen)) { return OTS_FAILURE(); } if (stack_size < 8) { Loading @@ -553,7 +542,7 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, return true; case ots::kRLineCurve: if (!(*in_out_found_width)) { if (!(cs_ctx.width_seen)) { return OTS_FAILURE(); } if (stack_size < 8) { Loading @@ -567,7 +556,7 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, return true; case ots::kVVCurveTo: if (!(*in_out_found_width)) { if (!(cs_ctx.width_seen)) { return OTS_FAILURE(); } if (stack_size < 4) { Loading @@ -583,7 +572,7 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, case ots::kHHCurveTo: { bool successful = false; if (!(*in_out_found_width)) { if (!(cs_ctx.width_seen)) { return OTS_FAILURE(); } if (stack_size < 4) { Loading @@ -604,7 +593,7 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, case ots::kVHCurveTo: case ots::kHVCurveTo: { bool successful = false; if (!(*in_out_found_width)) { if (!(cs_ctx.width_seen)) { return OTS_FAILURE(); } if (stack_size < 4) { Loading Loading @@ -747,7 +736,7 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, argument_stack->pop(); argument_stack->push(dummy_result); argument_stack->push(dummy_result); if (ArgumentStackOverflows(argument_stack, cff2)) { if (ArgumentStackOverflows(argument_stack, cs_ctx.cff2)) { return OTS_FAILURE(); } // TODO(yusukes): Implement this. We should push a real value for all Loading @@ -767,7 +756,7 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, return true; case ots::kHFlex: if (!(*in_out_found_width)) { if (!(cs_ctx.width_seen)) { return OTS_FAILURE(); } if (stack_size != 7) { Loading @@ -778,7 +767,7 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, return true; case ots::kFlex: if (!(*in_out_found_width)) { if (!(cs_ctx.width_seen)) { return OTS_FAILURE(); } if (stack_size != 13) { Loading @@ -789,7 +778,7 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, return true; case ots::kHFlex1: if (!(*in_out_found_width)) { if (!(cs_ctx.width_seen)) { return OTS_FAILURE(); } if (stack_size != 9) { Loading @@ -800,7 +789,7 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, return true; case ots::kFlex1: if (!(*in_out_found_width)) { if (!(cs_ctx.width_seen)) { return OTS_FAILURE(); } if (stack_size != 11) { Loading @@ -816,6 +805,7 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, // Executes |char_string| and updates |argument_stack|. // // cff: parent OpenTypeCFF reference // call_depth: The current call depth. Initial value is zero. // global_subrs_index: Global subroutines. // local_subrs_index: Local subroutines for the current glyph. Loading @@ -823,10 +813,16 @@ bool ExecuteCharStringOperator(ots::OpenTypeCFF& cff, // char_string: A charstring we'll execute. |char_string| can be a main routine // in CharString INDEX, or a subroutine in GlobalSubr/LocalSubr. // argument_stack: The stack which an operator in |char_string| operates. // out_found_endchar: true is set if |char_string| contains 'endchar'. // in_out_found_width: true is set if |char_string| contains 'width' byte (which // is 0 or 1 byte.) // in_out_num_stems: total number of hstems and vstems processed so far. // cs_ctx: a CharStringContext holding values to persist across subrs, etc. // endchar_seen: true is set if |char_string| contains 'endchar'. // width_seen: true is set if |char_string| contains 'width' byte (which // is 0 or 1 byte) or if cff2 // num_stems: total number of hstems and vstems processed so far. // cff2: true if this is a CFF2 table // blend_seen: initially false; set to true if 'blend' operator encountered. // vsindex_seen: initially false; set to true if 'vsindex' encountered. // vsindex: initially = PrivateDICT's vsindex; may be changed by 'vsindex' // operator in CharString bool ExecuteCharString(ots::OpenTypeCFF& cff, size_t call_depth, const ots::CFFIndex& global_subrs_index, Loading @@ -834,17 +830,12 @@ bool ExecuteCharString(ots::OpenTypeCFF& cff, ots::Buffer *cff_table, ots::Buffer *char_string, std::stack<int32_t> *argument_stack, bool *out_found_endchar, bool *in_out_found_width, size_t *in_out_num_stems, bool cff2) { ots::CharStringContext& cs_ctx) { if (call_depth > kMaxSubrNesting) { return OTS_FAILURE(); } *out_found_endchar = false; cs_ctx.endchar_seen = false; bool in_out_have_blend = false, in_out_have_visindex = false; int32_t in_out_vsindex = 0; const size_t length = char_string->length(); while (char_string->offset() < length) { int32_t operator_or_operand = 0; Loading Loading @@ -873,7 +864,7 @@ bool ExecuteCharString(ots::OpenTypeCFF& cff, if (!is_operator) { argument_stack->push(operator_or_operand); if (ArgumentStackOverflows(argument_stack, cff2)) { if (ArgumentStackOverflows(argument_stack, cs_ctx.cff2)) { return OTS_FAILURE(); } continue; Loading @@ -888,16 +879,10 @@ bool ExecuteCharString(ots::OpenTypeCFF& cff, cff_table, char_string, argument_stack, out_found_endchar, in_out_found_width, in_out_num_stems, &in_out_have_blend, &in_out_have_visindex, &in_out_vsindex, cff2)) { cs_ctx)) { return OTS_FAILURE(); } if (*out_found_endchar) { if (cs_ctx.endchar_seen) { return true; } if (operator_or_operand == ots::kReturn) { Loading @@ -905,13 +890,13 @@ bool ExecuteCharString(ots::OpenTypeCFF& cff, } } // No endchar operator is found. if (cff2) // No endchar operator is found (CFF1 only) if (cs_ctx.cff2) return true; return OTS_FAILURE(); } // Selects a set of subroutings for |glyph_index| from |cff| and sets it on // Selects a set of subroutines for |glyph_index| from |cff| and sets it on // |out_local_subrs_to_use|. Returns true on success. bool SelectLocalSubr(const ots::OpenTypeCFF& cff, uint16_t glyph_index, // 0-origin Loading Loading @@ -960,7 +945,6 @@ bool ValidateCFFCharStrings( return OTS_FAILURE(); // no charstring. } bool cff2 = (cff.major == 2); // For each glyph, validate the corresponding charstring. for (unsigned i = 1; i < char_strings_index.offsets.size(); ++i) { // Prepare a Buffer object, |char_string|, which contains the charstring Loading Loading @@ -993,20 +977,38 @@ bool ValidateCFFCharStrings( // Check a charstring for the |i|-th glyph. std::stack<int32_t> argument_stack; bool found_endchar = false; // Context to store values that must persist across subrs, etc. CharStringContext cs_ctx; cs_ctx.cff2 = (cff.major == 2); // CFF2 CharString has no value for width, so we start with true here to // error out if width is found. bool found_width = cff2; size_t num_stems = 0; cs_ctx.width_seen = cs_ctx.cff2; // CFF2 CharStrings' default vsindex comes from the associated PrivateDICT if (cs_ctx.cff2) { const auto& iter = cff.fd_select.find(glyph_index); auto fd_index = 0; if (iter != cff.fd_select.end()) { fd_index = iter->second; } if (fd_index >= (int32_t)cff.vsindex_per_font.size()) { // shouldn't get this far with a font in this condition, but just in case return OTS_FAILURE(); // fd_index out-of-range } cs_ctx.vsindex = cff.vsindex_per_font.at(fd_index); } #ifdef DUMP_T2CHARSTRING fprintf(stderr, "\n---- CharString %*d ----\n", 5, glyph_index); #endif if (!ExecuteCharString(cff, 0 /* initial call_depth is zero */, global_subrs_index, *local_subrs_to_use, cff_table, &char_string, &argument_stack, &found_endchar, &found_width, &num_stems, cff2)) { cs_ctx)) { return OTS_FAILURE(); } if (!cff2 && !found_endchar) { if (!cs_ctx.cff2 && !cs_ctx.endchar_seen) { return OTS_FAILURE(); } } Loading
gfx/ots/src/cff_charstring.h +13 −6 Original line number Diff line number Diff line Loading @@ -21,7 +21,7 @@ const size_t kMaxCFF2ArgumentStack = 513; // http://www.adobe.com/devnet/font/pdfs/5177.Type2.pdf // // The validation will fail if one of the following conditions is met: // 1. The code uses more than 48 values of argument stack. // 1. The code uses more than the max argument stack size (48 for CFF1; 513 for CFF2) // 2. The code uses deeply nested subroutine calls (more than 10 levels.) // 3. The code passes invalid number of operands to an operator. // 4. The code calls an undefined global or local subroutine. Loading @@ -29,12 +29,9 @@ const size_t kMaxCFF2ArgumentStack = 513; // an ordinary fonts, and could be dangerous: random, put, get, index, roll. // // Arguments: // cff: parent OpenTypeCFF reference // global_subrs_index: Global subroutines which could be called by a charstring // in |char_strings_index|. // fd_select: A map from glyph # to font #. // local_subrs_per_font: A list of Local Subrs associated with FDArrays. Can be // empty. // local_subrs: A Local Subrs associated with Top DICT. Can be NULL. // cff_table: A buffer which contains actual byte code of charstring, global // subroutines and local subroutines. bool ValidateCFFCharStrings( Loading Loading @@ -95,7 +92,17 @@ enum CharStringOperator { kFlex = (12 << 8) + 35, kHFlex1 = (12 << 8) + 36, kFlex1 = (12 << 8) + 37, // Operators that are undocumented, such as 'blend', will be rejected. // Operators that are undocumented will be rejected. }; struct CharStringContext { bool endchar_seen = false; bool width_seen = false; size_t num_stems = 0; bool cff2 = false; bool blend_seen = false; bool vsindex_seen = false; int32_t vsindex = 0; }; } // namespace ots Loading