Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-84363

[REG 5.13 -> 5.14] segmentation fault in QV4::MemoryManager::collectRoots at 4th call to a method that passes a closure to another method

    XMLWordPrintable

    Details

    • Platform/s:
      Linux/X11
    • Commits:
      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.

        Attachments

        No reviews matched the request. Check your Options in the drop-down menu of this sections header.

          Activity

            People

            Assignee:
            qt.team.quick.subscriptions Qt Quick and Widgets Team
            Reporter:
            be Be Wilson
            Votes:
            0 Vote for this issue
            Watchers:
            4 Start watching this issue

              Dates

              Created:
              Updated:
              Resolved:

                Gerrit Reviews

                There are no open Gerrit changes