[gvar] Fix invalid memory access by refactoring GlyphVarData fetch logic

Fixes https://crbug.com/oss-fuzz/20906
This commit is contained in:
Ebrahim Byagowi 2020-02-27 15:58:58 +03:30
parent f44e1dc07d
commit 8eba66c1c6
2 changed files with 36 additions and 34 deletions

View File

@ -416,19 +416,25 @@ struct gvar
unsigned int num_glyphs = c->plan->num_output_glyphs (); unsigned int num_glyphs = c->plan->num_output_glyphs ();
out->glyphCount = num_glyphs; out->glyphCount = num_glyphs;
hb_blob_ptr_t<gvar> table = hb_sanitize_context_t ().reference_table<gvar> (c->plan->source);
unsigned int subset_data_size = 0; unsigned int subset_data_size = 0;
for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++) for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++)
{ {
hb_codepoint_t old_gid; hb_codepoint_t old_gid;
if (!c->plan->old_gid_for_new_gid (gid, &old_gid)) continue; if (!c->plan->old_gid_for_new_gid (gid, &old_gid)) continue;
subset_data_size += get_glyph_var_data_length (old_gid); subset_data_size += get_glyph_var_data_bytes (table.get_blob (), old_gid).length;
} }
bool long_offset = subset_data_size & ~0xFFFFu; bool long_offset = subset_data_size & ~0xFFFFu;
out->flags = long_offset ? 1 : 0; out->flags = long_offset ? 1 : 0;
HBUINT8 *subset_offsets = c->serializer->allocate_size<HBUINT8> ((long_offset ? 4 : 2) * (num_glyphs + 1)); HBUINT8 *subset_offsets = c->serializer->allocate_size<HBUINT8> ((long_offset ? 4 : 2) * (num_glyphs + 1));
if (!subset_offsets) return_trace (false); if (!subset_offsets)
{
table.destroy ();
return_trace (false);
}
/* shared tuples */ /* shared tuples */
if (!sharedTupleCount || !sharedTuples) if (!sharedTupleCount || !sharedTuples)
@ -437,49 +443,58 @@ struct gvar
{ {
unsigned int shared_tuple_size = F2DOT14::static_size * axisCount * sharedTupleCount; unsigned int shared_tuple_size = F2DOT14::static_size * axisCount * sharedTupleCount;
F2DOT14 *tuples = c->serializer->allocate_size<F2DOT14> (shared_tuple_size); F2DOT14 *tuples = c->serializer->allocate_size<F2DOT14> (shared_tuple_size);
if (!tuples) return_trace (false); if (!tuples)
{
table.destroy ();
return_trace (false);
}
out->sharedTuples = (char *) tuples - (char *) out; out->sharedTuples = (char *) tuples - (char *) out;
memcpy (tuples, &(this+sharedTuples), shared_tuple_size); memcpy (tuples, &(this+sharedTuples), shared_tuple_size);
} }
char *subset_data = c->serializer->allocate_size<char> (subset_data_size); char *subset_data = c->serializer->allocate_size<char> (subset_data_size);
if (!subset_data) return_trace (false); if (!subset_data)
{
table.destroy ();
return_trace (false);
}
out->dataZ = subset_data - (char *)out; out->dataZ = subset_data - (char *)out;
unsigned int glyph_offset = 0; unsigned int glyph_offset = 0;
for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++) for (hb_codepoint_t gid = 0; gid < num_glyphs; gid++)
{ {
hb_codepoint_t old_gid; hb_codepoint_t old_gid;
unsigned int length = c->plan->old_gid_for_new_gid (gid, &old_gid) ? get_glyph_var_data_length (old_gid) : 0; hb_bytes_t var_data_bytes = c->plan->old_gid_for_new_gid (gid, &old_gid)
? get_glyph_var_data_bytes (table.get_blob (), old_gid)
: hb_bytes_t ();
if (long_offset) if (long_offset)
((HBUINT32 *) subset_offsets)[gid] = glyph_offset; ((HBUINT32 *) subset_offsets)[gid] = glyph_offset;
else else
((HBUINT16 *) subset_offsets)[gid] = glyph_offset / 2; ((HBUINT16 *) subset_offsets)[gid] = glyph_offset / 2;
if (length > 0) memcpy (subset_data, &get_glyph_var_data (old_gid), length); if (var_data_bytes.length > 0)
subset_data += length; memcpy (subset_data, var_data_bytes.arrayZ, var_data_bytes.length);
glyph_offset += length; subset_data += var_data_bytes.length;
glyph_offset += var_data_bytes.length;
} }
if (long_offset) if (long_offset)
((HBUINT32 *) subset_offsets)[num_glyphs] = glyph_offset; ((HBUINT32 *) subset_offsets)[num_glyphs] = glyph_offset;
else else
((HBUINT16 *) subset_offsets)[num_glyphs] = glyph_offset / 2; ((HBUINT16 *) subset_offsets)[num_glyphs] = glyph_offset / 2;
table.destroy ();
return_trace (true); return_trace (true);
} }
protected: protected:
const GlyphVarData &get_glyph_var_data (hb_codepoint_t glyph) const const hb_bytes_t get_glyph_var_data_bytes (hb_blob_t *blob, hb_codepoint_t glyph) const
{ {
unsigned int start_offset = get_offset (glyph); unsigned start_offset = get_offset (glyph);
unsigned int end_offset = get_offset (glyph+1); unsigned length = get_offset (glyph+1) - start_offset;
return unlikely (GlyphVarData::min_size > length)
if ((start_offset == end_offset) || ? hb_bytes_t ()
unlikely ((start_offset > get_offset (glyphCount)) || : blob->as_bytes ().sub_array (((unsigned) dataZ) + start_offset, length);
(start_offset + GlyphVarData::min_size > end_offset)))
return Null (GlyphVarData);
return (((unsigned char *) this + start_offset) + dataZ);
} }
bool is_long_offset () const { return (flags & 1) != 0; } bool is_long_offset () const { return (flags & 1) != 0; }
@ -492,15 +507,6 @@ struct gvar
return get_short_offset_array ()[i] * 2; return get_short_offset_array ()[i] * 2;
} }
unsigned int get_glyph_var_data_length (unsigned int glyph) const
{
unsigned int end_offset = get_offset (glyph + 1);
unsigned int start_offset = get_offset (glyph);
if (unlikely (start_offset > end_offset || end_offset > get_offset (glyphCount)))
return 0;
return end_offset - start_offset;
}
const HBUINT32 * get_long_offset_array () const { return (const HBUINT32 *) &offsetZ; } const HBUINT32 * get_long_offset_array () const { return (const HBUINT32 *) &offsetZ; }
const HBUINT16 *get_short_offset_array () const { return (const HBUINT16 *) &offsetZ; } const HBUINT16 *get_short_offset_array () const { return (const HBUINT16 *) &offsetZ; }
@ -568,12 +574,12 @@ struct gvar
coord_count = hb_min (coord_count, gvar_table->axisCount); coord_count = hb_min (coord_count, gvar_table->axisCount);
if (!coord_count || coord_count != gvar_table->axisCount) return true; if (!coord_count || coord_count != gvar_table->axisCount) return true;
const GlyphVarData &var_data = gvar_table->get_glyph_var_data (glyph); hb_bytes_t var_data_bytes = gvar_table->get_glyph_var_data_bytes (gvar_table.get_blob (), glyph);
if (!var_data.has_data ()) return true; const GlyphVarData *var_data = var_data_bytes.as<GlyphVarData> ();
if (!var_data->has_data ()) return true;
hb_vector_t<unsigned int> shared_indices; hb_vector_t<unsigned int> shared_indices;
GlyphVarData::tuple_iterator_t iterator; GlyphVarData::tuple_iterator_t iterator;
if (!GlyphVarData::get_tuple_iterator (&var_data, if (!GlyphVarData::get_tuple_iterator (var_data, var_data_bytes.length,
gvar_table->get_glyph_var_data_length (glyph),
gvar_table->axisCount, gvar_table->axisCount,
shared_indices, shared_indices,
&iterator)) &iterator))
@ -699,10 +705,6 @@ no_more_gaps:
unsigned int get_axis_count () const { return gvar_table->axisCount; } unsigned int get_axis_count () const { return gvar_table->axisCount; }
protected:
const GlyphVarData &get_glyph_var_data (hb_codepoint_t glyph) const
{ return gvar_table->get_glyph_var_data (glyph); }
private: private:
hb_blob_ptr_t<gvar> gvar_table; hb_blob_ptr_t<gvar> gvar_table;
hb_vector_t<F2DOT14> shared_tuples; hb_vector_t<F2DOT14> shared_tuples;