Ошибка сегментации при создании Qt QApplication с новым

У меня есть программа, в которой 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 и в Интернете - безуспешно. Спасибо


person Bush Walker    schedule 07.03.2014    source источник
comment
Я скопировал ваш код, и он работает в реальном режиме, но вылетает в режиме отладки. Я использую Qt 4.6.2, CentOS 6.2 x64   -  person hank    schedule 07.03.2014
comment
Вы читали ошибки / трассировку стека? Не могли бы вы показать свой код, где вы называете свой strlen? Почему вы не проверяете p != 0 в своем strlen?   -  person Dmitry Sazonov    schedule 07.03.2014
comment
Дмитрий, обратка чуть выше. Я не вызываю strlen. Это делает системная функция XSetCommand. Я просто заменил системный strlen на свой, чтобы проверить, что там передается. Изначально в программе не было strlen и вылет был. Как следует из вышеизложенного, XSetCommand передает поврежденный указатель (p=0x11). Так что проверка p!=0 здесь бесполезна. Опять же, мой strlen здесь только для целей отладки. Спасибо   -  person Bush Walker    schedule 07.03.2014
comment
Хэнк, здорово, что это воспроизводимо не только у меня. Спасибо.   -  person Bush Walker    schedule 07.03.2014


Ответы (2)


QApplication имеет особое (и ИМХО сомнительное) требование для argc и argv. См. документацию:

Внимание! Данные, на которые ссылаются argc и argv, должны оставаться действительными в течение всего времени существования объекта QApplication. Кроме того, argc должен быть больше нуля, а argv должен содержать хотя бы одну допустимую строку символов.

Если argc и argv уничтожаются во время выполнения, возникает неопределенное поведение. Он может работать на одних платформах и давать сбои на других. Измените свой код соответствующим образом и проверьте, решает ли он вашу проблему.

person scai    schedule 07.03.2014
comment
Этот. В частности, int argc, переданный QApplication, является аргументом функции, т.е. локальная переменная. - person hyde; 07.03.2014
comment
Scali, обратите внимание, что я не изменяю argc и argv. Я не понимаю, как они могут стать недействительными. И я уже читал этот фрагмент документации. Вкратце то, что моя программа делает перед созданием объекта QApplication, вызывает самые пустые функции и несколько манипуляций с std::string. Я попытался поставить конструкцию QApplication в начало основной функции - без сбоев. Но, возможно, это просто скрывает проблему, потому что я не понимаю, почему то, что есть сейчас, отличается. - person Bush Walker; 07.03.2014
comment
Ваши argc и argv привязаны ко времени существования конструктора Process и не остаются действительными в течение всего времени существования объекта QApplication. Вместо этого попробуйте передать их по ссылке в Process, а также в GUI, чтобы они не были скопированы. Или используйте статические или глобальные переменные (на мой взгляд, не очень хорошее решение). - person scai; 07.03.2014
comment
Ясно, argc QApplication — это ссылка... Это может все объяснить. Довольно контринтуитивный дизайн. Большое-большое спасибо вам, Хайд и Скали!!! - person Bush Walker; 07.03.2014
comment
Работает знаю. Спасибо!!! У меня было так много подозреваемых, почему моя исходная программа дала сбой, включая boost asio, многопоточность, тонны моего собственного кода и много других вещей. И виновником был маленький символ «&» в конструкторе QApplication. :-))) - person Bush Walker; 07.03.2014
comment
@BushWalker Добро пожаловать. Вы хотите принять мой ответ? :) - person scai; 07.03.2014
comment
Готово. Извини, вчера был слишком измотан. - person Bush Walker; 08.03.2014
comment
Не вопрос, просто впечатление. Я думал, почему Qt разработан таким образом, что переменная, на которую ссылается argc, должна быть активной в течение всего срока службы QApplication. Я понимаю, почему тот, на который указывает argv, должен быть живым, потому что это массив, который в любом случае обычно сохраняется во время выполнения C. Я понимаю, почему argc конструктора QApplication является ссылкой, потому что Qt может принимать свои собственные аргументы командной строки, а клиентский код может получать обновленные значения argc/argv после вызова конструктора QApplication. - person Bush Walker; 09.03.2014
comment
Но это не объясняет, почему судья argc должен быть жив после этого. Должен ли Qt периодически пересматривать аргументы командной строки или даже обновлять их время от времени? Это было бы странно. Мой сбой означает, что действительно он обновил argc после построения. Кроме того, хранение ссылки на argc обычно занимает не меньше памяти, чем хранение argc по значению. - person Bush Walker; 09.03.2014
comment
Я понятия не имею, почему Qt принял такое решение. Нет, нет причин пересматривать их позже, потому что они не могут измениться во время выполнения. - person scai; 09.03.2014
comment
Я подозреваю, что использование ссылочного параметра для argc было сделано для того, чтобы люди не передавали константы, например. 0 - что нарушит их код. Я не понимаю, почему они не берут собственную копию параметров командной строки. Поскольку параметр 0 содержит имя файла приложения и путь к каталогу, он используется во время выполнения для различных целей (например, расположение файла настроек и т. д.). - person Den-Jason; 14.07.2020
comment
Ага. Это сработало и для меня... Я передал argc по значению другой функции, которая передала его по значению в QApplication и вызвала случайные segfaults. Как только я изменил все это на передачу по ссылке - проблема исчезла. Спасибо! - person so.very.tired; 18.10.2020

Я столкнулся с той же проблемой и реализовал следующий класс для сохранения параметров командной строки:

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

}
person Den-Jason    schedule 14.07.2020