/* parse single actSubrecord */ static void gxv_just_actSubrecord_validate( FT_Bytes table, FT_Bytes limit, GXV_Validator gxvalid ) { FT_Bytes p = table; FT_UShort actionClass; FT_UShort actionType; FT_ULong actionLength; GXV_NAME_ENTER( "just actSubrecord" ); GXV_LIMIT_CHECK( 2 + 2 + 4 ); actionClass = FT_NEXT_USHORT( p ); actionType = FT_NEXT_USHORT( p ); actionLength = FT_NEXT_ULONG( p ); /* actionClass is related with justClass using 7bit only */ if ( ( actionClass & 0xFF80 ) != 0 ) GXV_SET_ERR_IF_PARANOID( FT_INVALID_DATA ); if ( actionType == 0 ) gxv_just_actSubrecord_type0_validate( p, limit, gxvalid ); else if ( actionType == 1 ) gxv_just_actSubrecord_type1_validate( p, limit, gxvalid ); else if ( actionType == 2 ) gxv_just_actSubrecord_type2_validate( p, limit, gxvalid ); else if ( actionType == 3 ) ; /* Stretch glyph action: no actionData */ else if ( actionType == 4 ) gxv_just_actSubrecord_type4_validate( p, limit, gxvalid ); else if ( actionType == 5 ) gxv_just_actSubrecord_type5_validate( p, limit, gxvalid ); else FT_INVALID_DATA; gxvalid->subtable_length = actionLength; GXV_EXIT; }
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" )); }
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; }
static void gxv_kern_subtable_fmt3_validate( FT_Bytes table, FT_Bytes limit, GXV_Validator gxvalid ) { FT_Bytes p = table + GXV_KERN_SUBTABLE_HEADER_SIZE; FT_UShort glyphCount; FT_Byte kernValueCount; FT_Byte leftClassCount; FT_Byte rightClassCount; FT_Byte flags; GXV_NAME_ENTER( "kern subtable format 3" ); GXV_LIMIT_CHECK( 2 + 1 + 1 + 1 + 1 ); glyphCount = FT_NEXT_USHORT( p ); kernValueCount = FT_NEXT_BYTE( p ); leftClassCount = FT_NEXT_BYTE( p ); rightClassCount = FT_NEXT_BYTE( p ); flags = FT_NEXT_BYTE( p ); if ( gxvalid->face->num_glyphs != glyphCount ) { GXV_TRACE(( "maxGID=%d, but glyphCount=%d\n", gxvalid->face->num_glyphs, glyphCount )); GXV_SET_ERR_IF_PARANOID( FT_INVALID_GLYPH_ID ); } if ( flags != 0 ) GXV_TRACE(( "kern subtable fmt3 has nonzero value" " (%d) in unused flag\n", flags )); /* * just skip kernValue[kernValueCount] */ GXV_LIMIT_CHECK( 2 * kernValueCount ); p += 2 * kernValueCount; /* * check leftClass[gid] < leftClassCount */ { FT_Byte min, max; GXV_LIMIT_CHECK( glyphCount ); gxv_array_getlimits_byte( p, p + glyphCount, &min, &max, gxvalid ); p += gxvalid->subtable_length; if ( leftClassCount < max ) FT_INVALID_DATA; } /* * check rightClass[gid] < rightClassCount */ { FT_Byte min, max; GXV_LIMIT_CHECK( glyphCount ); gxv_array_getlimits_byte( p, p + glyphCount, &min, &max, gxvalid ); p += gxvalid->subtable_length; if ( rightClassCount < max ) FT_INVALID_DATA; } /* * check kernIndex[i, j] < kernValueCount */ { FT_UShort i, j; for ( i = 0; i < leftClassCount; i++ ) { for ( j = 0; j < rightClassCount; j++ ) { GXV_LIMIT_CHECK( 1 ); if ( kernValueCount < FT_NEXT_BYTE( p ) ) FT_INVALID_OFFSET; } } } gxvalid->subtable_length = (FT_ULong)( p - table ); GXV_EXIT; }
static void gxv_mort_subtable_type2_ligActionOffset_validate( FT_Bytes table, FT_UShort ligActionOffset, GXV_Validator gxvalid) { /* access ligActionTable */ GXV_mort_subtable_type2_StateOptRecData optdata = (GXV_mort_subtable_type2_StateOptRecData)gxvalid->statetable.optdata; FT_Bytes lat_base = table + optdata->ligActionTable; FT_Bytes p = table + ligActionOffset; FT_Bytes lat_limit = lat_base + optdata->ligActionTable; GXV_32BIT_ALIGNMENT_VALIDATE(ligActionOffset); if (p < lat_base) { GXV_TRACE(("too short offset 0x%04x: p < lat_base (%d byte rewind)\n", ligActionOffset, lat_base - p)); /* FontValidator, ftxvalidator, ftxdumperfuser warn but continue */ GXV_SET_ERR_IF_PARANOID(FT_INVALID_OFFSET); } else if (lat_limit < p) { GXV_TRACE(("too large offset 0x%04x: lat_limit < p (%d byte overrun)\n", ligActionOffset, p - lat_limit)); /* FontValidator, ftxvalidator, ftxdumperfuser warn but continue */ GXV_SET_ERR_IF_PARANOID(FT_INVALID_OFFSET); } else { /* validate entry in ligActionTable */ FT_ULong lig_action; #ifdef GXV_LOAD_UNUSED_VARS FT_UShort last; FT_UShort store; #endif FT_ULong offset; lig_action = FT_NEXT_ULONG(p); #ifdef GXV_LOAD_UNUSED_VARS last = (FT_UShort)((lig_action >> 31) & 1); store = (FT_UShort)((lig_action >> 30) & 1); #endif /* Apple spec defines this offset as a word offset */ offset = lig_action & 0x3FFFFFFFUL; if (offset * 2 < optdata->ligatureTable) { GXV_TRACE(("too short offset 0x%08x:" " 2 x offset < ligatureTable (%d byte rewind)\n", offset, optdata->ligatureTable - offset * 2)); GXV_SET_ERR_IF_PARANOID(FT_INVALID_OFFSET); } else if (offset * 2 > optdata->ligatureTable + optdata->ligatureTable_length) { GXV_TRACE(("too long offset 0x%08x:" " 2 x offset > ligatureTable + ligatureTable_length" " (%d byte overrun)\n", offset, optdata->ligatureTable + optdata->ligatureTable_length - offset * 2)); GXV_SET_ERR_IF_PARANOID(FT_INVALID_OFFSET); } } }
static void gxv_morx_subtable_type2_ligActionIndex_validate( FT_Bytes table, FT_UShort ligActionIndex, GXV_Validator gxvalid ) { /* access ligActionTable */ GXV_morx_subtable_type2_StateOptRecData optdata = (GXV_morx_subtable_type2_StateOptRecData)gxvalid->xstatetable.optdata; FT_Bytes lat_base = table + optdata->ligActionTable; FT_Bytes p = lat_base + ligActionIndex * GXV_MORX_LIGACTION_ENTRY_SIZE; FT_Bytes lat_limit = lat_base + optdata->ligActionTable; if ( p < lat_base ) { GXV_TRACE(( "p < lat_base (%d byte rewind)\n", lat_base - p )); FT_INVALID_OFFSET; } else if ( lat_limit < p ) { GXV_TRACE(( "lat_limit < p (%d byte overrun)\n", p - lat_limit )); FT_INVALID_OFFSET; } { /* validate entry in ligActionTable */ FT_ULong lig_action; #ifdef GXV_LOAD_UNUSED_VARS FT_UShort last; FT_UShort store; #endif FT_ULong offset; FT_Long gid_limit; lig_action = FT_NEXT_ULONG( p ); #ifdef GXV_LOAD_UNUSED_VARS last = (FT_UShort)( ( lig_action >> 31 ) & 1 ); store = (FT_UShort)( ( lig_action >> 30 ) & 1 ); #endif offset = lig_action & 0x3FFFFFFFUL; /* this offset is 30-bit signed value to add to GID */ /* it is different from the location offset in mort */ if ( ( offset & 0x3FFF0000UL ) == 0x3FFF0000UL ) { /* negative offset */ gid_limit = gxvalid->face->num_glyphs - (FT_Long)( offset & 0x0000FFFFUL ); if ( gid_limit > 0 ) return; GXV_TRACE(( "ligature action table includes" " too negative offset moving all GID" " below defined range: 0x%04x\n", offset & 0xFFFFU )); GXV_SET_ERR_IF_PARANOID( FT_INVALID_OFFSET ); } else if ( ( offset & 0x3FFF0000UL ) == 0x00000000UL ) { /* positive offset */ if ( (FT_Long)offset < gxvalid->face->num_glyphs ) return; GXV_TRACE(( "ligature action table includes" " too large offset moving all GID" " over defined range: 0x%04x\n", offset & 0xFFFFU )); GXV_SET_ERR_IF_PARANOID( FT_INVALID_OFFSET ); } GXV_TRACE(( "ligature action table includes" " invalid offset to add to 16-bit GID:" " 0x%08x\n", offset )); GXV_SET_ERR_IF_PARANOID( FT_INVALID_OFFSET ); } }