diff --git a/src/hb-ot-cmap-table.hh b/src/hb-ot-cmap-table.hh index 0fab6cad3..b904bb46a 100644 --- a/src/hb-ot-cmap-table.hh +++ b/src/hb-ot-cmap-table.hh @@ -277,9 +277,17 @@ struct CmapSubtableFormat4 HBUINT16 *idRangeOffset = serialize_rangeoffset_glyid (c, format4_iter, endCode, startCode, idDelta, segcount); if (unlikely (!c->check_success (idRangeOffset))) return; - if (unlikely (!c->check_assign(this->length, - c->length () - table_initpos, - HB_SERIALIZE_ERROR_INT_OVERFLOW))) return; + this->length = c->length () - table_initpos; + if ((long long) this->length != (long long) c->length () - table_initpos) + { + // Length overflowed. Discard the current object before setting the error condition, otherwise + // discard is a noop which prevents the higher level code from reverting the serializer to the + // pre-error state in cmap4 overflow handling code. + c->pop_discard (); + c->err (HB_SERIALIZE_ERROR_INT_OVERFLOW); + return; + } + this->segCountX2 = segcount * 2; this->entrySelector = hb_max (1u, hb_bit_storage (segcount)) - 1; this->searchRange = 2 * (1u << this->entrySelector); @@ -1384,26 +1392,45 @@ struct cmap template - void serialize (hb_serialize_context_t *c, + bool serialize (hb_serialize_context_t *c, Iterator it, EncodingRecIter encodingrec_iter, const void *base, - const hb_subset_plan_t *plan) + const hb_subset_plan_t *plan, + bool drop_format_4 = false) { - if (unlikely (!c->extend_min ((*this)))) return; + if (unlikely (!c->extend_min ((*this)))) return false; this->version = 0; unsigned format4objidx = 0, format12objidx = 0, format14objidx = 0; + auto snap = c->snapshot (); for (const EncodingRecord& _ : encodingrec_iter) { + if (c->in_error ()) + return false; + unsigned format = (base+_.subtable).u.format; if (format != 4 && format != 12 && format != 14) continue; hb_set_t unicodes_set; (base+_.subtable).collect_unicodes (&unicodes_set); - if (format == 4) c->copy (_, + it | hb_filter (unicodes_set, hb_first), 4u, base, plan, &format4objidx); + if (!drop_format_4 && format == 4) + { + c->copy (_, + it | hb_filter (unicodes_set, hb_first), 4u, base, plan, &format4objidx); + if (c->in_error () && c->only_overflow ()) + { + // cmap4 overflowed, reset and retry serialization without format 4 subtables. + c->revert (snap); + return serialize (c, it, + encodingrec_iter, + base, + plan, + true); + } + } + else if (format == 12) { if (_can_drop (_, unicodes_set, base, + it | hb_map (hb_first), encodingrec_iter)) continue; @@ -1411,10 +1438,12 @@ struct cmap } else if (format == 14) c->copy (_, it, 14u, base, plan, &format14objidx); } - c->check_assign(this->encodingRecord.len, (c->length () - cmap::min_size)/EncodingRecord::static_size, HB_SERIALIZE_ERROR_INT_OVERFLOW); + + // Fail if format 4 was dropped and there is no cmap12. + return !drop_format_4 || format12objidx; } template _) { return (_.second != HB_MAP_VALUE_INVALID); }) ; - cmap_prime->serialize (c->serializer, it, encodingrec_iter, this, c->plan); - return_trace (true); + + return_trace (cmap_prime->serialize (c->serializer, it, encodingrec_iter, this, c->plan)); } const CmapSubtable *find_best_subtable (bool *symbol = nullptr) const diff --git a/src/hb-serialize.hh b/src/hb-serialize.hh index 5e07bb823..7212d9872 100644 --- a/src/hb-serialize.hh +++ b/src/hb-serialize.hh @@ -102,10 +102,11 @@ struct hb_serialize_context_t char *tail; object_t *current; // Just for sanity check unsigned num_links; + hb_serialize_error_t errors; }; snapshot_t snapshot () - { return snapshot_t { head, tail, current, current->links.length }; } + { return snapshot_t { head, tail, current, current->links.length, errors }; } hb_serialize_context_t (void *start_, unsigned int size) : start ((char *) start_), @@ -136,6 +137,12 @@ struct hb_serialize_context_t HB_NODISCARD bool ran_out_of_room () const { return errors & HB_SERIALIZE_ERROR_OUT_OF_ROOM; } HB_NODISCARD bool offset_overflow () const { return errors & HB_SERIALIZE_ERROR_OFFSET_OVERFLOW; } HB_NODISCARD bool only_offset_overflow () const { return errors == HB_SERIALIZE_ERROR_OFFSET_OVERFLOW; } + HB_NODISCARD bool only_overflow () const + { + return errors == HB_SERIALIZE_ERROR_OFFSET_OVERFLOW + || errors == HB_SERIALIZE_ERROR_INT_OVERFLOW + || errors == HB_SERIALIZE_ERROR_ARRAY_OVERFLOW; + } void reset (void *start_, unsigned int size) { @@ -317,9 +324,11 @@ struct hb_serialize_context_t void revert (snapshot_t snap) { - if (unlikely (in_error ())) return; + // Overflows that happened after the snapshot will be erased by the revert. + if (unlikely (in_error () && !only_overflow ())) return; assert (snap.current == current); current->links.shrink (snap.num_links); + errors = snap.errors; revert (snap.head, snap.tail); } @@ -363,6 +372,8 @@ struct hb_serialize_context_t assert (current->head <= (const char *) &ofs); auto& link = *current->links.push (); + if (current->links.in_error ()) + err (HB_SERIALIZE_ERROR_OTHER); link.width = sizeof (T); link.is_signed = hb_is_signed (hb_unwrap_type (T));