/***************************************************************************/ /* */ /* gxvfeat.c */ /* */ /* TrueTypeGX/AAT feat table validation (body). */ /* */ /* Copyright 2004-2018 by */ /* suzuki toshiya, Masatake YAMATO, Red Hat K.K., */ /* David Turner, Robert Wilhelm, and Werner Lemberg. */ /* */ /* This file is part of the FreeType project, and may only be used, */ /* modified, and distributed under the terms of the FreeType project */ /* license, LICENSE.TXT. By continuing to use, modify, or distribute */ /* this file you indicate that you have read the license and */ /* understand and accept it fully. */ /* */ /***************************************************************************/ /***************************************************************************/ /* */ /* gxvalid is derived from both gxlayout module and otvalid module. */ /* Development of gxlayout is supported by the Information-technology */ /* Promotion Agency(IPA), Japan. */ /* */ /***************************************************************************/ #include "gxvalid.h" #include "gxvcommn.h" #include "gxvfeat.h" /*************************************************************************/ /* */ /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ /* messages during execution. */ /* */ #undef FT_COMPONENT #define FT_COMPONENT trace_gxvfeat /*************************************************************************/ /*************************************************************************/ /***** *****/ /***** Data and Types *****/ /***** *****/ /*************************************************************************/ /*************************************************************************/ typedef struct GXV_feat_DataRec_ { FT_UInt reserved_size; FT_UShort feature; FT_UShort setting; } GXV_feat_DataRec, *GXV_feat_Data; #define GXV_FEAT_DATA( field ) GXV_TABLE_DATA( feat, field ) typedef enum GXV_FeatureFlagsMask_ { GXV_FEAT_MASK_EXCLUSIVE_SETTINGS = 0x8000U, GXV_FEAT_MASK_DYNAMIC_DEFAULT = 0x4000, GXV_FEAT_MASK_UNUSED = 0x3F00, GXV_FEAT_MASK_DEFAULT_SETTING = 0x00FF } GXV_FeatureFlagsMask; /*************************************************************************/ /*************************************************************************/ /***** *****/ /***** UTILITY FUNCTIONS *****/ /***** *****/ /*************************************************************************/ /*************************************************************************/ static void gxv_feat_registry_validate( FT_UShort feature, FT_UShort nSettings, FT_Bool exclusive, GXV_Validator gxvalid ) { GXV_NAME_ENTER( "feature in registry" ); GXV_TRACE(( " (feature = %u)\n", feature )); if ( feature >= gxv_feat_registry_length ) { GXV_TRACE(( "feature number %d is out of range %d\n", feature, gxv_feat_registry_length )); GXV_SET_ERR_IF_PARANOID( FT_INVALID_DATA ); goto Exit; } if ( gxv_feat_registry[feature].existence == 0 ) { GXV_TRACE(( "feature number %d is in defined range but doesn't exist\n", feature )); GXV_SET_ERR_IF_PARANOID( FT_INVALID_DATA ); goto Exit; } if ( gxv_feat_registry[feature].apple_reserved ) { /* Don't use here. Apple is reserved. */ GXV_TRACE(( "feature number %d is reserved by Apple\n", feature )); if ( gxvalid->root->level >= FT_VALIDATE_TIGHT ) FT_INVALID_DATA; } if ( nSettings != gxv_feat_registry[feature].nSettings ) { GXV_TRACE(( "feature %d: nSettings %d != defined nSettings %d\n", feature, nSettings, gxv_feat_registry[feature].nSettings )); if ( gxvalid->root->level >= FT_VALIDATE_TIGHT ) FT_INVALID_DATA; } if ( exclusive != gxv_feat_registry[feature].exclusive ) { GXV_TRACE(( "exclusive flag %d differs from predefined value\n", exclusive )); if ( gxvalid->root->level >= FT_VALIDATE_TIGHT ) FT_INVALID_DATA; } Exit: GXV_EXIT; } static void gxv_feat_name_index_validate( FT_Bytes table, FT_Bytes limit, GXV_Validator gxvalid ) { FT_Bytes p = table; FT_Short nameIndex; GXV_NAME_ENTER( "nameIndex" ); GXV_LIMIT_CHECK( 2 ); nameIndex = FT_NEXT_SHORT ( p ); GXV_TRACE(( " (nameIndex = %d)\n", nameIndex )); gxv_sfntName_validate( (FT_UShort)nameIndex, 255, 32768U, gxvalid ); GXV_EXIT; } static void gxv_feat_setting_validate( FT_Bytes table, FT_Bytes limit, FT_Bool exclusive, GXV_Validator gxvalid ) { FT_Bytes p = table; FT_UShort setting; GXV_NAME_ENTER( "setting" ); GXV_LIMIT_CHECK( 2 ); setting = FT_NEXT_USHORT( p ); /* If we have exclusive setting, the setting should be odd. */ if ( exclusive && ( setting & 1 ) == 0 ) FT_INVALID_DATA; gxv_feat_name_index_validate( p, limit, gxvalid ); GXV_FEAT_DATA( setting ) = setting; GXV_EXIT; } static void gxv_feat_name_validate( FT_Bytes table, FT_Bytes limit, GXV_Validator gxvalid ) { FT_Bytes p = table; FT_UInt reserved_size = GXV_FEAT_DATA( reserved_size ); FT_UShort feature; FT_UShort nSettings; FT_ULong settingTable; FT_UShort featureFlags; FT_Bool exclusive; FT_Int last_setting; FT_UInt i; GXV_NAME_ENTER( "name" ); /* feature + nSettings + settingTable + featureFlags */ GXV_LIMIT_CHECK( 2 + 2 + 4 + 2 ); feature = FT_NEXT_USHORT( p ); GXV_FEAT_DATA( feature ) = feature; nSettings = FT_NEXT_USHORT( p ); settingTable = FT_NEXT_ULONG ( p ); featureFlags = FT_NEXT_USHORT( p ); if ( settingTable < reserved_size ) FT_INVALID_OFFSET; if ( ( featureFlags & GXV_FEAT_MASK_UNUSED ) == 0 ) GXV_SET_ERR_IF_PARANOID( FT_INVALID_DATA ); exclusive = FT_BOOL( featureFlags & GXV_FEAT_MASK_EXCLUSIVE_SETTINGS ); if ( exclusive ) { FT_Byte dynamic_default; if ( featureFlags & GXV_FEAT_MASK_DYNAMIC_DEFAULT ) dynamic_default = (FT_Byte)( featureFlags & GXV_FEAT_MASK_DEFAULT_SETTING ); else dynamic_default = 0; /* If exclusive, check whether default setting is in the range. */ if ( !( dynamic_default < nSettings ) ) FT_INVALID_FORMAT; } gxv_feat_registry_validate( feature, nSettings, exclusive, gxvalid ); gxv_feat_name_index_validate( p, limit, gxvalid ); p = gxvalid->root->base + settingTable; for ( last_setting = -1, i = 0; i < nSettings; i++ ) { gxv_feat_setting_validate( p, limit, exclusive, gxvalid ); if ( (FT_Int)GXV_FEAT_DATA( setting ) <= last_setting ) GXV_SET_ERR_IF_PARANOID( FT_INVALID_FORMAT ); last_setting = (FT_Int)GXV_FEAT_DATA( setting ); /* setting + nameIndex */ p += ( 2 + 2 ); } GXV_EXIT; } /*************************************************************************/ /*************************************************************************/ /***** *****/ /***** feat TABLE *****/ /***** *****/ /*************************************************************************/ /*************************************************************************/ FT_LOCAL_DEF( void ) gxv_feat_validate( FT_Bytes table, FT_Face face, FT_Validator ftvalid ) { GXV_ValidatorRec gxvalidrec; GXV_Validator gxvalid = &gxvalidrec; GXV_feat_DataRec featrec; GXV_feat_Data feat = &featrec; FT_Bytes p = table; FT_Bytes limit = 0; FT_UInt featureNameCount; FT_UInt i; FT_Int last_feature; gxvalid->root = ftvalid; gxvalid->table_data = feat; gxvalid->face = face; FT_TRACE3(( "validating `feat' table\n" )); GXV_INIT; feat->reserved_size = 0; /* version + featureNameCount + none_0 + none_1 */ GXV_LIMIT_CHECK( 4 + 2 + 2 + 4 ); feat->reserved_size += 4 + 2 + 2 + 4; if ( FT_NEXT_ULONG( p ) != 0x00010000UL ) /* Version */ FT_INVALID_FORMAT; featureNameCount = FT_NEXT_USHORT( p ); GXV_TRACE(( " (featureNameCount = %d)\n", featureNameCount )); if ( !( IS_PARANOID_VALIDATION ) ) p += 6; /* skip (none) and (none) */ else { if ( FT_NEXT_USHORT( p ) != 0 ) FT_INVALID_DATA; if ( FT_NEXT_ULONG( p ) != 0 ) FT_INVALID_DATA; } feat->reserved_size += featureNameCount * ( 2 + 2 + 4 + 2 + 2 ); for ( last_feature = -1, i = 0; i < featureNameCount; i++ ) { gxv_feat_name_validate( p, limit, gxvalid ); if ( (FT_Int)GXV_FEAT_DATA( feature ) <= last_feature ) GXV_SET_ERR_IF_PARANOID( FT_INVALID_FORMAT ); last_feature = GXV_FEAT_DATA( feature ); p += 2 + 2 + 4 + 2 + 2; } FT_TRACE4(( "\n" )); } /* END */