[base] More sanity checks for Mac resources.
We use https://github.com/kreativekorp/ksfl/wiki/Macintosh-Resource-File-Format and https://developer.apple.com/legacy/library/documentation/mac/pdf/MoreMacintoshToolbox.pdf#page=151 as references. * include/freetype/internal/ftrfork.h (FT_RFork_Ref): Use FT_Short for `res_id'. * src/base/ftrfork.c (FT_Raccess_Get_HeaderInfo): Extract map length and use it to improve sanity checks. Follow the specification more closely;in particular, all data types are signed, not unsigned. (FT_Raccess_Get_DataOffsets): Follow the specification more closely; in particular, all data types are signed, not unsigned. Add some sanity checks.
This commit is contained in:
parent
b55730320c
commit
ded4bdb5d0
25
ChangeLog
25
ChangeLog
|
@ -1,3 +1,28 @@
|
||||||
|
2016-12-20 Werner Lemberg <wl@gnu.org>
|
||||||
|
|
||||||
|
[base] More sanity checks for Mac resources.
|
||||||
|
|
||||||
|
We use
|
||||||
|
|
||||||
|
https://github.com/kreativekorp/ksfl/wiki/Macintosh-Resource-File-Format
|
||||||
|
|
||||||
|
and
|
||||||
|
|
||||||
|
https://developer.apple.com/legacy/library/documentation/mac/pdf/MoreMacintoshToolbox.pdf#page=151
|
||||||
|
|
||||||
|
as references.
|
||||||
|
|
||||||
|
* include/freetype/internal/ftrfork.h (FT_RFork_Ref): Use FT_Short
|
||||||
|
for `res_id'.
|
||||||
|
|
||||||
|
* src/base/ftrfork.c (FT_Raccess_Get_HeaderInfo): Extract map length
|
||||||
|
and use it to improve sanity checks.
|
||||||
|
Follow the specification more closely;in particular, all data types
|
||||||
|
are signed, not unsigned.
|
||||||
|
(FT_Raccess_Get_DataOffsets): Follow the specification more closely;
|
||||||
|
in particular, all data types are signed, not unsigned.
|
||||||
|
Add some sanity checks.
|
||||||
|
|
||||||
2016-12-20 Werner Lemberg <wl@gnu.org>
|
2016-12-20 Werner Lemberg <wl@gnu.org>
|
||||||
|
|
||||||
[truetype] Improve logic for getting fast advance widths.
|
[truetype] Improve logic for getting fast advance widths.
|
||||||
|
|
|
@ -43,11 +43,12 @@ FT_BEGIN_HEADER
|
||||||
|
|
||||||
typedef struct FT_RFork_Ref_
|
typedef struct FT_RFork_Ref_
|
||||||
{
|
{
|
||||||
FT_UShort res_id;
|
FT_Short res_id;
|
||||||
FT_Long offset;
|
FT_Long offset;
|
||||||
|
|
||||||
} FT_RFork_Ref;
|
} FT_RFork_Ref;
|
||||||
|
|
||||||
|
|
||||||
#ifdef FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK
|
#ifdef FT_CONFIG_OPTION_GUESSING_EMBEDDED_RFORK
|
||||||
typedef FT_Error
|
typedef FT_Error
|
||||||
(*ft_raccess_guess_func)( FT_Library library,
|
(*ft_raccess_guess_func)( FT_Library library,
|
||||||
|
|
|
@ -56,7 +56,7 @@
|
||||||
{
|
{
|
||||||
FT_Error error;
|
FT_Error error;
|
||||||
unsigned char head[16], head2[16];
|
unsigned char head[16], head2[16];
|
||||||
FT_Long map_pos, rdata_len;
|
FT_Long map_pos, map_len, rdata_len;
|
||||||
int allzeros, allmatch, i;
|
int allzeros, allmatch, i;
|
||||||
FT_Long type_list;
|
FT_Long type_list;
|
||||||
|
|
||||||
|
@ -67,12 +67,15 @@
|
||||||
if ( error )
|
if ( error )
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
error = FT_Stream_Read( stream, (FT_Byte *)head, 16 );
|
error = FT_Stream_Read( stream, (FT_Byte*)head, 16 );
|
||||||
if ( error )
|
if ( error )
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
/* ensure positive values */
|
/* ensure positive values */
|
||||||
if ( head[0] >= 0x80 || head[4] >= 0x80 || head[8] >= 0x80 )
|
if ( head[0] >= 0x80 ||
|
||||||
|
head[4] >= 0x80 ||
|
||||||
|
head[8] >= 0x80 ||
|
||||||
|
head[12] >= 0x80 )
|
||||||
return FT_THROW( Unknown_File_Format );
|
return FT_THROW( Unknown_File_Format );
|
||||||
|
|
||||||
*rdata_pos = ( head[ 0] << 24 ) |
|
*rdata_pos = ( head[ 0] << 24 ) |
|
||||||
|
@ -87,14 +90,36 @@
|
||||||
( head[ 9] << 16 ) |
|
( head[ 9] << 16 ) |
|
||||||
( head[10] << 8 ) |
|
( head[10] << 8 ) |
|
||||||
head[11];
|
head[11];
|
||||||
|
map_len = ( head[12] << 24 ) |
|
||||||
|
( head[13] << 16 ) |
|
||||||
|
( head[14] << 8 ) |
|
||||||
|
head[15];
|
||||||
|
|
||||||
/* map_len = head[12] .. head[15] */
|
/* the map must not be empty */
|
||||||
|
if ( !map_pos )
|
||||||
if ( *rdata_pos != map_pos - rdata_len || map_pos == 0 )
|
|
||||||
return FT_THROW( Unknown_File_Format );
|
return FT_THROW( Unknown_File_Format );
|
||||||
|
|
||||||
if ( FT_LONG_MAX - rfork_offset < *rdata_pos ||
|
/* check whether rdata and map overlap */
|
||||||
FT_LONG_MAX - rfork_offset < map_pos )
|
if ( *rdata_pos < map_pos )
|
||||||
|
{
|
||||||
|
if ( *rdata_pos > map_pos - rdata_len )
|
||||||
|
return FT_THROW( Unknown_File_Format );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ( map_pos > *rdata_pos - map_len )
|
||||||
|
return FT_THROW( Unknown_File_Format );
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check whether end of rdata or map exceeds stream size */
|
||||||
|
if ( FT_LONG_MAX - rdata_len < *rdata_pos ||
|
||||||
|
FT_LONG_MAX - map_len < map_pos ||
|
||||||
|
|
||||||
|
FT_LONG_MAX - ( *rdata_pos + rdata_len ) < rfork_offset ||
|
||||||
|
FT_LONG_MAX - ( map_pos + map_len ) < rfork_offset ||
|
||||||
|
|
||||||
|
(FT_ULong)( rfork_offset + *rdata_pos + rdata_len ) > stream->size ||
|
||||||
|
(FT_ULong)( rfork_offset + map_pos + map_len ) > stream->size )
|
||||||
return FT_THROW( Unknown_File_Format );
|
return FT_THROW( Unknown_File_Format );
|
||||||
|
|
||||||
*rdata_pos += rfork_offset;
|
*rdata_pos += rfork_offset;
|
||||||
|
@ -124,15 +149,14 @@
|
||||||
|
|
||||||
/* If we have reached this point then it is probably a mac resource */
|
/* If we have reached this point then it is probably a mac resource */
|
||||||
/* file. Now, does it contain any interesting resources? */
|
/* file. Now, does it contain any interesting resources? */
|
||||||
/* Skip handle to next resource map, the file resource number, and */
|
|
||||||
/* attributes. */
|
|
||||||
(void)FT_STREAM_SKIP( 4 /* skip handle to next resource map */
|
(void)FT_STREAM_SKIP( 4 /* skip handle to next resource map */
|
||||||
+ 2 /* skip file resource number */
|
+ 2 /* skip file resource number */
|
||||||
+ 2 ); /* skip attributes */
|
+ 2 ); /* skip attributes */
|
||||||
|
|
||||||
if ( FT_READ_USHORT( type_list ) )
|
if ( FT_READ_SHORT( type_list ) )
|
||||||
return error;
|
return error;
|
||||||
if ( type_list == -1 )
|
if ( type_list < 0 )
|
||||||
return FT_THROW( Unknown_File_Format );
|
return FT_THROW( Unknown_File_Format );
|
||||||
|
|
||||||
error = FT_Stream_Seek( stream, (FT_ULong)( map_pos + type_list ) );
|
error = FT_Stream_Seek( stream, (FT_ULong)( map_pos + type_list ) );
|
||||||
|
@ -181,15 +205,34 @@
|
||||||
if ( error )
|
if ( error )
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
if ( FT_READ_USHORT( cnt ) )
|
if ( FT_READ_SHORT( cnt ) )
|
||||||
return error;
|
return error;
|
||||||
cnt++;
|
cnt++;
|
||||||
|
|
||||||
|
/* `rpos' is a signed 16bit integer offset to resource records; the */
|
||||||
|
/* size of a resource record is 12 bytes. The map header is 28 bytes, */
|
||||||
|
/* and a type list needs 10 bytes or more. If we assume that the name */
|
||||||
|
/* list is empty and we have only a single entry in the type list, */
|
||||||
|
/* there can be at most */
|
||||||
|
/* */
|
||||||
|
/* (32768 - 28 - 10) / 12 = 2727 */
|
||||||
|
/* */
|
||||||
|
/* resources. */
|
||||||
|
/* */
|
||||||
|
/* A type list starts with a two-byte counter, followed by 10-byte */
|
||||||
|
/* type records. Assuming that there are no resources, the number of */
|
||||||
|
/* type records can be at most */
|
||||||
|
/* */
|
||||||
|
/* (32768 - 28 - 2) / 8 = 4079 */
|
||||||
|
/* */
|
||||||
|
if ( cnt > 4079 )
|
||||||
|
return FT_THROW( Invalid_Table );
|
||||||
|
|
||||||
for ( i = 0; i < cnt; ++i )
|
for ( i = 0; i < cnt; ++i )
|
||||||
{
|
{
|
||||||
if ( FT_READ_LONG( tag_internal ) ||
|
if ( FT_READ_LONG( tag_internal ) ||
|
||||||
FT_READ_USHORT( subcnt ) ||
|
FT_READ_SHORT( subcnt ) ||
|
||||||
FT_READ_USHORT( rpos ) )
|
FT_READ_SHORT( rpos ) )
|
||||||
return error;
|
return error;
|
||||||
|
|
||||||
FT_TRACE2(( "Resource tags: %c%c%c%c\n",
|
FT_TRACE2(( "Resource tags: %c%c%c%c\n",
|
||||||
|
@ -205,6 +248,9 @@
|
||||||
*count = subcnt + 1;
|
*count = subcnt + 1;
|
||||||
rpos += map_offset;
|
rpos += map_offset;
|
||||||
|
|
||||||
|
if ( *count > 2727 )
|
||||||
|
return FT_THROW( Invalid_Table );
|
||||||
|
|
||||||
error = FT_Stream_Seek( stream, (FT_ULong)rpos );
|
error = FT_Stream_Seek( stream, (FT_ULong)rpos );
|
||||||
if ( error )
|
if ( error )
|
||||||
return error;
|
return error;
|
||||||
|
@ -214,34 +260,43 @@
|
||||||
|
|
||||||
for ( j = 0; j < *count; ++j )
|
for ( j = 0; j < *count; ++j )
|
||||||
{
|
{
|
||||||
if ( FT_READ_USHORT( ref[j].res_id ) )
|
if ( FT_READ_SHORT( ref[j].res_id ) )
|
||||||
goto Exit;
|
goto Exit;
|
||||||
if ( FT_STREAM_SKIP( 2 ) ) /* resource name */
|
if ( FT_STREAM_SKIP( 2 ) ) /* resource name offset */
|
||||||
goto Exit;
|
goto Exit;
|
||||||
if ( FT_READ_LONG( temp ) )
|
if ( FT_READ_LONG( temp ) ) /* attributes (8bit), offset (24bit) */
|
||||||
goto Exit;
|
goto Exit;
|
||||||
if ( FT_STREAM_SKIP( 4 ) ) /* mbz */
|
if ( FT_STREAM_SKIP( 4 ) ) /* mbz */
|
||||||
goto Exit;
|
goto Exit;
|
||||||
|
|
||||||
|
if ( ref[j].res_id < 0 || temp < 0 )
|
||||||
|
{
|
||||||
|
error = FT_THROW( Invalid_Table );
|
||||||
|
goto Exit;
|
||||||
|
}
|
||||||
|
|
||||||
ref[j].offset = temp & 0xFFFFFFL;
|
ref[j].offset = temp & 0xFFFFFFL;
|
||||||
|
|
||||||
FT_TRACE3(( " [%d]:"
|
FT_TRACE3(( " [%d]:"
|
||||||
" resource_id=0x%04x, offset=0x%08x\n",
|
" resource_id=0x%04x, offset=0x%08x\n",
|
||||||
j, ref[j].res_id, ref[j].offset ));
|
j, ref[j].res_id, ref[j].offset ));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sort_by_res_id)
|
if ( sort_by_res_id )
|
||||||
{
|
{
|
||||||
ft_qsort( ref, (size_t)*count, sizeof ( FT_RFork_Ref ),
|
ft_qsort( ref,
|
||||||
( int(*)(const void*, const void*) )
|
(size_t)*count,
|
||||||
ft_raccess_sort_ref_by_id );
|
sizeof ( FT_RFork_Ref ),
|
||||||
|
( int(*)(const void*,
|
||||||
|
const void*) )ft_raccess_sort_ref_by_id );
|
||||||
|
|
||||||
FT_TRACE3(( " -- sort resources by their ids --\n" ));
|
FT_TRACE3(( " -- sort resources by their ids --\n" ));
|
||||||
for ( j = 0; j < *count; ++ j ) {
|
|
||||||
|
for ( j = 0; j < *count; j++ )
|
||||||
FT_TRACE3(( " [%d]:"
|
FT_TRACE3(( " [%d]:"
|
||||||
" resource_id=0x%04x, offset=0x%08x\n",
|
" resource_id=0x%04x, offset=0x%08x\n",
|
||||||
j, ref[j].res_id, ref[j].offset ));
|
j, ref[j].res_id, ref[j].offset ));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if ( FT_NEW_ARRAY( offsets_internal, *count ) )
|
if ( FT_NEW_ARRAY( offsets_internal, *count ) )
|
||||||
goto Exit;
|
goto Exit;
|
||||||
|
@ -250,7 +305,7 @@
|
||||||
* gap between reference IDs are acceptable?
|
* gap between reference IDs are acceptable?
|
||||||
* further investigation on Apple implementation is needed.
|
* further investigation on Apple implementation is needed.
|
||||||
*/
|
*/
|
||||||
for ( j = 0; j < *count; ++j )
|
for ( j = 0; j < *count; j++ )
|
||||||
offsets_internal[j] = rdata_pos + ref[j].offset;
|
offsets_internal[j] = rdata_pos + ref[j].offset;
|
||||||
|
|
||||||
*offsets = offsets_internal;
|
*offsets = offsets_internal;
|
||||||
|
|
Loading…
Reference in New Issue