[subset] Fix null pointer deref, tidy up a bit

This commit is contained in:
Rod Sheeter 2019-05-16 19:16:52 -07:00
parent 8a84b540c7
commit 5cedda5e4a
2 changed files with 167 additions and 133 deletions

View File

@ -21,7 +21,7 @@
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Google Author(s): Behdad Esfahbod, Garret Reiger, Roderick Sheeter
* Google Author(s): Behdad Esfahbod, Garret Rieger, Roderick Sheeter
*/
#ifndef HB_OT_GLYF_TABLE_HH
@ -80,6 +80,34 @@ struct glyf
return_trace (true);
}
template<typename Iterator>
static void
_add_loca_and_head (hb_subset_plan_t * plan, Iterator padded_offsets)
{
unsigned int max_offset =
+ padded_offsets
| hb_reduce (hb_max, 0);
bool use_short_loca = max_offset <= 131070;
unsigned int loca_prime_size = (padded_offsets.len () + 1) * (use_short_loca ? 2 : 4);
char *loca_prime_data = (char *) calloc(1, loca_prime_size);
if (use_short_loca)
_write_loca <decltype (padded_offsets), HBUINT16> (padded_offsets, 2, loca_prime_data);
else
_write_loca <decltype (padded_offsets), HBUINT32> (padded_offsets, 1, loca_prime_data);
hb_blob_t * loca_blob = hb_blob_create (loca_prime_data,
loca_prime_size,
HB_MEMORY_MODE_READONLY,
loca_prime_data,
free);
plan->add_table (HB_OT_TAG_loca, loca_blob);
_add_head_and_set_loca_version(plan, use_short_loca);
hb_blob_destroy (loca_blob);
}
template<typename Iterator, typename EntryType>
static void
_write_loca (Iterator it, unsigned size_denom, char * dest)
@ -100,7 +128,6 @@ struct glyf
*loca_current = offset / size_denom;
}
// TODO don't pass in plan
template <typename Iterator>
bool serialize(hb_serialize_context_t *c,
Iterator it,
@ -108,33 +135,8 @@ struct glyf
{
TRACE_SERIALIZE (this);
HBUINT8 pad;
pad = 0;
+ it
| hb_apply ( [&] (hb_pair_t <SubsetGlyph, unsigned int> _) {
const SubsetGlyph& src_glyph = _.first;
unsigned int padded_size = _.second;
hb_bytes_t dest_glyph = src_glyph.start.copy(c);
src_glyph.end.copy(c);
dest_glyph.length += src_glyph.end.length;
unsigned int padding = padded_size - dest_glyph.length;
DEBUG_MSG(SUBSET, nullptr, "serialize %d byte glyph, width %d pad %d", dest_glyph.length, padded_size, padding);
while (padding > 0)
{
c->embed(pad);
padding--;
}
_fix_component_gids (plan, dest_glyph);
if (plan->drop_hints)
{
_zero_instruction_length (dest_glyph);
if (unlikely (!_remove_composite_instruction_flag (dest_glyph)))
{
// TODO signal irreversible failure
}
}
});
| hb_apply ( [&] (const SubsetGlyph& _) { _.serialize (c, plan); });
return_trace (true);
}
@ -149,102 +151,35 @@ struct glyf
OT::glyf::accelerator_t glyf;
glyf.init (c->plan->source);
// make an iterator of per-glyph hb_bytes_t.
// unpadded, hints removed if that was requested.
// TODO hb_sink so we don't redo this work for every + glyphs | ... use.
auto glyphs =
// Byte region(s) per glyph to output
// unpadded, hints removed if so requested
// If we fail to process a glyph we produce an empty (0-length) glyph
hb_vector_t<SubsetGlyph> glyphs;
+ hb_range (c->plan->num_output_glyphs ())
| hb_map ([&] (hb_codepoint_t new_gid) {
hb_codepoint_t old_gid;
SubsetGlyph subset_glyph;
subset_glyph.new_gid = new_gid;
// should never fail, ALL old gids should be mapped
if (!c->plan->old_gid_for_new_gid (new_gid, &old_gid)) return subset_glyph;
// should never fail: all old gids should be mapped
if (!c->plan->old_gid_for_new_gid (new_gid, &subset_glyph.old_gid)) return subset_glyph;
unsigned int start_offset, end_offset;
if (unlikely (!(glyf.get_offsets (old_gid, &start_offset, &end_offset) &&
glyf.remove_padding (start_offset, &end_offset))))
{
// TODO signal fatal error
DEBUG_MSG(SUBSET, nullptr, "Unable to get offset or remove padding for new_gid %d", new_gid);
return subset_glyph;
}
subset_glyph.start = hb_bytes_t (((const char *) this) + start_offset, end_offset - start_offset);
if (subset_glyph.start.length == 0) return subset_glyph;
if (unlikely (subset_glyph.start.length < GlyphHeader::static_size))
{
// TODO signal fatal error, invalid glyph
DEBUG_MSG(SUBSET, nullptr, "Glyph size smaller than minimum header %d", new_gid);
return subset_glyph;
}
if (!c->plan->drop_hints) return subset_glyph;
unsigned int instruction_length = 0;
if (!glyf.get_instruction_length (subset_glyph.start, &instruction_length))
{
// TODO signal fatal error
DEBUG_MSG(SUBSET, nullptr, "Unable to read instruction length for new_gid %d", new_gid);
return subset_glyph;
}
DEBUG_MSG(SUBSET, nullptr, "new_gid %d drop %d instruction bytes from %d byte glyph", new_gid, instruction_length, subset_glyph.start.length);
const GlyphHeader& header = StructAtOffset<GlyphHeader> (&subset_glyph.start, 0);
if (header.numberOfContours < 0)
{
// composite, just chop instructions off the end
subset_glyph.start = hb_bytes_t (&subset_glyph.start, subset_glyph.start.length - instruction_length);
}
else
{
// simple glyph
unsigned start_length = GlyphHeader::static_size + 2 * header.numberOfContours + 2;
subset_glyph.end = hb_bytes_t (&subset_glyph.start + start_length + instruction_length,
subset_glyph.start.length - start_length - instruction_length);
subset_glyph.start = hb_bytes_t (&subset_glyph.start, start_length);
}
subset_glyph.source_glyph = glyf.bytes_for_glyph ((const char *) this, subset_glyph.old_gid);
if (c->plan->drop_hints) subset_glyph.drop_hints (glyf);
else subset_glyph.dest_start = subset_glyph.source_glyph;
return subset_glyph;
});
})
| hb_sink (glyphs);
auto padded_offsets =
+ glyphs
| hb_map ([&] (SubsetGlyph _) {
unsigned length = _.start.length + _.end.length;
return length + length % 2;
});
glyf_prime->serialize (c->serializer, hb_iter (glyphs), c->plan);
glyf_prime->serialize (c->serializer, hb_zip (glyphs, padded_offsets), c->plan);
hb_vector_t<unsigned int> padded_offsets;
+ hb_iter (glyphs)
| hb_map ([&] (const SubsetGlyph& _) { return _.padded_size(); })
| hb_sink (padded_offsets);
// TODO whats the right way to serialize loca?
// _subset2 will think these bytes are part of glyf if we write to serializer
unsigned int max_offset = + padded_offsets | hb_reduce (hb_max, 0);
bool use_short_loca = max_offset <= 131070;
unsigned int loca_prime_size = (c->plan->num_output_glyphs () + 1) * (use_short_loca ? 2 : 4);
char *loca_prime_data = (char *) calloc(1, loca_prime_size);
DEBUG_MSG(SUBSET, nullptr, "calloc %u for loca", loca_prime_size); // TEMPORARY
_add_loca_and_head (c->plan, hb_iter (padded_offsets));
if (use_short_loca)
_write_loca <decltype (padded_offsets), HBUINT16> (padded_offsets, 2, loca_prime_data);
else
_write_loca <decltype (padded_offsets), HBUINT32> (padded_offsets, 1, loca_prime_data);
hb_blob_t * loca_blob = hb_blob_create (loca_prime_data,
loca_prime_size,
HB_MEMORY_MODE_READONLY,
loca_prime_data,
free);
if (unlikely (! (c->plan->add_table (HB_OT_TAG_loca, loca_blob)
&& _add_head_and_set_loca_version(c->plan, use_short_loca))))
{
// TODO signal fatal error
hb_blob_destroy (loca_blob);
return false;
}
hb_blob_destroy (loca_blob);
return_trace (true);
}
@ -259,11 +194,11 @@ DEBUG_MSG(SUBSET, nullptr, "calloc %u for loca", loca_prime_size); // TEMPORARY
{
do
{
hb_codepoint_t new_gid;
if (!plan->new_gid_for_old_gid (iterator.current->glyphIndex,
&new_gid))
hb_codepoint_t new_gid;
if (!plan->new_gid_for_old_gid (iterator.current->glyphIndex,
&new_gid))
continue;
((OT::glyf::CompositeGlyphHeader *) iterator.current)->glyphIndex = new_gid;
((OT::glyf::CompositeGlyphHeader *) iterator.current)->glyphIndex = new_gid;
} while (iterator.move_to_next ());
}
}
@ -314,13 +249,6 @@ DEBUG_MSG(SUBSET, nullptr, "calloc %u for loca", loca_prime_size); // TEMPORARY
return success;
}
struct SubsetGlyph
{
hb_bytes_t start;
hb_bytes_t end;
};
struct GlyphHeader
{
HBINT16 numberOfContours; /* If the number of contours is
@ -655,6 +583,25 @@ DEBUG_MSG(SUBSET, nullptr, "calloc %u for loca", loca_prime_size); // TEMPORARY
return true;
}
hb_bytes_t bytes_for_glyph (const char * glyf, hb_codepoint_t gid)
{
unsigned int start_offset, end_offset;
if (unlikely (!(get_offsets (gid, &start_offset, &end_offset) &&
remove_padding (start_offset, &end_offset))))
{
DEBUG_MSG(SUBSET, nullptr, "Unable to get offset or remove padding for %d", gid);
return hb_bytes_t ();
}
hb_bytes_t glyph = hb_bytes_t (glyf + start_offset, end_offset - start_offset);
if (glyph.length == 0) return glyph;
if (unlikely (glyph.length < GlyphHeader::static_size))
{
DEBUG_MSG(SUBSET, nullptr, "Glyph size smaller than minimum header %d", gid);
return hb_bytes_t ();
}
return glyph;
}
private:
bool short_offset;
unsigned int num_glyphs;
@ -662,6 +609,93 @@ DEBUG_MSG(SUBSET, nullptr, "calloc %u for loca", loca_prime_size); // TEMPORARY
hb_blob_ptr_t<glyf> glyf_table;
};
struct SubsetGlyph
{
hb_codepoint_t new_gid;
hb_codepoint_t old_gid;
hb_bytes_t source_glyph;
hb_bytes_t dest_start; // region of source_glyph to copy first
hb_bytes_t dest_end; // region of source_glyph to copy second
bool serialize (hb_serialize_context_t *c,
const hb_subset_plan_t *plan) const
{
TRACE_SERIALIZE (this);
hb_bytes_t dest_glyph = dest_start.copy(c);
dest_glyph = hb_bytes_t (&dest_glyph, dest_glyph.length + dest_end.copy(c).length);
unsigned int pad_length = padding ();
DEBUG_MSG(SUBSET, nullptr, "serialize %d byte glyph, width %d pad %d", dest_glyph.length, dest_glyph.length + pad_length, pad_length);
HBUINT8 pad;
pad = 0;
while (pad_length > 0)
{
c->embed(pad);
pad_length--;
}
if (dest_glyph.length)
{
_fix_component_gids (plan, dest_glyph);
if (plan->drop_hints)
{
_zero_instruction_length (dest_glyph);
c->check_success (_remove_composite_instruction_flag (dest_glyph));
}
}
return_trace (true);
}
void drop_hints (const OT::glyf::accelerator_t& glyf)
{
if (source_glyph.length == 0) return;
unsigned int instruction_length = 0;
if (!glyf.get_instruction_length (source_glyph, &instruction_length))
{
DEBUG_MSG(SUBSET, nullptr, "Unable to read instruction length for new_gid %d", new_gid);
return ;
}
const GlyphHeader& header = StructAtOffset<GlyphHeader> (&source_glyph, 0);
int16_t num_contours = (int16_t) header.numberOfContours;
DEBUG_MSG(SUBSET, nullptr, "new_gid %d (%d contours) drop %d instruction bytes from %d byte source glyph", new_gid, num_contours, instruction_length, source_glyph.length);
if (num_contours < 0)
{
// composite, just chop instructions off the end
dest_start = hb_bytes_t (&source_glyph, source_glyph.length - instruction_length);
}
else
{
// simple glyph
dest_start = hb_bytes_t (&source_glyph, GlyphHeader::static_size + 2 * header.numberOfContours + 2);
dest_end = hb_bytes_t (&source_glyph + dest_start.length + instruction_length,
source_glyph.length - dest_start.length - instruction_length);
DEBUG_MSG(SUBSET, nullptr, "source_len %d start len %d instruction_len %d end len %d", source_glyph.length, dest_start.length, instruction_length, dest_end.length);
}
}
unsigned int length () const
{
return dest_start.length + dest_end.length;
}
// pad to 2 to ensure 2-byte loca will be ok
unsigned int padding () const
{
return length () % 2;
}
unsigned int padded_size () const
{
return length () + padding ();
}
};
protected:
UnsizedArrayOf<HBUINT8> dataZ; /* Glyphs data. */
public:

View File

@ -190,9 +190,9 @@ test_subset_glyf_noop (void)
face_abc_subset = hb_subset_test_create_subset (face_abc, hb_subset_test_create_input (codepoints));
hb_set_destroy (codepoints);
hb_subset_test_check (face_abc, face_abc_subset, HB_TAG ('g','l','y','f'));
hb_subset_test_check (face_abc, face_abc_subset, HB_TAG ('l','o','c', 'a'));
check_maxp_num_glyphs(face_abc_subset, 4, true);
hb_subset_test_check (face_abc, face_abc_subset, HB_TAG ('l','o','c', 'a'));
hb_subset_test_check (face_abc, face_abc_subset, HB_TAG ('g','l','y','f'));
hb_face_destroy (face_abc_subset);
hb_face_destroy (face_abc);
@ -214,9 +214,9 @@ test_subset_glyf_strip_hints_simple (void)
face_abc_subset = hb_subset_test_create_subset (face_abc, input);
hb_set_destroy (codepoints);
check_maxp_num_glyphs(face_abc_subset, 3, false);
hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('l','o','c', 'a'));
hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('g','l','y','f'));
check_maxp_num_glyphs(face_abc_subset, 3, false);
hb_face_destroy (face_abc_subset);
hb_face_destroy (face_abc);
@ -239,9 +239,9 @@ test_subset_glyf_strip_hints_composite (void)
face_generated_subset = hb_subset_test_create_subset (face_components, input);
hb_set_destroy (codepoints);
hb_subset_test_check (face_subset, face_generated_subset, HB_TAG ('g','l','y','f'));
hb_subset_test_check (face_subset, face_generated_subset, HB_TAG ('l','o','c', 'a'));
check_maxp_num_glyphs(face_generated_subset, 4, false);
hb_subset_test_check (face_subset, face_generated_subset, HB_TAG ('l','o','c', 'a'));
hb_subset_test_check (face_subset, face_generated_subset, HB_TAG ('g','l','y','f'));
hb_face_destroy (face_generated_subset);
hb_face_destroy (face_subset);
@ -296,9 +296,9 @@ test_subset_glyf_retain_gids (void)
face_abc_subset = hb_subset_test_create_subset (face_abc, input);
hb_set_destroy (codepoints);
hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('g','l','y','f'));
hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('l','o','c', 'a'));
check_maxp_num_glyphs(face_abc_subset, 4, true);
hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('l','o','c', 'a'));
hb_subset_test_check (face_ac, face_abc_subset, HB_TAG ('g','l','y','f'));
hb_face_destroy (face_abc_subset);
hb_face_destroy (face_abc);
@ -320,9 +320,9 @@ test_subset_glyf_retain_gids_truncates (void)
face_abc_subset = hb_subset_test_create_subset (face_abc, input);
hb_set_destroy (codepoints);
hb_subset_test_check (face_a, face_abc_subset, HB_TAG ('g','l','y','f'));
hb_subset_test_check (face_a, face_abc_subset, HB_TAG ('l','o','c', 'a'));
check_maxp_num_glyphs(face_abc_subset, 2, true);
hb_subset_test_check (face_a, face_abc_subset, HB_TAG ('l','o','c', 'a'));
hb_subset_test_check (face_a, face_abc_subset, HB_TAG ('g','l','y','f'));
hb_face_destroy (face_abc_subset);
hb_face_destroy (face_abc);