Sun 26 Feb 2012 07:21:55 PM UTC, comment #7:
I have performed a cursory investigation and I believe I found the bug, as well as the reason it only manifests itself on 64-bit platforms. See below.
First, let's break on the call to FT_Render_Glyph which leads to the SIGSEGV, and investigate the face->glyph structure passed as the "slot" parameter:
--- cut ---
(gdb) where
#0 test_render (timer=0x7fffffffe210, face=0x60a9b0, user_data=<optimized out>) at ft2demos-2.4.8/src/ftbench.c:274
#1 0x0000000000402192 in benchmark (face=0x60a9b0, test=0x7fffffffe290, max_iter=0, max_time=<optimized out>)
at ft2demos-2.4.8/src/ftbench.c:191
#2 0x000000000040260e in main (argc=<optimized out>, argv=<optimized out>) at ft2demos-2.4.8/src/ftbench.c:874
(gdb) l
269 {
270 if ( FT_Load_Glyph( face, i, load_flags ) )
271 continue;
272
273 TIMER_START( timer );
274 if ( !FT_Render_Glyph( face->glyph, render_mode ) )
275 done++;
276 TIMER_STOP( timer );
277 }
278
(gdb) print *face->glyph
$3 = {library = 0x605040, face = 0x60a9b0, next = 0x0, reserved = 0, generic = {data = 0x0, finalizer = 0}, metrics = {width = 448, height = 274877907328,
horiBearingX = 0, horiBearingY = 274877907328, horiAdvance = 512, vertBearingX = -256, vertBearingY = 320, vertAdvance = 640}, linearHoriAdvance = 491200,
linearVertAdvance = 655360, advance = {x = 512, y = 0}, format = FT_GLYPH_FORMAT_OUTLINE, bitmap = {rows = 0, width = 0, pitch = 0, buffer = 0x0, num_grays = 256,
pixel_mode = 0 '\000', palette_mode = 0 '\000', palette = 0x0}, bitmap_left = 0, bitmap_top = 0, outline = {n_contours = 2, n_points = 50, points = 0x60f320,
tags = 0x60d820 "5", contours = 0x610b40, flags = 256}, num_subglyphs = 0, subglyphs = 0x0, control_data = 0x60d5d0, control_len = 164, lsb_delta = 0,
rsb_delta = 0, other = 0x0, internal = 0x60bd60}
(gdb) print face->glyph->outline->points[0]
$4 = {x = 326, y = 274877907300}
--- cut ---
Please note the very large values of face->glyph->metrics->height (274877907328) and face->glyph->outline->points[0].y (274877907300).
Next then, the execution is recursively passed to the ft_smooth_render_generic routine (src/smooth/ftsmooth.c):
--- cut ---
(gdb) b ft_smooth_render_generic
Breakpoint 2 at 0x7ffff7ba5d61: file freetype2/src/smooth/ftsmooth.c, line 106.
(gdb) c
Continuing.
Breakpoint 2, ft_smooth_render_generic (render=0x60a670, slot=0x60bc20, mode=FT_RENDER_MODE_NORMAL, origin=0x0, required_mode=FT_RENDER_MODE_NORMAL)
at freetype2/src/smooth/ftsmooth.c:106
106 FT_Outline* outline = NULL;
(gdb) where
#0 ft_smooth_render_generic (render=0x60a670, slot=0x60bc20, mode=FT_RENDER_MODE_NORMAL, origin=0x0, required_mode=FT_RENDER_MODE_NORMAL)
at freetype2/src/smooth/ftsmooth.c:106
#1 0x00007ffff7ba6393 in ft_smooth_render (render=0x60a670, slot=0x60bc20, mode=FT_RENDER_MODE_NORMAL, origin=0x0)
at freetype2/src/smooth/ftsmooth.c:383
#2 0x00007ffff7b3354a in FT_Render_Glyph_Internal (library=0x605040, slot=0x60bc20, render_mode=FT_RENDER_MODE_NORMAL)
at freetype2/src/base/ftobjs.c:4025
#3 0x00007ffff7b3361f in FT_Render_Glyph (slot=0x60bc20, render_mode=FT_RENDER_MODE_NORMAL) at freetype2/src/base/ftobjs.c:4065
#4 0x0000000000401f33 in test_render (timer=0x7fffffffe210, face=0x60a9b0, user_data=<optimized out>) at ft2demos-2.4.8/src/ftbench.c:274
#5 0x0000000000402192 in benchmark (face=0x60a9b0, test=0x7fffffffe290, max_iter=0, max_time=<optimized out>)
at ft2demos-2.4.8/src/ftbench.c:191
#6 0x000000000040260e in main (argc=<optimized out>, argv=<optimized out>) at ft2demos-2.4.8/src/ftbench.c:874
--- cut ---
The function proceeds to calculating the glyph height and stores it in a local variable:
--- cut ---
(gdb) n
164 height = (FT_UInt)( ( cbox.yMax - cbox.yMin ) >> 6 );
(gdb) print cbox.yMax
$9 = 274877907328
(gdb) print cbox.yMin
$10 = 0
(gdb) print (cbox.yMax - cbox.yMin) >> 6
$11 = 4294967302
(gdb) print (FT_UInt)((cbox.yMax - cbox.yMin) >> 6)
$12 = 6
(gdb) ptype cbox.yMax
type = long int
(gdb) ptype height
type = unsigned int
--- cut ---
As can be seen, there's an inconsistency between the types of the local variable later use for glyph size sanitization, and the structure fields storing the metrics. This leads to truncation of very large values (> 32bit values) and can result in bypassing the glyph size sanity check. Since the widths of "long int" and "unsigned int" are equal on 32-bit architectures, the problem does not manifest there.
--- cut ---
(gdb) b freetype2/src/smooth/ftsmooth.c:226
Breakpoint 4 at 0x7ffff7ba6007: file freetype2/src/smooth/ftsmooth.c, line 226.
(gdb) c
Continuing.
Breakpoint 4, ft_smooth_render_generic (render=0x60a670, slot=0x60bc20, mode=FT_RENDER_MODE_NORMAL, origin=0x0, required_mode=FT_RENDER_MODE_NORMAL)
at freetype2/src/smooth/ftsmooth.c:226
226 if ( width > 0x7FFFU || height > 0x7FFFU )
(gdb) print width
$13 = 7
(gdb) print height
$14 = 6
(gdb) n
235 bitmap->pixel_mode = FT_PIXEL_MODE_GRAY;
--- cut ---
Next, the execution goes down to gray_set_cell, which sets the value of worker->ey to the original face->glyph->outline->points[0].y:
--- cut ---
(gdb) b freetype2/src/smooth/ftgrays.c:561
Breakpoint 5 at 0x7ffff7ba3231: file freetype2/src/smooth/ftgrays.c, line 561.
(gdb) c
Continuing.
Breakpoint 5, gray_set_cell (worker=0x6051c0, ex=5, ey=4294967301) at freetype2/src/smooth/ftgrays.c:561
561 ras.ey = ey;
(gdb) print ey
$15 = 4294967301
(gdb) where
#0 gray_set_cell (worker=0x6051c0, ex=5, ey=4294967301) at freetype2/src/smooth/ftgrays.c:561
#1 0x00007ffff7ba333a in gray_start_cell (worker=0x6051c0, ex=5, ey=4294967301) at freetype2/src/smooth/ftgrays.c:588
#2 0x00007ffff7ba49b4 in gray_move_to (to=0x7fffffffdb40, worker=0x6051c0) at freetype2/src/smooth/ftgrays.c:1135
#3 0x00007ffff7b34534 in FT_Outline_Decompose (outline=0x6058e8, func_interface=0x7ffff7dda820, user=0x6051c0)
at freetype2/src/base/ftoutln.c:138
#4 0x00007ffff7ba50b6 in gray_convert_glyph_inner (worker=0x6051c0) at freetype2/src/smooth/ftgrays.c:1717
#5 0x00007ffff7ba5550 in gray_convert_glyph (worker=0x6051c0) at freetype2/src/smooth/ftgrays.c:1819
#6 0x00007ffff7ba5a54 in gray_raster_render (raster=0x60a720, params=0x7fffffffdf90) at freetype2/src/smooth/ftgrays.c:1949
#7 0x00007ffff7ba6115 in ft_smooth_render_generic (render=0x60a670, slot=0x60bc20, mode=FT_RENDER_MODE_NORMAL, origin=0x0, required_mode=FT_RENDER_MODE_NORMAL)
at freetype2/src/smooth/ftsmooth.c:297
#8 0x00007ffff7ba6393 in ft_smooth_render (render=0x60a670, slot=0x60bc20, mode=FT_RENDER_MODE_NORMAL, origin=0x0)
at freetype2/src/smooth/ftsmooth.c:383
#9 0x00007ffff7b3354a in FT_Render_Glyph_Internal (library=0x605040, slot=0x60bc20, render_mode=FT_RENDER_MODE_NORMAL)
at freetype2/src/base/ftobjs.c:4025
#10 0x00007ffff7b3361f in FT_Render_Glyph (slot=0x60bc20, render_mode=FT_RENDER_MODE_NORMAL) at freetype2/src/base/ftobjs.c:4065
#11 0x0000000000401f33 in test_render (timer=0x7fffffffe210, face=0x60a9b0, user_data=<optimized out>) at ft2demos-2.4.8/src/ftbench.c:274
#12 0x0000000000402192 in benchmark (face=0x60a9b0, test=0x7fffffffe290, max_iter=0, max_time=<optimized out>)
at ft2demos-2.4.8/src/ftbench.c:191
#13 0x000000000040260e in main (argc=<optimized out>, argv=<optimized out>) at ft2demos-2.4.8/src/ftbench.c:874
--- cut ---
Eventually, the gray_find_cell function is called; it uses the just-set worker->ey value as an index to worker->ycells, and since it fails to reference the out-of-bounds address, a SIGSEGV is generated:
--- cut ---
(gdb) b freetype2/src/smooth/ftgrays.c:477
Breakpoint 6 at 0x7ffff7ba301b: file freetype2/src/smooth/ftgrays.c, line 477.
(gdb) c
Continuing.
Breakpoint 6, gray_find_cell (worker=0x6051c0) at freetype2/src/smooth/ftgrays.c:477
477 pcell = &ras.ycells[ras.ey];
(gdb) print worker->ey
$16 = 4294967301
(gdb) c
Continuing.
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7ba303e in gray_find_cell (worker=0x6051c0) at freetype2/src/smooth/ftgrays.c:480
480 cell = *pcell;
(gdb) print pcell
$17 = (PCell *) 0x800605b68
(gdb) where
#0 0x00007ffff7ba303e in gray_find_cell (worker=0x6051c0) at freetype2/src/smooth/ftgrays.c:480
#1 0x00007ffff7ba3150 in gray_record_cell (worker=0x6051c0) at freetype2/src/smooth/ftgrays.c:511
#2 0x00007ffff7ba320f in gray_set_cell (worker=0x6051c0, ex=4, ey=5) at freetype2/src/smooth/ftgrays.c:554
#3 0x00007ffff7ba3b26 in gray_render_line (worker=0x6051c0, to_x=1124, to_y=1452) at freetype2/src/smooth/ftgrays.c:814
#4 0x00007ffff7ba418c in gray_render_conic (worker=0x6051c0, control=0x7fffffffdb50, to=0x7fffffffdb20) at freetype2/src/smooth/ftgrays.c:946
#5 0x00007ffff7ba4a49 in gray_conic_to (control=0x7fffffffdb50, to=0x7fffffffdb20, worker=0x6051c0) at freetype2/src/smooth/ftgrays.c:1157
#6 0x00007ffff7b346b3 in FT_Outline_Decompose (outline=0x6058e8, func_interface=0x7ffff7dda820, user=0x6051c0)
at freetype2/src/base/ftoutln.c:190
#7 0x00007ffff7ba50b6 in gray_convert_glyph_inner (worker=0x6051c0) at freetype2/src/smooth/ftgrays.c:1717
#8 0x00007ffff7ba5550 in gray_convert_glyph (worker=0x6051c0) at freetype2/src/smooth/ftgrays.c:1819
#9 0x00007ffff7ba5a54 in gray_raster_render (raster=0x60a720, params=0x7fffffffdf90) at freetype2/src/smooth/ftgrays.c:1949
#10 0x00007ffff7ba6115 in ft_smooth_render_generic (render=0x60a670, slot=0x60bc20, mode=FT_RENDER_MODE_NORMAL, origin=0x0, required_mode=FT_RENDER_MODE_NORMAL)
at freetype2/src/smooth/ftsmooth.c:297
#11 0x00007ffff7ba6393 in ft_smooth_render (render=0x60a670, slot=0x60bc20, mode=FT_RENDER_MODE_NORMAL, origin=0x0)
at freetype2/src/smooth/ftsmooth.c:383
#12 0x00007ffff7b3354a in FT_Render_Glyph_Internal (library=0x605040, slot=0x60bc20, render_mode=FT_RENDER_MODE_NORMAL)
at freetype2/src/base/ftobjs.c:4025
#13 0x00007ffff7b3361f in FT_Render_Glyph (slot=0x60bc20, render_mode=FT_RENDER_MODE_NORMAL) at freetype2/src/base/ftobjs.c:4065
#14 0x0000000000401f33 in test_render (timer=0x7fffffffe210, face=0x60a9b0, user_data=<optimized out>) at ft2demos-2.4.8/src/ftbench.c:274
#15 0x0000000000402192 in benchmark (face=0x60a9b0, test=0x7fffffffe290, max_iter=0, max_time=<optimized out>)
at ft2demos-2.4.8/src/ftbench.c:191
#16 0x000000000040260e in main (argc=<optimized out>, argv=<optimized out>) at ft2demos-2.4.8/src/ftbench.c:874
--- cut ---
It appears to me that a sufficient fix would be to change the types of "height", "width", "pitch", "org_height", "org_width" local variables in ft_smooth_render_generic from FT_UInt to FT_Pos (or equivalent), and remove the type casts at lines 154 and 164. After applying such a patch, ftbench doesn't crash anymore:
--- cut ---
==10300== Memcheck, a memory error detector
==10300== Copyright (C) 2002-2011, and GNU GPL'd, by Julian Seward et al.
==10300== Using Valgrind-3.8.0.SVN and LibVEX; rerun with -h for copyright info
==10300== Command: ./lt-ftbench tmp/freetype/Umpush.ttf.9f7.205
==10300==
Load : 250.017 us/op
Load_Advances (Normal) : 31.394 us/op
Load_Advances (Fast) : 0.631 us/op
Render : 22029.244 us/op
Get_Glyph : 49.401 us/op
Get_CBox : 14.452 us/op
Get_Char_Index : 1.017 us/op
Iterate CMap : 49845.864 us/op
New_Face : 915.805 us/op
Embolden : 10.391 us/op
==10300==
==10300== HEAP SUMMARY:
==10300== in use at exit: 0 bytes in 0 blocks
==10300== total heap usage: 116,603 allocs, 116,603 frees, 51,093,726 bytes allocated
==10300==
==10300== All heap blocks were freed -- no leaks are possible
==10300==
==10300== For counts of detected and suppressed errors, rerun with: -v
==10300== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 4 from 4)
--- cut ---
|