[truetype] Keep variation store consistent. `tt_var_load_item_variation_store` fills out a `GX_ItemVarStore`. While it may return an error, the item store must be left in a consistent state so that any use or destruction of the item store can properly use or free the data in it. Before this change the counts from the font data were read directly into the item store before the actual allocation of the arrays to which they referred. There exist many opportunities between the time the counts are read and the arrays are allocated to return early due to invalid data. When this happened the item store claimed to have entires it actually did not, leading to crashes later when it was used. Fixes: https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=54449 * src/truetype/ttgxvar.c (tt_var_load_item_variation_store): Read the counts into local variables and store them in the item store only after the related arrays are actually created on the item store.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217
diff --git a/src/truetype/ttgxvar.c b/src/truetype/ttgxvar.c
index d2452f8..7422fe0 100644
--- a/src/truetype/ttgxvar.c
+++ b/src/truetype/ttgxvar.c
@@ -502,13 +502,15 @@
FT_Error error;
FT_UShort format;
FT_ULong region_offset;
- FT_UInt i, j, k;
- FT_UInt wordDeltaCount;
- FT_Bool long_words;
- GX_Blend blend = face->blend;
- GX_ItemVarData varData;
+ FT_UInt data_count;
+ FT_UShort axis_count;
+ FT_UInt region_count;
+ FT_UInt i, j, k;
+ FT_Bool long_words;
+
+ GX_Blend blend = face->blend;
FT_ULong* dataOffsetArray = NULL;
@@ -525,12 +527,12 @@
}
/* read top level fields */
- if ( FT_READ_ULONG( region_offset ) ||
- FT_READ_USHORT( itemStore->dataCount ) )
+ if ( FT_READ_ULONG( region_offset ) ||
+ FT_READ_USHORT( data_count ) )
goto Exit;
/* we need at least one entry in `itemStore->varData' */
- if ( !itemStore->dataCount )
+ if ( !data_count )
{
FT_TRACE2(( "tt_var_load_item_variation_store: missing varData\n" ));
error = FT_THROW( Invalid_Table );
@@ -539,10 +541,10 @@
/* make temporary copy of item variation data offsets; */
/* we will parse region list first, then come back */
- if ( FT_QNEW_ARRAY( dataOffsetArray, itemStore->dataCount ) )
+ if ( FT_QNEW_ARRAY( dataOffsetArray, data_count ) )
goto Exit;
- for ( i = 0; i < itemStore->dataCount; i++ )
+ for ( i = 0; i < data_count; i++ )
{
if ( FT_READ_ULONG( dataOffsetArray[i] ) )
goto Exit;
@@ -552,11 +554,11 @@
if ( FT_STREAM_SEEK( offset + region_offset ) )
goto Exit;
- if ( FT_READ_USHORT( itemStore->axisCount ) ||
- FT_READ_USHORT( itemStore->regionCount ) )
+ if ( FT_READ_USHORT( axis_count ) ||
+ FT_READ_USHORT( region_count ) )
goto Exit;
- if ( itemStore->axisCount != (FT_Long)blend->mmvar->num_axis )
+ if ( axis_count != (FT_Long)blend->mmvar->num_axis )
{
FT_TRACE2(( "tt_var_load_item_variation_store:"
" number of axes in item variation store\n" ));
@@ -565,9 +567,10 @@
error = FT_THROW( Invalid_Table );
goto Exit;
}
+ itemStore->axisCount = axis_count;
/* new constraint in OpenType 1.8.4 */
- if ( itemStore->regionCount >= 32768U )
+ if ( region_count >= 32768U )
{
FT_TRACE2(( "tt_var_load_item_variation_store:"
" too many variation region tables\n" ));
@@ -575,16 +578,16 @@
goto Exit;
}
- if ( FT_NEW_ARRAY( itemStore->varRegionList, itemStore->regionCount ) )
+ if ( FT_NEW_ARRAY( itemStore->varRegionList, region_count ) )
goto Exit;
+ itemStore->regionCount = region_count;
for ( i = 0; i < itemStore->regionCount; i++ )
{
GX_AxisCoords axisCoords;
- if ( FT_NEW_ARRAY( itemStore->varRegionList[i].axisList,
- itemStore->axisCount ) )
+ if ( FT_NEW_ARRAY( itemStore->varRegionList[i].axisList, axis_count ) )
goto Exit;
axisCoords = itemStore->varRegionList[i].axisList;
@@ -608,47 +611,53 @@
/* end of region list parse */
/* use dataOffsetArray now to parse varData items */
- if ( FT_NEW_ARRAY( itemStore->varData, itemStore->dataCount ) )
+ if ( FT_NEW_ARRAY( itemStore->varData, data_count ) )
goto Exit;
+ itemStore->dataCount = data_count;
- for ( i = 0; i < itemStore->dataCount; i++ )
+ for ( i = 0; i < data_count; i++ )
{
- varData = &itemStore->varData[i];
+ GX_ItemVarData varData = &itemStore->varData[i];
+
+ FT_UInt item_count;
+ FT_UInt word_delta_count;
+ FT_UInt region_idx_count;
+
if ( FT_STREAM_SEEK( offset + dataOffsetArray[i] ) )
goto Exit;
- if ( FT_READ_USHORT( varData->itemCount ) ||
- FT_READ_USHORT( wordDeltaCount ) ||
- FT_READ_USHORT( varData->regionIdxCount ) )
+ if ( FT_READ_USHORT( item_count ) ||
+ FT_READ_USHORT( word_delta_count ) ||
+ FT_READ_USHORT( region_idx_count ) )
goto Exit;
- long_words = !!( wordDeltaCount & 0x8000 );
- wordDeltaCount &= 0x7FFF;
+ long_words = !!( word_delta_count & 0x8000 );
+ word_delta_count &= 0x7FFF;
/* check some data consistency */
- if ( wordDeltaCount > varData->regionIdxCount )
+ if ( word_delta_count > region_idx_count )
{
FT_TRACE2(( "bad short count %d or region count %d\n",
- wordDeltaCount,
- varData->regionIdxCount ));
+ word_delta_count,
+ region_idx_count ));
error = FT_THROW( Invalid_Table );
goto Exit;
}
- if ( varData->regionIdxCount > itemStore->regionCount )
+ if ( region_idx_count > itemStore->regionCount )
{
FT_TRACE2(( "inconsistent regionCount %d in varData[%d]\n",
- varData->regionIdxCount,
+ region_idx_count,
i ));
error = FT_THROW( Invalid_Table );
goto Exit;
}
/* parse region indices */
- if ( FT_NEW_ARRAY( varData->regionIndices,
- varData->regionIdxCount ) )
+ if ( FT_NEW_ARRAY( varData->regionIndices, region_idx_count ) )
goto Exit;
+ varData->regionIdxCount = region_idx_count;
for ( j = 0; j < varData->regionIdxCount; j++ )
{
@@ -664,33 +673,33 @@
}
}
- /* Parse delta set. */
- /* */
- /* On input, deltas are (wordDeltaCount + regionIdxCount) bytes */
- /* each if `long_words` isn't set, and twice as much otherwise. */
- /* */
- /* On output, deltas are expanded to `regionIdxCount` shorts each. */
- if ( FT_NEW_ARRAY( varData->deltaSet,
- varData->regionIdxCount * varData->itemCount ) )
+ /* Parse delta set. */
+ /* */
+ /* On input, deltas are (word_delta_count + region_idx_count) bytes */
+ /* each if `long_words` isn't set, and twice as much otherwise. */
+ /* */
+ /* On output, deltas are expanded to `region_idx_count` shorts each. */
+ if ( FT_NEW_ARRAY( varData->deltaSet, item_count * region_idx_count ) )
goto Exit;
+ varData->itemCount = item_count;
- for ( j = 0; j < varData->itemCount * varData->regionIdxCount; )
+ for ( j = 0; j < item_count * region_idx_count; )
{
if ( long_words )
{
- for ( k = 0; k < wordDeltaCount; k++, j++ )
+ for ( k = 0; k < word_delta_count; k++, j++ )
if ( FT_READ_LONG( varData->deltaSet[j] ) )
goto Exit;
- for ( ; k < varData->regionIdxCount; k++, j++ )
+ for ( ; k < region_idx_count; k++, j++ )
if ( FT_READ_SHORT( varData->deltaSet[j] ) )
goto Exit;
}
else
{
- for ( k = 0; k < wordDeltaCount; k++, j++ )
+ for ( k = 0; k < word_delta_count; k++, j++ )
if ( FT_READ_SHORT( varData->deltaSet[j] ) )
goto Exit;
- for ( ; k < varData->regionIdxCount; k++, j++ )
+ for ( ; k < region_idx_count; k++, j++ )
if ( FT_READ_CHAR( varData->deltaSet[j] ) )
goto Exit;
}