Very strange heap corruption (exit code -1073740940/0xC0000374) with static inline const QString, Release only



  • Hello,

    I have a bizarre issue that just has me shrugging until my shoulders float off my body as I have no idea what to make of this. It feels like some kind of undefined behavior related to how Qt manages the memory of QObjects because of how particular this issue is (also while trying to narrow down the issue I had some weird inconsistencies where simply changing the name of a class would make the issue stop), but that's just a vague guess. I originally discovered this issue in a larger program, but I made a copy of it and reduced it down until only the portions that seemed to be related to the issue remained. That is what I'll show here.

    Just a tad of background: This is barely even a Qt application as this is a simple launcher app that simply reads an .ini file, writes to a log, and starts a detached process. Because of this I don't have the application enter the typical "Qt loop" by calling "return a.exec()" at the end of main and instead simply do "return 0" since everything I want the program to do is taken care of by the end of main.

    Anyway...

    ExitTest.pro

    QT       += core gui
    greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
    TARGET = ExitTest
    TEMPLATE = app
    DEFINES += QT_DEPRECATED_WARNINGS
    CONFIG += c++17
    QMAKE_CXXFLAGS += /std:c++17
    SOURCES += \
            main.cpp \
        basicutilities.cpp
    HEADERS += \
        basicutilities.h
    qnx: target.path = /tmp/$${TARGET}/bin
    else: unix:!android: target.path = /opt/$${TARGET}/bin
    !isEmpty(target.path): INSTALLS += target
    

    basicutilities.h

    #ifndef BASICUTILITIES_H
    #define BASICUTILITIES_H
    
    #include <QString>
    
    class BasicUtilities
    {
    public:
        static inline const QString MMRB_FORMAT = "hi";
    };
    
    #endif // BASICUTILITIES_H
    

    basicutilities.cpp

    #include "basicutilities.h"
    

    main.cpp

    #include <QApplication>
    #include "basicutilities.h"
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
    
        return 0;
    }
    

    Building the release of this program and starting it as is causes it to run completely but then after "return 0" I see the following in the Application Output window:

    13:54:12: Starting C:\Users\Chris\Engineering\Development\Qt\Qt 5.12\build-ExitTest-Desktop_Qt_5_12_3_MSVC2017_32bit_Static-Release\release\ExitTest.exe...
    13:54:13: The program has unexpectedly finished.
    13:54:13: The process was ended forcefully.
    13:54:13: C:/Users/Chris/Engineering/Development/Qt/Qt 5.12/build-ExitTest-Desktop_Qt_5_12_3_MSVC2017_32bit_Static-Release/release/ExitTest.exe crashed.
    

    Starting the exe through the command prompt and then entering"echo %errorlevel%" returns "-1073740940" which seems to fall under 0xC0000374/heap corruption. If the program is run enough times this way, eventually the Fault Tolerant Heap picks it up and when starting the program afterwards it exits normally with code "0", but obviously I'd like to solve this strange bug and not rely on my exe always being added to the FTH system. I can add code between the QApplication and return lines and it is all executed successfully as it always crashes on/after the "return 0" line and therefore is definitely related to clean-up. Unfortunately I cannot step into that line to see if I can get any additional info as the problem does not occur on the Debug build.

    Any of following single changes alone cause the problem to disappear and the program to exit normally with exit code "0":

    • Removing basicutilities.cpp from the project or commenting out the "#include basicutilities.h" line in that file
    • Removing "#include basicutilities.h" from main.cpp (similar to the above; as long as the header is only included once the program exits normally)
    • Commenting out/removing the variable "MMRB_FORMAT"
    • Changing the variable "MMRB_FORMAT" to just a constant (removing "static inline")
    • including <string> and changing the variable "MMRB_FORMAT" to "static inline const std:string"
    • Changing the variable "MMRB_FORMAT" to any other type while maintaining "static inline const" (even another QObject like QChar works)
    • Changing the build type from Release to Debug

    Again while this code may seem odd/random, I'm having this problem in a much larger program and this is just the minimum code required for me to reproduce the issue.

    Qt: Tested with 5.12.1, 5.12.3, and 5.12.3 static
    C++: MSVC2017, C++ 17
    OS: Windows 10

    I could just ignore this since the program does everything I want it to but this is still a pretty annoying bug. Any idea what the heck is going on?


  • Lifetime Qt Champion

    Hi
    Just to let you know its reproducible.

    21:46:55: Starting F:\Dropbox_qtprojects\build-exittest-Desktop_Qt_5_12_3_MSVC2017_64bit-Release\release\ExitTest.exe
    21:46:57: The program has unexpectedly finished.
    21:46:57: The process was ended forcefully.
    21:46:57: F:/Dropbox/_qtprojects/build-exittest-Desktop_Qt_5_12_3_MSVC2017_64bit-Release/release/ExitTest.exe crashed.

    My best bet is visual studio generate some code that's not 100% compatible with QString in this use
    case but one would have to study the assembler to get a clue if that's that.



  • Not seeing this under these conditions:

    Ubuntu 18.04
    gcc 7.3.0
    64 bit
    Qt 5.12.2
    

    Had to change this to compile under Linux:

    QMAKE_CXXFLAGS += -std=c++17
    

    Hopefully that narrows your search for answers.



  • It is seeming the issue is unique to the MSVC compiler. I can just use std::string and convert to QString when the variable is first used, or just define the strings in the cpp and avoid inline. Everything is written nicely the way it is now though. I'll see if I can figure out more, though since its Release only its almost certainly due to optimizations. Unfortunately, I'm not experienced enough with assembly I didn't write myself to probably learn much from looking at the disassembly view.

    It's just annoying. I'll see what I can do and if anyone else has anything to say I'll be here lol.