8004986: Better handling of glyph table
authorprr
Tue, 26 Feb 2013 10:07:26 -0800
changeset 16890 36b30720a997
parent 16889 3df90f344221
child 16891 91e99bed64ae
8004986: Better handling of glyph table 8004987: Improve font layout 8004994: Improve checking of glyph table Reviewed-by: srl, jgodinez
jdk/src/share/native/sun/font/layout/ArabicLayoutEngine.cpp
jdk/src/share/native/sun/font/layout/ContextualGlyphSubstProc2.cpp
jdk/src/share/native/sun/font/layout/LETypes.h
jdk/src/share/native/sun/font/layout/LayoutEngine.cpp
jdk/src/share/native/sun/font/layout/LigatureSubstProc.cpp
jdk/src/share/native/sun/font/layout/LigatureSubstProc2.cpp
jdk/src/share/native/sun/font/layout/LookupProcessor.cpp
jdk/src/share/native/sun/font/layout/OpenTypeLayoutEngine.cpp
jdk/src/share/native/sun/font/layout/OpenTypeLayoutEngine.h
jdk/src/share/native/sun/font/layout/StateTableProcessor.cpp
jdk/src/share/native/sun/font/layout/StateTableProcessor2.cpp
jdk/src/share/native/sun/font/layout/StateTables.h
--- a/jdk/src/share/native/sun/font/layout/ArabicLayoutEngine.cpp	Fri Feb 15 13:07:17 2013 -0800
+++ b/jdk/src/share/native/sun/font/layout/ArabicLayoutEngine.cpp	Tue Feb 26 10:07:26 2013 -0800
@@ -26,7 +26,7 @@
 
 /*
  *
- * (C) Copyright IBM Corp. 1998-2005 - All Rights Reserved
+ * (C) Copyright IBM Corp. 1998-2013 - All Rights Reserved
  *
  */
 
@@ -152,17 +152,16 @@
 }
 
 UnicodeArabicOpenTypeLayoutEngine::UnicodeArabicOpenTypeLayoutEngine(const LEFontInstance *fontInstance, le_int32 scriptCode, le_int32 languageCode, le_int32 typoFlags, LEErrorCode &success)
-    : ArabicOpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, success)
+    : ArabicOpenTypeLayoutEngine(fontInstance, scriptCode, languageCode, typoFlags | LE_CHAR_FILTER_FEATURE_FLAG, success)
 {
     fGSUBTable = (const GlyphSubstitutionTableHeader *) CanonShaping::glyphSubstitutionTable;
     fGDEFTable = (const GlyphDefinitionTableHeader *) CanonShaping::glyphDefinitionTable;
-
-    fSubstitutionFilter = new CharSubstitutionFilter(fontInstance);
+    /* OpenTypeLayoutEngine will allocate a substitution filter */
 }
 
 UnicodeArabicOpenTypeLayoutEngine::~UnicodeArabicOpenTypeLayoutEngine()
 {
-    delete fSubstitutionFilter;
+    /* OpenTypeLayoutEngine will cleanup the substitution filter */
 }
 
 // "glyphs", "indices" -> glyphs, indices
--- a/jdk/src/share/native/sun/font/layout/ContextualGlyphSubstProc2.cpp	Fri Feb 15 13:07:17 2013 -0800
+++ b/jdk/src/share/native/sun/font/layout/ContextualGlyphSubstProc2.cpp	Tue Feb 26 10:07:26 2013 -0800
@@ -38,7 +38,6 @@
 #include "ContextualGlyphSubstProc2.h"
 #include "LEGlyphStorage.h"
 #include "LESwaps.h"
-#include <stdio.h>
 
 U_NAMESPACE_BEGIN
 
@@ -123,7 +122,7 @@
             break;
         }
         case ltfSegmentArray: {
-            printf("Context Lookup Table Format4: specific interpretation needed!\n");
+            //printf("Context Lookup Table Format4: specific interpretation needed!\n");
             break;
         }
         case ltfSingleTable:
--- a/jdk/src/share/native/sun/font/layout/LETypes.h	Fri Feb 15 13:07:17 2013 -0800
+++ b/jdk/src/share/native/sun/font/layout/LETypes.h	Tue Feb 26 10:07:26 2013 -0800
@@ -339,6 +339,35 @@
 
 
 #ifndef U_HIDE_INTERNAL_API
+
+#ifndef LE_ASSERT_BAD_FONT
+#define LE_ASSERT_BAD_FONT 0
+#endif
+
+#if LE_ASSERT_BAD_FONT
+#include <stdio.h>
+#define LE_DEBUG_BAD_FONT(x) fprintf(stderr,"%s:%d: BAD FONT: %s\n", __FILE__, __LINE__, (x));
+#else
+#define LE_DEBUG_BAD_FONT(x)
+#endif
+
+/**
+ * Max value representable by a uintptr
+ */
+#ifndef UINTPTR_MAX
+#ifndef UINT32_MAX
+#define LE_UINTPTR_MAX 0xFFFFFFFFU
+#else
+#define LE_UINTPTR_MAX UINT32_MAX
+#endif
+#else
+#define LE_UINTPTR_MAX UINTPTR_MAX
+#endif
+
+/**
+ * Range check for overflow
+ */
+#define LE_RANGE_CHECK(type, count, ptrfn) (( (LE_UINTPTR_MAX / sizeof(type)) < count ) ? NULL : (ptrfn))
 /**
  * A convenience macro to get the length of an array.
  *
@@ -360,7 +389,7 @@
  *
  * @internal
  */
-#define LE_NEW_ARRAY(type, count) (type *) uprv_malloc((count) * sizeof(type))
+#define LE_NEW_ARRAY(type, count) (type *)  LE_RANGE_CHECK(type,count,uprv_malloc((count) * sizeof(type)))
 
 /**
  * Re-allocate an array of basic types. This is used to isolate the rest of
@@ -403,7 +432,7 @@
  *
  * @internal
  */
-#define LE_NEW_ARRAY(type, count) (type *) malloc((count) * sizeof(type))
+#define LE_NEW_ARRAY(type, count) LE_RANGE_CHECK(type,count,(type *) malloc((count) * sizeof(type)))
 
 /**
  * Re-allocate an array of basic types. This is used to isolate the rest of
@@ -696,6 +725,8 @@
 
 #define LE_CHAR_FILTER_FEATURE_FLAG (1 << LE_CHAR_FILTER_FEATURE_ENUM)
 
+#define LE_DEFAULT_FEATURE_FLAG (LE_Kerning_FEATURE_FLAG | LE_Ligatures_FEATURE_FLAG) /**< default features */
+
 /**
  * Error codes returned by the LayoutEngine.
  *
--- a/jdk/src/share/native/sun/font/layout/LayoutEngine.cpp	Fri Feb 15 13:07:17 2013 -0800
+++ b/jdk/src/share/native/sun/font/layout/LayoutEngine.cpp	Tue Feb 26 10:07:26 2013 -0800
@@ -428,7 +428,7 @@
 
     adjustMarkGlyphs(&chars[offset], count, reverse, glyphStorage, &filter, success);
 
-    if (fTypoFlags & 0x1) { /* kerning enabled */
+    if (fTypoFlags & LE_Kerning_FEATURE_FLAG) { /* kerning enabled */
       static const le_uint32 kernTableTag = LE_KERN_TABLE_TAG;
 
       KernTable kt(fFontInstance, getFontTable(kernTableTag));
@@ -571,8 +571,8 @@
 
 LayoutEngine *LayoutEngine::layoutEngineFactory(const LEFontInstance *fontInstance, le_int32 scriptCode, le_int32 languageCode, LEErrorCode &success)
 {
-  // 3 -> kerning and ligatures
-  return LayoutEngine::layoutEngineFactory(fontInstance, scriptCode, languageCode, 3, success);
+  //kerning and ligatures - by default
+  return LayoutEngine::layoutEngineFactory(fontInstance, scriptCode, languageCode, LE_DEFAULT_FEATURE_FLAG, success);
 }
 
 LayoutEngine *LayoutEngine::layoutEngineFactory(const LEFontInstance *fontInstance, le_int32 scriptCode, le_int32 languageCode, le_int32 typoFlags, LEErrorCode &success)
@@ -660,11 +660,11 @@
         }
     } else {
         MorphTableHeader2 *morxTable = (MorphTableHeader2 *)fontInstance->getFontTable(morxTableTag);
-        if (morxTable != NULL) {
+        if (morxTable != NULL && SWAPL(morxTable->version)==0x00020000) {
             result = new GXLayoutEngine2(fontInstance, scriptCode, languageCode, morxTable, typoFlags, success);
         } else {
             const MorphTableHeader *mortTable = (MorphTableHeader *) fontInstance->getFontTable(mortTableTag);
-            if (mortTable != NULL) { // mort
+            if (mortTable != NULL && SWAPL(mortTable->version)==0x00010000) { // mort
                 result = new GXLayoutEngine(fontInstance, scriptCode, languageCode, mortTable, success);
             } else {
                 switch (scriptCode) {
--- a/jdk/src/share/native/sun/font/layout/LigatureSubstProc.cpp	Fri Feb 15 13:07:17 2013 -0800
+++ b/jdk/src/share/native/sun/font/layout/LigatureSubstProc.cpp	Tue Feb 26 10:07:26 2013 -0800
@@ -25,7 +25,7 @@
 
 /*
  *
- * (C) Copyright IBM Corp. 1998-2004 - All Rights Reserved
+ * (C) Copyright IBM Corp. 1998-2013 - All Rights Reserved
  *
  */
 
@@ -79,6 +79,10 @@
         }
 
         componentStack[m] = currGlyph;
+    } else if ( m == -1) {
+        // bad font- skip this glyph.
+        currGlyph++;
+        return newState;
     }
 
     ByteOffset actionOffset = flags & lsfActionOffsetMask;
@@ -102,7 +106,21 @@
             offset = action & lafComponentOffsetMask;
             if (offset != 0) {
                 const le_int16 *offsetTable = (const le_int16 *)((char *) &ligatureSubstitutionHeader->stHeader + 2 * SignExtend(offset, lafComponentOffsetMask));
+                const le_int16 *tableEnd = (const le_int16 *)((char *) &ligatureSubstitutionHeader + 1 * SWAPW(ligatureSubstitutionHeader->length));
 
+                // Check if the font is internally consistent
+                if(tableEnd < (const le_int16*)&ligatureSubstitutionHeader  // stated end wrapped around?
+                   || offsetTable > tableEnd) { // offset past end of stated length?
+                  currGlyph++;
+                  LE_DEBUG_BAD_FONT("off end of ligature substitution header");
+                  return newState; // get out! bad font
+                }
+
+                if(componentGlyph > glyphStorage.getGlyphCount()) {
+                  LE_DEBUG_BAD_FONT("preposterous componentGlyph");
+                  currGlyph++;
+                  return newState; // get out! bad font
+                }
                 i += SWAPW(offsetTable[LE_GET_GLYPH(glyphStorage[componentGlyph])]);
 
                 if (action & (lafLast | lafStore))  {
@@ -110,13 +128,22 @@
                     TTGlyphID ligatureGlyph = SWAPW(*ligatureOffset);
 
                     glyphStorage[componentGlyph] = LE_SET_GLYPH(glyphStorage[componentGlyph], ligatureGlyph);
+                    if(mm==nComponents) {
+                      LE_DEBUG_BAD_FONT("exceeded nComponents");
+                      mm--; // don't overrun the stack.
+                    }
                     stack[++mm] = componentGlyph;
                     i = 0;
                 } else {
                     glyphStorage[componentGlyph] = LE_SET_GLYPH(glyphStorage[componentGlyph], 0xFFFF);
                 }
             }
-        } while (!(action & lafLast));
+#if LE_ASSERT_BAD_FONT
+            if(m<0) {
+              LE_DEBUG_BAD_FONT("m<0")
+            }
+#endif
+        } while (!(action & lafLast)  && (m>=0) ); // stop if last bit is set, or if run out of items
 
         while (mm >= 0) {
             if (++m >= nComponents) {
--- a/jdk/src/share/native/sun/font/layout/LigatureSubstProc2.cpp	Fri Feb 15 13:07:17 2013 -0800
+++ b/jdk/src/share/native/sun/font/layout/LigatureSubstProc2.cpp	Tue Feb 26 10:07:26 2013 -0800
@@ -79,6 +79,11 @@
             m = 0;
         }
         componentStack[m] = currGlyph;
+    } else if ( m == -1) {
+        // bad font- skip this glyph.
+        LE_DEBUG_BAD_FONT("m==-1")
+        currGlyph+= dir;
+        return nextStateIndex;
     }
 
     ByteOffset actionOffset = flags & lsfPerformAction;
@@ -93,6 +98,16 @@
 
         const le_uint16 *componentTable = (const le_uint16 *)((char *) &ligatureSubstitutionHeader->stHeader + componentOffset);
 
+        const le_uint16 *tableEnd = (const le_uint16 *)((char *) &ligatureSubstitutionHeader + SWAPL(ligatureSubstitutionHeader->length));
+
+        // Check if the font is internally consistent
+        if(tableEnd < (const le_uint16*)&ligatureSubstitutionHeader  // stated end wrapped around?
+           || componentTable > tableEnd) { // offset past end of stated length?
+          currGlyph+= dir;
+          LE_DEBUG_BAD_FONT("ligatureSubstHeader off end of table")
+            return nextStateIndex; // get out! bad font
+        }
+
         do {
             le_uint32 componentGlyph = componentStack[m--]; // pop off
 
@@ -104,19 +119,32 @@
 
             offset = action & lafComponentOffsetMask;
             if (offset != 0) {
-
+                if(componentGlyph > glyphStorage.getGlyphCount()) {
+                  LE_DEBUG_BAD_FONT("preposterous componentGlyph");
+                  currGlyph+= dir;
+                  return nextStateIndex; // get out! bad font
+                }
                 i += SWAPW(componentTable[LE_GET_GLYPH(glyphStorage[componentGlyph]) + (SignExtend(offset, lafComponentOffsetMask))]);
 
                 if (action & (lafLast | lafStore))  {
                     TTGlyphID ligatureGlyph = SWAPW(ligatureTable[i]);
                     glyphStorage[componentGlyph] = LE_SET_GLYPH(glyphStorage[componentGlyph], ligatureGlyph);
+                    if(mm==nComponents) {
+                      LE_DEBUG_BAD_FONT("exceeded nComponents");
+                      mm--; // don't overrun the stack.
+                    }
                     stack[++mm] = componentGlyph;
                     i = 0;
                 } else {
                     glyphStorage[componentGlyph] = LE_SET_GLYPH(glyphStorage[componentGlyph], 0xFFFF);
                 }
             }
-        } while (!(action & lafLast));
+#if LE_ASSERT_BAD_FONT
+            if(m<0) {
+              LE_DEBUG_BAD_FONT("m<0")
+            }
+#endif
+        } while (!(action & lafLast) && (m>=0) ); // stop if last bit is set, or if run out of items
 
         while (mm >= 0) {
             if (++m >= nComponents) {
--- a/jdk/src/share/native/sun/font/layout/LookupProcessor.cpp	Fri Feb 15 13:07:17 2013 -0800
+++ b/jdk/src/share/native/sun/font/layout/LookupProcessor.cpp	Tue Feb 26 10:07:26 2013 -0800
@@ -208,7 +208,7 @@
     lookupSelectCount = lookupListCount;
 
     le_int32 count, order = 0;
-    le_int32 featureReferences = 0;
+    le_uint32 featureReferences = 0;
     const FeatureTable *featureTable = NULL;
     LETag featureTag;
 
@@ -219,7 +219,7 @@
     // be the maximum number of entries in the lookupOrderArray. We can't use
     // lookupListCount because some lookups might be referenced by more than
     // one feature.
-    for (le_int32 feature = 0; feature < featureCount; feature += 1) {
+    for (le_uint32 feature = 0; feature < featureCount; feature += 1) {
         le_uint16 featureIndex = SWAPW(langSysTable->featureIndexArray[feature]);
 
         featureTable = featureListTable->getFeatureTable(featureIndex, &featureTag);
--- a/jdk/src/share/native/sun/font/layout/OpenTypeLayoutEngine.cpp	Fri Feb 15 13:07:17 2013 -0800
+++ b/jdk/src/share/native/sun/font/layout/OpenTypeLayoutEngine.cpp	Tue Feb 26 10:07:26 2013 -0800
@@ -123,7 +123,7 @@
     {ccmpFeatureTag, ccmpFeatureMask},
     {ligaFeatureTag, ligaFeatureMask},
     {cligFeatureTag, cligFeatureMask},
-        {kernFeatureTag, kernFeatureMask},
+    {kernFeatureTag, kernFeatureMask},
     {paltFeatureTag, paltFeatureMask},
     {markFeatureTag, markFeatureMask},
     {mkmkFeatureTag, mkmkFeatureMask},
@@ -160,6 +160,23 @@
     static const le_uint32 gposTableTag = LE_GPOS_TABLE_TAG;
     const GlyphPositioningTableHeader *gposTable = (const GlyphPositioningTableHeader *) getFontTable(gposTableTag);
 
+    applyTypoFlags();
+
+    setScriptAndLanguageTags();
+
+    fGDEFTable = (const GlyphDefinitionTableHeader *) getFontTable(gdefTableTag);
+
+// JK patch, 2008-05-30 - see Sinhala bug report and LKLUG font
+//    if (gposTable != NULL && gposTable->coversScriptAndLanguage(fScriptTag, fLangSysTag)) {
+    if (gposTable != NULL && gposTable->coversScript(fScriptTag)) {
+        fGPOSTable = gposTable;
+    }
+}
+
+void OpenTypeLayoutEngine::applyTypoFlags() {
+    const le_int32& typoFlags = fTypoFlags;
+    const LEFontInstance *fontInstance = fFontInstance;
+
     switch (typoFlags & (LE_SS01_FEATURE_FLAG
                          | LE_SS02_FEATURE_FLAG
                          | LE_SS03_FEATURE_FLAG
@@ -221,15 +238,6 @@
       fSubstitutionFilter = new CharSubstitutionFilter(fontInstance);
     }
 
-    setScriptAndLanguageTags();
-
-    fGDEFTable = (const GlyphDefinitionTableHeader *) getFontTable(gdefTableTag);
-
-// JK patch, 2008-05-30 - see Sinhala bug report and LKLUG font
-//    if (gposTable != NULL && gposTable->coversScriptAndLanguage(fScriptTag, fLangSysTag)) {
-    if (gposTable != NULL && gposTable->coversScript(fScriptTag)) {
-        fGPOSTable = gposTable;
-    }
 }
 
 void OpenTypeLayoutEngine::reset()
@@ -246,13 +254,15 @@
     : LayoutEngine(fontInstance, scriptCode, languageCode, typoFlags, success), fFeatureOrder(FALSE),
       fGSUBTable(NULL), fGDEFTable(NULL), fGPOSTable(NULL), fSubstitutionFilter(NULL)
 {
-    setScriptAndLanguageTags();
+  applyTypoFlags();
+  setScriptAndLanguageTags();
 }
 
 OpenTypeLayoutEngine::~OpenTypeLayoutEngine()
 {
-    if (fTypoFlags & 0x80000000L) {
+    if (fTypoFlags & LE_CHAR_FILTER_FEATURE_FLAG) {
         delete fSubstitutionFilter;
+        fSubstitutionFilter = NULL;
     }
 
     reset();
--- a/jdk/src/share/native/sun/font/layout/OpenTypeLayoutEngine.h	Fri Feb 15 13:07:17 2013 -0800
+++ b/jdk/src/share/native/sun/font/layout/OpenTypeLayoutEngine.h	Tue Feb 26 10:07:26 2013 -0800
@@ -24,7 +24,7 @@
  */
 
 /*
- * (C) Copyright IBM Corp. 1998-2010 - All Rights Reserved
+ * (C) Copyright IBM Corp. 1998-2013 - All Rights Reserved
  *
  */
 
@@ -184,6 +184,11 @@
      */
     static const LETag scriptTags[];
 
+    /**
+     * apply the typoflags. Only called by the c'tors.
+     */
+    void applyTypoFlags();
+
 protected:
     /**
      * A set of "default" features. The default characterProcessing method
--- a/jdk/src/share/native/sun/font/layout/StateTableProcessor.cpp	Fri Feb 15 13:07:17 2013 -0800
+++ b/jdk/src/share/native/sun/font/layout/StateTableProcessor.cpp	Tue Feb 26 10:07:26 2013 -0800
@@ -65,6 +65,9 @@
 
 void StateTableProcessor::process(LEGlyphStorage &glyphStorage)
 {
+
+    LE_STATE_PATIENCE_INIT();
+
     // Start at state 0
     // XXX: How do we know when to start at state 1?
     ByteOffset currentState = stateArrayOffset;
@@ -76,6 +79,7 @@
     beginStateTable();
 
     while (currGlyph <= glyphCount) {
+        if(LE_STATE_PATIENCE_DECR()) break; // patience exceeded.
         ClassCode classCode = classCodeOOB;
         if (currGlyph == glyphCount) {
             // XXX: How do we handle EOT vs. EOL?
@@ -92,8 +96,9 @@
 
         const EntryTableIndex *stateArray = (const EntryTableIndex *) ((char *) &stateTableHeader->stHeader + currentState);
         EntryTableIndex entryTableIndex = stateArray[(le_uint8)classCode];
-
+        LE_STATE_PATIENCE_CURR(le_int32, currGlyph);
         currentState = processStateEntry(glyphStorage, currGlyph, entryTableIndex);
+        LE_STATE_PATIENCE_INCR(currGlyph);
     }
 
     endStateTable();
--- a/jdk/src/share/native/sun/font/layout/StateTableProcessor2.cpp	Fri Feb 15 13:07:17 2013 -0800
+++ b/jdk/src/share/native/sun/font/layout/StateTableProcessor2.cpp	Tue Feb 26 10:07:26 2013 -0800
@@ -38,7 +38,6 @@
 #include "LEGlyphStorage.h"
 #include "LESwaps.h"
 #include "LookupTables.h"
-#include <stdio.h>
 
 U_NAMESPACE_BEGIN
 
@@ -72,6 +71,8 @@
     le_uint16 currentState = 0;
     le_int32 glyphCount = glyphStorage.getGlyphCount();
 
+    LE_STATE_PATIENCE_INIT();
+
     le_int32 currGlyph = 0;
     if ((coverage & scfReverse2) != 0) {  // process glyphs in descending order
         currGlyph = glyphCount - 1;
@@ -86,6 +87,10 @@
 #ifdef TEST_FORMAT
             SimpleArrayLookupTable *lookupTable0 = (SimpleArrayLookupTable *) classTable;
             while ((dir == 1 && currGlyph <= glyphCount) || (dir == -1 && currGlyph >= -1)) {
+                if(LE_STATE_PATIENCE_DECR()) {
+                  LE_DEBUG_BAD_FONT("patience exceeded - state table not moving")
+                  break; // patience exceeded.
+                }
                 LookupValue classCode = classCodeOOB;
                 if (currGlyph == glyphCount || currGlyph == -1) {
                     // XXX: How do we handle EOT vs. EOL?
@@ -101,7 +106,9 @@
                     }
                 }
                 EntryTableIndex2 entryTableIndex = SWAPW(stateArray[classCode + currentState * nClasses]);
+                LE_STATE_PATIENCE_CURR(le_int32, currGlyph);
                 currentState = processStateEntry(glyphStorage, currGlyph, entryTableIndex); // return a zero-based index instead of a byte offset
+                LE_STATE_PATIENCE_INCR(currGlyph);
             }
 #endif
             break;
@@ -109,6 +116,10 @@
         case ltfSegmentSingle: {
             SegmentSingleLookupTable *lookupTable2 = (SegmentSingleLookupTable *) classTable;
             while ((dir == 1 && currGlyph <= glyphCount) || (dir == -1 && currGlyph >= -1)) {
+                if(LE_STATE_PATIENCE_DECR()) {
+                  LE_DEBUG_BAD_FONT("patience exceeded - state table not moving")
+                  break; // patience exceeded.
+                }
                 LookupValue classCode = classCodeOOB;
                 if (currGlyph == glyphCount || currGlyph == -1) {
                     // XXX: How do we handle EOT vs. EOL?
@@ -127,21 +138,31 @@
                     }
                 }
                 EntryTableIndex2 entryTableIndex = SWAPW(stateArray[classCode + currentState * nClasses]);
+                LE_STATE_PATIENCE_CURR(le_int32, currGlyph);
                 currentState = processStateEntry(glyphStorage, currGlyph, entryTableIndex);
+                LE_STATE_PATIENCE_INCR(currGlyph);
             }
             break;
         }
         case ltfSegmentArray: {
-            printf("Lookup Table Format4: specific interpretation needed!\n");
+          //printf("Lookup Table Format4: specific interpretation needed!\n");
             break;
         }
         case ltfSingleTable: {
             SingleTableLookupTable *lookupTable6 = (SingleTableLookupTable *) classTable;
             while ((dir == 1 && currGlyph <= glyphCount) || (dir == -1 && currGlyph >= -1)) {
+                if(LE_STATE_PATIENCE_DECR()) {
+                  LE_DEBUG_BAD_FONT("patience exceeded - state table not moving")
+                  break; // patience exceeded.
+                }
                 LookupValue classCode = classCodeOOB;
                 if (currGlyph == glyphCount || currGlyph == -1) {
                     // XXX: How do we handle EOT vs. EOL?
                     classCode = classCodeEOT;
+                } else if(currGlyph > glyphCount) {
+                  // note if > glyphCount, we've run off the end (bad font)
+                  currGlyph = glyphCount;
+                  classCode = classCodeEOT;
                 } else {
                     LEGlyphID gid = glyphStorage[currGlyph];
                     TTGlyphID glyphCode = (TTGlyphID) LE_GET_GLYPH(gid);
@@ -156,7 +177,9 @@
                     }
                 }
                 EntryTableIndex2 entryTableIndex = SWAPW(stateArray[classCode + currentState * nClasses]);
+                LE_STATE_PATIENCE_CURR(le_int32, currGlyph);
                 currentState = processStateEntry(glyphStorage, currGlyph, entryTableIndex);
+                LE_STATE_PATIENCE_INCR(currGlyph);
             }
             break;
         }
@@ -166,6 +189,11 @@
             TTGlyphID lastGlyph  = firstGlyph + SWAPW(lookupTable8->glyphCount);
 
             while ((dir == 1 && currGlyph <= glyphCount) || (dir == -1 && currGlyph >= -1)) {
+                if(LE_STATE_PATIENCE_DECR()) {
+                  LE_DEBUG_BAD_FONT("patience exceeded - state table not moving")
+                  break; // patience exceeded.
+                }
+
                 LookupValue classCode = classCodeOOB;
                 if (currGlyph == glyphCount || currGlyph == -1) {
                     // XXX: How do we handle EOT vs. EOL?
@@ -179,7 +207,9 @@
                     }
                 }
                 EntryTableIndex2 entryTableIndex = SWAPW(stateArray[classCode + currentState * nClasses]);
+                LE_STATE_PATIENCE_CURR(le_int32, currGlyph);
                 currentState = processStateEntry(glyphStorage, currGlyph, entryTableIndex);
+                LE_STATE_PATIENCE_INCR(currGlyph);
             }
             break;
         }
--- a/jdk/src/share/native/sun/font/layout/StateTables.h	Fri Feb 15 13:07:17 2013 -0800
+++ b/jdk/src/share/native/sun/font/layout/StateTables.h	Tue Feb 26 10:07:26 2013 -0800
@@ -42,6 +42,41 @@
 
 U_NAMESPACE_BEGIN
 
+
+
+
+/*
+ * State table loop detection.
+ * Detects if too many ( LE_STATE_PATIENCE_COUNT ) state changes occur without moving the glyph index 'g'.
+ *
+ * Usage (pseudocode):
+ *
+ * {
+ *   LE_STATE_PATIENCE_INIT();
+ *
+ *   int g=0; // the glyph index - expect it to be moving
+ *
+ *   for(;;) {
+ *     if(LE_STATE_PATIENCE_DECR()) { // decrements the patience counter
+ *        // ran out of patience, get out.
+ *        break;
+ *     }
+ *
+ *     LE_STATE_PATIENCE_CURR(int, g); // store the 'current'
+ *     state = newState(state,g);
+ *     g+= <something, could be zero>;
+ *     LE_STATE_PATIENCE_INCR(g);  // if g has moved, increment the patience counter. Otherwise leave it.
+ *   }
+ *
+ */
+
+#define LE_STATE_PATIENCE_COUNT 4096 /**< give up if a state table doesn't move the glyph after this many iterations */
+#define LE_STATE_PATIENCE_INIT()  le_uint32 le_patience_count = LE_STATE_PATIENCE_COUNT
+#define LE_STATE_PATIENCE_DECR()  --le_patience_count==0
+#define LE_STATE_PATIENCE_CURR(type,x)  type le_patience_curr=(x)
+#define LE_STATE_PATIENCE_INCR(x)    if((x)!=le_patience_curr) ++le_patience_count;
+
+
 struct StateTableHeader
 {
     le_int16 stateSize;