Bug 426025

Summary: kmplot crashed with any implicit functions include tangent
Product: [Applications] kmplot Reporter: gh_origin <gh_origin>
Component: generalAssignee: Christoph Feck <cfeck>
Status: ASSIGNED ---    
Severity: crash CC: gabriel.barrantes.dev, gilbertd+kde
Priority: NOR    
Version First Reported In: unspecified   
Target Milestone: ---   
Platform: FreeBSD Ports   
OS: FreeBSD   
Latest Commit: Version Fixed/Implemented In:
Sentry Crash Report:

Description gh_origin 2020-08-31 16:59:52 UTC
SUMMARY


STEPS TO REPRODUCE
1. FreeBSD 11.4-p2, install kmplot from pkg [quarterly repo]
2. Lauch kmplot and plot any implicit functions include tangent
3. For example: tan(x+y)=tan(x)+tan(y), hit enter

OBSERVED RESULT

The program crashed immediately and give a core dump

EXPECTED RESULT

It used to work fine with these functions

SOFTWARE/OS VERSIONS
Windows: 
macOS: 
Linux/KDE Plasma: 
(available in About System)
KDE Plasma Version: 
KDE Frameworks Version: 
Qt Version: 

ADDITIONAL INFORMATION

Check all of these information on freshports: https://www.freshports.org/math/kmplot/
Comment 1 Christoph Feck 2020-08-31 17:21:12 UTC
The example reproduces the crash, but isn't useful. Which actual implicit function did you want to plot that shows the crash?
Comment 2 gh_origin 2020-09-01 02:52:51 UTC
(In reply to Christoph Feck from comment #1)
> The example reproduces the crash, but isn't useful. Which actual implicit
> function did you want to plot that shows the crash?

What is useful I wonder? I found a website of good looking functions graphs of a Math prof and it contains many implicit functions. I tried to graph them using kmplot and many of them contain tangent and cotangent. It's a critical bug because any implicit functions that have tangent or cotangent caused kmplot to crash and core dump immediately.

Additional info: I downloaded ROSA Linux and use their KDE4 version. I could sure that the KDE4's kmplot could deal with these kind of functions without any problems. Only the current KDE5 based kmplot has trouble with them.
Comment 3 Christoph Feck 2020-09-10 09:12:30 UTC
Could you point me to this website?
Comment 4 Justin Zobel 2022-10-06 06:37:16 UTC
Thank you for reporting this crash in KDE software. As it has been a while since this issue was reported, can we please ask you to see if you can reproduce the crash with a recent software version?

If you can reproduce the issue, please change the status to "CONFIRMED" when replying. Thank you!
Comment 5 Christoph Feck 2022-10-06 07:17:44 UTC
Crash still reproducible with "tan(x+y)=tan(x)+tan(y)" implicit function.

(gdb) bt
#0  0x00007fffe80b702d in Parser::number (value=<error reading variable: Cannot access memory at address 0x3500330036002e>)
    at /usr/src/debug/kmplot-22.11.70git.20221003T020421~a69f00b-ku.6.1.x86_64/kmplot/parser.cpp:1171
#1  View::drawImplicit (this=<optimized out>, function=0x555556138470, painter=<optimized out>)
    at /usr/src/debug/kmplot-22.11.70git.20221003T020421~a69f00b-ku.6.1.x86_64/kmplot/view.cpp:1176
#2  0x00007fffe80bdb8e in View::draw (this=0x555555bed120, dev=<optimized out>, medium=<optimized out>)
    at /usr/src/debug/kmplot-22.11.70git.20221003T020421~a69f00b-ku.6.1.x86_64/kmplot/view.cpp:363
#3  0x00007fffe80aa70d in View::draw (medium=View::Screen, dev=0x555555bed2a8, this=0x555555bed120)
    at /usr/src/debug/kmplot-22.11.70git.20221003T020421~a69f00b-ku.6.1.x86_64/kmplot/view.cpp:2637
#4  View::drawPlot (this=0x555555bed120) at /usr/src/debug/kmplot-22.11.70git.20221003T020421~a69f00b-ku.6.1.x86_64/kmplot/view.cpp:2636
#5  0x00007fffe80986b6 in FunctionEditor::saveImplicit (this=0x55555588ed90)
    at /usr/src/debug/kmplot-22.11.70git.20221003T020421~a69f00b-ku.6.1.x86_64/kmplot/functioneditor.cpp:639
#6  0x00007ffff6712fcd in QtPrivate::QSlotObjectBase::call (a=0x7fffffffd1c0, r=0x55555588ed90, this=0x55555583faa0)
    at ../../include/QtCore/../../src/corelib/kernel/qobjectdefs_impl.h:398
#7  doActivate<false> (sender=0x55555583f6b0, signal_index=3, argv=0x7fffffffd1c0) at kernel/qobject.cpp:3919
#8  0x00007ffff670c44f in QMetaObject::activate (sender=<optimized out>, m=m@entry=0x7ffff69b39e0, 
    local_signal_index=local_signal_index@entry=0, argv=argv@entry=0x7fffffffd1c0) at kernel/qobject.cpp:3979
#9  0x00007ffff6716e0a in QTimer::timeout (this=<optimized out>, _t1=...) at .moc/moc_qtimer.cpp:205
Comment 6 Dave Gilbert 2023-04-13 23:47:32 UTC
still happens on current head ( a89ed5ddfea2ac96cfdce2bc4d1b49f268065edd ) - that pointer value is obviously complete garbage:
#0  0x00007fffdd0d6e1d in Parser::number(double) (value=Python Exception <class 'gdb.MemoryError'>: Cannot access memory at address 0x34003600360031

looks like someone is writing ascii where a pointer should be.
Comment 7 Dave Gilbert 2023-04-14 01:02:42 UTC
I think I kind of understand what's going on, but not 100% and hmm.

void View::drawImplicit(Function *function, QPainter *painter)
{
....
        QList<QPointF> singular;
...
        for (const QPointF &point : qAsConst(singular)) {
...
            for (double t : qAsConst(roots)) {
                double x = point.x() + epsilon * lcos(t);
                double y = point.y() + epsilon * lsin(t);
                drawImplicitInSquare(plot, painter, x, y, {}, &singular);
            }

so, 'singular' - which I think is a list being walked in the outer for, gets modified by 'drawImplicitInSquare' in a corner case - which I think is the 
fact tan is not-contiguous.

I think turning that loop into a QMutableListIterator might work.
Comment 8 Dave Gilbert 2023-04-14 01:20:53 UTC
Hmm the QMutableListIterator didn't work and I've not figured out why yet; the valgrind I'm looking at is:

==39870== Invalid read of size 8
==39870==    at 0x1DEF2D01: UnknownInlinedFun (qlist.h:153)
==39870==    by 0x1DEF2D01: UnknownInlinedFun (qlist.h:313)
==39870==    by 0x1DEF2D01: View::drawImplicit(Function*, QPainter*) (view.cpp:1168)
==39870==    by 0x1DEF997D: View::draw(QPaintDevice*, View::PlotMedium) [clone .part.0] (view.cpp:363)
==39870==    by 0x1DEE62D4: UnknownInlinedFun (view.cpp:2637)
==39870==    by 0x1DEE62D4: View::drawPlot() (view.cpp:2636)
==39870==    by 0x1DED4129: FunctionEditor::saveImplicit() (functioneditor.cpp:639)
==39870==    by 0x5E5DE95: call (qobjectdefs_impl.h:398)
==39870==    by 0x5E5DE95: void doActivate<false>(QObject*, int, void**) (qobject.cpp:3923)
==39870==    by 0x5E6121D: QTimer::timeout(QTimer::QPrivateSignal) (moc_qtimer.cpp:205)
==39870==    by 0x5E54FC4: QObject::event(QEvent*) (qobject.cpp:1369)
==39870==    by 0x4FA2D61: QApplicationPrivate::notify_helper(QObject*, QEvent*) (qapplication.cpp:3640)
==39870==    by 0x5E2A4E7: QCoreApplication::notifyInternal2(QObject*, QEvent*) (qcoreapplication.cpp:1064)
==39870==    by 0x5E7A980: QTimerInfoList::activateTimers() (qtimerinfo_unix.cpp:643)
==39870==    by 0x5E7B25B: timerSourceDispatch(_GSource*, int (*)(void*), void*) (qeventdispatcher_glib.cpp:183)
==39870==    by 0x78C2C7E: UnknownInlinedFun (gmain.c:3454)
==39870==    by 0x78C2C7E: g_main_context_dispatch (gmain.c:4172)
==39870==  Address 0x2d53d948 is 40 bytes inside a block of size 120 free'd
==39870==    at 0x48486AF: realloc (vg_replace_malloc.c:1451)
==39870==    by 0x5CB2156: QListData::realloc_grow(int) (qlist.cpp:170)
==39870==    by 0x5CB2201: QListData::append(int) (qlist.cpp:196)
==39870==    by 0x1DEF507C: UnknownInlinedFun (qlist.h:632)
==39870==    by 0x1DEF507C: QList<QPointF>::append(QPointF const&) (qlist.h:620)
==39870==    by 0x1DEEC03F: UnknownInlinedFun (qlist.h:402)
==39870==    by 0x1DEEC03F: View::drawImplicitInSquare(Plot const&, QPainter*, double, double, QFlags<Qt::Orientation>, QList<QPointF>*) (view.cpp:1358)
==39870==    by 0x1DEF33D5: View::drawImplicit(Function*, QPainter*) (view.cpp:1199)
==39870==    by 0x1DEF997D: View::draw(QPaintDevice*, View::PlotMedium) [clone .part.0] (view.cpp:363)
==39870==    by 0x1DEE62D4: UnknownInlinedFun (view.cpp:2637)
==39870==    by 0x1DEE62D4: View::drawPlot() (view.cpp:2636)
==39870==    by 0x1DED4129: FunctionEditor::saveImplicit() (functioneditor.cpp:639)
==39870==    by 0x5E5DE95: call (qobjectdefs_impl.h:398)
==39870==    by 0x5E5DE95: void doActivate<false>(QObject*, int, void**) (qobject.cpp:3923)
==39870==    by 0x5E6121D: QTimer::timeout(QTimer::QPrivateSignal) (moc_qtimer.cpp:205)
==39870==    by 0x5E54FC4: QObject::event(QEvent*) (qobject.cpp:1369)
==39870==  Block was alloc'd at
==39870==    at 0x484386F: malloc (vg_replace_malloc.c:393)
==39870==    by 0x5CB207F: QListData::detach(int) (qlist.cpp:137)
==39870==    by 0x1DEF3D0E: UnknownInlinedFun (qlist.h:833)
==39870==    by 0x1DEF3D0E: UnknownInlinedFun (qlist.h:613)
==39870==    by 0x1DEF3D0E: UnknownInlinedFun (qmap.h:1028)
==39870==    by 0x1DEF3D0E: View::drawImplicit(Function*, QPainter*) (view.cpp:1166)
==39870==    by 0x1DEF997D: View::draw(QPaintDevice*, View::PlotMedium) [clone .part.0] (view.cpp:363)
==39870==    by 0x1DEE62D4: UnknownInlinedFun (view.cpp:2637)
==39870==    by 0x1DEE62D4: View::drawPlot() (view.cpp:2636)
==39870==    by 0x1DED4129: FunctionEditor::saveImplicit() (functioneditor.cpp:639)
==39870==    by 0x5E5DE95: call (qobjectdefs_impl.h:398)  
==39870==    by 0x5E5DE95: void doActivate<false>(QObject*, int, void**) (qobject.cpp:3923)
==39870==    by 0x5E6121D: QTimer::timeout(QTimer::QPrivateSignal) (moc_qtimer.cpp:205)
==39870==    by 0x5E54FC4: QObject::event(QEvent*) (qobject.cpp:1369)
==39870==    by 0x4FA2D61: QApplicationPrivate::notify_helper(QObject*, QEvent*) (qapplication.cpp:3640)
==39870==    by 0x5E2A4E7: QCoreApplication::notifyInternal2(QObject*, QEvent*) (qcoreapplication.cpp:1064)
==39870==    by 0x5E7A980: QTimerInfoList::activateTimers() (qtimerinfo_unix.cpp:643)
==39870==
Comment 9 Dave Gilbert 2023-04-14 16:38:38 UTC
Unfortunately QMutableListIterator doesn't cope with  growing the list either; so it needs a larger rework.
Comment 10 Bug Janitor Service 2023-04-15 00:39:57 UTC
A possibly relevant merge request was started @ https://invent.kde.org/education/kmplot/-/merge_requests/13
Comment 11 Dave Gilbert 2023-04-15 00:46:07 UTC
    I've just created:

    https://invent.kde.org/education/kmplot/-/merge_requests/13

    for this.  It seems to clear the crash;  I'm not 100% sure I understand all the details of that function though - but
    I think I'm just fixing it's list manipulation.

    (Note there are other problems with this program; like the fact that it refreshes the graph as you type and the smallest typo, or it refreshing as you type part of the equation can mean it takes minutes locking up X in that function to do the graph plotting)