1 /**************************************************************************** 2 * 3 * gxvmort2.c 4 * 5 * TrueTypeGX/AAT mort table validation 6 * body for type2 (Ligature Substitution) subtable. 7 * 8 * Copyright (C) 2005-2020 by 9 * suzuki toshiya, Masatake YAMATO, Red Hat K.K., 10 * David Turner, Robert Wilhelm, and Werner Lemberg. 11 * 12 * This file is part of the FreeType project, and may only be used, 13 * modified, and distributed under the terms of the FreeType project 14 * license, LICENSE.TXT. By continuing to use, modify, or distribute 15 * this file you indicate that you have read the license and 16 * understand and accept it fully. 17 * 18 */ 19 20 /**************************************************************************** 21 * 22 * gxvalid is derived from both gxlayout module and otvalid module. 23 * Development of gxlayout is supported by the Information-technology 24 * Promotion Agency(IPA), Japan. 25 * 26 */ 27 28 29 #include "gxvmort.h" 30 31 32 /************************************************************************** 33 * 34 * The macro FT_COMPONENT is used in trace mode. It is an implicit 35 * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log 36 * messages during execution. 37 */ 38 #undef FT_COMPONENT 39 #define FT_COMPONENT gxvmort 40 41 42 typedef struct GXV_mort_subtable_type2_StateOptRec_ 43 { 44 FT_UShort ligActionTable; 45 FT_UShort componentTable; 46 FT_UShort ligatureTable; 47 FT_UShort ligActionTable_length; 48 FT_UShort componentTable_length; 49 FT_UShort ligatureTable_length; 50 51 } GXV_mort_subtable_type2_StateOptRec, 52 *GXV_mort_subtable_type2_StateOptRecData; 53 54 #define GXV_MORT_SUBTABLE_TYPE2_HEADER_SIZE \ 55 ( GXV_STATETABLE_HEADER_SIZE + 2 + 2 + 2 ) 56 57 58 static void gxv_mort_subtable_type2_opttable_load(FT_Bytes table,FT_Bytes limit,GXV_Validator gxvalid)59 gxv_mort_subtable_type2_opttable_load( FT_Bytes table, 60 FT_Bytes limit, 61 GXV_Validator gxvalid ) 62 { 63 FT_Bytes p = table; 64 GXV_mort_subtable_type2_StateOptRecData optdata = 65 (GXV_mort_subtable_type2_StateOptRecData)gxvalid->statetable.optdata; 66 67 68 GXV_LIMIT_CHECK( 2 + 2 + 2 ); 69 optdata->ligActionTable = FT_NEXT_USHORT( p ); 70 optdata->componentTable = FT_NEXT_USHORT( p ); 71 optdata->ligatureTable = FT_NEXT_USHORT( p ); 72 73 GXV_TRACE(( "offset to ligActionTable=0x%04x\n", 74 optdata->ligActionTable )); 75 GXV_TRACE(( "offset to componentTable=0x%04x\n", 76 optdata->componentTable )); 77 GXV_TRACE(( "offset to ligatureTable=0x%04x\n", 78 optdata->ligatureTable )); 79 } 80 81 82 static void gxv_mort_subtable_type2_subtable_setup(FT_UShort table_size,FT_UShort classTable,FT_UShort stateArray,FT_UShort entryTable,FT_UShort * classTable_length_p,FT_UShort * stateArray_length_p,FT_UShort * entryTable_length_p,GXV_Validator gxvalid)83 gxv_mort_subtable_type2_subtable_setup( FT_UShort table_size, 84 FT_UShort classTable, 85 FT_UShort stateArray, 86 FT_UShort entryTable, 87 FT_UShort *classTable_length_p, 88 FT_UShort *stateArray_length_p, 89 FT_UShort *entryTable_length_p, 90 GXV_Validator gxvalid ) 91 { 92 FT_UShort o[6]; 93 FT_UShort *l[6]; 94 FT_UShort buff[7]; 95 96 GXV_mort_subtable_type2_StateOptRecData optdata = 97 (GXV_mort_subtable_type2_StateOptRecData)gxvalid->statetable.optdata; 98 99 100 GXV_NAME_ENTER( "subtable boundaries setup" ); 101 102 o[0] = classTable; 103 o[1] = stateArray; 104 o[2] = entryTable; 105 o[3] = optdata->ligActionTable; 106 o[4] = optdata->componentTable; 107 o[5] = optdata->ligatureTable; 108 l[0] = classTable_length_p; 109 l[1] = stateArray_length_p; 110 l[2] = entryTable_length_p; 111 l[3] = &(optdata->ligActionTable_length); 112 l[4] = &(optdata->componentTable_length); 113 l[5] = &(optdata->ligatureTable_length); 114 115 gxv_set_length_by_ushort_offset( o, l, buff, 6, table_size, gxvalid ); 116 117 GXV_TRACE(( "classTable: offset=0x%04x length=0x%04x\n", 118 classTable, *classTable_length_p )); 119 GXV_TRACE(( "stateArray: offset=0x%04x length=0x%04x\n", 120 stateArray, *stateArray_length_p )); 121 GXV_TRACE(( "entryTable: offset=0x%04x length=0x%04x\n", 122 entryTable, *entryTable_length_p )); 123 GXV_TRACE(( "ligActionTable: offset=0x%04x length=0x%04x\n", 124 optdata->ligActionTable, 125 optdata->ligActionTable_length )); 126 GXV_TRACE(( "componentTable: offset=0x%04x length=0x%04x\n", 127 optdata->componentTable, 128 optdata->componentTable_length )); 129 GXV_TRACE(( "ligatureTable: offset=0x%04x length=0x%04x\n", 130 optdata->ligatureTable, 131 optdata->ligatureTable_length )); 132 133 GXV_EXIT; 134 } 135 136 137 static void gxv_mort_subtable_type2_ligActionOffset_validate(FT_Bytes table,FT_UShort ligActionOffset,GXV_Validator gxvalid)138 gxv_mort_subtable_type2_ligActionOffset_validate( 139 FT_Bytes table, 140 FT_UShort ligActionOffset, 141 GXV_Validator gxvalid ) 142 { 143 /* access ligActionTable */ 144 GXV_mort_subtable_type2_StateOptRecData optdata = 145 (GXV_mort_subtable_type2_StateOptRecData)gxvalid->statetable.optdata; 146 147 FT_Bytes lat_base = table + optdata->ligActionTable; 148 FT_Bytes p = table + ligActionOffset; 149 FT_Bytes lat_limit = lat_base + optdata->ligActionTable; 150 151 152 GXV_32BIT_ALIGNMENT_VALIDATE( ligActionOffset ); 153 if ( p < lat_base ) 154 { 155 GXV_TRACE(( "too short offset 0x%04x: p < lat_base (%d byte rewind)\n", 156 ligActionOffset, lat_base - p )); 157 158 /* FontValidator, ftxvalidator, ftxdumperfuser warn but continue */ 159 GXV_SET_ERR_IF_PARANOID( FT_INVALID_OFFSET ); 160 } 161 else if ( lat_limit < p ) 162 { 163 GXV_TRACE(( "too large offset 0x%04x: lat_limit < p (%d byte overrun)\n", 164 ligActionOffset, p - lat_limit )); 165 166 /* FontValidator, ftxvalidator, ftxdumperfuser warn but continue */ 167 GXV_SET_ERR_IF_PARANOID( FT_INVALID_OFFSET ); 168 } 169 else 170 { 171 /* validate entry in ligActionTable */ 172 FT_ULong lig_action; 173 #ifdef GXV_LOAD_UNUSED_VARS 174 FT_UShort last; 175 FT_UShort store; 176 #endif 177 FT_ULong offset; 178 179 180 lig_action = FT_NEXT_ULONG( p ); 181 #ifdef GXV_LOAD_UNUSED_VARS 182 last = (FT_UShort)( ( lig_action >> 31 ) & 1 ); 183 store = (FT_UShort)( ( lig_action >> 30 ) & 1 ); 184 #endif 185 186 /* Apple spec defines this offset as a word offset */ 187 offset = lig_action & 0x3FFFFFFFUL; 188 if ( offset * 2 < optdata->ligatureTable ) 189 { 190 GXV_TRACE(( "too short offset 0x%08x:" 191 " 2 x offset < ligatureTable (%d byte rewind)\n", 192 offset, optdata->ligatureTable - offset * 2 )); 193 194 GXV_SET_ERR_IF_PARANOID( FT_INVALID_OFFSET ); 195 } else if ( offset * 2 > 196 optdata->ligatureTable + optdata->ligatureTable_length ) 197 { 198 GXV_TRACE(( "too long offset 0x%08x:" 199 " 2 x offset > ligatureTable + ligatureTable_length" 200 " (%d byte overrun)\n", 201 offset, 202 optdata->ligatureTable + optdata->ligatureTable_length 203 - offset * 2 )); 204 205 GXV_SET_ERR_IF_PARANOID( FT_INVALID_OFFSET ); 206 } 207 } 208 } 209 210 211 static void gxv_mort_subtable_type2_entry_validate(FT_Byte state,FT_UShort flags,GXV_StateTable_GlyphOffsetCPtr glyphOffset_p,FT_Bytes table,FT_Bytes limit,GXV_Validator gxvalid)212 gxv_mort_subtable_type2_entry_validate( 213 FT_Byte state, 214 FT_UShort flags, 215 GXV_StateTable_GlyphOffsetCPtr glyphOffset_p, 216 FT_Bytes table, 217 FT_Bytes limit, 218 GXV_Validator gxvalid ) 219 { 220 #ifdef GXV_LOAD_UNUSED_VARS 221 FT_UShort setComponent; 222 FT_UShort dontAdvance; 223 #endif 224 FT_UShort offset; 225 226 FT_UNUSED( state ); 227 FT_UNUSED( glyphOffset_p ); 228 FT_UNUSED( limit ); 229 230 231 #ifdef GXV_LOAD_UNUSED_VARS 232 setComponent = (FT_UShort)( ( flags >> 15 ) & 1 ); 233 dontAdvance = (FT_UShort)( ( flags >> 14 ) & 1 ); 234 #endif 235 236 offset = (FT_UShort)( flags & 0x3FFFU ); 237 238 if ( 0 < offset ) 239 gxv_mort_subtable_type2_ligActionOffset_validate( table, offset, 240 gxvalid ); 241 } 242 243 244 static void gxv_mort_subtable_type2_ligatureTable_validate(FT_Bytes table,GXV_Validator gxvalid)245 gxv_mort_subtable_type2_ligatureTable_validate( FT_Bytes table, 246 GXV_Validator gxvalid ) 247 { 248 GXV_mort_subtable_type2_StateOptRecData optdata = 249 (GXV_mort_subtable_type2_StateOptRecData)gxvalid->statetable.optdata; 250 251 FT_Bytes p = table + optdata->ligatureTable; 252 FT_Bytes limit = table + optdata->ligatureTable 253 + optdata->ligatureTable_length; 254 255 256 GXV_NAME_ENTER( "mort chain subtable type2 - substitutionTable" ); 257 if ( 0 != optdata->ligatureTable ) 258 { 259 /* Apple does not give specification of ligatureTable format */ 260 while ( p < limit ) 261 { 262 FT_UShort lig_gid; 263 264 265 GXV_LIMIT_CHECK( 2 ); 266 lig_gid = FT_NEXT_USHORT( p ); 267 268 if ( gxvalid->face->num_glyphs < lig_gid ) 269 GXV_SET_ERR_IF_PARANOID( FT_INVALID_GLYPH_ID ); 270 } 271 } 272 GXV_EXIT; 273 } 274 275 276 FT_LOCAL_DEF( void ) gxv_mort_subtable_type2_validate(FT_Bytes table,FT_Bytes limit,GXV_Validator gxvalid)277 gxv_mort_subtable_type2_validate( FT_Bytes table, 278 FT_Bytes limit, 279 GXV_Validator gxvalid ) 280 { 281 FT_Bytes p = table; 282 283 GXV_mort_subtable_type2_StateOptRec lig_rec; 284 285 286 GXV_NAME_ENTER( "mort chain subtable type2 (Ligature Substitution)" ); 287 288 GXV_LIMIT_CHECK( GXV_MORT_SUBTABLE_TYPE2_HEADER_SIZE ); 289 290 gxvalid->statetable.optdata = 291 &lig_rec; 292 gxvalid->statetable.optdata_load_func = 293 gxv_mort_subtable_type2_opttable_load; 294 gxvalid->statetable.subtable_setup_func = 295 gxv_mort_subtable_type2_subtable_setup; 296 gxvalid->statetable.entry_glyphoffset_fmt = 297 GXV_GLYPHOFFSET_NONE; 298 gxvalid->statetable.entry_validate_func = 299 gxv_mort_subtable_type2_entry_validate; 300 301 gxv_StateTable_validate( p, limit, gxvalid ); 302 303 p += gxvalid->subtable_length; 304 gxv_mort_subtable_type2_ligatureTable_validate( table, gxvalid ); 305 306 gxvalid->subtable_length = (FT_ULong)( p - table ); 307 308 GXV_EXIT; 309 } 310 311 312 /* END */ 313