使用new创建Qt QApplication时出现分段错误

9
我有一个程序,其中使用new运算符创建了QApplication。它因未知原因崩溃。我使用的是RedHat Linux、G++ 4.8.2和Qt 4.7.2,它们都是用相同的编译器构建的。
这个源代码包含许多看起来无用但无害的部分,例如具有四个未使用参数的“func”函数。如果我尝试删除它们并进一步简化程序,就无法再复现崩溃了,当然这并不意味着问题已经解决。
崩溃发生在strlen函数中,该函数从系统函数XSetCommand调用。添加自己的简单实现使我可以看到strlen接收到了一个损坏的指针,如下所示。
#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();
}

崩溃回溯:

#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

正如您所见,这发生在strlen中。这就是为什么我包含了自己的实现方式。它的参数p指向无处。我尝试使用Qt的调试版本进行复制,但没有成功。所有这些让我想到存在着恶意内存损坏。但是它可能发生在哪里呢?我只是在这里做了一些无辜的事情。

我使用CMake来构建它:

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

我在Qt文档和互联网上寻找线索,但没有找到。谢谢。

1
我已经复制粘贴了你的代码,在实际模式下它可以运行,但在调试模式下会崩溃。我使用的是Qt 4.6.2,CentOS 6.2 x64。 - hank
你读了错误/堆栈跟踪吗?你能展示一下你调用 strlen 的代码吗?为什么你在 strlen 中不检查 p != 0 - Dmitry Sazonov
Dmitry,回溯信息就在上面。我没有调用strlen函数,而是系统函数XSetCommand这样做的。我只是用自己的函数替换了系统的strlen函数,以检查传递了什么。最初程序中并没有包含strlen函数,但崩溃就发生在那里。从上面可以看出,XSetCommand传递了一个损坏的指针(p=0x11)。因此,在这里检查p!=0是没有用的。再次说明,我这里的strlen函数仅用于调试目的。谢谢。 - Bush Walker
Hank,太好了,这个问题不仅对我来说是可复现的。谢谢。 - Bush Walker
2个回答

19

QApplication有一个特殊的(并且在我看来有争议的)要求,需要使用argcargv。请参见文档

警告:argc和argv所引用的数据必须在QApplication对象的整个生命周期中保持有效。此外,argc必须大于0,argv必须包含至少一个有效的字符字符串。

如果在运行时销毁了argcargv,则会导致未定义的行为。它可能在某些平台上工作,并且在其他平台上崩溃。相应地更改您的代码,并检查是否解决了问题。


2
这里特别指出,传递给 QApplicationint argc 是一个函数参数,即局部变量。 - hyde
1
你的 argcargv 只在 Process 构造函数的生命周期内有效,并不在整个 QApplication 对象的生命周期内保持有效。尝试通过引用将它们传递给 ProcessGUI,这样它们就不会被 复制。或者使用静态或全局变量(在我看来并不是一个好的解决方案)。 - scai
1
我明白了,QApplication的argc是一个引用... 这可以解释一切。相当反直觉的设计。非常感谢你Hyde和Scali!!! - Bush Walker
2
现在它可以工作了。谢谢!!!我一开始的程序崩溃有很多嫌疑,包括boost asio、线程、大量自己的代码和其他很多东西。而罪魁祸首竟然是QApplication构造函数中的一个小'&'符号。:-))) - Bush Walker
1
没错,这对我也起作用了...我将argc按值传递给另一个函数,该函数按值传递给QApplication,导致随机segfaults。 一旦我改为全部按引用传递 - 问题就解决了。 谢谢! - so.very.tired
显示剩余7条评论

0
我遇到了同样的问题,并实现了以下类来持久化命令行参数:
.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();
}

用法:


PersistentArgs* args;


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

}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接