Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Spiro segmentation fault #3437

Closed
ctrlcctrlv opened this issue Feb 1, 2019 · 61 comments
Closed

Spiro segmentation fault #3437

ctrlcctrlv opened this issue Feb 1, 2019 · 61 comments

Comments

@ctrlcctrlv
Copy link
Member

ctrlcctrlv commented Feb 1, 2019

  • Enable Spiro mode
  • Draw a circle (create four curve points) (it seems like only one is strictly necessary)

c

  • Right click one of the points, select Get Info…
  • Close the dialog via your window manager. It won't close. Try again, you'll get a segfault.

Tested on Arch Linux, commit c96d3eb

Program received signal SIGSEGV, Segmentation fault.
0x0000155554a665e0 in CVRemoveTopUndo () from /usr/lib/libfontforge.so.2
(gdb) bt
#0  0x0000155554a665e0 in CVRemoveTopUndo () at /usr/lib/libfontforge.so.2
#1  0x000015555523c505 in  () at /usr/lib/libfontforgeexe.so.2
#2  0x000015555523c9e9 in  () at /usr/lib/libfontforgeexe.so.2
#3  0x00001555550cb958 in  () at /usr/lib/libgdraw.so.5
#4  0x00001555550cbf18 in  () at /usr/lib/libgdraw.so.5
#5  0x0000155555116bb6 in  () at /usr/lib/libgdraw.so.5
#6  0x0000155555119141 in  () at /usr/lib/libgdraw.so.5
#7  0x000015555533da3f in fontforge_main () at /usr/lib/libfontforgeexe.so.2
#8  0x0000555555555159 in main (argc=1, argv=0x7fffffffe188) at main.c:39
@ctrlcctrlv
Copy link
Member Author

I think I figured out why this happens, CVRemoveTopUndo attempts to use memory freed by the previous invocation of CVRemoveTopUndo. The solution would be to do the same action as the "Close" button on the dialog to the "X" provided by window manager.

@ctrlcctrlv
Copy link
Member Author

Just figured out how to get gdb to produce an even better dump :-)

Program received signal SIGSEGV, Segmentation fault.
0x000015555486a90e in CVRemoveTopUndo (cv=0x55555580cc10) at cvundoes.c:1138
1138        cv->layerheads[cv->drawmode]->undoes = undo->next;
(gdb) bt
#0  0x000015555486a90e in CVRemoveTopUndo (cv=0x55555580cc10) at cvundoes.c:1138
#1  0x00001555551d97b3 in PI_DoCancel (ci=0x55555593f5b0) at cvgetinfo.c:1750
#2  0x00001555551d984c in pi_e_h (gw=0x5555559462b0, event=0x7fffffffbd40) at cvgetinfo.c:1762
#3  0x0000155554ff9966 in _GWidget_Container_eh (gw=0x5555559462b0, event=0x7fffffffbd40) at gcontainer.c:403
#4  0x0000155554ffaf1e in _GWidget_TopLevel_eh (gw=0x5555559462b0, event=0x7fffffffbd40) at gcontainer.c:745
#5  0x0000155555075d01 in _GGDKDraw_CallEHChecked (gw=0x5555559462b0, event=0x7fffffffbd40, eh=0x155554ffa590 <_GWidget_TopLevel_eh>)
    at ggdkdraw.c:335
#6  0x000015555507846d in _GGDKDraw_DispatchEvent (event=0x555555919a00, data=0x5555556422c0) at ggdkdraw.c:1203
#7  0x000015555417fae6 in  () at /usr/lib/libgdk-3.so.0
#8  0x00001555541b1985 in  () at /usr/lib/libgdk-3.so.0
#9  0x00001555535aaa2f in g_main_context_dispatch () at /usr/lib/libglib-2.0.so.0
#10 0x00001555535ac5e9 in  () at /usr/lib/libglib-2.0.so.0
#11 0x00001555535ac62e in g_main_context_iteration () at /usr/lib/libglib-2.0.so.0
#12 0x000015555507af6a in GGDKDrawEventLoop (gdisp=0x5555556422c0) at ggdkdraw.c:2168
#13 0x0000155554ffe486 in GDrawEventLoop (gdisp=0x5555556422c0) at gdraw.c:787
#14 0x000015555539c7d3 in fontforge_main (argc=1, argv=0x7fffffffe168) at startui.c:1389
#15 0x0000555555555159 in main (argc=1, argv=0x7fffffffe168) at main.c:39

@ctrlcctrlv
Copy link
Member Author

ctrlcctrlv commented Feb 10, 2019

I realize this is not my blog, but I tried to remove wam_restrict from every wattrs.mask in cvgetinfo.c, but I think that just made everything worse. :O)

Will revert and try other things, but I should admit clearly that I am far from experienced with gdraw and have no idea why this crash occurs.

#0  0x0000155555169c63 in CVDrawSplineSetOutlineOnly
    (cv=0x55555588fd60, pixmap=0x55555591b6d0, set=0x55555595f100, fg=0, dopoints=1, clip=0x7fffffffb400, strokeFillMode=sfm_stroke) at charview.c:1564
#1  0x000015555516a8fd in CVDrawSplineSetSpecialized
    (cv=0x55555588fd60, pixmap=0x55555591b6d0, set=0x55555595f100, fg=0, dopoints=1, clip=0x7fffffffb400, strokeFillMode=sfm_stroke, AlphaChannelOverride=0)
    at charview.c:1700
#2  0x000015555516aca7 in CVDrawLayerSplineSet
    (cv=0x55555588fd60, pixmap=0x55555591b6d0, layer=0x555555838cc0, fg=0, dopoints=1, clip=0x7fffffffb400, strokeFillMode=sfm_stroke) at charview.c:1763
#3  0x0000155555173662 in CVExpose (cv=0x55555588fd60, pixmap=0x55555591b6d0, event=0x7fffffffb6a0) at charview.c:3028
#4  0x000015555517fac1 in v_e_h (gw=0x55555591b6d0, event=0x7fffffffb6a0) at charview.c:5749
#5  0x0000155554ff8e69 in _GWidget_Container_eh (gw=0x55555591b6d0, event=0x7fffffffb6a0) at gcontainer.c:279
#6  0x0000155555075d01 in _GGDKDraw_CallEHChecked (gw=0x55555591b6d0, event=0x7fffffffb6a0, eh=0x155554ff8cb3 <_GWidget_Container_eh>) at ggdkdraw.c:335
#7  0x000015555507846d in _GGDKDraw_DispatchEvent (event=0x7fffffffb740, data=0x555555644670) at ggdkdraw.c:1203
#8  0x000015555417fae6 in  () at /usr/lib/libgdk-3.so.0
#9  0x0000155554190720 in  () at /usr/lib/libgdk-3.so.0
#10 0x0000155554190697 in  () at /usr/lib/libgdk-3.so.0
#11 0x00001555541918f1 in  () at /usr/lib/libgdk-3.so.0
#12 0x0000155554191ab5 in  () at /usr/lib/libgdk-3.so.0
#13 0x00001555536923c5 in g_closure_invoke () at /usr/lib/libgobject-2.0.so.0
#14 0x000015555367f195 in  () at /usr/lib/libgobject-2.0.so.0
#15 0x000015555368301e in g_signal_emit_valist () at /usr/lib/libgobject-2.0.so.0
#16 0x0000155553683a80 in g_signal_emit () at /usr/lib/libgobject-2.0.so.0
#17 0x00001555541894ce in  () at /usr/lib/libgdk-3.so.0
#18 0x0000155554173b7b in  () at /usr/lib/libgdk-3.so.0
#19 0x00001555535aa1c3 in  () at /usr/lib/libglib-2.0.so.0
#20 0x00001555535aa8d1 in g_main_context_dispatch () at /usr/lib/libglib-2.0.so.0
#21 0x00001555535ac5e9 in  () at /usr/lib/libglib-2.0.so.0
#22 0x00001555535ac62e in g_main_context_iteration () at /usr/lib/libglib-2.0.so.0
#23 0x000015555507af6a in GGDKDrawEventLoop (gdisp=0x555555644670) at ggdkdraw.c:2168
#24 0x0000155554ffe486 in GDrawEventLoop (gdisp=0x555555644670) at gdraw.c:787
#25 0x000015555539c7d3 in fontforge_main (argc=1, argv=0x7fffffffe168) at startui.c:1389
#26 0x0000555555555159 in main (argc=1, argv=0x7fffffffe168) at main.c:39

@ctrlcctrlv
Copy link
Member Author

Paging @jtanx , who perhaps can lend a hand

@skef
Copy link
Contributor

skef commented Feb 10, 2019

One wrinkle here is whether closing the dialog using the top controls should be equivalent to "OK" or "Cancel". It might be preferable to request that the window be displayed without those controls, as long as there is still some evident way to drag it.

@ctrlcctrlv
Copy link
Member Author

@skef I think it should be equal to OK (PI_SpiroOk?)

@ctrlcctrlv
Copy link
Member Author

I also have another change that unfortunately doesn't work. I hope that posting what doesn't work will help us find what does work.

diff --git a/fontforgeexe/cvgetinfo.c b/fontforgeexe/cvgetinfo.c
index 200be05af..d221ef01d 100644
--- a/fontforgeexe/cvgetinfo.c
+++ b/fontforgeexe/cvgetinfo.c
@@ -3345,6 +3345,9 @@ static void PointGetInfo(CharView *cv, SplinePoint *sp, SplinePointList *spl) {
                    GGadgetScale(GDrawPointsToPixels(NULL,PI_Width)),
                    GDrawPointsToPixels(NULL,PI_Height));
        GDrawSetVisible(gi->gw,true);
+    while ( !gi->done )
+       GDrawProcessOneEvent(NULL);
+    GDrawDestroyWindow(gi->gw);
 }
 
 /* ************************************************************************** */

Doesn't work, results in same (or similar) crash as #3437 (comment)

@ctrlcctrlv
Copy link
Member Author

This one had such promise, but results in same crash as #3437 (comment)

Running out of ideas.

diff --git a/fontforgeexe/cvgetinfo.c b/fontforgeexe/cvgetinfo.c
index 200be05af..46d5299cf 100644
--- a/fontforgeexe/cvgetinfo.c
+++ b/fontforgeexe/cvgetinfo.c
@@ -3752,6 +3752,7 @@ static void SpiroPointGetInfo(CharView *cv, spiro_cp *scp, SplinePointList *spl)
     GDrawSetVisible(gip->gw,true);
     while ( !gip->done )
        GDrawProcessOneEvent(NULL);
+    GDrawDestroyWindow(gip->gw);
 }
 
 void CVGetInfo(CharView *cv) {

@jtanx
Copy link
Contributor

jtanx commented Feb 10, 2019

Generally clicking 'x' is akin to cancelling the dialogue. I can repro.

@ctrlcctrlv
Copy link
Member Author

I would say that "X" should be interpreted as "OK" in this case, as someone who both uses Spiro and designs fonts in FontForge.

@skef
Copy link
Contributor

skef commented Feb 10, 2019

The relevant contrast is how PI_DoCancel is being called. Here is the trace in the Spiro Get Info dialog when you press the actual cancel button:

#0  0x00007ffff7c4e9ba in PI_DoCancel (ci=0x555556bad310) at cvgetinfo.c:1744
#1  0x00007ffff7c4f317 in PI_Cancel (g=0x555556c1d450, e=0x7fffffffa7b0) at cvgetinfo.c:1880
#2  0x00007ffff7a61067 in GButtonInvoked (b=0x555556c1d450, ev=0x7fffffffaa60) at gbuttons.c:255
#3  0x00007ffff7a61fef in gbutton_mouse (g=0x555556c1d450, event=0x7fffffffaa60) at gbuttons.c:483
#4  0x00007ffff7a6c52b in _GWidget_Container_eh (gw=0x555556c1bab0, event=0x7fffffffaa60) at gcontainer.c:302
#5  0x00007ffff7a6e39e in _GWidget_TopLevel_eh (gw=0x555556c1bab0, event=0x7fffffffaa60) at gcontainer.c:745
#6  0x00007ffff7aecdb9 in dispatchEvent (gdisp=0x55555562d1c0, event=0x7fffffffac10) at gxdraw.c:3293
#7  0x00007ffff7aecf62 in GXDrawProcessOneEvent (gdisp=0x55555562d1c0) at gxdraw.c:3325
#8  0x00007ffff7a71823 in GDrawProcessOneEvent (gdisp=0x55555562d1c0) at gdraw.c:767
#9  0x00007ffff7c5dff6 in SpiroPointGetInfo (cv=0x555556cfd5a0, scp=0x555556c7b100, spl=0x555556ce01a0)
    at cvgetinfo.c:3754
#10 0x00007ffff7c5e217 in CVPGetInfo (cv=0x555556cfd5a0) at cvgetinfo.c:3789
#11 0x00007ffff7c78f07 in CVPopupSelectInvoked (v=0x555556c0eab0, mi=0x555556bab7a0, e=0x7fffffffc190)
    at cvpalettes.c:3472
#12 0x00007ffff7a9e976 in gmenu_mouse (m=0x555556babc20, event=0x7fffffffc190) at gmenu.c:943
#13 0x00007ffff7aa0425 in gmenu_eh (w=0x555556baa770, ge=0x7fffffffc190) at gmenu.c:1381
#14 0x00007ffff7aecdb9 in dispatchEvent (gdisp=0x55555562d1c0, event=0x7fffffffc340) at gxdraw.c:3293
#15 0x00007ffff7aed362 in GXDrawEventLoop (gd=0x55555562d1c0) at gxdraw.c:3401
#16 0x00007ffff7a71906 in GDrawEventLoop (gdisp=0x55555562d1c0) at gdraw.c:787
#17 0x00007ffff7e0f36a in fontforge_main (argc=2, argv=0x7fffffffe668) at startui.c:1389
#18 0x0000555555555159 in main (argc=2, argv=0x7fffffffe668) at main.c:39

And here it is when you press the "close" decoration:

#0  0x00007ffff7c4e9ba in PI_DoCancel (ci=0x555556bad310) at cvgetinfo.c:1744
#1  0x00007ffff7c4eb05 in pi_e_h (gw=0x555556bacea0, event=0x7fffffffaa60) at cvgetinfo.c:1762
#2  0x00007ffff7a6cde6 in _GWidget_Container_eh (gw=0x555556bacea0, event=0x7fffffffaa60) at gcontainer.c:403
#3  0x00007ffff7a6e39e in _GWidget_TopLevel_eh (gw=0x555556bacea0, event=0x7fffffffaa60) at gcontainer.c:745
#4  0x00007ffff7aecdb9 in dispatchEvent (gdisp=0x55555562d1c0, event=0x7fffffffac10) at gxdraw.c:3293
#5  0x00007ffff7aecf62 in GXDrawProcessOneEvent (gdisp=0x55555562d1c0) at gxdraw.c:3325
#6  0x00007ffff7a71823 in GDrawProcessOneEvent (gdisp=0x55555562d1c0) at gdraw.c:767
#7  0x00007ffff7c5dff6 in SpiroPointGetInfo (cv=0x555556cfd5a0, scp=0x555556babd90, spl=0x555556bad3d0)
    at cvgetinfo.c:3754
#8  0x00007ffff7c5e217 in CVPGetInfo (cv=0x555556cfd5a0) at cvgetinfo.c:3789
#9  0x00007ffff7c78f07 in CVPopupSelectInvoked (v=0x555556c0eab0, mi=0x555556c1cfe0, e=0x7fffffffc190)
    at cvpalettes.c:3472
#10 0x00007ffff7a9e976 in gmenu_mouse (m=0x555556bab560, event=0x7fffffffc190) at gmenu.c:943
#11 0x00007ffff7aa0425 in gmenu_eh (w=0x555556c1c030, ge=0x7fffffffc190) at gmenu.c:1381
#12 0x00007ffff7aecdb9 in dispatchEvent (gdisp=0x55555562d1c0, event=0x7fffffffc340) at gxdraw.c:3293
#13 0x00007ffff7aed362 in GXDrawEventLoop (gd=0x55555562d1c0) at gxdraw.c:3401
#14 0x00007ffff7a71906 in GDrawEventLoop (gdisp=0x55555562d1c0) at gdraw.c:787
#15 0x00007ffff7e0f36a in fontforge_main (argc=2, argv=0x7fffffffe668) at startui.c:1389
#16 0x0000555555555159 in main (argc=2, argv=0x7fffffffe668) at main.c:39

PI_Cancel is:

static int PI_Cancel(GGadget *g, GEvent *e) {
    if ( e->type==et_controlevent && e->u.control.subtype == et_buttonactivate ) {
        PI_DoCancel( GDrawGetUserData(GGadgetGetWindow(g)));
        PI_Close(g);
    }
return( true );
}

So the nature of the problem is fairly clear: the function that does the cleanup of the window data (PI_DoCancel) is being called while the function that actually closes the window isn't. On the next try the state is corrupted. The remaining question is why the plumbing is arranged this way.

@ctrlcctrlv
Copy link
Member Author

Are both of those crashes, or breakpoints? Because pressing the actual cancel button causes no crash for me.

@jtanx
Copy link
Contributor

jtanx commented Feb 10, 2019

When you click cancel, it goes through here PI_Cancel: https://github.com/fontforge/fontforge/blob/master/fontforgeexe/cvgetinfo.c#L1880-L1881

When you click the close button, it routes through the et_close event:
https://github.com/fontforge/fontforge/blob/master/fontforgeexe/cvgetinfo.c#L1762

Note it doesn't call PI_Destroy (which apparently causes the window to close)

Not entirely sure how the d->nonmodal clause is activated.

@ctrlcctrlv
Copy link
Member Author

d->nonmodal, whatever it is, seems important, because trying to remove it results in a new crash:

diff --git a/fontforgeexe/cvgetinfo.c b/fontforgeexe/cvgetinfo.c
index 200be05af..b0462fcf0 100644
--- a/fontforgeexe/cvgetinfo.c
+++ b/fontforgeexe/cvgetinfo.c
@@ -1756,11 +1756,7 @@ static void PI_DoCancel(GIData *ci) {
 static int pi_e_h(GWindow gw, GEvent *event) {
     if ( event->type==et_close ) {
        GIData  *d = GDrawGetUserData(gw);
-       if( d->nonmodal ) {
-           PI_Destroy((struct dlistnode *)d);
-       } else {
-           PI_DoCancel( GDrawGetUserData(gw));
-       }
+    PI_Destroy((struct dlistnode *)d);
     } else if ( event->type==et_char ) {
        if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
            help("getinfo.html");
Program received signal SIGSEGV, Segmentation fault.
0x0000155555167ac0 in DrawSpiroPoint (cv=0x555555890700, 
    pixmap=0x55555591bf80, cp=0x18, spl=0x55555596ecb0, cp_i=1, 
    AlphaChannelOverride=0) at charview.c:1170
1170        char ty = cp->ty&0x7f;
(gdb) bt
#0  0x0000155555167ac0 in DrawSpiroPoint (cv=0x555555890700, pixmap=0x55555591bf80, cp=0x18, spl=0x55555596ecb0, cp_i=1, AlphaChannelOverride=0) at charview.c:1170
#1  0x000015555516a758 in CVDrawSplineSetSpecialized
    (cv=0x555555890700, pixmap=0x55555591bf80, set=0x55555596ecb0, fg=0, dopoints=1, clip=0x7fffffffb410, strokeFillMode=sfm_stroke, AlphaChannelOverride=0)
    at charview.c:1669
#2  0x000015555516aca7 in CVDrawLayerSplineSet
    (cv=0x555555890700, pixmap=0x55555591bf80, layer=0x555555838010, fg=0, dopoints=1, clip=0x7fffffffb410, strokeFillMode=sfm_stroke) at charview.c:1763
#3  0x0000155555173662 in CVExpose (cv=0x555555890700, pixmap=0x55555591bf80, event=0x7fffffffb6b0) at charview.c:3028
#4  0x000015555517fac1 in v_e_h (gw=0x55555591bf80, event=0x7fffffffb6b0) at charview.c:5749
#5  0x0000155554ff8e69 in _GWidget_Container_eh (gw=0x55555591bf80, event=0x7fffffffb6b0) at gcontainer.c:279
#6  0x0000155555075d01 in _GGDKDraw_CallEHChecked (gw=0x55555591bf80, event=0x7fffffffb6b0, eh=0x155554ff8cb3 <_GWidget_Container_eh>) at ggdkdraw.c:335
#7  0x000015555507846d in _GGDKDraw_DispatchEvent (event=0x7fffffffb750, data=0x555555643d70) at ggdkdraw.c:1203
#8  0x000015555417fae6 in  () at /usr/lib/libgdk-3.so.0
#9  0x0000155554190720 in  () at /usr/lib/libgdk-3.so.0
#10 0x0000155554190697 in  () at /usr/lib/libgdk-3.so.0
#11 0x00001555541918f1 in  () at /usr/lib/libgdk-3.so.0
#12 0x0000155554191ab5 in  () at /usr/lib/libgdk-3.so.0
#13 0x00001555536923c5 in g_closure_invoke () at /usr/lib/libgobject-2.0.so.0
#14 0x000015555367f195 in  () at /usr/lib/libgobject-2.0.so.0
#15 0x000015555368301e in g_signal_emit_valist () at /usr/lib/libgobject-2.0.so.0
#16 0x0000155553683a80 in g_signal_emit () at /usr/lib/libgobject-2.0.so.0
#17 0x00001555541894ce in  () at /usr/lib/libgdk-3.so.0
#18 0x0000155554173b7b in  () at /usr/lib/libgdk-3.so.0
#19 0x00001555535aa1c3 in  () at /usr/lib/libglib-2.0.so.0
#20 0x00001555535aa8d1 in g_main_context_dispatch () at /usr/lib/libglib-2.0.so.0
#21 0x00001555535ac5e9 in  () at /usr/lib/libglib-2.0.so.0
#22 0x00001555535ac62e in g_main_context_iteration () at /usr/lib/libglib-2.0.so.0
#23 0x000015555507af6a in GGDKDrawEventLoop (gdisp=0x555555643d70) at ggdkdraw.c:2168
#24 0x0000155554ffe486 in GDrawEventLoop (gdisp=0x555555643d70) at gdraw.c:787
#25 0x000015555539c7d3 in fontforge_main (argc=1, argv=0x7fffffffe178) at startui.c:1389
#26 0x0000555555555159 in main (argc=1, argv=0x7fffffffe178) at main.c:39

PS. If what I'm doing is not helping feel free to tell me to stop.

@jtanx
Copy link
Contributor

jtanx commented Feb 10, 2019

Ok, I think I see, basically unconditionally calling PI_Destroy((struct dlistnode *)d); on et_close is probably sufficient, conditionally calling PI_DoCancel beforehand for modal dialogues.

nonmodal is true for point infos on non-spiro points - i.e. when you have a spiro info box open, you can't interact with the charview (modal) , but with non spiro points you can (non-modal).

@ctrlcctrlv
Copy link
Member Author

@jtanx I tried unconditionally calling it and still get a crash, see above :)

@jtanx
Copy link
Contributor

jtanx commented Feb 10, 2019

@ctrlcctrlv you need to leave in the PI_DoCancel beforehand.

What I said actually doesn't fully make sense, PI_Cancel is unconditionally called regardless of being modal or nonmodal, so not entirely sure why et_cancel checks for it.

@skef
Copy link
Contributor

skef commented Feb 10, 2019

Are both of those crashes, or breakpoints? Because pressing the actual cancel button causes no crash for me.

breakpoints

@ctrlcctrlv
Copy link
Member Author

@jtanx I don't get what you mean. Removing the condition still results in a crash—I tried both permutations, PI_Destroy first then PI_DoCancel, or PI_DoCancel then PI_Destroy, both result in identical crash.

diff --git a/fontforgeexe/cvgetinfo.c b/fontforgeexe/cvgetinfo.c
index 200be05af..809b3ec3f 100644
--- a/fontforgeexe/cvgetinfo.c
+++ b/fontforgeexe/cvgetinfo.c
@@ -1756,11 +1756,8 @@ static void PI_DoCancel(GIData *ci) {
 static int pi_e_h(GWindow gw, GEvent *event) {
     if ( event->type==et_close ) {
        GIData  *d = GDrawGetUserData(gw);
-       if( d->nonmodal ) {
-           PI_Destroy((struct dlistnode *)d);
-       } else {
-           PI_DoCancel( GDrawGetUserData(gw));
-       }
+    PI_DoCancel( GDrawGetUserData(gw));
+    PI_Destroy((struct dlistnode *)d);
     } else if ( event->type==et_char ) {
        if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
            help("getinfo.html");
Program received signal SIGSEGV, Segmentation fault.
0x000015555486a90e in CVRemoveTopUndo (cv=0x555555806550) at cvundoes.c:1138
1138        cv->layerheads[cv->drawmode]->undoes = undo->next;
(gdb) bt
#0  0x000015555486a90e in CVRemoveTopUndo (cv=0x555555806550) at cvundoes.c:1138
#1  0x00001555551d97b3 in PI_DoCancel (ci=0x555555739480) at cvgetinfo.c:1750
#2  0x00001555551d984c in pi_e_h (gw=0x555555944fa0, event=0x7fffffffbd40) at cvgetinfo.c:1762
#3  0x0000155554ff9966 in _GWidget_Container_eh (gw=0x555555944fa0, event=0x7fffffffbd40) at gcontainer.c:403
#4  0x0000155554ffaf1e in _GWidget_TopLevel_eh (gw=0x555555944fa0, event=0x7fffffffbd40) at gcontainer.c:745
#5  0x0000155555075d01 in _GGDKDraw_CallEHChecked (gw=0x555555944fa0, event=0x7fffffffbd40, eh=0x155554ffa590 <_GWidget_TopLevel_eh>) at ggdkdraw.c:335
#6  0x000015555507846d in _GGDKDraw_DispatchEvent (event=0x5555559148c0, data=0x555555645620) at ggdkdraw.c:1203
#7  0x000015555417fae6 in  () at /usr/lib/libgdk-3.so.0
#8  0x00001555541b1985 in  () at /usr/lib/libgdk-3.so.0
#9  0x00001555535aaa2f in g_main_context_dispatch () at /usr/lib/libglib-2.0.so.0
#10 0x00001555535ac5e9 in  () at /usr/lib/libglib-2.0.so.0
#11 0x00001555535ac62e in g_main_context_iteration () at /usr/lib/libglib-2.0.so.0
#12 0x000015555507af6a in GGDKDrawEventLoop (gdisp=0x555555645620) at ggdkdraw.c:2168
#13 0x0000155554ffe486 in GDrawEventLoop (gdisp=0x555555645620) at gdraw.c:787
#14 0x000015555539c7d3 in fontforge_main (argc=1, argv=0x7fffffffe168) at startui.c:1389
#15 0x0000555555555159 in main (argc=1, argv=0x7fffffffe168) at main.c:39

@ctrlcctrlv
Copy link
Member Author

In any event, the X should result in an OK and not a Cancel! 🙄

@ctrlcctrlv
Copy link
Member Author

Actually, I think the word "Cancel" should be replaced with "Revert", because as an actual user it's seldom used. This crash has bitten me many times and I've lost beautiful glyphs because of it. Making it undo my work on "X" would be the same as a crash, bad.

@jtanx
Copy link
Contributor

jtanx commented Feb 10, 2019

I'm going to leave this for tonight, but just some notes:

I had

    if ( event->type==et_close ) {
	GIData  *d = GDrawGetUserData(gw);
	if( !d->nonmodal ) {
	    PI_DoCancel(d);
	}
	PI_Destroy((struct dlistnode *)d);

Which wasn't crashing for me.

I think the reason why it's conditional is because of the behaviour you mention - for normal points, it doesn't revert when you click the close button, So to have that behaviour with Spiro points, you just don't call PI_DoCancel on the et_close event too, just PI_Destroy.

Not entirely sure what that means for cleaning up resources though.

@ctrlcctrlv
Copy link
Member Author

Here's what you wrote as a patch:

diff --git a/fontforgeexe/cvgetinfo.c b/fontforgeexe/cvgetinfo.c
index 200be05af..ae3949729 100644
--- a/fontforgeexe/cvgetinfo.c
+++ b/fontforgeexe/cvgetinfo.c
@@ -1757,10 +1757,9 @@ static int pi_e_h(GWindow gw, GEvent *event) {
     if ( event->type==et_close ) {
        GIData  *d = GDrawGetUserData(gw);
        if( d->nonmodal ) {
-           PI_Destroy((struct dlistnode *)d);
-       } else {
            PI_DoCancel( GDrawGetUserData(gw));
-       }
+    }
+    PI_Destroy((struct dlistnode *)d);
     } else if ( event->type==et_char ) {
        if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
            help("getinfo.html");

It crashes for me:

Program received signal SIGSEGV, Segmentation fault.
SplineFindBounds (sp=0x155553690940, bounds=0x7fffffffb720) at splineutil.c:551
551                 if ( sp->to->me.x<bounds->minx ) bounds->minx = sp->to->me.x;
(gdb) bt
#0  0x0000155554b11a84 in SplineFindBounds (sp=0x155553690940, bounds=0x7fffffffb720) at splineutil.c:551
#1  0x0000155554b12347 in _SplineSetFindClippedBounds (spl=0x55555592fde0, bounds=0x7fffffffb720, clipb=0x7fffffffb740) at splineutil.c:637
#2  0x0000155554b12b82 in _SplineCharLayerFindBounds (sc=0x5555558377b0, layer=1, bounds=0x7fffffffb830) at splineutil.c:700
#3  0x0000155554b13155 in SplineCharLayerFindBounds (sc=0x5555558377b0, layer=1, bounds=0x7fffffffb830) at splineutil.c:745
#4  0x00001555548bf262 in SplineCharFreeTypeRasterizeNoHints (sc=0x5555558377b0, layer=1, ptsize=107, dpi=72, depth=4) at freetype.c:989
#5  0x0000155554a925b0 in BDFPieceMeal (bdf=0x55555582f060, index=0) at splinefill.c:1697
#6  0x0000155554a92778 in BDFPieceMealCheck (bdf=0x55555582f060, index=0) at splinefill.c:1721
#7  0x000015555528e615 in FVRefreshChar (fv=0x5555557f3c70, gid=0) at fontview.c:5944
#8  0x000015555528e808 in FVRegenChar (fv=0x5555557f3c70, sc=0x5555558377b0) at fontview.c:5975
#9  0x0000155555174328 in SC_UpdateAll (sc=0x5555558377b0) at charview.c:3168
#10 0x00001555551d969a in PI_ShowHints (sc=0x5555558377b0, list=0x0, set=0) at cvgetinfo.c:1736
#11 0x00001555551d96f2 in _PI_ShowHints (ci=0x55555595c620, set=0) at cvgetinfo.c:1740
#12 0x00001555551d97d4 in PI_DoCancel (ci=0x55555595c620) at cvgetinfo.c:1752
#13 0x00001555551d984c in pi_e_h (gw=0x55555595e720, event=0x7fffffffbd40) at cvgetinfo.c:1762
#14 0x0000155554ff9966 in _GWidget_Container_eh (gw=0x55555595e720, event=0x7fffffffbd40) at gcontainer.c:403
#15 0x0000155554ffaf1e in _GWidget_TopLevel_eh (gw=0x55555595e720, event=0x7fffffffbd40) at gcontainer.c:745
#16 0x0000155555075d01 in _GGDKDraw_CallEHChecked (gw=0x55555595e720, event=0x7fffffffbd40, eh=0x155554ffa590 <_GWidget_TopLevel_eh>) at ggdkdraw.c:335
#17 0x000015555507846d in _GGDKDraw_DispatchEvent (event=0x5555555b42a0, data=0x555555643110) at ggdkdraw.c:1203
#18 0x000015555417fae6 in  () at /usr/lib/libgdk-3.so.0
#19 0x00001555541b1985 in  () at /usr/lib/libgdk-3.so.0
#20 0x00001555535aaa2f in g_main_context_dispatch () at /usr/lib/libglib-2.0.so.0
#21 0x00001555535ac5e9 in  () at /usr/lib/libglib-2.0.so.0
#22 0x00001555535ac62e in g_main_context_iteration () at /usr/lib/libglib-2.0.so.0
#23 0x000015555507af6a in GGDKDrawEventLoop (gdisp=0x555555643110) at ggdkdraw.c:2168
#24 0x0000155554ffe486 in GDrawEventLoop (gdisp=0x555555643110) at gdraw.c:787
#25 0x000015555539c7d3 in fontforge_main (argc=1, argv=0x7fffffffe168) at startui.c:1389
#26 0x0000555555555159 in main (argc=1, argv=0x7fffffffe168) at main.c:39

@skef
Copy link
Contributor

skef commented Feb 10, 2019

Can you try it on a fresh font file? That looks like a potentially unrelated crash.

@ctrlcctrlv
Copy link
Member Author

I click "New" each time.

@skef
Copy link
Contributor

skef commented Feb 10, 2019

Try running fontforge with a breakpoint on BDFPieceMealCheck and see if it gets called when just pressing the cancel button as opposed to the X decoration.

@ctrlcctrlv
Copy link
Member Author

proof.zip

@skef
Copy link
Contributor

skef commented Feb 10, 2019

@ctrlcctrlv : Your patch left out the ! on line 1759 (should be if( !d->nonmodal ) {).

@ctrlcctrlv
Copy link
Member Author

ctrlcctrlv commented Feb 10, 2019

Still crashes even with the forgotten exclam...sorry about that by the way, that's why I always communicate in patches, not snippets. :-)

diff --git a/fontforgeexe/cvgetinfo.c b/fontforgeexe/cvgetinfo.c
index 200be05af..3433e4463 100644
--- a/fontforgeexe/cvgetinfo.c
+++ b/fontforgeexe/cvgetinfo.c
@@ -1756,11 +1756,10 @@ static void PI_DoCancel(GIData *ci) {
 static int pi_e_h(GWindow gw, GEvent *event) {
     if ( event->type==et_close ) {
        GIData  *d = GDrawGetUserData(gw);
-       if( d->nonmodal ) {
-           PI_Destroy((struct dlistnode *)d);
-       } else {
-           PI_DoCancel( GDrawGetUserData(gw));
-       }
+       if( !d->nonmodal ) {
+           PI_DoCancel(d);
+    }
+    PI_Destroy((struct dlistnode *)d);
     } else if ( event->type==et_char ) {
        if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
            help("getinfo.html");
#0  0x000015555486a90e in CVRemoveTopUndo (cv=0x5555558091d0) at cvundoes.c:1138
#1  0x00001555551d97b3 in PI_DoCancel (ci=0x5555557d5480) at cvgetinfo.c:1750
#2  0x00001555551d984c in pi_e_h (gw=0x555555948010, event=0x7fffffffbd50) at cvgetinfo.c:1762
#3  0x0000155554ff9966 in _GWidget_Container_eh (gw=0x555555948010, event=0x7fffffffbd50) at gcontainer.c:403
#4  0x0000155554ffaf1e in _GWidget_TopLevel_eh (gw=0x555555948010, event=0x7fffffffbd50) at gcontainer.c:745
#5  0x0000155555075d01 in _GGDKDraw_CallEHChecked (gw=0x555555948010, event=0x7fffffffbd50, eh=0x155554ffa590 <_GWidget_TopLevel_eh>) at ggdkdraw.c:335
#6  0x000015555507846d in _GGDKDraw_DispatchEvent (event=0x5555555b5700, data=0x555555646100) at ggdkdraw.c:1203
#7  0x000015555417fae6 in  () at /usr/lib/libgdk-3.so.0
#8  0x00001555541b1985 in  () at /usr/lib/libgdk-3.so.0
#9  0x00001555535aaa2f in g_main_context_dispatch () at /usr/lib/libglib-2.0.so.0
#10 0x00001555535ac5e9 in  () at /usr/lib/libglib-2.0.so.0
#11 0x00001555535ac62e in g_main_context_iteration () at /usr/lib/libglib-2.0.so.0
#12 0x000015555507af6a in GGDKDrawEventLoop (gdisp=0x555555646100) at ggdkdraw.c:2168
#13 0x0000155554ffe486 in GDrawEventLoop (gdisp=0x555555646100) at gdraw.c:787
#14 0x000015555539c7d3 in fontforge_main (argc=1, argv=0x7fffffffe178) at startui.c:1389
#15 0x0000555555555159 in main (argc=1, argv=0x7fffffffe178) at main.c:39

@skef
Copy link
Contributor

skef commented Feb 10, 2019

If you save as an sfd before opening the dialog, and then open fontforge with that file and immediately open the dialog on the middle point, does it reproduce? If so, can you attach the file you created in the earlier step?

@ctrlcctrlv
Copy link
Member Author

KDE/Kwin

@skef
Copy link
Contributor

skef commented Feb 11, 2019

Plasma?

@ctrlcctrlv
Copy link
Member Author

Yes

@skef
Copy link
Contributor

skef commented Feb 11, 2019

Huh, so our systems are unusually close, then.

Would it be possible to determine if PI_DoCancel is being called twice? That would be useful.

@ctrlcctrlv
Copy link
Member Author

I set a breakpoint just now but it never triggers. The first time I close the dialog, nothing appears to happen. The second time, as above, the crash occurs in PI_DoCancel, PI_Destroy is never called…

@skef
Copy link
Contributor

skef commented Feb 11, 2019

OK, so you're still seeing the dialog not close on the first try. Hmm. There are straightforward ways of tracking this down but it will take a little bit of research. Let's leave it there for now, I'll try to update later with a "strategy" and things can go from there.

@ctrlcctrlv
Copy link
Member Author

As @jtanx asked, I just did this:

[fred@pc fontforge]$ gdb /home/fred/Workspace/fontforge/fontforgeexe/.libs/fontforge 
GNU gdb (GDB) 8.2.1
Copyright (C) 2018 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from /home/fred/Workspace/fontforge/fontforgeexe/.libs/fontforge...done.
(gdb) break cvgetinfo.c:1758
No source file named cvgetinfo.c.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (cvgetinfo.c:1758) pending.
(gdb) run /tmp/Untitled1.sfd 
Starting program: /home/fred/Workspace/fontforge/fontforgeexe/.libs/fontforge /tmp/Untitled1.sfd
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".
Copyright (c) 2000-2018 by George Williams. See AUTHORS for Contributors.
 License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
 with many parts BSD <http://fontforge.org/license.html>. Please read LICENSE.
 Based on sources from 13:37 UTC  1-Feb-2019-ML-TtfDb-D.
 Based on source from git with hash: c96d3eb78197aeb1447a8957b37b687398a1b529
PythonUI_Init()
copyUIMethodsToBaseTable()
copyUIMethodsToBaseTable()
Failed to open hotkey definition file: /home/fred/Workspace/fontforge/fontforgeexe/share/fontforge/hotkeys/default
Failed to open resource file: /home/fred/Workspace/fontforge/fontforgeexe/share/fontforge/pixmaps/resources
XXXXXX as:0  ds:0
XXXXXX h:0
tool:0 pointer:1 ctl:0 alt:0

Breakpoint 1, pi_e_h (gw=0x55555592aa40, event=0x7fffffffa650) at cvgetinfo.c:1758
warning: Source file is more recent than executable.
1758            GIData  *d = GDrawGetUserData(gw);
(gdb) continue
Continuing.

Breakpoint 1, pi_e_h (gw=0x55555592aa40, event=0x7fffffffbd40) at cvgetinfo.c:1758
1758            GIData  *d = GDrawGetUserData(gw);
(gdb) continue
Continuing.

Program received signal SIGSEGV, Segmentation fault.
0x000015555486a90e in CVRemoveTopUndo (cv=0x555555811ff0) at cvundoes.c:1138
1138        cv->layerheads[cv->drawmode]->undoes = undo->next;

The first time, I press X, nothing seems to happen, the UI changes not at all, only breakpoint triggers. The second time, crash!

@skef
Copy link
Contributor

skef commented Feb 11, 2019

Oh, no, wait this is very simple. In your patch you've replaced PI_DoCancel( GDrawGetUserData(gw)); with PI_DoCancel(d);.

Added: No, I'm wrong, those should be the same.

@ctrlcctrlv
Copy link
Member Author

@skef Uh, so did @jtanx - #3437 (comment)

I really would appreciate if in the future we all communicated in standard patch format rather than fallible humans copying and pasting code (or rewriting it) 😉

@ctrlcctrlv
Copy link
Member Author

Could this possibly have something to do with it? Is my invocation of gdb wrong?

warning: Source file is more recent than executable.

@ctrlcctrlv
Copy link
Member Author

Perhaps it's loading the libraries from the system FontForge while running the local binary...just a wild guess.

@ctrlcctrlv
Copy link
Member Author

Even if I do make && gdb ... I still get this warning.

@skef
Copy link
Contributor

skef commented Feb 11, 2019

Those warnings are pretty common. But it couldn't hurt to "make clean" and do a fresh build.

If you need to confirm that the tests are running the compiled code you can always add something that just won't work. However, you've already seen different symptoms from different code combinations so that's less likely.

@ctrlcctrlv
Copy link
Member Author

OK, yeah if that's all I need to do to confirm then yes I'm using the right code because I put "Q" in a few strings in the dialog so I'd know which I'm using. (I stash those silly changes before diffing, hehe.)

@skef
Copy link
Contributor

skef commented Feb 11, 2019

In that case, what's the complete diff from the current master that you're testing with?

@ctrlcctrlv
Copy link
Member Author

If this is enough to cause problems, we all should give up because FontForge is far too brittle for sane men to ever work on :o)

diff --git a/fontforgeexe/cvgetinfo.c b/fontforgeexe/cvgetinfo.c
index 200be05af..5c9c9b6d0 100644
--- a/fontforgeexe/cvgetinfo.c
+++ b/fontforgeexe/cvgetinfo.c
@@ -1756,11 +1756,10 @@ static void PI_DoCancel(GIData *ci) {
 static int pi_e_h(GWindow gw, GEvent *event) {
     if ( event->type==et_close ) {
 	GIData  *d = GDrawGetUserData(gw);
-	if( d->nonmodal ) {
-	    PI_Destroy((struct dlistnode *)d);
-	} else {
-	    PI_DoCancel( GDrawGetUserData(gw));
-	}
+	if( !d->nonmodal ) {
+	    PI_DoCancel(d);
+    }
+    PI_Destroy((struct dlistnode *)d);
     } else if ( event->type==et_char ) {
 	if ( event->u.chr.keysym == GK_F1 || event->u.chr.keysym == GK_Help ) {
 	    help("getinfo.html");
@@ -3240,7 +3239,7 @@ static void PointGetInfo(CharView *cv, SplinePoint *sp, SplinePointList *spl) {
 /* Why 3? */
 	mgcd[j].gd.pos.x = mgcd[j-2].gd.pos.x-50+3; mgcd[j].gd.pos.y = mgcd[j-1].gd.pos.y+26;
 	mgcd[j].gd.flags = gg_visible | gg_enabled;
-	mlabel[j].text = (unichar_t *) _("Prev On Contour");
+	mlabel[j].text = (unichar_t *) _("QPrev On Contour");
 	mlabel[j].text_is_1byte = true;
 	mgcd[j].gd.label = &mlabel[j];
 	mgcd[j].gd.cid = CID_PrevC;
@@ -3250,7 +3249,7 @@ static void PointGetInfo(CharView *cv, SplinePoint *sp, SplinePointList *spl) {
 
 	mgcd[j].gd.pos.x = mgcd[j-2].gd.pos.x; mgcd[j].gd.pos.y = mgcd[j-1].gd.pos.y;
 	mgcd[j].gd.flags = gg_visible | gg_enabled;
-	mlabel[j].text = (unichar_t *) _("Next On Contour");
+	mlabel[j].text = (unichar_t *) _("QNext On Contour");
 	mlabel[j].text_is_1byte = true;
 	mgcd[j].gd.label = &mlabel[j];
 	mgcd[j].gd.cid = CID_NextC;

@skef
Copy link
Contributor

skef commented Feb 11, 2019

Sometimes in these cases of weird differences it helps to follow up the possibilities without judgment. But I've just tried this exact code and I don't see the problem.

The next step is to find out specifically what's happening the first time you hit the close button. But I don't have a "recipe" for that at the moment.

@ctrlcctrlv
Copy link
Member Author

I just determined there's a difference between running gdb /home/fred/Workspace/fontforge/fontforgeexe/.libs/fontforge vs /home/fred/Workspace/fontforge/fontforgeexe/fontforge, although I'm not sure this difference is causing my problem. Some strings are appearing as expected, others are not. I am, as I suspected, testing a hybrid build. It's essentially impossible, as far as I can tell, to run my locally built FontForge in GDB via the (obnoxious, though perhaps needed for some reason) autotools created sh script, as GDB requires a binary, and giving it /bin/sh proved too difficult for me to figure out when I first started just running the one in .libs. If you know of a better way, which will actually work exactly the same, let me know - going to bed for now.

@ctrlcctrlv
Copy link
Member Author

Good news!

Right before dozing off I thought of some keywords I just had to try to search for. I found a solution to my problem, and that problem was causing the crash.

I confirm this patch works: #3437 (comment)

I believe this command, the proper invocation of gdb, should be added both to CONTRIBUTING.md and to here:

libtool --mode=execute gdb fontforgeexe/fontforge

Thank you so much to everyone who helped. When I wake up tomorrow I'll make documentation PR's.

@skef
Copy link
Contributor

skef commented Feb 11, 2019

I lazily avoid these problems by just make installing whatever I'm testing. I long ago made a PKGBUILD for a custom-built fontforge and just make sure that's "installed" whenever I'm testing. That way I can switch between Arch's Fontforge and "mine" with pacman.

Regardless, good to hear we have this pinned down.

ctrlcctrlv added a commit to ctrlcctrlv/fontforge that referenced this issue Feb 12, 2019
Clarify the correct way to debug FontForge to prevent a recurrence of fontforge#3437 (comment), a very subtle bug.
ctrlcctrlv pushed a commit to ctrlcctrlv/fontforge that referenced this issue Feb 12, 2019
ctrlcctrlv pushed a commit to ctrlcctrlv/fontforge that referenced this issue Feb 12, 2019
@jtanx
Copy link
Contributor

jtanx commented Feb 12, 2019

Right, yeah makes sense in retrospect, your backtraces were referencing the line numbers of the unmodified code. I also just go with make installing before running gdb, but that libtool command is neat, I'll remember that one.

This did however, highlight a bug in gdk - the way I got it to crash was to open the point info window, then go back to the font view, attempt to activate another charview, then try to close the point info. This screws up the undo state that the point info dialogue expects, but gxdraw disallows this.

@ctrlcctrlv
Copy link
Member Author

Fixed in ctrlcctrlv@42c95d4

Omnikron13 pushed a commit to Omnikron13/fontforge that referenced this issue May 31, 2022
Clarify the correct way to debug FontForge to prevent a recurrence of fontforge#3437 (comment), a very subtle bug.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants