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 crash when merging points #3569

Closed
ctrlcctrlv opened this issue Mar 23, 2019 · 24 comments · Fixed by #4141
Closed

Spiro crash when merging points #3569

ctrlcctrlv opened this issue Mar 23, 2019 · 24 comments · Fixed by #4141

Comments

@ctrlcctrlv
Copy link
Member

Arch Linux, 3aabfd2

Program received signal SIGABRT, Aborted.
0x00001555526f1d7f in raise () from /usr/lib/libc.so.6
(gdb) bt
#0  0x00001555526f1d7f in raise () at /usr/lib/libc.so.6
#1  0x00001555526dc672 in abort () at /usr/lib/libc.so.6
#2  0x0000155552734878 in __libc_message () at /usr/lib/libc.so.6
#3  0x000015555273b18a in  () at /usr/lib/libc.so.6
#4  0x000015555273ca26 in _int_free () at /usr/lib/libc.so.6
#5  0x0000155554b13161 in SplineFree (spline=0x555555a520b0) at splineutil.c:159
#6  0x0000155554b13464 in SplinePointsFree (spl=0x555555c542e0) at splineutil.c:226
#7  0x0000155554b134d9 in SplineSetBeziersClear (spl=0x555555c542e0) at splineutil.c:238
#8  0x0000155554a816ec in SSRegenerateFromSpiros (spl=0x555555c542e0) at spiro.c:286
#9  0x00001555551d04ce in CVMouseMoveSpiroPoint (cv=0x555555c731a0, p=0x7fffffffba50)
    at cvaddpoints.c:702
#10 0x00001555551d0559 in CVMouseMovePoint (cv=0x555555c731a0, p=0x7fffffffba50) at cvaddpoints.c:716
#11 0x00001555551992ae in CVMouseMove (cv=0x555555c731a0, event=0x7fffffffbcf0) at charview.c:5446
#12 0x000015555519a5e1 in v_e_h (gw=0x555555cb8920, event=0x7fffffffbcf0) at charview.c:5763
#13 0x0000155554fffde6 in _GWidget_Container_eh (gw=0x555555cb8920, event=0x7fffffffbcf0)
    at gcontainer.c:403
#14 0x000015555507fd89 in dispatchEvent (gdisp=0x55555562f0c0, event=0x7fffffffbea0) at gxdraw.c:3293
#15 0x0000155555080332 in GXDrawEventLoop (gd=0x55555562f0c0) at gxdraw.c:3401
#16 0x0000155555004906 in GDrawEventLoop (gdisp=0x55555562f0c0) at gdraw.c:787
#17 0x00001555553a2282 in fontforge_main (argc=2, argv=0x7fffffffe1c8) at startui.c:1396
#18 0x0000555555555159 in main (argc=2, argv=0x7fffffffe1c8) at main.c:39

Reproduction steps:

  1. Open Crash.zip
  2. Select point circled in red below image
    Screenshot_20190323_121426
  3. Select corner tool
  4. Attempt to connect with the point circled in blue by dragging a corner point up to it
  5. Crash!—with below CharView right before the crash helpfully frozen by gdb
    crash charview

Could be related to #3568 and by association #1004, but different backtrace.

@ctrlcctrlv
Copy link
Member Author

Another crash:

Program received signal SIGSEGV, Segmentation fault.
0x0000155554b13103 in LinearApproxFree (la=0x40707970a3d70a3d) at splineutil.c:150
150             next = la->next;
(gdb) bt
#0  0x0000155554b13103 in LinearApproxFree (la=0x40707970a3d70a3d) at splineutil.c:150
#1  0x0000155554b13155 in SplineFree (spline=0x555555d389c0) at splineutil.c:158
#2  0x0000155554b13464 in SplinePointsFree (spl=0x555555d01e70) at splineutil.c:226
#3  0x0000155554b134d9 in SplineSetBeziersClear (spl=0x555555d01e70) at splineutil.c:238
#4  0x0000155554a816ec in SSRegenerateFromSpiros (spl=0x555555d01e70) at spiro.c:286
#5  0x00001555551d04ce in CVMouseMoveSpiroPoint (cv=0x555555ac3f20, p=0x7fffffffba50)
    at cvaddpoints.c:702
#6  0x00001555551d0559 in CVMouseMovePoint (cv=0x555555ac3f20, p=0x7fffffffba50) at cvaddpoints.c:716
#7  0x00001555551992ae in CVMouseMove (cv=0x555555ac3f20, event=0x7fffffffbcf0) at charview.c:5446
#8  0x000015555519a5e1 in v_e_h (gw=0x555555b97fd0, event=0x7fffffffbcf0) at charview.c:5763
#9  0x0000155554fffde6 in _GWidget_Container_eh (gw=0x555555b97fd0, event=0x7fffffffbcf0)
    at gcontainer.c:403
#10 0x000015555507fd89 in dispatchEvent (gdisp=0x55555562f0c0, event=0x7fffffffbea0) at gxdraw.c:3293
#11 0x0000155555080332 in GXDrawEventLoop (gd=0x55555562f0c0) at gxdraw.c:3401
#12 0x0000155555004906 in GDrawEventLoop (gdisp=0x55555562f0c0) at gdraw.c:787
#13 0x00001555553a2282 in fontforge_main (argc=2, argv=0x7fffffffe1c8) at startui.c:1396
#14 0x0000555555555159 in main (argc=2, argv=0x7fffffffe1c8) at main.c:39

Unfortunately I don't have a file for this one, but the bt should help debug the code because I was making the same action when it happened.

@ctrlcctrlv
Copy link
Member Author

After working with this for a while, and experiencing around twenty crashes, I've come to an observation. It's more likely to happen if you drag two points together directly then if you drop the point, then select it with the arrow tool and drag it together.

@ctrlcctrlv
Copy link
Member Author

I spent three fruitless hours today trying to figure this out with no luck. Obviously, just commenting out SplineSetBeziersClear in SSRegenerateFromSpiros does the trick, but that is not acceptable to merge. 😩

I tried...

  • Using free instead of chunkfree everywhere
  • Adding checks for spline->to not being NULL @ splineutil.c:225
  • Changing order of a bunch of functions
  • Adding a bunch of printf to see if I could figure out what was going on

I think I'm not (yet) skilled enough to fix this. Someone better than me will have to do it.

@ctrlcctrlv
Copy link
Member Author

This crash has completely and utterly defeated me.

Today I thought, let's try Valgrind...I don't have much experience with it, but let's fire it up. So I did.

And FontForge DOESN'T CRASH WITH MY TEST CASE UNDER VALGRIND, BUT CRASHES WITH GDB AND REGULARLY.

I'm sorry to write in all caps, but that pill was hard to swallow, to put it mildly.

Valgrind pukes out a lot, here's a snippet:

==24219== Invalid read of size 4
==24219==    at 0x58B967B: ??? (in /usr/lib/libpython3.7m.so.1.0)
==24219==    by 0x5969CA5: ??? (in /usr/lib/libpython3.7m.so.1.0)
==24219==    by 0x596F564: ??? (in /usr/lib/libpython3.7m.so.1.0)
==24219==    by 0x596F430: ??? (in /usr/lib/libpython3.7m.so.1.0)
==24219==    by 0x5992E72: ??? (in /usr/lib/libpython3.7m.so.1.0)
==24219==    by 0x5995C6C: ??? (in /usr/lib/libpython3.7m.so.1.0)
==24219==    by 0x5997E38: PyAST_CompileObject (in /usr/lib/libpython3.7m.so.1.0)
==24219==    by 0x59FF678: ??? (in /usr/lib/libpython3.7m.so.1.0)
==24219==    by 0x5A00B6D: PyRun_FileExFlags (in /usr/lib/libpython3.7m.so.1.0)
==24219==    by 0x5A04034: PyRun_SimpleFileExFlags (in /usr/lib/libpython3.7m.so.1.0)
==24219==    by 0x52A336F: LoadFilesInPythonInitDir (python.c:19400)
==24219==    by 0x52A36BC: PyFF_ProcessInitFiles (python.c:19467)
==24219==  Address 0xb9cf020 is 0 bytes inside a block of size 704 free'd
==24219==    at 0x4839D7B: realloc (vg_replace_malloc.c:826)
==24219==    by 0x590CEA4: PyList_Append (in /usr/lib/libpython3.7m.so.1.0)
==24219==    by 0x590F0E1: PyArena_AddPyObject (in /usr/lib/libpython3.7m.so.1.0)
==24219==    by 0x590F187: ??? (in /usr/lib/libpython3.7m.so.1.0)
==24219==    by 0x599A782: ??? (in /usr/lib/libpython3.7m.so.1.0)
==24219==    by 0x599A7DC: ??? (in /usr/lib/libpython3.7m.so.1.0)
==24219==    by 0x599BE50: ??? (in /usr/lib/libpython3.7m.so.1.0)
==24219==    by 0x599A73F: ??? (in /usr/lib/libpython3.7m.so.1.0)
==24219==    by 0x599D178: ??? (in /usr/lib/libpython3.7m.so.1.0)
==24219==    by 0x599F232: PyAST_FromNodeObject (in /usr/lib/libpython3.7m.so.1.0)
==24219==    by 0x5A00A7D: PyParser_ASTFromFileObject (in /usr/lib/libpython3.7m.so.1.0)
==24219==    by 0x5A00B34: PyRun_FileExFlags (in /usr/lib/libpython3.7m.so.1.0)
==24219==  Block was alloc'd at
==24219==    at 0x4839D7B: realloc (vg_replace_malloc.c:826)
==24219==    by 0x590CEA4: PyList_Append (in /usr/lib/libpython3.7m.so.1.0)
==24219==    by 0x590F0E1: PyArena_AddPyObject (in /usr/lib/libpython3.7m.so.1.0)
==24219==    by 0x599A23F: ??? (in /usr/lib/libpython3.7m.so.1.0)
==24219==    by 0x599D178: ??? (in /usr/lib/libpython3.7m.so.1.0)
==24219==    by 0x599F232: PyAST_FromNodeObject (in /usr/lib/libpython3.7m.so.1.0)
==24219==    by 0x5A00A7D: PyParser_ASTFromFileObject (in /usr/lib/libpython3.7m.so.1.0)
==24219==    by 0x5A00B34: PyRun_FileExFlags (in /usr/lib/libpython3.7m.so.1.0)
==24219==    by 0x5A04034: PyRun_SimpleFileExFlags (in /usr/lib/libpython3.7m.so.1.0)
==24219==    by 0x52A336F: LoadFilesInPythonInitDir (python.c:19400)
==24219==    by 0x52A36BC: PyFF_ProcessInitFiles (python.c:19467)
==24219==    by 0x4B05C5E: fontforge_main (startui.c:1250)
==24219== 
==24219== Invalid read of size 8
==24219==    at 0x48F6F52: CVInfoDrawText (charview.c:4095)
==24219==    by 0x48F7851: CVInfoDraw (charview.c:4169)
==24219==    by 0x48FD115: CVMouseMove (charview.c:5415)
==24219==    by 0x48FE5D6: v_e_h (charview.c:5755)
==24219==    by 0x4CC595D: _GWidget_Container_eh (gcontainer.c:403)
==24219==    by 0x4D41D56: _GGDKDraw_CallEHChecked (ggdkdraw.c:336)
==24219==    by 0x4D44574: _GGDKDraw_DispatchEvent (ggdkdraw.c:1211)
==24219==    by 0x6492C85: ??? (in /usr/lib/libgdk-3.so.0.2404.3)
==24219==    by 0x64C4AB4: ??? (in /usr/lib/libgdk-3.so.0.2404.3)
==24219==    by 0x72DA7BE: g_main_context_dispatch (in /usr/lib/libglib-2.0.so.0.6000.0)
==24219==    by 0x72DC738: ??? (in /usr/lib/libglib-2.0.so.0.6000.0)
==24219==    by 0x72DC77D: g_main_context_iteration (in /usr/lib/libglib-2.0.so.0.6000.0)
==24219==  Address 0xd9a88c0 is 0 bytes inside a block of size 240 free'd
==24219==    at 0x4839D7B: realloc (vg_replace_malloc.c:826)
==24219==    by 0x49340BE: CVMergeSplineSets (cvaddpoints.c:658)
==24219==    by 0x49344B7: CVMouseMoveSpiroPoint (cvaddpoints.c:700)
==24219==    by 0x493454E: CVMouseMovePoint (cvaddpoints.c:716)
==24219==    by 0x48FD2A3: CVMouseMove (charview.c:5438)
==24219==    by 0x48FE5D6: v_e_h (charview.c:5755)
==24219==    by 0x4CC595D: _GWidget_Container_eh (gcontainer.c:403)
==24219==    by 0x4D41D56: _GGDKDraw_CallEHChecked (ggdkdraw.c:336)
==24219==    by 0x4D44574: _GGDKDraw_DispatchEvent (ggdkdraw.c:1211)
==24219==    by 0x6492C85: ??? (in /usr/lib/libgdk-3.so.0.2404.3)
==24219==    by 0x64C4AB4: ??? (in /usr/lib/libgdk-3.so.0.2404.3)
==24219==    by 0x72DA7BE: g_main_context_dispatch (in /usr/lib/libglib-2.0.so.0.6000.0)
==24219==  Block was alloc'd at
==24219==    at 0x48376AF: malloc (vg_replace_malloc.c:298)
==24219==    by 0x4839DE7: realloc (vg_replace_malloc.c:826)
==24219==    by 0x5314302: SFDGetSpiros (sfd.c:3903)
==24219==    by 0x53146A0: SFDGetSplineSet (sfd.c:3952)
==24219==    by 0x531C0E9: SFDGetChar (sfd.c:5577)
==24219==    by 0x532B174: SFD_GetFont (sfd.c:8836)
==24219==    by 0x532B859: SFD_Read (sfd.c:8929)
==24219==    by 0x532BA74: _SFDRead (sfd.c:8962)
==24219==    by 0x534D3AF: _ReadSplineFont (splinefont.c:1188)
==24219==    by 0x534E49B: ReadSplineFont (splinefont.c:1331)
==24219==    by 0x534E762: LoadSplineFont (splinefont.c:1389)
==24219==    by 0x516C6F1: ViewPostScriptFont (fontviewbase.c:1341)
==24219== 
==24219== Invalid read of size 8
==24219==    at 0x48F6F65: CVInfoDrawText (charview.c:4096)
==24219==    by 0x48F7851: CVInfoDraw (charview.c:4169)
==24219==    by 0x48FD115: CVMouseMove (charview.c:5415)
==24219==    by 0x48FE5D6: v_e_h (charview.c:5755)
==24219==    by 0x4CC595D: _GWidget_Container_eh (gcontainer.c:403)
==24219==    by 0x4D41D56: _GGDKDraw_CallEHChecked (ggdkdraw.c:336)
==24219==    by 0x4D44574: _GGDKDraw_DispatchEvent (ggdkdraw.c:1211)
==24219==    by 0x6492C85: ??? (in /usr/lib/libgdk-3.so.0.2404.3)
==24219==    by 0x64C4AB4: ??? (in /usr/lib/libgdk-3.so.0.2404.3)
==24219==    by 0x72DA7BE: g_main_context_dispatch (in /usr/lib/libglib-2.0.so.0.6000.0)
==24219==    by 0x72DC738: ??? (in /usr/lib/libglib-2.0.so.0.6000.0)
==24219==    by 0x72DC77D: g_main_context_iteration (in /usr/lib/libglib-2.0.so.0.6000.0)
==24219==  Address 0xd9a88c8 is 8 bytes inside a block of size 240 free'd
==24219==    at 0x4839D7B: realloc (vg_replace_malloc.c:826)
==24219==    by 0x49340BE: CVMergeSplineSets (cvaddpoints.c:658)
==24219==    by 0x49344B7: CVMouseMoveSpiroPoint (cvaddpoints.c:700)
==24219==    by 0x493454E: CVMouseMovePoint (cvaddpoints.c:716)
==24219==    by 0x48FD2A3: CVMouseMove (charview.c:5438)
==24219==    by 0x48FE5D6: v_e_h (charview.c:5755)
==24219==    by 0x4CC595D: _GWidget_Container_eh (gcontainer.c:403)
==24219==    by 0x4D41D56: _GGDKDraw_CallEHChecked (ggdkdraw.c:336)
==24219==    by 0x4D44574: _GGDKDraw_DispatchEvent (ggdkdraw.c:1211)
==24219==    by 0x6492C85: ??? (in /usr/lib/libgdk-3.so.0.2404.3)
==24219==    by 0x64C4AB4: ??? (in /usr/lib/libgdk-3.so.0.2404.3)
==24219==    by 0x72DA7BE: g_main_context_dispatch (in /usr/lib/libglib-2.0.so.0.6000.0)
==24219==  Block was alloc'd at
==24219==    at 0x48376AF: malloc (vg_replace_malloc.c:298)
==24219==    by 0x4839DE7: realloc (vg_replace_malloc.c:826)
==24219==    by 0x5314302: SFDGetSpiros (sfd.c:3903)
==24219==    by 0x53146A0: SFDGetSplineSet (sfd.c:3952)
==24219==    by 0x531C0E9: SFDGetChar (sfd.c:5577)
==24219==    by 0x532B174: SFD_GetFont (sfd.c:8836)
==24219==    by 0x532B859: SFD_Read (sfd.c:8929)
==24219==    by 0x532BA74: _SFDRead (sfd.c:8962)
==24219==    by 0x534D3AF: _ReadSplineFont (splinefont.c:1188)
==24219==    by 0x534E49B: ReadSplineFont (splinefont.c:1331)
==24219==    by 0x534E762: LoadSplineFont (splinefont.c:1389)
==24219==    by 0x516C6F1: ViewPostScriptFont (fontviewbase.c:1341)
==24219== 
==24219== Invalid read of size 8
==24219==    at 0x4934295: CVMouseMoveSpiroPoint (cvaddpoints.c:682)
==24219==    by 0x493454E: CVMouseMovePoint (cvaddpoints.c:716)
==24219==    by 0x48FD2A3: CVMouseMove (charview.c:5438)
==24219==    by 0x48FE5D6: v_e_h (charview.c:5755)
==24219==    by 0x4CC595D: _GWidget_Container_eh (gcontainer.c:403)
==24219==    by 0x4D41D56: _GGDKDraw_CallEHChecked (ggdkdraw.c:336)
==24219==    by 0x4D44574: _GGDKDraw_DispatchEvent (ggdkdraw.c:1211)
==24219==    by 0x6492C85: ??? (in /usr/lib/libgdk-3.so.0.2404.3)
==24219==    by 0x64C4AB4: ??? (in /usr/lib/libgdk-3.so.0.2404.3)
==24219==    by 0x72DA7BE: g_main_context_dispatch (in /usr/lib/libglib-2.0.so.0.6000.0)
==24219==    by 0x72DC738: ??? (in /usr/lib/libglib-2.0.so.0.6000.0)
==24219==    by 0x72DC77D: g_main_context_iteration (in /usr/lib/libglib-2.0.so.0.6000.0)
==24219==  Address 0xd9a8920 is 96 bytes inside a block of size 240 free'd
==24219==    at 0x4839D7B: realloc (vg_replace_malloc.c:826)
==24219==    by 0x49340BE: CVMergeSplineSets (cvaddpoints.c:658)
==24219==    by 0x49344B7: CVMouseMoveSpiroPoint (cvaddpoints.c:700)
==24219==    by 0x493454E: CVMouseMovePoint (cvaddpoints.c:716)
==24219==    by 0x48FD2A3: CVMouseMove (charview.c:5438)
==24219==    by 0x48FE5D6: v_e_h (charview.c:5755)
==24219==    by 0x4CC595D: _GWidget_Container_eh (gcontainer.c:403)
==24219==    by 0x4D41D56: _GGDKDraw_CallEHChecked (ggdkdraw.c:336)
==24219==    by 0x4D44574: _GGDKDraw_DispatchEvent (ggdkdraw.c:1211)
==24219==    by 0x6492C85: ??? (in /usr/lib/libgdk-3.so.0.2404.3)
==24219==    by 0x64C4AB4: ??? (in /usr/lib/libgdk-3.so.0.2404.3)
==24219==    by 0x72DA7BE: g_main_context_dispatch (in /usr/lib/libglib-2.0.so.0.6000.0)
==24219==  Block was alloc'd at
==24219==    at 0x48376AF: malloc (vg_replace_malloc.c:298)
==24219==    by 0x4839DE7: realloc (vg_replace_malloc.c:826)
==24219==    by 0x5314302: SFDGetSpiros (sfd.c:3903)
==24219==    by 0x53146A0: SFDGetSplineSet (sfd.c:3952)
==24219==    by 0x531C0E9: SFDGetChar (sfd.c:5577)
==24219==    by 0x532B174: SFD_GetFont (sfd.c:8836)
==24219==    by 0x532B859: SFD_Read (sfd.c:8929)
==24219==    by 0x532BA74: _SFDRead (sfd.c:8962)
==24219==    by 0x534D3AF: _ReadSplineFont (splinefont.c:1188)
==24219==    by 0x534E49B: ReadSplineFont (splinefont.c:1331)
==24219==    by 0x534E762: LoadSplineFont (splinefont.c:1389)
==24219==    by 0x516C6F1: ViewPostScriptFont (fontviewbase.c:1341)
==24219== 
==24219== Invalid read of size 8
==24219==    at 0x49342B5: CVMouseMoveSpiroPoint (cvaddpoints.c:682)
==24219==    by 0x493454E: CVMouseMovePoint (cvaddpoints.c:716)
==24219==    by 0x48FD2A3: CVMouseMove (charview.c:5438)
==24219==    by 0x48FE5D6: v_e_h (charview.c:5755)
==24219==    by 0x4CC595D: _GWidget_Container_eh (gcontainer.c:403)
==24219==    by 0x4D41D56: _GGDKDraw_CallEHChecked (ggdkdraw.c:336)
==24219==    by 0x4D44574: _GGDKDraw_DispatchEvent (ggdkdraw.c:1211)
==24219==    by 0x6492C85: ??? (in /usr/lib/libgdk-3.so.0.2404.3)
==24219==    by 0x64C4AB4: ??? (in /usr/lib/libgdk-3.so.0.2404.3)
==24219==    by 0x72DA7BE: g_main_context_dispatch (in /usr/lib/libglib-2.0.so.0.6000.0)
==24219==    by 0x72DC738: ??? (in /usr/lib/libglib-2.0.so.0.6000.0)
==24219==    by 0x72DC77D: g_main_context_iteration (in /usr/lib/libglib-2.0.so.0.6000.0)
==24219==  Address 0xd9a8928 is 104 bytes inside a block of size 240 free'd
==24219==    at 0x4839D7B: realloc (vg_replace_malloc.c:826)
==24219==    by 0x49340BE: CVMergeSplineSets (cvaddpoints.c:658)
==24219==    by 0x49344B7: CVMouseMoveSpiroPoint (cvaddpoints.c:700)
==24219==    by 0x493454E: CVMouseMovePoint (cvaddpoints.c:716)
==24219==    by 0x48FD2A3: CVMouseMove (charview.c:5438)
==24219==    by 0x48FE5D6: v_e_h (charview.c:5755)
==24219==    by 0x4CC595D: _GWidget_Container_eh (gcontainer.c:403)
==24219==    by 0x4D41D56: _GGDKDraw_CallEHChecked (ggdkdraw.c:336)
==24219==    by 0x4D44574: _GGDKDraw_DispatchEvent (ggdkdraw.c:1211)
==24219==    by 0x6492C85: ??? (in /usr/lib/libgdk-3.so.0.2404.3)
==24219==    by 0x64C4AB4: ??? (in /usr/lib/libgdk-3.so.0.2404.3)
==24219==    by 0x72DA7BE: g_main_context_dispatch (in /usr/lib/libglib-2.0.so.0.6000.0)
==24219==  Block was alloc'd at
==24219==    at 0x48376AF: malloc (vg_replace_malloc.c:298)
==24219==    by 0x4839DE7: realloc (vg_replace_malloc.c:826)
==24219==    by 0x5314302: SFDGetSpiros (sfd.c:3903)
==24219==    by 0x53146A0: SFDGetSplineSet (sfd.c:3952)
==24219==    by 0x531C0E9: SFDGetChar (sfd.c:5577)
==24219==    by 0x532B174: SFD_GetFont (sfd.c:8836)
==24219==    by 0x532B859: SFD_Read (sfd.c:8929)
==24219==    by 0x532BA74: _SFDRead (sfd.c:8962)
==24219==    by 0x534D3AF: _ReadSplineFont (splinefont.c:1188)
==24219==    by 0x534E49B: ReadSplineFont (splinefont.c:1331)
==24219==    by 0x534E762: LoadSplineFont (splinefont.c:1389)
==24219==    by 0x516C6F1: ViewPostScriptFont (fontviewbase.c:1341)
==24219== 

I hate to name drop you constantly, but I'm wondering if @skef, @jtanx maybe even @JoesCat have ever seen this before and have any advice. I need someone with more experience debugging C code to lend a hand or give me some advice.

@ctrlcctrlv
Copy link
Member Author

OK maybe I have a lead.

This patch leads to no crash:

diff --git a/fontforge/sfd.c b/fontforge/sfd.c
index b6030f035..060026f82 100644
--- a/fontforge/sfd.c
+++ b/fontforge/sfd.c
@@ -3900,7 +3900,7 @@ static void SFDGetSpiros(FILE *sfd,SplineSet *cur) {
     while ( fscanf(sfd,"%lg %lg %c", &cp.x, &cp.y, &cp.ty )==3 ) {
        if ( cur!=NULL ) {
            if ( cur->spiro_cnt>=cur->spiro_max )
-               cur->spiros = realloc(cur->spiros,(cur->spiro_max+=10)*sizeof(spiro_cp));
+               cur->spiros = realloc(cur->spiros,(cur->spiro_max+=1000)*sizeof(spiro_cp));
            cur->spiros[cur->spiro_cnt++] = cp;
        }
     }

I got the idea from studying the Valgrind output, but 1000 is completely arbitrary. But then again, so is 10!! What's the right number? Going to try to find out by studying more...

@ctrlcctrlv
Copy link
Member Author

Update: I have no idea what the right number is, but that line is definitely related to the crash. 😞 😭

@skef
Copy link
Contributor

skef commented Apr 6, 2019

The intention of the code is that the number doesn't matter as long as realloc succeeds. This shouldn't matter either, but what values do spiro_cnt and spiro_max have to start with? And can you verify the "incremental" realloc before the failure is successful (returns non-null)?

@ctrlcctrlv
Copy link
Member Author

ctrlcctrlv commented Apr 6, 2019

Both are initialized to zero. I know it's weird that this matters, I really do. The code has so many problems and is written so bizarrely, with use-after-frees required in SplinePointsFree. Rules are being broken all over the place. That 1000 stops the crash, and the crash doesn't even happen in Valgrind...

I did a

diff --git a/fontforge/sfd.c b/fontforge/sfd.c
index b6030f035..1efe7500e 100644
--- a/fontforge/sfd.c
+++ b/fontforge/sfd.c
@@ -3901,12 +3901,18 @@ static void SFDGetSpiros(FILE *sfd,SplineSet *cur) {
        if ( cur!=NULL ) {
            if ( cur->spiro_cnt>=cur->spiro_max )
                cur->spiros = realloc(cur->spiros,(cur->spiro_max+=10)*sizeof(spiro_cp));
+        if (cur->spiros == NULL) {
+            printf("Realloc failure");
+        }
            cur->spiros[cur->spiro_cnt++] = cp;
        }
     }
     if ( cur!=NULL && (cur->spiros[cur->spiro_cnt-1].ty&0x7f)!=SPIRO_END ) {
        if ( cur->spiro_cnt>=cur->spiro_max )
            cur->spiros = realloc(cur->spiros,(cur->spiro_max+=1)*sizeof(spiro_cp));
+        if (cur->spiros == NULL) {
+            printf("Realloc failure");
+        }
        memset(&cur->spiros[cur->spiro_cnt],0,sizeof(spiro_cp));
        cur->spiros[cur->spiro_cnt++].ty = SPIRO_END;
     }

Realloc failure is not printed with this code.

@skef
Copy link
Contributor

skef commented Apr 6, 2019

Oh, wait, do you know that the crash happens around that line. Because if not there's probably just a miscalculation downstream and the large amount of memory from the 1000 overcomes it.

@ctrlcctrlv
Copy link
Member Author

@skef Oh no, it doesn't crash here. It crashes at the free. But I know that changing this fixes it, so this line is at least related.

@JoesCat
Copy link
Contributor

JoesCat commented Apr 7, 2019

And FontForge DOESN'T CRASH WITH MY TEST CASE UNDER VALGRIND, BUT CRASHES WITH GDB AND REGULARLY.

I think Valgrind does a calloc().
The calloc() is probably hiding a problem with the code.
Spiros will internally estimate the number of codepoints based on angles to and from a code point.
This is done in spiros.c:solve_spiro where it dives into count_vec(compute_jinc( cp points)).
+10 and +1000 are basically arbritary values.

From our point of view, you treat libspiro as a "blackbox" and nobody really knows "how many" points there are, but you "do know" results from spiros comes-out through bezctx_ff.c, and you require space for:
+1 x,y point for bezctx_ff_moveto() (this starts a new spline set)
+1 x,y point for bezctx_ff_lineto()
+2 x,y points for bezctx_ff_quadto()
+3 x,y points for bezctx_ff_curveto()
Also realize that when you merge two spiros, you don't merge them, you should make a third spiro.
Creating a spiro starts in spiro.c:91..136 where you see bc=new_bezctx_ff().
Looking at the crash report for #1004 _CVMergeToLine() probably needs attention to be aware of this new spiro.

At the moment, I can't follow-up with this since my computer is needing to be updated, so I haven't been doing anything fontforge GUI related in the last couple months.

@ctrlcctrlv
Copy link
Member Author

@JoesCat Thanks a lot, you've given me a lot to think about. This is concerning:

From our point of view, you treat libspiro as a "blackbox" and nobody really knows "how many" points there are

To me, this sounds like, even though everything is working correctly, if my merged Spiro spline has more than ten points, it will crash? That's bad. Any idea how we can stop using a constant, or is raising it enough?

@JoesCat
Copy link
Contributor

JoesCat commented Apr 7, 2019 via email

@JoesCat
Copy link
Contributor

JoesCat commented Apr 7, 2019

...one more item to add.
The second spiro that starts with "{" has an arbritary angle that is created due to the location and angle of the next point on the second spiro. If you are joining spiros as lines, you'll be ok with angles, but if the second spiro is a curve, and you need to maintain that curve, you'll want to know the starting angle of the curve so that you can keep it, and you'll probably need to look at using "a" and "h" which is in libspiro HEAD, so the "{" would be replaced by "a" plus a "h" to maintain the same angle, or if the second point is "]", then this first point should be "[", and you'll need to adjust the first spiro accordingly, for example, if the prior point is "v", which it is now, you'll be ok going into "[" as the new point.

You may want to put your crash spiro as data in libspiro call-test and run it in verbose mode to try out different ideas. In summary, it's more complicated than a simple merge. Hope all this info helps.

@jtanx
Copy link
Contributor

jtanx commented Apr 10, 2019

I've been looking at this, and the spiro code is outright dodgy. spiros on the spline set is an array of spiro_cps, which can get realloc'ed as the number grows. Yet there's multiple places where a pointer is just held into a spiro_cp in that array - which can get invalidated whenever realloc is called - and where that pointer is never updated after the realloc. That's what valgrind is complaining about.

I'm not sure though if that's a red herring, since the actual segfault seems to be when freeing the spline and not the spiros. But what that earlier patch to increase the realloc size is doing is effectively making it much rarer that realloc will ever have to be called ever again on the spiros list, so maybe it is related.

@JoesCat
Copy link
Contributor

JoesCat commented Apr 11, 2019

Merging spline points is like pushing a rope. You don't want to be editing the results, what you want to do is edit the source information - The spline is supposed to be the result of the spiro calculations, so you should build a third spiro as the source for the new spiro.
Using Crash.zip as the example here, we already have a problem that one goes in the opposite direction of the other at the blue and red circles, but here's an approximation of where you want to go:

  Spiro
    540.099 114.84 {  <-blue circle
    559.272 90.8721 v
    405.881 -9.79004 v
    209.349 277.818 v
    357.945 507.905 v
    367.53 507.9 v
    434.642 469.558 o
    496.96 507.9 v
    516.13 507.9 v
    493.795 432.338 o
    0 0 z
  EndSpiro

  Spiro
    496.956 86.0791 { (0) <-red circle (we will reverse this spiro)
    300.424 378.481 v (1)
    310.01 397.65 v (2)
    401.087 364.101 o (3)
    0 0 z (4)
  EndSpiro

This might not look exactly the same, but may be close enough....
red circle moved to blue circle, we need to reverse one spiro, so I've reversed the second spiro here.
Also note, 'o'#3 is default, but is lost because there is no information to point 'z'#4, 'v'#2 was information to location 'o'#3, but we need to put the 'v'#2 on point 'o'#3 due to direction change, 'v'#1 is information going to 'v'#2, but due to direction change, 'v'#1 moves to location to the other point. The 'v' replaces '{' because fortunately it's a straight line to the next point, however, as a spiro, maybe '[' might be a better choice to add a curved merge instead.

  Spiro
    401.087 364.101 v (2)
    310.01 397.65 v (1)
    300.424 378.481 v (0)
    540.099 114.84 v or perhaps '[' might also be ok here <-red merged to blue circle
    559.272 90.8721 v
    405.881 -9.79004 v
    209.349 277.818 v
    357.945 507.905 v
    367.53 507.9 v
    434.642 469.558 o
    496.96 507.9 v
    516.13 507.9 v
    493.795 432.338 o
    0 0 z
  EndSpiro

...one more edit 'v' #2 becomes '{' since this is still an open spiro.

  Spiro
    401.087 364.101 {
    310.01 397.65 v (1)
    300.424 378.481 v (0)
    540.099 114.84 v or perhaps '[' might also be ok here <-red merged to blue circle
    559.272 90.8721 v
    405.881 -9.79004 v
    209.349 277.818 v
    357.945 507.905 v
    367.53 507.9 v
    434.642 469.558 o
    496.96 507.9 v
    516.13 507.9 v
    493.795 432.338 o
    0 0 z
  EndSpiro

@ctrlcctrlv
Copy link
Member Author

Thank you for all the help guys. I haven't forgotten this, I'm just working on another patchset to replace #3617, which I hope to PR in a few hours.

@jtanx
Copy link
Contributor

jtanx commented Apr 19, 2019

I don't think I'll be providing a quick solution to this any time soon, but just wanted to say - I'm pretty confident that this (and likely a few other spiro bugs) are caused by how that spiros array is handled (in particular that it can be resized with realloc, and that raw pointers are just held into that array - but which can be invalidated whenever a realloc occurs).

Fixing that will not be trivial.

@ctrlcctrlv
Copy link
Member Author

@jtanx Yeah, I've definitely seen. I might take a crack at it since I've got a few projects brewing that use Spiros.

@jtanx
Copy link
Contributor

jtanx commented Apr 21, 2019

It's pretty rough, but this is where I got with this - jtanx@468d11d and jtanx@0d70729

It avoids the crash and valgrind errors, but the behaviour's still pretty weird. Whenever you close a spiro path and continue moving the points around, for some reason it adds a (0,0) point like:
image
image

Although I sort of see similar behaviour on master. Maybe I'm just using it wrong 🤷

My change for lastselcp might be wrong too (since it won't pick up any side-effects of modifying the actual selected spiro point)

@JoesCat
Copy link
Contributor

JoesCat commented Apr 21, 2019

Looking at your screenshot, it's got curves so you seem to have a successful spiro solution (straight lines would be failure).

The (0,0) may be due to the spiro "z" which may be at the end of the string (0,0).
sending "z" to libspiro signifies to loop it as a closed spiro.

You may want to try and see if libspiro "HEAD" is different. It probably may behave better. I ran into scaling problems and you might be running into similar issues as your xmin..xmax ymin..ymax change.

It was a problem found when making one of the tests, and looking at the original spiro code, it appears spiro was really designed to work between -1..+1, not the -1000...+1000 or larger values we use in working with fonts. I made some modifications in libspiro that did rescaling in "Allow Spiros to be scaled and/or shifted. Scaling bug fixed."

@ctrlcctrlv
Copy link
Member Author

@jtanx I've never received a point at 0,0 using master, so am very curious how you did so. However, I have received such points numerous times while trying to fix this bug myself...😔

@skef
Copy link
Contributor

skef commented Jan 31, 2020

#3569 (comment) :

It avoids the crash and valgrind errors, but the behaviour's still pretty weird. Whenever you close a spiro path and continue moving the points around, for some reason it adds a (0,0) point like ...

Interesting. #4064 addressed a similar symptom but that was quadratic-conversion-specific, and was always a control point, while that's clearly a cubic splineset and there's an on-curve point there.

@JoesCat
Copy link
Contributor

JoesCat commented Feb 1, 2020

I was taking a closer look at libspiro to loop a,h (and also to see about resolving tests 10 and 11), and it appears there is a long standing bug since 2007.
As @ctrlcctrlv mentioned elsewhere, he couldn't start a spiro with V, and you can see this if you run libspiro/tests/call-test0 which start with V and ends in Z.

tests/call-test0
.....etc.....
test_mark_knot()_14
test_lineto(0,0)
test_mark_knot()_15
test_lineto(334,117)

I've held-off on releasing the bugfixed libspiro because it can use an improved 'cyclic' loop.

ctrlcctrlv added a commit to ctrlcctrlv/fontforge that referenced this issue Feb 7, 2020
ctrlcctrlv added a commit to ctrlcctrlv/fontforge that referenced this issue Feb 8, 2020
ctrlcctrlv added a commit that referenced this issue Mar 12, 2020
* Fix Spiro-related merge point crash

Closes #3569.

* Fix Spiro-related merge point crash

Closes #1004.
Omnikron13 pushed a commit to Omnikron13/fontforge that referenced this issue May 31, 2022
* Fix Spiro-related merge point crash

Closes fontforge#3569.

* Fix Spiro-related merge point crash

Closes fontforge#1004.
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

Successfully merging a pull request may close this issue.

4 participants