Прагма-пакет (push) без соответствующего pop приводит к разрушению стека - PullRequest
1 голос
/ 02 октября 2019

Я использовал #pragma pack(push, 2) в начале структуры в заголовочном файле, но забыл соответствующий #pragma pack(pop). После включения этого заголовочного файла я включил fstream. При создании объекта ofstream я вижу разрушение стека. Подробности точного сценария и кода следующие:

Я проходил курс C ++ и написал код для проекта. Моя программа зависала из-за разрушения стека. Я пытался найти какие-либо очевидные ошибки переполнения, но не смог их найти. Я изменил почти весь мой код, чтобы он напоминал код, предоставленный инструктором. Единственная разница заключается в порядке включения заголовочных файлов. Я включил заголовочные файлы, за которыми следовал fstream, в то время как инструктор включил заголовочный файл fstream вверху. Тем не менее я получал ту же проблему. Поэтому я изменил даже порядок заголовочных файлов и вуаля, проблема исчезла.

Поскольку это было странно для меня, я попытался локализовать проблему в своем коде.
Я вставил операторы печати по всему коду, чтобы найтифункция, где происходило разрушение стека.

Найдя функцию, я использовал gdb, чтобы установить отслеживание канареечного значения, используемого моей программой для проверки разрушения стека. Я обнаружил разрушение стека в конструкторе объекта ofstream.

В этот момент я знал, что какой-то заголовочный файл, включенный до того, как fstream мешал ему. Итак, теперь я проверил все мои заголовочные файлы на наличие каких-либо глупых ошибок и обнаружил структуру, которой предшествует #pragma pack(push, 2), но за которой не следует соответствующая #pragma pack(pop). Эта структура должна была быть записана в виде двоичного файла. Исправление этой проблемы решило проблему.

Поскольку весь проект не имеет значения, я воспроизвел проблему с помощью простого фрагмента кода. Хотя проблема решена, я хотел бы знать, почему это произошло. Я понимаю, что директива pragma pack использовалась для того, чтобы компилятор не вставлял отступы внутри структуры, так как структура должна была быть записана в двоичный файл. Пропуск pack(pop) в конце структуры использует то же самое для всех последующих заголовочных файлов. Но может ли это привести к тому, что конструктор ofstream будет писать поверх фрейма стека?

Я использую gcc v7.4.0 в Ubuntu 18.04.

/* code.cpp */ 
#include "header.h"
#include <fstream>
using namespace std;

int main(){
    ofstream fout;
    fout.open("file.ext", ios::out|ios::binary);
    fout.close();
    return 0;
}

Файл заголовка

#ifndef HEADER_H_
#define HEADER_H_

#pragma pack(push, 2)
struct something{
    int a;
};
//#pragma pack(pop)
//Uncommenting the above line solves the problem

#endif

1 Ответ

1 голос
/ 02 октября 2019

Поскольку pragma pack влияет на компоновку экземпляров классов, ваша версия ofstream не выглядит так же, как та, которая использовалась для компиляции вашей стандартной библиотеки. Формально, у вас есть нарушение ODR, и это приводит к неопределенному поведению.

На практике функции из вашей среды выполнения C ++ работают вслепую с данными с неправильной компоновкой, так что имеет смысл только запуск фейерверка. В частности, ожидается разрушение стека, поскольку упакованный класс короче распакованного, поэтому запись в конец экземпляра переполняет пространство стека main, зарезервированное для него.

...