Details
-
Bug
-
Resolution: Done
-
P1: Critical
-
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