Bug 2675

Summary: SIGFPE in _cairo_fixed_from_double
Product: cairo Reporter: ellson
Component: generalAssignee: Carl Worth <cworth>
Status: RESOLVED FIXED QA Contact: cairo-bugs mailing list <cairo-bugs>
Severity: normal    
Priority: high CC: billy.biggs, jwatt
Version: 0.9.3   
Hardware: x86 (IA32)   
OS: Linux (All)   
Whiteboard:
i915 platform: i915 features:
Attachments: Source code illustrating one case of FPE in cairo.

Description ellson 2005-03-08 07:15:07 UTC
My application gets a SIGFPE during a zooming operation on an image that was
previously drawn successfully at lower zooming levels.


Program received signal SIGFPE, Arithmetic exception.
0x00c5830e in _cairo_fixed_from_double (d=-33750.7673118194)
    at cairo_fixed.c:47
47      {
(gdb) list
42          return i << 16;
43      }
44
45      cairo_fixed_t
46      _cairo_fixed_from_double (double d)
47      {
48          return (cairo_fixed_t) (d * 65536);
49      }
50
51      cairo_fixed_t
(gdb) where
#0  0x00c5830e in _cairo_fixed_from_double (d=-33750.7673118194)
    at cairo_fixed.c:47
#1  0x00c619ae in _cairo_traps_tessellate_polygon (traps=0xbfe64320,
    poly=0x8220c78, fill_rule=CAIRO_FILL_RULE_WINDING) at cairo_traps.c:529
#2  0x00c5fe2e in _cairo_pen_stroke_spline (pen=0xbfe64188, spline=0xbfe640cc,
    tolerance=warning: Unhandled dwarf expresion opcode DW_OP_piece
0.10000000000000001, traps=0xbfe64320) at cairo_pen.c:583
#3  0x00c5f392 in _cairo_stroker_curve_to (closure=0xbfe64254, b=0xbfe641f4,
    c=0xbfe641fc, d=0xbfe64204) at cairo_path_stroke.c:754
#4  0x00c5de15 in _cairo_path_interpret (path=0x81b0d40,
    dir=CAIRO_DIRECTION_FORWARD, move_to=0xc5eba4 <_cairo_stroker_move_to>,
    line_to=0xc5ebe0 <_cairo_stroker_line_to>,
    curve_to=0xc5f164 <_cairo_stroker_curve_to>,
    close_path=0xc5f39c <_cairo_stroker_close_path>, closure=0xbfe64254)
    at cairo_path.c:481
#5  0x00c5f4f7 in _cairo_path_stroke_to_traps (path=0x81b0d40,
    gstate=0x81b0c40, traps=0xbfe64320) at cairo_path_stroke.c:813
#6  0x00c5ae2f in _cairo_gstate_stroke (gstate=0x81b0c40)
    at cairo_gstate.c:1338
#7  0x00c5645b in cairo_stroke (cr=0x818bce8) at cairo.c:889
#8  0x0804dd5c in cairogen_bezier (job=0x8220c78, A=0x8226fb0, n=4,
    arrow_at_start=0, arrow_at_end=0) at gvrender_cairo.c:865
#9  0x00adb94e in gvrender_beziercurve (gvc=0x8139d58, AF=0x8226498, n=4,
    arrow_at_start=0, arrow_at_end=0) at gvrender.c:865
#10 0x00377750 in emit_edge (gvc=0x8139d58, e=0x8147930) at emit.c:881
#11 0x00378bf9 in emit_graph (gvc=0x8139d58, g=0x813f470) at emit.c:1226
#12 0x00adc0a5 in gvevent_refresh (job=0x8220c78) at gvevent.c:37
#13 0x0804d121 in cairogen_finalize (gvc=0x8139d58) at gvrender_cairo.c:175
#14 0x00ada6cc in gvrender_finalize (gvc=0x8139d58) at gvrender.c:138
#15 0x0037a02d in emit_jobs (gvc=0x8139d58, g=0x813f470) at emit.c:1630
#16 0x0804cb12 in main (argc=3, argv=0xbfe64a44) at dot.c:172
(gdb)
Comment 1 Billy Biggs 2005-07-28 15:11:30 UTC
I can only get that to generate an FPE if I explicitly enable FE_INVALID.  Is
your code is enabling this exception?

#include <stdio.h>
#include <stdint.h>
#define _GNU_SOURCE
#include <fenv.h>

int main()
{
    double d = -33750.7673118194;
    feenableexcept(FE_INVALID);
    fprintf (stderr, "foo: %d\n", (int32_t) (d * 65536));
    return 0;
}
Comment 2 ellson 2005-07-28 21:20:47 UTC
Ah yes, so that probably explains why I was unable to create a small test case.
In dot.c (from graphviz-2.4) I set:
    feenableexcept( FE_DIVBYZERO | FE_OVERFLOW | FE_INVALID )
Comment 3 ellson 2005-08-03 23:53:01 UTC
Thanks to Billy Biggs, I been able to create a small test case illustrating this
bug. Please find attached.
Comment 4 ellson 2005-08-03 23:57:46 UTC
Created attachment 3233 [details] [review]
xcursor-4244-reset-theme-name.diff
Comment 5 ellson 2005-08-03 23:58:13 UTC
Created attachment 3234 [details] [review]
Mighty Mouse Horizontal Scrolling with inverting z axis.
Comment 6 Owen Taylor 2005-08-18 18:16:25 UTC
Unfortunately, we've lost many of the attachments to freedesktop.org bugzilla.
Would you happen to have the attachments still around?
Comment 7 ellson 2005-08-18 19:38:22 UTC
I'll attach the recent "simple" test case (close, if not exactly the same as the
previous attachment).

Cairo seems to rely on some sort of automatic corrective behavior for FP errors
(Is this behavior documented anywhere?  Can it be counted on cross-platform?).

If you enable all the possible FP errors there are a flood of problems.

In graphviz we are FP error clean and normally keep the error reporting enabled. 
I finally worked out how to disable the FP error reporting just around the calls
to cairo.
Comment 8 ellson 2005-08-18 19:42:22 UTC
Created attachment 2923 [details]
Source code illustrating one case of FPE in cairo.
Comment 9 Owen Taylor 2005-08-19 08:57:05 UTC
No, Cairo isn't intentionally generating FP errors ... though it's certainly
possible there are some places that it's generating FP errors in places
that don't matter - the moral equivalent of:

 c = a / b;
 if (b > 0)
    return c; 
 else 
    return 0;

Can you check if compiling Cairo with -ffloat-store fixes the problem -
your test case seems to trigger a problem with 

 if (m1 == m2) 
   return 0;

  y_intersect = _cairo_fixed_from_double ((b2 - b1) / (m1 - m2));

Which looks like a typical 80-bit float register problem.
Comment 10 ellson 2005-08-19 09:44:01 UTC
( I don't think your example is mathmatically correct .. but no matter. )

I've been using the cairo-0.9.2-2 shipped with Fedora, which produces FE_INVALID.
Something must have changed since then because cairo from CVS produces FE_INEXACT.

(I wish I knew how to detect which FP error has occurred.
fetestexcept(FE_ALL_EXCEPT) always returns zero.
I've been checking for any error with FP_ALL_EXCEPT, then narrowing it down by
trying the specific exceptions one at a time.)

Rebulding cairo with -ffloat-store made no difference.
Comment 11 Owen Taylor 2005-08-19 11:28:04 UTC
Investigated a little bit and I think it's basically a stroker problem - 
_cairo_pen_stroke_spline() calls _cairo_traps_tessellate_polygon() with
a polygon that has two nearly parallel edges, and intersecting
them causes a division by zero, or a catastrophic lack of accuracy.

Looking at the polygon that gets passed in, it has, for example:

(gdb) p polygon->edges[1]
$5 = {edge = {p1 = {x = 818734, y = 1992760}, p2 = {x = 926867, y = 2089629}},
clockWise = 0, current_x = 818734}
(gdb) p polygon->edges[2]
$6 = {edge = {p1 = {x = 818734, y = 1992760}, p2 = {x = 926867, y = 2089629}},
clockWise = 0, current_x = 823407}

I don't really understand the polygon structure, but that looks fishy.
Anyways, would require some detailed digging to figure it out.
Comment 12 Carl Worth 2005-08-22 17:15:28 UTC
Move bugs against "cvs" version to "0.9.3" so we can remove the "cvs" version.
Comment 13 Kalle Vahlman 2007-07-02 11:37:13 UTC
Seems to still happen, though in a different code path than in comment 11.

Since the exception is FE_INEXACT, it sounds like the accuracy of the FP is not enough to compute the result exactly. I wonder if this has real-life consequences or not (extreme scaling could of course bring the error visible).

Other than detecting the accuracy of the FP unit (can that be done?) and rounding the values to acceptable values, I'm guessing there isn't much to do here. This is a problem that needs to be specifically requested for by setting the handler anyway.

Current backtrace looks like:

Program received signal SIGFPE, Arithmetic exception.
*INT_cairo_matrix_transform_distance (matrix=0x8053ec8, dx=0xbfeb11d8, 
    dy=0xbfeb11d0) at cairo-matrix.c:331
331         new_x = (matrix->xx * *dx + matrix->xy * *dy);
(gdb) bt
#0  *INT_cairo_matrix_transform_distance (matrix=0x8053ec8, dx=0xbfeb11d8, 
    dy=0xbfeb11d0) at cairo-matrix.c:331
#1  0xb7f3b815 in *INT_cairo_matrix_transform_point (matrix=0x8053ec8, 
    x=0xbfeb11d8, y=0xbfeb11d0) at cairo-matrix.c:350
#2  0xb7f375ba in _cairo_gstate_user_to_backend (gstate=0x8053e10, 
    x=0xbfeb11d8, y=0xbfeb11d0) at cairo-gstate.c:731
#3  0xb7f3134a in *INT_cairo_move_to (cr=0x8053df0, x=19.800000000000001, 
    y=35.200000000000003) at cairo.c:1381
#4  0x080487e9 in main ()
Comment 14 Chris Wilson 2008-09-29 02:58:49 UTC
We routinely now test with FPE enabled in the test-suite [since 1.6], although only for the fatal traps DivideByZero, Overflow and Invalid. Underflow and Inexac,t I consider to be mere warnings that the floating point arithmetic can no longer represent the result without loss of accuracy. If you consider the application of such values within cairo (e.g. conversion to 24.8 fixed point during rasteristation, or printing using %.18f to vector targets) then those two exceptions are of no significant consequence (one hopes).

Wrt the original bug, cairo_polygon_t now discards degenerate edges and so the division-by-zero is prevented at source.

So given the specific bug has been fixed and the general bug has been incorporated into routine testing, I feel confident in closing this bug. Many thanks for the original report.

Use of freedesktop.org services, including Bugzilla, is subject to our Code of Conduct. How we collect and use information is described in our Privacy Policy.