Comment 4 for bug 1653368

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)]