9

I have a program where QApplication is created with the new operator. It crashes for unknown reason. I use RedHat Linux, G++ 4.8.2, Qt 4.7.2 which was built with the same compiler.

This source contains many looking useless but harmless pieces, e.g the 'func' function with four unused arguments. If I try to remove them and simplify the program a bit further the crash can't be reproduced anymore which of course doesn't mean the problem has been solved.

The crash happens in the function strlen, which is called from system function XSetCommand. Adding my own simple implementatioon allowed me to see that strlen receives a corrupted pointer, see below.

#include <QApplication>
#include <QMessageBox>

void func(void *, void *, void *, void *)  {}

struct Gui
{
  QApplication qApplication;
  Gui(int argc, char ** argv) : qApplication(argc, argv)  {}
};

struct Process
{
  Process(const std::string &, int argc, char ** argv) {
    func(ptr(), ptr(), ptr(), ptr());
    std::string recent;
    std::string path = std::string("Hi!");
    recent           = std::string("Hi!");
    m_message        = std::string("Hi!");
    m_gui = new Gui(argc, argv);
  }
  ~Process()  { delete m_gui; }

  int exec(void) {
    return QMessageBox::warning(0, "Exit", "Sure?", QMessageBox::Ok);
  }

  void * ptr(void)  { return 0; }

  Gui       * m_gui;
  std::string m_message;
};

std::size_t strlen(const char * p) {
  std::size_t s = 0;
  while (*p++)
    s++;
  return s;
}

int main(int argc, char ** argv) {
  Process process("SomeString", argc, argv);
  return process.exec();
}

Crash backtrace:

#0  0x0000000000400f13 in strlen (p=0x11 <Address 0x11 out of bounds>) at /home/alex/test/megaCrash/myprog.cpp:39
#1  0x0000003880a41922 in XSetCommand () from /usr/lib64/libX11.so.6
#2  0x0000003880a45fa6 in XSetWMProperties () from /usr/lib64/libX11.so.6
#3  0x00002aaaaad2e5ea in QWidgetPrivate::create_sys(unsigned long, bool, bool) () from /usr/local/Trolltech/Qt-4.7.2/lib/libQtGui.so.4
#4  0x00002aaaaace735d in QWidget::create(unsigned long, bool, bool) () from /usr/local/Trolltech/Qt-4.7.2/lib/libQtGui.so.4
#5  0x00002aaaaacef73a in QWidget::setVisible(bool) () from /usr/local/Trolltech/Qt-4.7.2/lib/libQtGui.so.4
#6  0x00002aaaab11de5e in QDialog::setVisible(bool) () from /usr/local/Trolltech/Qt-4.7.2/lib/libQtGui.so.4
#7  0x00002aaaab11d9e6 in QDialog::exec() () from /usr/local/Trolltech/Qt-4.7.2/lib/libQtGui.so.4
#8  0x00002aaaab13bb40 in ?? () from /usr/local/Trolltech/Qt-4.7.2/lib/libQtGui.so.4
#9  0x00002aaaab13bc7f in QMessageBox::warning(QWidget*, QString const&, QString const&, QFlags<QMessageBox::StandardButton>, QMessageBox::StandardButton) () from /usr/local/Trolltech/Qt-4.7.2/lib/libQtGui.so.4
#10 0x0000000000401514 in Process::exec (this=0x7fffffffe4e0) at /home/alex/test/megaCrash/myprog.cpp:27
#11 0x0000000000400fb6 in main (argc=1, argv=0x7fffffffe5f8) at /home/alex/test/megaCrash/myprog.cpp:47

As you see it happens in strlen. And that's why I included my own implementation for it. Its argument p points to nowhere. I tried to reproduce it with debug build of Qt - no luck. This all makes me think that there is a nasty memory corruption here. But where could it happen? I do only innocent things here.

I use CMake to build it:

cmake_minimum_required (VERSION 2.8) 

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-unused-local-typedefs -fpic -fvisibility=hidden -m64")
set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -ggdb -DDEBUG -gdwarf-2 -fstack-protector-all")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-z,defs")

include_directories(${CMAKE_CURRENT_SOURCE_DIR})

find_package(Qt4 REQUIRED)
include(${QT_USE_FILE})

add_executable(myprog myprog.cpp)

target_link_libraries(myprog ${QT_LIBRARIES})

I tried to find hints in Qt documentation and the Internet - no luck. Thank you

Bush Walker
  • 91
  • 1
  • 5
  • 1
    I've copy-pasted your code and it works in realse mode but crashes in debug mode. I use Qt 4.6.2, CentOS 6.2 x64 – hank Mar 07 '14 at 07:58
  • Did you read errors / stacktrace? Could you show your code, where do you call your `strlen`? Why you don't check `p != 0` in your `strlen`? – Dmitry Sazonov Mar 07 '14 at 08:15
  • Dmitry, the backtrace is just above. I don't call strlen. It is the system function XSetCommand which does so. I just replaced system's strlen with my own to check what is passed there. Originally the program didn't contain strlen and the crash was there. As it follows from above XSetCommand passes a corrupted pointer (p=0x11). So checking p!=0 would not be useful here. Again my strlen here is for debugging purposes only. Thanks – Bush Walker Mar 07 '14 at 08:25
  • Hank, it is great that this is reproducible not only for me. Thanks. – Bush Walker Mar 07 '14 at 08:26

2 Answers2

19

QApplication has a special (and IMHO questionable) requirement for argc and argv. See the documentation:

Warning: The data referred to by argc and argv must stay valid for the entire lifetime of the QApplication object. In addition, argc must be greater than zero and argv must contain at least one valid character string.

If argc and argv get destroyed during runtime, undefined behavior occurs. It may work on some platforms and it will crash on others. Change your code accordingly and check if it fixes your problem.

scai
  • 20,297
  • 4
  • 56
  • 72
  • 2
    This. In particular, `int argc` passed to `QApplication` is a function argument, ie. a local variable. – hyde Mar 07 '14 at 08:37
  • Scali, please, notice that I don't modify argc and argv. I don't see how they can become invalid. And I've read that fragment of documentation before. In a nutshell what my program does before constructing the QApplication object is calling moslty empty functions and a few std::string manipulation. I tried to put QApplication consruction into beginning of the main function - no crash. But maybe this just hides the problem because I don't understand why having it where it is now is any different. – Bush Walker Mar 07 '14 at 08:38
  • 1
    Your `argc` and `argv` are bound to the lifetime of the `Process` constructor and don't stay valid for the entire lifetime of the `QApplication` object. Try to pass them by reference instead, to `Process` as well as to `GUI` so that they don't get *copied*. Or use static or global variables (not a nice solution in my opinion). – scai Mar 07 '14 at 08:44
  • 1
    I see, QApplication's argc is a reference... This can explain everything. Pretty counter-intuitive design. Many many thanks to you Hyde and Scali!!! – Bush Walker Mar 07 '14 at 08:47
  • 2
    It works know. Thanks!!! I had so many suspects why my original program crashed including boost asio, threading, tons of my own code, and lot of other stuff. And the culprit was a small '&' symbol in QApplication's constructor. :-))) – Bush Walker Mar 07 '14 at 08:50
  • Done. Sorry was too exhausted yesterday. – Bush Walker Mar 08 '14 at 11:04
  • Not a question, just impression. I have been thinking why Qt is designed in such a way that the variable referred by argc is required to be alive for lifetime of the QApplication. I see why the one pointed by argv should be alive - bacause it is an array, which anyway typically is stored by C run-time. I can see why QApplication constructor's argc is a reference - because Qt can take out its own command line arguments, and the client code can receive updated argc/argv values after calling QApplication constructor. – Bush Walker Mar 09 '14 at 10:19
  • But this doesn't explain why the argc's referee should be alive after that. Is Qt supposed to reexamine the command line arguments periodically or even update them every now and then? That would be strange. My crash means that indeed it updated the argc after construction. Also storing a reference to argc typically doesn't take less memory than storing argc by value. – Bush Walker Mar 09 '14 at 10:19
  • I have no idea why Qt did this decision. No, there is no reason to reexamine them later because they cannot change during runtime. – scai Mar 09 '14 at 17:38
  • I suspect the use of a reference parameter for argc was to prevent people passing constants e.g. 0 - which would upset their code. I don't understand why they don't take their own copy of the command line parameters. Since parameter 0 contains the application filename and directory path this is used at runtime for various things (e.g. settings file location etc). – Den-Jason Jul 14 '20 at 20:54
  • 1
    Yep. This worked for me as well... I passed argc by value to another function that passed it by value to QApplication and that raised random segfaults. Once I changed it all to be passed by reference - problem is gone. Thanks! – so.very.tired Oct 18 '20 at 11:17
0

I encountered the same problem and implemented the following class to persist the command line parameters:

.h:

#pragma once

#include <string>
#include <vector>

using std::string;
using std::vector;

class PersistentArgs
{
protected:
    vector<string> mArgStrs;

public:
    int mArgc;
    vector<char*> mArgv;

    PersistentArgs() = delete;
    PersistentArgs(int argc, char* argv[]);
    virtual ~PersistentArgs();
};

.cpp:

#include "PersistentArgs.h"

PersistentArgs::PersistentArgs(int argc, char* argv[])
{
    mArgc = argc;

    for (int i = 0;  i < argc;  i++)
    {
        string arg(argv[i]);
        mArgStrs.push_back (arg);
    }

    for (int i = 0;  i < argc;  i++)
    {
        mArgv.push_back ((char*)mArgStrs[i].c_str());
    }

    mArgv.push_back (nullptr);
}

PersistentArgs::~PersistentArgs()
{
    mArgv.clear();
    mArgStrs.clear();
}

usage:


PersistentArgs* args;


void atStart (int argc, char* argv[])
{
    args = new PersistentArgs (argc, argv);
    QApplication* qtApp = new QApplication (args->mArgc, &args->mArgv[0]);

}
Den-Jason
  • 2,395
  • 1
  • 22
  • 17