Windows x86 static build crashes

Bug #1653368 reported by RJ Skerry-Ryan
6
This bug affects 1 person
Affects Status Importance Assigned to Milestone
Mixxx
Fix Released
Critical
RJ Skerry-Ryan

Bug Description

When building a Windows x86 static build, there are multiple problems where x64 is fine. I've encountered three distinct crashes. One is a Qt assertion failure on startup in DlgPrefEq, two are on shutdown (one in ~WMenuBar and one happens when deleting the library widgets).

I've already dumped significant debugging time into this issue and could use some more eyes on the problem.

1) DlgPrefEq::slotSingleEqChecked. https://github.com/mixxxdj/mixxx/blob/master/src/preferences/dialog/dlgprefeq.cpp#L267

2) When deleting a library dialog like DlgHidden.

3) When deleting WMenuBar.

When responding, please indicate which crash you're talking about at the top of your post.

This bug is Mixxx 2.1 launch blocking, so could we please have all hands on deck for it? (e.g. everyone with a Windows environment, please focus on this bug).

RJ Skerry-Ryan (rryan)
Changed in mixxx:
milestone: none → 2.1.0
importance: Undecided → Critical
status: New → Triaged
Revision history for this message
RJ Skerry-Ryan (rryan) wrote :
Download full text (6.4 KiB)

Crash #2 stacktrace:

WTrackTableView::~WTrackTableView() Line 118 is "delete m_pReloadMetadataFromMusicBrainzAct;".

  ntdll.dll!_RtlpCoalesceFreeBlocks@16
() Unknown
  ntdll.dll!@RtlpFreeHeap@16
() Unknown
  ntdll.dll!_RtlFreeHeap@12
() Unknown
  ucrtbase.dll!free() Unknown
  mixxx.exe!operator delete(void * block=0x022d0fd0, unsigned int __formal=20) Line 15 C++
  mixxx.exe!QActionPrivate::~QActionPrivate() Line 103 C++
  mixxx.exe!QActionPrivate::`scalar deleting destructor'(unsigned int) C++
  mixxx.exe!QObject::~QObject() Line 967 C++
  mixxx.exe!QAction::~QAction() Line 676 C++
  mixxx.exe!QAction::`scalar deleting destructor'(unsigned int) C++
 mixxx.exe!WTrackTableView::~WTrackTableView() Line 118 C++
  mixxx.exe!WAnalysisLibraryTableView::`scalar deleting destructor'(unsigned int) C++
  mixxx.exe!QObjectPrivate::deleteChildren() Line 1932 C++
  mixxx.exe!QWidget::~QWidget() Line 1682 C++
  mixxx.exe!QStackedWidget::~QStackedWidget() Line 196 C++
  mixxx.exe!WLibrary::`scalar deleting destructor'(unsigned int) C++
  mixxx.exe!QObjectPrivate::deleteChildren() Line 1932 C++
  mixxx.exe!QWidget::~QWidget() Line 1682 C++
  mixxx.exe!QStackedWidget::~QStackedWidget() Line 196 C++
  mixxx.exe!WWidgetGroup::~WWidgetGroup() C++
  mixxx.exe!WWidgetGroup::`scalar deleting destructor'(unsigned int) C++
  mixxx.exe!QObjectPrivate::deleteChildren() Line 1932 C++
  mixxx.exe!QWidget::~QWidget() Line 1682 C++
  mixxx.exe!QSplitter::~QSplitter() Line 1056 C++
  mixxx.exe!WSplitter::`scalar deleting destructor'(unsigned int) C++
  mixxx.exe!QObjectPrivate::deleteChildren() Line 1932 C++
  mixxx.exe!QWidget::~QWidget() Line 1682 C++
  mixxx.exe!QStackedWidget::~QStackedWidget() Line 196 C++
  mixxx.exe!WWidgetGroup::~WWidgetGroup() C++
  mixxx.exe!WWidgetGroup::`scalar deleting destructor'(unsigned int) C++
  mixxx.exe!QObjectPrivate::deleteChildren() Line 1932 C++
  mixxx.exe!QWidget::~QWidget() Line 1682 C++
  mixxx.exe!QStackedWidget::~QStackedWidget() Line 196 C++
  mixxx.exe!WWidgetGroup::~WWidgetGroup() C++
  mixxx.exe!WWidgetGroup::`scalar deleting destructor'(unsigned int) C++
  mixxx.exe!QObjectPrivate::deleteChildren() Line 1932 C++
  mixxx.exe!QWidget::~QWidget() Line 1682 C++
  mixxx.exe!QStackedWidget::~QStackedWidget() Line 196 C++
  mixxx.exe!WWidgetGroup::~WWidgetGroup() C++
  mixxx.exe!WWidgetGroup::`scalar deleting destructor'(unsigned int) C++
  mixxx.exe!QObject::event(QEvent * e=0x10dd62c8) Line 1258 C++
  mixxx.exe!QWidget::event(QEvent * event=0x10dd62c8) Line 8862 C++
  mixxx.exe!QFrame::event(QEvent * e=0x10dd62c8) Line 559 C++
  mixxx.exe!WLibrary::event(QEvent * pEvent=0x10dd62c8) Line 74 C++
  mixxx.exe!QApplicationPrivate::notify_helper(QObject * receiver=0x1085a348, QEvent * e=0x10dd62c8) Line 4568 C++
  mixxx.exe!QApplication::notify(QObject * receiver=0x1085a348, QEvent * e=0x10dd62c8) Line 4535 C++
  mixxx.exe!MixxxApplication::notify(QObject * target=0x1085a348, QEvent * event=0x10dd62c8) Line 139 C++
  mixxx.exe!QCoreApplication::notifyInternal(QObject * receiver=0x1085a348, QEvent * event=0x10dd62c8) Line 968 C++
  mixxx.exe!QCoreApplicationPrivate::sendPostedEvents(QObject * receiver=0x1085a348, int event_type, QT...

Read more...

Revision history for this message
RJ Skerry-Ryan (rryan) wrote :
Download full text (5.6 KiB)

Crash #2, as seen by DrMemory:

Error #4: INVALID HEAP ARGUMENT to free 0x02a40fd0
# 0 replace_operator_delete_nothrow [d:\drmemory_package\common\alloc_replace.c:2974]
# 1 operator delete [f:\dd\vctools\crt\vcstartup\src\heap\delete_scalar_size.cpp:15]
# 2 QActionPrivate::~QActionPrivate [c:\mixxx\environments\msvc15-static-x86-release\build\qt-everywhere-opensource-src-4.8.7\src\gui\kernel\qaction.cpp:103]
# 3 QActionPrivate::`scalar deleting destructor'
# 4 QAction::~QAction [c:\mixxx\environments\msvc15-static-x86-release\build\qt-everywhere-opensource-src-4.8.7\src\gui\kernel\qaction.cpp:676]
# 5 QAction::`scalar deleting destructor'
# 6 QWidget::~QWidget [c:\mixxx\environments\msvc15-static-x86-release\build\qt-everywhere-opensource-src-4.8.7\src\gui\kernel\qwidget.cpp:1679]
# 7 QStackedWidget::~QStackedWidget [c:\mixxx\environments\msvc15-static-x86-release\build\qt-everywhere-opensource-src-4.8.7\src\gui\widgets\qstackedwidget.cpp:196]
# 8 QWidget::~QWidget [c:\mixxx\environments\msvc15-static-x86-release\build\qt-everywhere-opensource-src-4.8.7\src\gui\kernel\qwidget.cpp:1679]
# 9 QStackedWidget::~QStackedWidget [c:\mixxx\environments\msvc15-static-x86-release\build\qt-everywhere-opensource-src-4.8.7\src\gui\widgets\qstackedwidget.cpp:196]
#10 WWidgetGroup::~WWidgetGroup
#11 WWidgetGroup::`scalar deleting destructor'
#12 QObjectPrivate::deleteChildren [c:\mixxx\environments\msvc15-static-x86-release\build\qt-everywhere-opensource-src-4.8.7\src\corelib\kernel\qobject.cpp:1935]
#13 QWidget::~QWidget [c:\mixxx\environments\msvc15-static-x86-release\build\qt-everywhere-opensource-src-4.8.7\src\gui\kernel\qwidget.cpp:1679]
#14 QSplitter::~QSplitter [c:\mixxx\environments\msvc15-static-x86-release\build\qt-everywhere-opensource-src-4.8.7\src\gui\widgets\qsplitter.cpp:1056]
#15 WSplitter::`scalar deleting destructor'
#16 QWidget::~QWidget [c:\mixxx\environments\msvc15-static-x86-release\build\qt-everywhere-opensource-src-4.8.7\src\gui\kernel\qwidget.cpp:1679]
#17 QStackedWidget::~QStackedWidget [c:\mixxx\environments\msvc15-static-x86-release\build\qt-everywhere-opensource-src-4.8.7\src\gui\widgets\qstackedwidget.cpp:196]
#18 WWidgetGroup::~WWidgetGroup
#19 WWidgetGroup::`scalar deleting destructor'
#20 QObjectPrivate::deleteChildren [c:\mixxx\environments\msvc15-static-x86-release\build\qt-everywhere-opensource-src-4.8.7\src\corelib\kernel\qobject.cpp:1935]
#21 QWidget::~QWidget [c:\mixxx\environments\msvc15-static-x86-release\build\qt-everywhere-opensource-src-4.8.7\src\gui\kernel\qwidget.cpp:1679]
#22 QStackedWidget::~QStackedWidget [c:\mixxx\environments\msvc15-static-x86-release\build\qt-everywhere-ope...

Read more...

Revision history for this message
RJ Skerry-Ryan (rryan) wrote :
Download full text (5.6 KiB)

Crash #3 seems to be caused when deleting the "extension" widget in QMenuBar. This is a Qt-created widget that is used for showing an overflow menu. The widget has a QKeySequence "shortcut" member in its QAbstractButtonPrivate structure.

Stepping through the code one assembly instruction at a time, I can see that QKeySequence is a shared_empty instance and its reference count is reduced to 0, which causes ~QAbstractButtonPrivate to call delete on it. QKeySequencePrivate shared_empty is a static variable, so this results triggers the crash.

Here's the stack frame that produces the crash. I commented all of WMenuBar::initialize except for creating the File menu and adding it. This is enough to trigger the crash (without creating a menu, the problem does not occur).

Attached screenshot shows the shared_empty's reference count dropped to zero right before QAbstractButtonPrivate calls delete. (this is right after the lock xadd instruction that decremented).

 mixxx.exe!QAbstractButtonPrivate::~QAbstractButtonPrivate() C++
  mixxx.exe!QToolButtonPrivate::~QToolButtonPrivate() C++
  mixxx.exe!QToolButtonPrivate::`scalar deleting destructor'(unsigned int) C++
  mixxx.exe!QObject::~QObject() Line 967 C++
  mixxx.exe!QWidget::~QWidget() Line 1703 C++
  mixxx.exe!QAbstractButton::~QAbstractButton() Line 606 C++
  mixxx.exe!QToolButton::`scalar deleting destructor'(unsigned int) C++
  mixxx.exe!QObjectPrivate::deleteChildren() Line 1932 C++
  mixxx.exe!QWidget::~QWidget() Line 1682 C++
  mixxx.exe!QMenuBar::~QMenuBar() Line 857 C++
  mixxx.exe!WMainMenuBar::`scalar deleting destructor'(unsigned int) C++
  mixxx.exe!QObject::event(QEvent * e=0x11061d68) Line 1258 C++
  mixxx.exe!QWidget::event(QEvent * event=0x11061d68) Line 8862 C++
  mixxx.exe!QMenuBar::event(QEvent * e=0x11061d68) Line 1608 C++
  mixxx.exe!QApplicationPrivate::notify_helper(QObject * receiver=0x004a7718, QEvent * e=0x11061d68) Line 4568 C++
  mixxx.exe!QApplication::notify(QObject * receiver=0x004a7718, QEvent * e=0x11061d68) Line 4535 C++
  mixxx.exe!MixxxApplication::notify(QObject * target=0x004a7718, QEvent * event=0x11061d68) Line 139 C++
  mixxx.exe!QCoreApplication::notifyInternal(QObject * receiver=0x004a7718, QEvent * event=0x11061d68) Line 968 C++
  mixxx.exe!QCoreApplicationPrivate::sendPostedEvents(QObject * receiver=0x004a7718, int event_type, QThreadData * data=0x004025f0) Line 1579 C++
  mixxx.exe!MixxxMainWindow::finalize() Line 496 C++
  mixxx.exe!MixxxMainWindow::closeEvent(QCloseEvent * event=0x0025d470) Line 1204 C++
  mixxx.exe!QWidget::event(QEvent * event=0x0025d470) Line 8861 C++
  mixxx.exe!QApplicationPrivate::notify_helper(QObject * receiver=0x00480ca0, QEvent * e=0x0025d470) Line 4568 C++
  mixxx.exe!QApplication::notify(QObject * receiver=0x00480ca0, QEvent * e=0x0025d470) Line 4535 C++
  mixxx.exe!MixxxApplication::notify(QObject * target=0x00480ca0, QEvent * event=0x0025d470) Line 139 C++
  mixxx.exe!QCoreApplication::notifyInternal(QObject * receiver=0x00480ca0, QEvent * event=0x0025d470) Line 968 C++
  mixxx.exe!QWidgetPrivate::close_helper(QWidgetPrivate::CloseMode mode=CloseWithSpontaneousEvent) Line 7956 C++
  mixxx.exe!QtWndProc(HWND__ * hwnd=0...

Read more...

Revision history for this message
RJ Skerry-Ryan (rryan) wrote :

Crash #2

Looks like this is some static initialization bug.

When deleting the QMenuBar in ~QWidget, the QKeySequence() temporary on this line:
https://code.qt.io/cgit/qt/qt.git/tree/src/gui/kernel/qwidget.cpp#n1585
causes QKeySequencePrivate's shared_empty static variable to be re-initialized.

Here's the disassembly:

        qApp->d_func()->shortcutMap.removeShortcut(0, this, QKeySequence());
0132CD2A mov eax,dword ptr fs:[0000002Ch]
^ accessing thread local state for thread-safe static initialization (new in MSVC 2015 I think).

0132CD30 mov ecx,dword ptr [eax]
0132CD32 mov eax,dword ptr ds:[026A1F48h]
^ 0x026A1F48 is where the record of shared_empty's initialization is kept.

0132CD37 cmp eax,dword ptr [ecx+4]
0132CD3D jle QWidget::~QWidget+53Fh (0132CD9Fh)
^ 0x00000000 is not less than 0x80000060, so we don't jump

0132CD3F push 26A1F48h
0132CD44 call _Init_thread_header (01EC414Dh)
0132CD49 add esp,4
0132CD4C mov eax,dword ptr ds:[026A1F48h]
0132CD51 cmp eax,0FFFFFFFFh
0132CD54 jne QWidget::~QWidget+53Fh (0132CD9Fh)
0132CD56 mov dword ptr [shared_empty (02678FD8h)],0
0132CD60 mov dword ptr [shared_empty (02678FD8h)],1
^ Boom, reference count reset to 1. Proceed to crash on the next reference count decrement.

0132CD6A mov dword ptr ds:[2678FE8h],0
0132CD74 mov dword ptr ds:[2678FE4h],0
0132CD7E mov dword ptr ds:[2678FE0h],0
0132CD88 mov dword ptr ds:[2678FDCh],0
0132CD92 push 26A1F48h
0132CD97 call _Init_thread_footer (01EC410Eh)
0132CD9C add esp,4
0132CD9F mov dword ptr [type],offset shared_empty (02678FD8h)
0132CDA6 lock inc dword ptr [shared_empty (02678FD8h)]

What's weird (and maybe I don't understand static initialization well enough) is that other instances I can find of QKeySequence::QKeySequence() do not include this static initialization blurb. They all assume the shared_empty is already initialized.

For example, here's the section of QActionPrivate::QActionPrivate() that initializes its QKeySequence member via the no-args constructor:

01402B23 mov dword ptr [esi+68h],offset shared_empty (02678FD8h)
01402B2A lock inc dword ptr [shared_empty (02678FD8h)]

Revision history for this message
RJ Skerry-Ryan (rryan) wrote :

Argh, and I've already messed up my numberings. That post (post #4) was for Crash #3 (triggered by QMenuBar deletion).

Revision history for this message
RJ Skerry-Ryan (rryan) wrote :

I think crash #3 is a bug in magic static support introduced in MSVC15. Whether the bug is in Qt, Mixxx or MSVC -- I'm not sure. To test this hypothesis, I'll build the whole environment with /Zc:threadSafeInit- to turn off magic static support.

Pretty much every Qt class relies on a static shared_null for efficiency -- I'm hopeful crash #1 and #2 are also related.

Revision history for this message
RJ Skerry-Ryan (rryan) wrote :

https://connect.microsoft.com/VisualStudio/feedback/details/1789709/visual-c-2015-runtime-broken-on-windows-server-2003-c-11-magic-statics

This bug doesn't quite cover this situation because it's for Windows XP (I'm using Windows 7 to debug) and it refers to thread local storage when loading DLLs (which we aren't doing in a static build).

Regardless, if we're trying to support XP this strongly indicates we should be turning off magic statics, since they are implemented with TLS.

Revision history for this message
RJ Skerry-Ryan (rryan) wrote :

Update -- all tests pass and Mixxx starts and shuts down normally on x86 when using Qt built dynamically but all other libraries built statically. (with magic statics enabled, I haven't tried disabling them yet).

localecompare=1 is broken in this setup since Qt is linking sqlite statically, so Mixxx's sqlite and Qt's sqlite are different. We would need to hack SQlite to build a DLL in staticdeps mode.

Revision history for this message
RJ Skerry-Ryan (rryan) wrote :

Woohoo! No crashes on x86 with magic static disabled across all dependencies.

Changed in mixxx:
assignee: nobody → RJ Ryan (rryan)
Revision history for this message
RJ Skerry-Ryan (rryan) wrote :

Disabling magic statics in our build environment and Mixxx leads to working builds on x86 and x64:

https://github.com/mixxxdj/mixxx/pull/1131
https://github.com/mixxxdj/buildserver/commit/3b4b58cb5acde25e2d2b1e54037f1d2a34ce80fd

RJ Skerry-Ryan (rryan)
Changed in mixxx:
status: Triaged → Fix Committed
Changed in mixxx:
status: Fix Committed → Fix Released
Revision history for this message
Swiftb0y (swiftb0y) wrote :

Mixxx now uses GitHub for bug tracking. This bug has been migrated to:
https://github.com/mixxxdj/mixxx/issues/8744

lock status: Metadata changes locked and limited to project staff
To post a comment you must log in.
This report contains Public information  
Everyone can see this information.

Other bug subscribers

Remote bug watches

Bug watches keep track of this bug in other bug trackers.