Details
-
Bug
-
Resolution: Done
-
P1: Critical
-
5.14.2
-
Fedora 32
-
-
082247604219316035484cfdc83662936df2edb5
Description
Calling QJSEngine::evaluate on this code causes a segmentation fault in QV4::MemoryManager::collectRoots
let Demonstrator = function () { this.methodThatTakesAFunction = function () { }; this.methodThatCreatesAClosure = function(methodArg) { let closure = function() { print(methodArg); }; this.methodThatTakesAFunction(closure); }; }; let d1 = new Demonstrator(); d1.methodThatCreatesAClosure(); d1.methodThatCreatesAClosure(); d1.methodThatCreatesAClosure(); d1.methodThatCreatesAClosure();
methodThatTakesAFunction must be a method of the constructed object. If it is defined as a local variable within the constructor, the crash does not occur:
let Demonstrator = function () { let methodThatTakesAFunction = function () { }; this.methodThatCreatesAClosure = function(methodArg) { let closure = function() { print(methodArg); }; methodThatTakesAFunction(closure); }; }; let d1 = new Demonstrator(); d1.methodThatCreatesAClosure(); d1.methodThatCreatesAClosure(); d1.methodThatCreatesAClosure(); d1.methodThatCreatesAClosure();
Likewise, defining a function in methodThatCreatesAClosure that takes the closure function as an argument and calling that does not reproduce the crash:
let Demonstrator = function () { this.methodThatCreatesAClosure = function(methodArg) { let closure = function() { print(methodArg); }; let methodThatTakesAFunction = function () { }; methodThatTakesAFunction(closure); }; }; let d1 = new Demonstrator(); d1.methodThatCreatesAClosure(); d1.methodThatCreatesAClosure(); d1.methodThatCreatesAClosure(); d1.methodThatCreatesAClosure();
methodThatCreatesAClosure must be called 4 times and it does not matter which instances of Demonstrator it is called on. These all reproduce the crash:
let Demonstrator = function () { this.methodThatTakesAFunction = function () { }; this.methodThatCreatesAClosure = function(methodArg) { let closure = function() { print(methodArg); }; this.methodThatTakesAFunction(closure); }; }; let d1 = new Demonstrator(); d1.methodThatCreatesAClosure(); d1.methodThatCreatesAClosure(); let d2 = new Demonstrator(); d2.methodThatCreatesAClosure(); d2.methodThatCreatesAClosure();
let Demonstrator = function () { this.methodThatTakesAFunction = function () { }; this.methodThatCreatesAClosure = function(methodArg) { let closure = function() { print(methodArg); }; this.methodThatTakesAFunction(closure); }; }; let d1 = new Demonstrator(); d1.methodThatCreatesAClosure(); let d2 = new Demonstrator(); d2.methodThatCreatesAClosure(); let d3 = new Demonstrator(); d3.methodThatCreatesAClosure(); d3.methodThatCreatesAClosure();
let Demonstrator = function () { this.methodThatTakesAFunction = function () { }; this.methodThatCreatesAClosure = function(methodArg) { let closure = function() { print(methodArg); }; this.methodThatTakesAFunction(closure); }; }; let d1 = new Demonstrator(); d1.methodThatCreatesAClosure(); let d2 = new Demonstrator(); d2.methodThatCreatesAClosure(); let d3 = new Demonstrator(); d3.methodThatCreatesAClosure(); let d4 = new Demonstrator(); d4.methodThatCreatesAClosure();
Commenting out any of the 4 invocations of Demonstrator.methodThatCreatesAClosure does not reproduce the crash.
The crash does not occur using the ES6 `class` keyword. None of these reproduce the crash:
class Demonstrator { constructor() { this.methodThatTakesAFunction = function () { }; this.methodThatCreatesAClosure = function(methodArg) { let closure = function() { print(methodArg); }; this.methodThatTakesAFunction(closure); }; } }; let d1 = new Demonstrator(); d1.methodThatCreatesAClosure(); d1.methodThatCreatesAClosure(); d1.methodThatCreatesAClosure(); d1.methodThatCreatesAClosure();
class Demonstrator { constructor() { this.methodThatCreatesAClosure = function(methodArg) { let closure = function() { print(methodArg); }; this.methodThatTakesAFunction(closure); }; } methodThatTakesAFunction() { } }; let d1 = new Demonstrator(); d1.methodThatCreatesAClosure(); d1.methodThatCreatesAClosure(); d1.methodThatCreatesAClosure(); d1.methodThatCreatesAClosure();
class Demonstrator { constructor() { } methodThatTakesAFunction() { } methodThatCreatesAClosure(methodArg) { let closure = function() { print(methodArg); }; this.methodThatTakesAFunction(closure); } }; let d1 = new Demonstrator(); d1.methodThatCreatesAClosure(); d1.methodThatCreatesAClosure(); d1.methodThatCreatesAClosure(); d1.methodThatCreatesAClosure();
Here is the backtrace from LLDB:
* thread #30, name = 'Controller', stop reason = signal SIGSEGV: invalid address (fault address: 0x28) frame #0: 0x00007ffff75d1f0c libQt5Qml.so.5`___lldb_unnamed_symbol445$$libQt5Qml.so.5 + 44 libQt5Qml.so.5`___lldb_unnamed_symbol445$$libQt5Qml.so.5: -> 0x7ffff75d1f0c <+44>: callq *0x28(%rax) 0x7ffff75d1f0f <+47>: movq (%rbx), %rax 0x7ffff75d1f12 <+50>: cmpq 0x8(%rbx), %rax 0x7ffff75d1f16 <+54>: ja 0x7ffff75d1ef0 ; <+16> (lldb) bt * thread #30, name = 'Controller', stop reason = signal SIGSEGV: invalid address (fault address: 0x28) * frame #0: 0x00007ffff75d1f0c libQt5Qml.so.5`___lldb_unnamed_symbol445$$libQt5Qml.so.5 + 44 frame #1: 0x00007ffff7649539 libQt5Qml.so.5`QV4::PersistentValueStorage::mark(QV4::MarkStack*) + 169 frame #2: 0x00007ffff75d229d libQt5Qml.so.5`QV4::MemoryManager::collectRoots(QV4::MarkStack*) + 61 frame #3: 0x00007ffff75d2484 libQt5Qml.so.5`QV4::MemoryManager::mark() + 100 frame #4: 0x00007ffff75d3ffe libQt5Qml.so.5`___lldb_unnamed_symbol456$$libQt5Qml.so.5 + 62 frame #5: 0x00007ffff75d5bc5 libQt5Qml.so.5`QV4::MemoryManager::allocData(unsigned long) + 181 frame #6: 0x00007ffff75d5cc3 libQt5Qml.so.5`QV4::MemoryManager::allocObjectWithMemberData(QV4::VTable const*, unsigned int) + 83 frame #7: 0x00007ffff76713e4 libQt5Qml.so.5`QV4::FunctionObject::createScriptFunction(QV4::ExecutionContext*, QV4::Function*) + 356 frame #8: 0x00007ffff76e00d5 libQt5Qml.so.5`QV4::Runtime::Closure::call(QV4::ExecutionEngine*, int) + 133 frame #9: 0x00007fffe0175e93 frame #10: 0x00007ffff76cce31 libQt5Qml.so.5`___lldb_unnamed_symbol2495$$libQt5Qml.so.5 + 321 frame #11: 0x00007ffff766f918 libQt5Qml.so.5`___lldb_unnamed_symbol1767$$libQt5Qml.so.5 + 376 frame #12: 0x00007ffff76dbc59 libQt5Qml.so.5`QV4::Runtime::CallProperty::call(QV4::ExecutionEngine*, QV4::Value const&, int, QV4::Value*, int) + 777 frame #13: 0x00007ffff76c8f99 libQt5Qml.so.5`___lldb_unnamed_symbol2494$$libQt5Qml.so.5 + 6505 frame #14: 0x00007ffff76ccee7 libQt5Qml.so.5`___lldb_unnamed_symbol2495$$libQt5Qml.so.5 + 503 frame #15: 0x00007ffff766eb4d libQt5Qml.so.5`QV4::Function::call(QV4::Value const*, QV4::Value const*, int, QV4::ExecutionContext const*) + 349 frame #16: 0x00007ffff76a3eec libQt5Qml.so.5`QV4::Script::run(QV4::Value const*) + 220 frame #17: 0x00007ffff762dcd2 libQt5Qml.so.5`QJSEngine::evaluate(QString const&, QString const&, int) + 370 frame #18: 0x0000000000834564 mixxx`ControllerEngine::evaluateCodeString(QString const&, QString const&, int) at controllerengine.cpp:382:83 frame #19: 0x000000000083efc1 mixxx`ControllerEngine::evaluateScriptFile(QFileInfo const&) at controllerengine.cpp:960:70 frame #20: 0x000000000083f9d1 mixxx`ControllerEngine::loadScriptFiles(this=0x00007ffedc150720, scripts=0x00007ffedc153720) at controllerengine.cpp:266:32 frame #21: 0x000000000083ffde mixxx`ControllerEngine::reloadScripts() at controllerengine.cpp:298:25 frame #22: 0x00007ffff5df34e3 libQt5Core.so.5`___lldb_unnamed_symbol4848$$libQt5Core.so.5 + 1219 frame #23: 0x00007ffff5d30e3f libQt5Core.so.5`QFileSystemWatcher::fileChanged(QString const&, QFileSystemWatcher::QPrivateSignal) + 47 frame #24: 0x00007ffff5df3515 libQt5Core.so.5`___lldb_unnamed_symbol4848$$libQt5Core.so.5 + 1269 frame #25: 0x00007ffff5d30ec3 libQt5Core.so.5`___lldb_unnamed_symbol3907$$libQt5Core.so.5 + 51 frame #26: 0x00007ffff5d35efb libQt5Core.so.5`___lldb_unnamed_symbol3945$$libQt5Core.so.5 + 571 frame #27: 0x00007ffff5df3515 libQt5Core.so.5`___lldb_unnamed_symbol4848$$libQt5Core.so.5 + 1269 frame #28: 0x00007ffff5df67a1 libQt5Core.so.5`QSocketNotifier::activated(int, QSocketNotifier::QPrivateSignal) + 65 frame #29: 0x00007ffff5df6aa1 libQt5Core.so.5`QSocketNotifier::event(QEvent*) + 193 frame #30: 0x00007ffff6ac017f libQt5Widgets.so.5`QApplicationPrivate::notify_helper(QObject*, QEvent*) + 127 frame #31: 0x00007ffff5dc0842 libQt5Core.so.5`QCoreApplication::notifyInternal2(QObject*, QEvent*) + 386 frame #32: 0x00007ffff5e135c5 libQt5Core.so.5`___lldb_unnamed_symbol4990$$libQt5Core.so.5 + 101 frame #33: 0x00007ffff58827cf libglib-2.0.so.0`g_main_context_dispatch + 367 frame #34: 0x00007ffff5882b58 libglib-2.0.so.0`g_main_context_iterate.constprop.0 + 488 frame #35: 0x00007ffff5882c23 libglib-2.0.so.0`g_main_context_iteration + 51 frame #36: 0x00007ffff5e12a9f libQt5Core.so.5`QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) + 95 frame #37: 0x00007ffff5dbf32b libQt5Core.so.5`QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) + 283 frame #38: 0x00007ffff5bda15e libQt5Core.so.5`QThread::exec() + 78 frame #39: 0x00007ffff5bdb141 libQt5Core.so.5`___lldb_unnamed_symbol2522$$libQt5Core.so.5 + 289 frame #40: 0x00007ffff4caa432 libpthread.so.0`start_thread + 226 frame #41: 0x00007ffff4bd89d3 libc.so.6`__clone + 67
With the QV4_MM_AGGRESSIVE_GC environment variable set to 1, the backtrace is the same except for the very last frame:
* thread #30, name = 'Controller', stop reason = signal SIGSEGV: address access protected (fault address: 0x7ffea78b0118) frame #0: 0x00007ffff7900212 libQt5Qml.so.5`QV4::MemoryManager::collectFromJSStack(QV4::MarkStack*) const + 98 libQt5Qml.so.5`QV4::MemoryManager::collectFromJSStack: -> 0x7ffff7900212 <+98>: movq (%rsi), %rdi 0x7ffff7900215 <+101>: testq %rdi, %rcx 0x7ffff7900218 <+104>: jne 0x7ffff790022c ; <+124> 0x7ffff790021a <+106>: orq %rdi, %rcx (lldb) bt error: mixxx DWARF DIE at 0x04b11c59 (class ControllerManager) has a member variable 0x04b12082 (m_pollTimer) whose type is a forward declaration, not a complete definition. Please file a bug against the compiler and include the preprocessed output for /home/be/sw/mixxx/lin64_build/src/controllers/moc_controllermanager.cc * thread #30, name = 'Controller', stop reason = signal SIGSEGV: address access protected (fault address: 0x7ffea78b0118) * frame #0: 0x00007ffff7900212 libQt5Qml.so.5`QV4::MemoryManager::collectFromJSStack(QV4::MarkStack*) const + 98 frame #1: 0x00007ffff790028e libQt5Qml.so.5`QV4::MemoryManager::collectRoots(QV4::MarkStack*) + 46 frame #2: 0x00007ffff7900484 libQt5Qml.so.5`QV4::MemoryManager::mark() + 100 frame #3: 0x00007ffff7902476 libQt5Qml.so.5`___lldb_unnamed_symbol456$$libQt5Qml.so.5 + 1206 frame #4: 0x00007ffff7903bc5 libQt5Qml.so.5`QV4::MemoryManager::allocData(unsigned long) + 181 frame #5: 0x00007ffff7903cc3 libQt5Qml.so.5`QV4::MemoryManager::allocObjectWithMemberData(QV4::VTable const*, unsigned int) + 83 frame #6: 0x00007ffff7a0b181 libQt5Qml.so.5`QV4::Runtime::CreateMappedArgumentsObject::call(QV4::ExecutionEngine*) + 33 frame #7: 0x00007ffeb848206e frame #8: 0x00007ffff79fae31 libQt5Qml.so.5`___lldb_unnamed_symbol2495$$libQt5Qml.so.5 + 321 frame #9: 0x00007ffff799d918 libQt5Qml.so.5`___lldb_unnamed_symbol1767$$libQt5Qml.so.5 + 376 frame #10: 0x00007ffff7a09c59 libQt5Qml.so.5`QV4::Runtime::CallProperty::call(QV4::ExecutionEngine*, QV4::Value const&, int, QV4::Value*, int) + 777 frame #11: 0x00007ffeb85bb9cb frame #12: 0x00007ffff79fae31 libQt5Qml.so.5`___lldb_unnamed_symbol2495$$libQt5Qml.so.5 + 321 frame #13: 0x00007ffff799d918 libQt5Qml.so.5`___lldb_unnamed_symbol1767$$libQt5Qml.so.5 + 376 frame #14: 0x00007ffff799df53 libQt5Qml.so.5`___lldb_unnamed_symbol1773$$libQt5Qml.so.5 + 99 frame #15: 0x00007ffff7a09c59 libQt5Qml.so.5`QV4::Runtime::CallProperty::call(QV4::ExecutionEngine*, QV4::Value const&, int, QV4::Value*, int) + 777 frame #16: 0x00007ffff79f6f99 libQt5Qml.so.5`___lldb_unnamed_symbol2494$$libQt5Qml.so.5 + 6505 frame #17: 0x00007ffff79faee7 libQt5Qml.so.5`___lldb_unnamed_symbol2495$$libQt5Qml.so.5 + 503 frame #18: 0x00007ffff799fa58 libQt5Qml.so.5`___lldb_unnamed_symbol1784$$libQt5Qml.so.5 + 648 frame #19: 0x00007ffff79f7973 libQt5Qml.so.5`___lldb_unnamed_symbol2494$$libQt5Qml.so.5 + 9027 frame #20: 0x00007ffff79faee7 libQt5Qml.so.5`___lldb_unnamed_symbol2495$$libQt5Qml.so.5 + 503 frame #21: 0x00007ffff799d918 libQt5Qml.so.5`___lldb_unnamed_symbol1767$$libQt5Qml.so.5 + 376 frame #22: 0x00007ffff79f7fbd libQt5Qml.so.5`___lldb_unnamed_symbol2494$$libQt5Qml.so.5 + 10637 frame #23: 0x00007ffff79faee7 libQt5Qml.so.5`___lldb_unnamed_symbol2495$$libQt5Qml.so.5 + 503 frame #24: 0x00007ffff799cb4d libQt5Qml.so.5`QV4::Function::call(QV4::Value const*, QV4::Value const*, int, QV4::ExecutionContext const*) + 349 frame #25: 0x00007ffff79d1eec libQt5Qml.so.5`QV4::Script::run(QV4::Value const*) + 220 frame #26: 0x00007ffff795bcd2 libQt5Qml.so.5`QJSEngine::evaluate(QString const&, QString const&, int) + 370 frame #27: 0x0000000000a8fb27 mixxx`ControllerEngine::evaluateCodeString(QString const&, QString const&, int) at controllerengine.cpp:380:67 frame #28: 0x0000000000a9a290 mixxx`ControllerEngine::evaluateScriptFile(QFileInfo const&) at controllerengine.cpp:957:70 frame #29: 0x0000000000a9acbb mixxx`ControllerEngine::loadScriptFiles(this=0x00007ffedc10adb0, scripts=0x00007ffeedffa758) at controllerengine.cpp:264:32 frame #30: 0x0000000000a82192 mixxx`Controller::applyPreset(bool) at controller.cpp:72:46 frame #31: 0x0000000000cfc973 mixxx`MidiController::applyPreset(this=0x00007ffedc163f20, initializeScripts=<unavailable>) at midicontroller.cpp:63:42 frame #32: 0x000000000075ebaf mixxx`ControllerManager::openController(Controller*) at controllermanager.cpp:381:33 frame #33: 0x000000000075f245 mixxx`ControllerManager::slotApplyPreset(Controller*, QSharedPointer<ControllerPreset>, bool) at controllermanager.cpp:427:23 frame #34: 0x0000000000a8c7d1 mixxx`QtPrivate::QSlotObject<void (ControllerManager::*)(Controller*, QSharedPointer<ControllerPreset>, bool), QtPrivate::List<Controller*, QSharedPointer<ControllerPreset>, bool>, void>::impl(int, QtPrivate::QSlotObjectBase*, QObject*, void**, bool*) [inlined] QtPrivate::FunctorCall<QtPrivate::IndexesList<0, 1, 2>, QtPrivate::List<Controller*, QSharedPointer<ControllerPreset>, bool>, void, void (ControllerManager::*)(Controller*, QSharedPointer<ControllerPreset>, bool)>::call(arg=<unavailable>, o=<unavailable>, f=<unavailable>)(Controller*, QSharedPointer<ControllerPreset>, bool), ControllerManager*, void**) at qobjectdefs_impl.h:152:20 frame #35: 0x0000000000a8c77d mixxx`QtPrivate::QSlotObject<void (ControllerManager::*)(Controller*, QSharedPointer<ControllerPreset>, bool), QtPrivate::List<Controller*, QSharedPointer<ControllerPreset>, bool>, void>::impl(int, QtPrivate::QSlotObjectBase*, QObject*, void**, bool*) [inlined] void QtPrivate::FunctionPointer<void (ControllerManager::*)(Controller*, QSharedPointer<ControllerPreset>, bool)>::call<QtPrivate::List<Controller*, QSharedPointer<ControllerPreset>, bool>, void>(arg=<unavailable>, o=<unavailable>, f=<unavailable>)(Controller*, QSharedPointer<ControllerPreset>, bool), ControllerManager*, void**) at qobjectdefs_impl.h:185 frame #36: 0x0000000000a8c77d mixxx`QtPrivate::QSlotObject<void (ControllerManager::*)(Controller*, QSharedPointer<ControllerPreset>, bool), QtPrivate::List<Controller*, QSharedPointer<ControllerPreset>, bool>, void>::impl(which=<unavailable>, this_=<unavailable>, r=<unavailable>, a=<unavailable>, ret=<unavailable>) at qobjectdefs_impl.h:418 frame #37: 0x00007ffff62a6231 libQt5Core.so.5`QObject::event(QEvent*) + 641 frame #38: 0x00007ffff6dee17f libQt5Widgets.so.5`QApplicationPrivate::notify_helper(QObject*, QEvent*) + 127 frame #39: 0x00007ffff627c842 libQt5Core.so.5`QCoreApplication::notifyInternal2(QObject*, QEvent*) + 386 frame #40: 0x00007ffff627ef11 libQt5Core.so.5`QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) + 353 frame #41: 0x00007ffff62cf3b3 libQt5Core.so.5`___lldb_unnamed_symbol4989$$libQt5Core.so.5 + 19 frame #42: 0x00007ffff52ae7cf libglib-2.0.so.0`g_main_context_dispatch + 367 frame #43: 0x00007ffff52aeb58 libglib-2.0.so.0`g_main_context_iterate.constprop.0 + 488 frame #44: 0x00007ffff52aec23 libglib-2.0.so.0`g_main_context_iteration + 51 frame #45: 0x00007ffff62cea9f libQt5Core.so.5`QEventDispatcherGlib::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) + 95 frame #46: 0x00007ffff627b32b libQt5Core.so.5`QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) + 283 frame #47: 0x00007ffff609615e libQt5Core.so.5`QThread::exec() + 78 frame #48: 0x00007ffff6097141 libQt5Core.so.5`___lldb_unnamed_symbol2522$$libQt5Core.so.5 + 289 frame #49: 0x00007ffff578d432 libpthread.so.0`start_thread + 226 frame #50: 0x00007ffff4a2a9d3 libc.so.6`__clone + 67
This crash is 100% reproducible every time with Qt 5.14.2. It does not occur with Qt 5.13.3, nor does it occur with the deprecated QScriptEngine.
This was discovered in Mixxx in our effort to migrate from QScriptEngine to QJSEngine: https://github.com/mixxxdj/mixxx/pull/2682#issuecomment-627846409
It was discovered when that branch was tested on Arch Linux which packages Qt 5.14.2. I could not reproduce it with Fedora 32's package of Qt 5.13.2. I had to build Qt 5.14.2 locally and build Mixxx with that to reproduce the bug.