Commit b069a590a9c9e6f06f4d1d5c16df732bd6483536

Werner Lemberg 2016-03-26T08:00:07

[pfr] Robustify bitmap strike handling (#47514). We did a binary search for a charcode without ensuring that the searched data is ordered. Validating the order is now done lazily, this is, the first access to a bitmap glyph triggers the order check in the corresponding bitmap strike. * src/pfr/pfrtypes.h (PFR_BitmapFlags): New values `PFR_BITMAP_VALID_CHARCODES' and `PFR_BITMAP_CHARCODES_VALIDATED'. * src/pfr/pfrsbit.c (pfr_lookup_bitmap_data): Make `flags' argument a pointer. Handle new PFR_BITMAP_XXX flags. (pfr_slot_load_bitmap): Updated.

diff --git a/ChangeLog b/ChangeLog
index 91063ce..33c18b9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,4 +1,20 @@
-2016-03-25  Werner Lemberg  <wl@gnu.org>
+2016-03-26  Werner Lemberg  <wl@gnu.org>
+
+	[pfr] Robustify bitmap strike handling (#47514).
+
+	We did a binary search for a charcode without ensuring that the
+	searched data is ordered.  Validating the order is now done lazily,
+	this is, the first access to a bitmap glyph triggers the order check
+	in the corresponding bitmap strike.
+
+	* src/pfr/pfrtypes.h (PFR_BitmapFlags): New values
+	`PFR_BITMAP_VALID_CHARCODES' and `PFR_BITMAP_CHARCODES_VALIDATED'.
+
+	* src/pfr/pfrsbit.c (pfr_lookup_bitmap_data): Make `flags' argument
+	a pointer.  Handle new PFR_BITMAP_XXX flags.
+	(pfr_slot_load_bitmap): Updated.
+
+2016-03-26  Werner Lemberg  <wl@gnu.org>
 
 	[pfr] Fix handling of compound glyphs.
 
diff --git a/src/pfr/pfrsbit.c b/src/pfr/pfrsbit.c
index 9bc5d41..d271593 100644
--- a/src/pfr/pfrsbit.c
+++ b/src/pfr/pfrsbit.c
@@ -277,24 +277,76 @@
   pfr_lookup_bitmap_data( FT_Byte*   base,
                           FT_Byte*   limit,
                           FT_UInt    count,
-                          FT_UInt    flags,
+                          FT_UInt*   flags,
                           FT_UInt    char_code,
                           FT_ULong*  found_offset,
                           FT_ULong*  found_size )
   {
     FT_UInt   left, right, char_len;
-    FT_Bool   two = FT_BOOL( flags & PFR_BITMAP_2BYTE_CHARCODE );
+    FT_Bool   two = FT_BOOL( *flags & PFR_BITMAP_2BYTE_CHARCODE );
     FT_Byte*  buff;
 
 
     char_len = 4;
     if ( two )
       char_len += 1;
-    if ( flags & PFR_BITMAP_2BYTE_SIZE )
+    if ( *flags & PFR_BITMAP_2BYTE_SIZE )
       char_len += 1;
-    if ( flags & PFR_BITMAP_3BYTE_OFFSET )
+    if ( *flags & PFR_BITMAP_3BYTE_OFFSET )
       char_len += 1;
 
+    if ( !( *flags & PFR_BITMAP_CHARCODES_VALIDATED ) )
+    {
+      FT_Byte*  p;
+      FT_Byte*  lim;
+      FT_UInt   code;
+      FT_Long   prev_code;
+
+
+      *flags    |= PFR_BITMAP_VALID_CHARCODES;
+      prev_code  = -1;
+      lim        = base + count * char_len;
+
+      if ( lim > limit )
+      {
+        FT_TRACE0(( "pfr_lookup_bitmap_data:"
+                    " number of bitmap records too large,\n"
+                    "                       "
+                    " thus ignoring all bitmaps in this strike\n" ));
+        *flags &= ~PFR_BITMAP_VALID_CHARCODES;
+      }
+      else
+      {
+        /* check whether records are sorted by code */
+        for ( p = base; p < lim; p += char_len )
+        {
+          if ( two )
+            code = FT_PEEK_USHORT( p );
+          else
+            code = *p;
+
+          if ( code <= prev_code )
+          {
+            FT_TRACE0(( "pfr_lookup_bitmap_data:"
+                        " bitmap records are not sorted,\n"
+                        "                       "
+                        " thus ignoring all bitmaps in this strike\n" ));
+            *flags &= ~PFR_BITMAP_VALID_CHARCODES;
+            break;
+          }
+
+          prev_code = code;
+        }
+      }
+
+      *flags |= PFR_BITMAP_CHARCODES_VALIDATED;
+    }
+
+    /* ignore bitmaps in case table is not valid     */
+    /* (this might be sanitized, but PFR is dead...) */
+    if ( !( *flags & PFR_BITMAP_VALID_CHARCODES ) )
+      goto Fail;
+
     left  = 0;
     right = count;
 
@@ -306,11 +358,6 @@
       middle = ( left + right ) >> 1;
       buff   = base + middle * char_len;
 
-      /* check that we are not outside of the table -- */
-      /* this is possible with broken fonts...         */
-      if ( buff + char_len > limit )
-        goto Fail;
-
       if ( two )
         code = PFR_NEXT_USHORT( buff );
       else
@@ -332,12 +379,12 @@
     return;
 
   Found_It:
-    if ( flags & PFR_BITMAP_2BYTE_SIZE )
+    if ( *flags & PFR_BITMAP_2BYTE_SIZE )
       *found_size = PFR_NEXT_USHORT( buff );
     else
       *found_size = PFR_NEXT_BYTE( buff );
 
-    if ( flags & PFR_BITMAP_3BYTE_OFFSET )
+    if ( *flags & PFR_BITMAP_3BYTE_OFFSET )
       *found_offset = PFR_NEXT_ULONG( buff );
     else
       *found_offset = PFR_NEXT_USHORT( buff );
@@ -588,7 +635,7 @@
       pfr_lookup_bitmap_data( stream->cursor,
                               stream->limit,
                               strike->num_bitmaps,
-                              strike->flags,
+                              &strike->flags,
                               character->char_code,
                               &gps_offset,
                               &gps_size );
diff --git a/src/pfr/pfrtypes.h b/src/pfr/pfrtypes.h
index 09649b9..24ca4d3 100644
--- a/src/pfr/pfrtypes.h
+++ b/src/pfr/pfrtypes.h
@@ -121,6 +121,10 @@ FT_BEGIN_HEADER
 
   typedef enum  PFR_BitmapFlags_
   {
+    /* not part of the specification but used for implementation */
+    PFR_BITMAP_VALID_CHARCODES     = 0x80,
+    PFR_BITMAP_CHARCODES_VALIDATED = 0x40,
+
     PFR_BITMAP_3BYTE_OFFSET   = 4,
     PFR_BITMAP_2BYTE_SIZE     = 2,
     PFR_BITMAP_2BYTE_CHARCODE = 1