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

Segfault in Bluetooth module's Windows COM de-init

    XMLWordPrintable

Details

    • Windows, WinRT
    • 3
    • f762e9b64 (dev), 0e8bd451a (6.7), 68d04b599 (6.6), 3d5967dec (tqtc/lts-6.5), 682b99936 (tqtc/lts-6.2)
    • Foundations Sprint 97

    Description

      The short version: the internal mainThreadCoUnInit() function in qtconnectivity/src/bluetooth
      /qbluetoothutils_winrt.cpp fails to check if QCoreApplication::instance() returns nullptr, before attempting to dereference the result, causing a segmentation fault if Bluetooth objects are destroyed as part of the QCoreApplication's QObject-based auto-deletion of children.

      Consider, for example, the following example (there may be better ways to do this, it just shows one trivial way to cause a segfault within Qt libraries; the example code is also attached in case it doesn't render well here):

       

      #include <QBluetoothDeviceDiscoveryAgent>
      #include <QCoreApplication>
      #include <QTimer>

      int main(int argc, char *argv[])
      {{{}}
          QCoreApplication app(argc, argv);

          QBluetoothDeviceDiscoveryAgent * discoveryAgent = new QBluetoothDeviceDiscoveryAgent(&app);

          QObject::connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::deviceDiscovered,
              [](const QBluetoothDeviceInfo &info) {
                  qDebug() << "discovered" << info.address() << info.deviceUuid() << info.name();
              });    QObject::connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::errorOccurred,
              [](const QBluetoothDeviceDiscoveryAgent::Error &error) {
                  qDebug() << "discovery error" << error;
                  QCoreApplication::exit(EXIT_FAILURE);
              });    QObject::connect(discoveryAgent, &QBluetoothDeviceDiscoveryAgent::finished,
              []() {
                  qDebug() << "discovery finished";
                  QCoreApplication::exit(EXIT_SUCCESS);
              });    QTimer::singleShot(0, QCoreApplication::instance(), [discoveryAgent](){
              discoveryAgent->start();
          });    const int result = QCoreApplication::exec();
          //delete discoveryAgent;
          return result;
      }

       

      Here, because the discoveryAgent is parented by the core application (in real-world examples it wouldn't be a direct child, but rather descend several levels). So when the application is being destroyed, QCoreApplication clears it internal data, before its super (QObject) deletes the discoveryAgent.  So when the discoveryAgent is being destroyed, it results in mainThreadCoUnInit() being called internally, which tries to access QCoreApplication::instance() which is now nullptr.

       

      Presumably, the fix is to update mainThreadCoUnInit() to replace the line:

       

      if (QThread::currentThread() != QCoreApplication::instance()->thread()) {

       

      with something like:

       

      if (QCoreApplication::instance() != nullptr && QThread::currentThread() != QCoreApplication::instance()->thread()) {

       

      In the meantime, I'll need to ensure I manually delete my discovery agent before exiting the application. For example, if I uncomment the "delete discoveryAgent" line above, the application will exit cleanly.

      Let me know if I can help any more.

      Cheers.

      PS Might be helpful to note, the internal Qt code in question was added as part of https://bugreports.qt.io/browse/QTBUG-101953

      Attachments

        For Gerrit Dashboard: QTBUG-119063
        # Subject Branch Project Status CR V

        Activity

          People

            ivan.solovev Ivan Solovev
            paul Paul Colby
            Vladimir Minenko Vladimir Minenko
            Alex Blasche Alex Blasche
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved:

              Gerrit Reviews

                There are no open Gerrit changes