[truetype/GX] Read points and deltas more carefully.
Hopefully fixes newly introduced buffer overflows:
https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=70807
https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=70809
* src/truetype/ttgxvar.c (ft_var_readpacked{points,deltas}): Explicitly
check stream frame limits and modify run counting.
diff --git a/src/truetype/ttgxvar.c b/src/truetype/ttgxvar.c
index 80a9312..b4325b1 100644
--- a/src/truetype/ttgxvar.c
+++ b/src/truetype/ttgxvar.c
@@ -129,9 +129,6 @@
* stream ::
* The data stream.
*
- * size ::
- * The size of the table holding the data.
- *
* @Output:
* point_cnt ::
* The number of points read. A zero value means that
@@ -144,12 +141,11 @@
*/
static FT_UShort*
ft_var_readpackedpoints( FT_Stream stream,
- FT_ULong size,
FT_UInt *point_cnt )
{
FT_UShort *points = NULL;
FT_UInt n;
- FT_UInt runcnt;
+ FT_UInt runcnt, cnt;
FT_UInt i, j;
FT_UShort first;
FT_Byte* p;
@@ -170,59 +166,60 @@
n |= FT_GET_BYTE();
}
- if ( n > size )
- {
- FT_TRACE1(( "ft_var_readpackedpoints: number of points too large\n" ));
+ if ( FT_QNEW_ARRAY( points, n ) )
return NULL;
- }
-
- /* in the nested loops below we increase `i' twice; */
- /* it is faster to simply allocate one more slot */
- /* than to add another test within the loop */
- if ( FT_QNEW_ARRAY( points, n + 1 ) )
- return NULL;
-
- *point_cnt = n;
p = stream->cursor;
first = 0;
i = 0;
while ( i < n )
{
+ if ( p >= stream->limit )
+ goto Fail;
+
runcnt = FT_NEXT_BYTE( p );
+ cnt = runcnt & GX_PT_POINT_RUN_COUNT_MASK;
+
+ /* first point not included in run count */
+ cnt++;
+ if ( i + cnt > n )
+ cnt = n - i;
+
if ( runcnt & GX_PT_POINTS_ARE_WORDS )
{
- runcnt &= GX_PT_POINT_RUN_COUNT_MASK;
- first += FT_NEXT_USHORT( p );
- points[i++] = first;
+ if ( p + 2 * cnt > stream->limit )
+ goto Fail;
- /* first point not included in run count */
- for ( j = 0; j < runcnt; j++ )
+ for ( j = 0; j < cnt; j++ )
{
first += FT_NEXT_USHORT( p );
points[i++] = first;
- if ( i >= n )
- break;
}
}
else
{
- first += FT_NEXT_BYTE( p );
- points[i++] = first;
+ if ( p + cnt > stream->limit )
+ goto Fail;
- for ( j = 0; j < runcnt; j++ )
+ for ( j = 0; j < cnt; j++ )
{
first += FT_NEXT_BYTE( p );
points[i++] = first;
- if ( i >= n )
- break;
}
}
}
stream->cursor = p;
+ *point_cnt = n;
+
return points;
+
+ Fail:
+ FT_TRACE1(( "ft_var_readpackedpoints: invalid table\n" ));
+
+ FT_FREE( points );
+ return NULL;
}
@@ -244,9 +241,6 @@
* stream ::
* The data stream.
*
- * size ::
- * The size of the table holding the data.
- *
* delta_cnt ::
* The number of deltas to be read.
*
@@ -262,13 +256,11 @@
*/
static FT_Fixed*
ft_var_readpackeddeltas( FT_Stream stream,
- FT_ULong size,
FT_UInt delta_cnt )
{
FT_Fixed *deltas = NULL;
FT_UInt runcnt, cnt;
FT_UInt i, j;
- FT_UInt bytes_used;
FT_Byte* p;
FT_Memory memory = stream->memory;
FT_Error error;
@@ -277,64 +269,44 @@
if ( FT_QNEW_ARRAY( deltas, delta_cnt ) )
return NULL;
- p = stream->cursor;
- i = 0;
- bytes_used = 0;
-
- while ( i < delta_cnt && bytes_used < size )
+ p = stream->cursor;
+ i = 0;
+ while ( i < delta_cnt )
{
+ if ( p >= stream->limit )
+ {
+ goto Fail;
+ }
+
runcnt = FT_NEXT_BYTE( p );
cnt = runcnt & GX_DT_DELTA_RUN_COUNT_MASK;
- bytes_used++;
+ /* first point not included in run count */
+ cnt++;
+ if ( i + cnt > delta_cnt )
+ cnt = delta_cnt - i;
if ( runcnt & GX_DT_DELTAS_ARE_ZERO )
{
- /* `cnt` + 1 zeroes get added */
- for ( j = 0; j <= cnt && i < delta_cnt; j++ )
+ for ( j = 0; j < cnt; j++ )
deltas[i++] = 0;
}
else if ( runcnt & GX_DT_DELTAS_ARE_WORDS )
{
- /* `cnt` + 1 shorts from the stack */
- bytes_used += 2 * ( cnt + 1 );
- if ( bytes_used > size )
- {
- FT_TRACE1(( "ft_var_readpackeddeltas:"
- " number of short deltas too large\n" ));
+ if ( p + 2 * cnt > stream->limit )
goto Fail;
- }
- for ( j = 0; j <= cnt && i < delta_cnt; j++ )
+ for ( j = 0; j < cnt; j++ )
deltas[i++] = FT_intToFixed( FT_NEXT_SHORT( p ) );
}
else
{
- /* `cnt` + 1 signed bytes from the stack */
- bytes_used += cnt + 1;
- if ( bytes_used > size )
- {
- FT_TRACE1(( "ft_var_readpackeddeltas:"
- " number of byte deltas too large\n" ));
+ if ( p + cnt > stream->limit )
goto Fail;
- }
- for ( j = 0; j <= cnt && i < delta_cnt; j++ )
+ for ( j = 0; j < cnt; j++ )
deltas[i++] = FT_intToFixed( FT_NEXT_CHAR( p ) );
}
-
- if ( j <= cnt )
- {
- FT_TRACE1(( "ft_var_readpackeddeltas:"
- " number of deltas too large\n" ));
- goto Fail;
- }
- }
-
- if ( i < delta_cnt )
- {
- FT_TRACE1(( "ft_var_readpackeddeltas: not enough deltas\n" ));
- goto Fail;
}
stream->cursor = p;
@@ -342,6 +314,8 @@
return deltas;
Fail:
+ FT_TRACE1(( "ft_var_readpackeddeltas: invalid table\n" ));
+
FT_FREE( deltas );
return NULL;
}
@@ -3612,9 +3586,8 @@
FT_Stream_SeekSet( stream, offsetToData );
- sharedpoints = ft_var_readpackedpoints( stream,
- table_len,
- &spoint_count );
+ sharedpoints = ft_var_readpackedpoints( stream, &spoint_count );
+
offsetToData = FT_Stream_FTell( stream );
FT_Stream_SeekSet( stream, here );
@@ -3688,9 +3661,7 @@
if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS )
{
- localpoints = ft_var_readpackedpoints( stream,
- table_len,
- &point_count );
+ localpoints = ft_var_readpackedpoints( stream, &point_count );
points = localpoints;
}
else
@@ -3701,7 +3672,6 @@
}
deltas = ft_var_readpackeddeltas( stream,
- table_len,
point_count == 0 ? face->cvt_size
: point_count );
@@ -4150,9 +4120,8 @@
FT_Stream_SeekSet( stream, offsetToData );
- sharedpoints = ft_var_readpackedpoints( stream,
- blend->gvar_size,
- &spoint_count );
+ sharedpoints = ft_var_readpackedpoints( stream, &spoint_count );
+
offsetToData = FT_Stream_FTell( stream );
FT_Stream_SeekSet( stream, here );
@@ -4236,9 +4205,7 @@
if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS )
{
- localpoints = ft_var_readpackedpoints( stream,
- blend->gvar_size,
- &point_count );
+ localpoints = ft_var_readpackedpoints( stream, &point_count );
points = localpoints;
}
else
@@ -4248,11 +4215,9 @@
}
deltas_x = ft_var_readpackeddeltas( stream,
- blend->gvar_size,
point_count == 0 ? n_points
: point_count );
deltas_y = ft_var_readpackeddeltas( stream,
- blend->gvar_size,
point_count == 0 ? n_points
: point_count );