Commit 09f0e0fcbef4d99f3be9312554d7bb1688911411

Werner Lemberg 2016-05-16T19:44:19

[cid] Fix scanning for `StartData' and `/sfnts' (#47892). * src/cid/cidparse.c (STARTDATA, STARTDATA_LEN, SFNTS, SFNTS_LEN): New macros. (cid_parser_new): Fix and document algorithm.

diff --git a/ChangeLog b/ChangeLog
index fe34f64..02539f6 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,11 @@
+2016-05-16  Werner Lemberg  <wl@gnu.org>
+
+	[cid] Fix scanning for `StartData' and `/sfnts' (#47892).
+
+	* src/cid/cidparse.c (STARTDATA, STARTDATA_LEN, SFNTS, SFNTS_LEN):
+	New macros.
+	(cid_parser_new): Fix and document algorithm.
+
 2016-05-16  suzuki toshiya  <mpsuzuki@hiroshima-u.ac.jp>
 
 	[truetype] Improve the recursive reference detector.
diff --git a/src/cid/cidparse.c b/src/cid/cidparse.c
index f1c39f6..73aca2a 100644
--- a/src/cid/cidparse.c
+++ b/src/cid/cidparse.c
@@ -47,6 +47,12 @@
   /*************************************************************************/
 
 
+#define STARTDATA      "StartData"
+#define STARTDATA_LEN  ( sizeof ( STARTDATA ) - 1 )
+#define SFNTS          "/sfnts"
+#define SFNTS_LEN      ( sizeof ( SFNTS ) - 1 )
+
+
   FT_LOCAL_DEF( FT_Error )
   cid_parser_new( CID_Parser*    parser,
                   FT_Stream      stream,
@@ -85,9 +91,29 @@
     /* now, read the rest of the file until we find */
     /* `StartData' or `/sfnts'                      */
     {
-      FT_Byte   buffer[256 + 10];
-      FT_ULong  read_len = 256 + 10;
-      FT_Byte*  p        = buffer;
+      /*
+       * The algorithm is as follows (omitting the case with less than 256
+       * bytes to fill for simplicity).
+       *
+       * 1. Fill the buffer with 256 + STARTDATA_LEN bytes.
+       *
+       * 2. Search for the STARTDATA and SFNTS strings at positions
+       *    buffer[0], buffer[1], ...,
+       *    buffer[255 + STARTDATA_LEN - SFNTS_LEN].
+       *
+       * 3. Move the last STARTDATA_LEN bytes to buffer[0].
+       *
+       * 4. Fill the buffer with 256 bytes, starting at STARTDATA_LEN.
+       *
+       * 5. Repeat with step 2.
+       *
+       */
+      FT_Byte  buffer[256 + STARTDATA_LEN + 1];
+
+      /* values for the first loop */
+      FT_ULong  read_len    = 256 + STARTDATA_LEN;
+      FT_ULong  read_offset = 0;
+      FT_Byte*  p           = buffer;
 
 
       for ( offset = FT_STREAM_POS(); ; offset += 256 )
@@ -96,40 +122,48 @@
 
 
         stream_len = stream->size - FT_STREAM_POS();
-        if ( stream_len == 0 )
-        {
-          FT_TRACE2(( "cid_parser_new: no `StartData' keyword found\n" ));
-          error = FT_THROW( Invalid_File_Format );
-          goto Exit;
-        }
 
         read_len = FT_MIN( read_len, stream_len );
         if ( FT_STREAM_READ( p, read_len ) )
           goto Exit;
 
-        if ( read_len < 256 )
-          p[read_len]  = '\0';
+        /* ensure that we do not compare with data beyond the buffer */
+        p[read_len] = '\0';
 
-        limit = p + read_len - 10;
+        limit = p + read_len - SFNTS_LEN;
 
         for ( p = buffer; p < limit; p++ )
         {
-          if ( p[0] == 'S' && ft_strncmp( (char*)p, "StartData", 9 ) == 0 )
+          if ( p[0] == 'S'                                           &&
+               ft_strncmp( (char*)p, STARTDATA, STARTDATA_LEN ) == 0 )
           {
             /* save offset of binary data after `StartData' */
-            offset += (FT_ULong)( p - buffer + 10 );
+            offset += (FT_ULong)( p - buffer ) + STARTDATA_LEN;
             goto Found;
           }
-          else if ( p[1] == 's' && ft_strncmp( (char*)p, "/sfnts", 6 ) == 0 )
+          else if ( p[1] == 's'                                   &&
+                    ft_strncmp( (char*)p, SFNTS, SFNTS_LEN ) == 0 )
           {
-            offset += (FT_ULong)( p - buffer + 7 );
+            offset += (FT_ULong)( p - buffer ) + SFNTS_LEN;
             goto Found;
           }
         }
 
-        FT_MEM_MOVE( buffer, p, 10 );
-        read_len = 256;
-        p = buffer + 10;
+        if ( read_offset + read_len < STARTDATA_LEN )
+        {
+          FT_TRACE2(( "cid_parser_new: no `StartData' keyword found\n" ));
+          error = FT_THROW( Invalid_File_Format );
+          goto Exit;
+        }
+
+        FT_MEM_MOVE( buffer,
+                     buffer + read_offset + read_len - STARTDATA_LEN,
+                     STARTDATA_LEN );
+
+        /* values for the next loop */
+        read_len    = 256;
+        read_offset = STARTDATA_LEN;
+        p           = buffer + read_offset;
       }
     }
 
@@ -165,7 +199,7 @@
     limit = parser->root.limit;
     cur   = parser->root.cursor;
 
-    while ( cur < limit )
+    while ( cur < limit - SFNTS_LEN )
     {
       if ( parser->root.error )
       {
@@ -173,7 +207,9 @@
         goto Exit;
       }
 
-      if ( cur[0] == 'S' && ft_strncmp( (char*)cur, "StartData", 9 ) == 0 )
+      if ( cur[0] == 'S'                                           &&
+           cur < limit - STARTDATA_LEN                             &&
+           ft_strncmp( (char*)cur, STARTDATA, STARTDATA_LEN ) == 0 )
       {
         if ( ft_strncmp( (char*)arg1, "(Hex)", 5 ) == 0 )
         {
@@ -191,7 +227,8 @@
 
         goto Exit;
       }
-      else if ( cur[1] == 's' && ft_strncmp( (char*)cur, "/sfnts", 6 ) == 0 )
+      else if ( cur[1] == 's'                                   &&
+                ft_strncmp( (char*)cur, SFNTS, SFNTS_LEN ) == 0 )
       {
         FT_TRACE2(( "cid_parser_new: cannot handle Type 11 fonts\n" ));
         error = FT_THROW( Unknown_File_Format );
@@ -216,6 +253,12 @@
   }
 
 
+#undef STARTDATA
+#undef STARTDATA_LEN
+#undef SFNTS
+#undef SFNTS_LEN
+
+
   FT_LOCAL_DEF( void )
   cid_parser_done( CID_Parser*  parser )
   {