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

QScroller crashes on certain screen and/or window arrangements

    XMLWordPrintable

Details

    • Bug
    • Resolution: Done
    • P1: Critical
    • 5.12.11
    • 5.12, 5.13
    • None
    • Qt < 5.14, screen arrangement with (0, 0) not covered by any screen.
    • 751892a21c796130cdceaa8cee4aeeea18618c5e (qt/qtbase/5.12)

    Description

      QScrollerPrivate::setDpiFromWidget() reads a wrong QScreen* pointer when the target does not intersect with a physical screen. Then it will crash when it tries to read from this pointer.

      void QScrollerPrivate::setDpiFromWidget(QWidget *widget)
      {
          const QScreen *screen = QGuiApplication::screens().at(QApplication::desktop()->screenNumber(widget));
          setDpi(QPointF(screen->physicalDotsPerInchX(), screen->physicalDotsPerInchY()));
      }
      

      QDesktopWidget::screenNumber() will return -1 when widget does not intersect with a physical screen. This is typically the case when you instantiate QScroller on a not-yet shown widget, and have extended your laptop screen with a bigger screen to the right. In that case, the widget will be at global position (0, 0), but this point is not covered by any screen.

      QList<QScreen*>::at(-1) will then silently read (on a release build) from QList internals, instead of a list element. Ironically, this will return your current screen count instead of a screen pointer.

      This bug does not apply to Qt 5.14, because there QDesktopWidget is not used anymore, and setDpiFromWidget() handles this case robustly. But it does apply to Qt 5.12 LTS, which is still used by some of our users.

      Original bug report: https://bugs.kde.org/show_bug.cgi?id=425188

      Code to reproduce:

          // The target is a QScrollArea, because that accepts a ScrollPrepare event.
          // Otherwise, QScroller will stay inactive and nothing happens.
          QWidget bigWidget;
          bigWidget.resize(10000, 10000);
          QScrollArea testWidget;
          testWidget.setWidget(&bigWidget);
          qDebug() << testWidget.geometry();
          qDebug() << QApplication::desktop()->screenNumber(&testWidget);
          // Places the scroll area outside any physical screen:
          testWidget.setGeometry(0, 0, 50, 50);
          qDebug() << testWidget.geometry();
          qDebug() << QApplication::desktop()->screenNumber(&testWidget);
          QTest::qWait(5);
          qDebug() << testWidget.geometry();
          qDebug() << QApplication::desktop()->screenNumber(&testWidget);
          QScroller *testScroller = QScroller::scroller(&testWidget);
      
          qDebug() << "Scrolling a not shown widget.";
          testScroller->scrollTo(QPoint(50, 20), 0); // <- Crash
      

      Attachments

        Activity

          People

            qt.team.quick.subscriptions Qt Quick and Widgets Team
            checkedtrashcan David Hurka
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: