[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [ft-devel] Outline n_points are int16 so overflow is possible becaus

From: mpsuzuki
Subject: Re: [ft-devel] Outline n_points are int16 so overflow is possible because missing check
Date: Tue, 7 Jul 2009 21:41:43 +0900


Sorry for my lated reply to your important suggestion.

On Thu, 25 Jun 2009 16:59:30 +0200 (CEST)
Werner LEMBERG <address@hidden> wrote:

>> I want to report bug in handling huge (in number of points) glyphs.
>> (I can provide such generated font on request, don't sending to list
>> as it has 1.2MB)
>Thanks for the report.  Please send this font to me.
>> If you create Type1 (could apply also to different types) font with
>> more than 32767 points in one glyph outline->n_points will wrap
>> around 16bits to -32768 in t1_builder_add_point and start corrupting
>> memory.

I think you pointed out an issue caused by the type
definition of `n_points' in include/freetype/ftimage.h:

  typedef struct  FT_Outline_
    short       n_contours;      /* number of contours in glyph        */
    short       n_points;        /* number of points in the glyph      */

    FT_Vector*  points;          /* the outline's points               */
    char*       tags;            /* the points flags                   */
    short*      contours;        /* the contour end points             */

    int         flags;           /* outline masks                      */

  } FT_Outline;

As you can created a Type1 glyph description including 64k 
control points, future version of FT2 is expected to have
larger types for n_contours, n_points and contours.

I checked the font format specifications roughly and estimated
the maximum number of points.

Adobe Type1 CharStrings:
        According to "Adobe Type 1 Font Format", Charstring
        length for a single glyph is limited to 64k octets.
        The operators `hvcurveto' and `vhcurveto' take 4
        operands to add 3 points. Considering the requirement
        of `closepath' and `endchar', the possible maximum
        number of the points in a glyph would be estimated as
                ( ( 65535 - 2 ) x 3/5 ) = 39318
        It is greater than 0x7FFF.

Adobe Type2 CharStrings:
        According to Adobe TechNote 5177, Charstring length
        for a single glyph is limited to 64k octets, and
        the maximum stack depth is 48.
        `hlineto' and `vlineto' in Adobe Type2 Charstring
        draw a zig-zag line by a serie of steggered height
        and width, so short line segment can be coded by
        single operand. Thus, 1 operator and 47 operands
        can define 46 points. The estimated maximum number
        of the points in a glyph would be
                ( ( 65535 - 2 ) x 46/48 ) = 62790.
        It is greater than 0x7FFF.

        TrueType instruction permits to write a looping
        structure in a font program, so it is impossible to
        estimate the maximum number of the points by its
        syntax and the length of glyph description.

        In the header of an entry in `glyf' table, there
        is an array of the point indices that collects
        the ending point of each contours. The element of
        the array is declared as unsigned 16-bit. So, it
        might be reasonable to assume the maximum number
        of the points in a single glyph as 65535. Also
        the `maxp' table has the entries of the maximum
        number of the points for simple and composite glyphs,
        and their type is unsigned 16-bit. It is greater
        than 0x7FFF.

        However, some TrueType instructions (SRP0, SRP1,
        SRP2) take the point index as unsigned 32-bit.
        I'm not sure if TrueType bytecode interpreter
        should truncate 32-bit point index (as current
        FT2) or should return any error. I will ask it
        to ISO/IEC 14496-22 editors.

        According to PFR 1.3 specification, PFR header
        has an entry to declare the maximum length of
        any (simple/composite) glyph, pfrHeader.gpsMaxSize,
        its type is unsigned 16-bit.
        To specify the points in PFR glyph description
        program, there are 3 ways: by unsigned 8-bit point
        index (the indexed points should be predefined in
        the header), by signed 16-bit absolute position,
        by signed 8-bit relative position.
        The operators `hvCurveTo' and `vhCurveTo' can add
        3 points by 4 octet operands. Considering the
        one octet for EndGlyph operator, the maximum number
        of points in PFR by can be estimated as:
                ( ( 65535 - 1 ) x 3 / 5 ) = 39320.
        It is greater than 0x7FFF.

That's all for existing supports of outline font formats in
FreeType2. It seems that "unsigned short" can cover.

BTW, the path construction of Adobe PostScript language
restricts the number of points to 1500 (so, PS Type3 font
has this limitation). It's far smaller than font description
languages in above. About SVG, yet I couldn't find such
clarified limitation.

>          IMHO check for this limitation needs to be added to
>> FT_GlyphLoader_CheckPoints function.

I agree. How do you think following patch?

diff --git a/include/freetype/ftimage.h b/include/freetype/ftimage.h
index 171f1b3..524d2e8 100644
--- a/include/freetype/ftimage.h
+++ b/include/freetype/ftimage.h
@@ -364,6 +364,9 @@ FT_BEGIN_HEADER
   } FT_Outline;
+#define FT_OUTLINE_CONTOURS_MAX SHRT_MAX /* must be synchronized to FT_Outline 
+#define FT_OUTLINE_POINTS_MAX   SHRT_MAX /* must be synchronized to FT_Outline 
   /*                                                                       */

diff --git a/src/base/ftgloadr.c b/src/base/ftgloadr.c
index ab52621..ac0010d 100644
--- a/src/base/ftgloadr.c
+++ b/src/base/ftgloadr.c
@@ -218,6 +218,9 @@
       new_max = FT_PAD_CEIL( new_max, 8 );
+      if ( new_max > FT_OUTLINE_POINTS_MAX )
+        return FT_Err_Array_Too_Large;
       if ( FT_RENEW_ARRAY( base->points, old_max, new_max ) ||
            FT_RENEW_ARRAY( base->tags,   old_max, new_max ) )
         goto Exit;
@@ -246,6 +249,10 @@
     if ( new_max > old_max )
       new_max = FT_PAD_CEIL( new_max, 4 );
+      if ( new_max > FT_OUTLINE_CONTOURS_MAX )
+        return FT_Err_Array_Too_Large;
       if ( FT_RENEW_ARRAY( base->contours, old_max, new_max ) )
         goto Exit;

>>                                      But there could be another
>> problem with local overflow in:
>> #define FT_GLYPHLOADER_CHECK_P( _loader, _count )                    \
>>    ( (_count) == 0 || (int)((_loader)->base.outline.n_points    +    \
>>                             (_loader)->current.outline.n_points +    \
>>                             (_count)) <= (int)(_loader)->max_points )
>> In some cases sum could be also done in shorts then widened to int
>> and not detecting overflow, so it will not call
>> FT_GlyphLoader_CheckPoints.

I'm not sure the scenario you discussed is for 16-bit
systems, or a (possible-but-irregular) implementation 
of 32-bit system, but, at least, an overflow would be
overlooked on 16-bit systems.

I've checked the Embedded Linux Kernel System (ELKS,
16-bit kernel based on Linux) by Bruce's C compiler (bcc),

        (int) ( (short) 0x7FFF + (short) 0x7FFF ) < 0x7FFF

is true. On 32-bit Linux kernel with gcc, it is false.
Therfore, a cast to larger type before summation is
required. How do you think of following patch?

diff --git a/include/freetype/internal/ftgloadr.h 
index 548481a..588304b 100644
--- a/include/freetype/internal/ftgloadr.h
+++ b/include/freetype/internal/ftgloadr.h
@@ -121,15 +121,15 @@ FT_BEGIN_HEADER
                               FT_UInt         n_contours );
-#define FT_GLYPHLOADER_CHECK_P( _loader, _count )                    \
-   ( (_count) == 0 || (int)((_loader)->base.outline.n_points    +    \
-                            (_loader)->current.outline.n_points +    \
-                            (_count)) <= (int)(_loader)->max_points )
+#define FT_GLYPHLOADER_CHECK_P( _loader, _count )            \
+   ( (_count) == 0 || ((_loader)->base.outline.n_points    + \
+                       (_loader)->current.outline.n_points + \
+                       (unsigned long)(_count)) <= (_loader)->max_points )
-#define FT_GLYPHLOADER_CHECK_C( _loader, _count )                     \
-  ( (_count) == 0 || (int)((_loader)->base.outline.n_contours    +    \
-                           (_loader)->current.outline.n_contours +    \
-                           (_count)) <= (int)(_loader)->max_contours )
+#define FT_GLYPHLOADER_CHECK_C( _loader, _count )             \
+  ( (_count) == 0 || ((_loader)->base.outline.n_contours    + \
+                      (_loader)->current.outline.n_contours + \
+                      (unsigned long)(_count)) <= (_loader)->max_contours )
 #define FT_GLYPHLOADER_CHECK_POINTS( _loader, _points,_contours )      \
   ( ( FT_GLYPHLOADER_CHECK_P( _loader, _points )   &&                  \

I guess, casting `_count' to unsigned long is sufficient
to make C compilers to calculate both sides as unsigned

>> Maybe better solution would be completely remove this limitation by
>> changing all these shorts to ints and actually increase speed on
>> most platforms (unfortunately - this would need to be done on many
>> places look at contours array).
>IIRC, Toshiya's fixes take care of this problem too.

I agree. Even if replacing by int is too much, replacing
by unsigned short is expected. I will check the impact of
the change. But, before all, I wish your comment for the
proposed overflow checks in above for quick fix.


reply via email to

[Prev in Thread] Current Thread [Next in Thread]