Segfault на fprintf с (предположительно) действительным указателем FILE - PullRequest
0 голосов
/ 26 ноября 2018

Итак, в этом довольно большом исходном файле у меня есть следующий раздел, скажем, в функции foo, которая вызывается из main:

FILE *logfile = NULL
if (log_engabled) {
    char fname[30];
    snprintf(fname, 30, ".logs/%d.txt", time(NULL));
    logfile = fopen(fname, "w");
}
fprintf(logfile, "test\n");

Это выполняется без проблем.(Да, я должен остерегаться logfile == NULL, но для краткости я это опустил).В моих тестовых прогонах log_file == 0x840fa50 и &log_file == 0x7ffffffedca8.

Затем я вызываю другую функцию, bar, которая принимает несколько аргументов, включая FILE **log_stream.Его тело выглядит так:

if (*log_stream)
    fprintf(*log_stream, "test\n");
/*a bunch of other stuff, including more fprintf calls*/

Теперь вот странная часть.Если я позвоню bar из foo непосредственно после указанного выше сегмента кода, все пройдет гладко.Однако на более позднем этапе в foo снова вызывается bar с другими параметрами, но с тем же указателем logfile.Тогда я получаю SIGSEGV прямо на fprintf звонок.Я проверил с помощью gdb, и расположение указателя точно такое же, как и значение, на которое он указывает (и я никогда не называю fclose между).

Что может быть причиной такого поведения?Это должно быть связано с кодом, который я выполняю между вызовами bar, но ни одно из этих утверждений не включает logfile во что-либо, кроме fprintf вызовов.

Может быть, я вызываю segfaultгде-то, кроме вызова fprintf, и GDB дает мне неправильный номер строки, но я скомпилировал с помощью gcc -O0 и -g, и когда я перемещаю оператор print, появляется сообщение об ошибке.

Я также попытался объявить logfile глобальной переменной и не передавать ее в bar, но безрезультатно.Любая помощь приветствуется.

РЕДАКТИРОВАТЬ:

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

foo() {
    FILE *logfile = fopen("log.txt", "w");
    fprintf(logfile, "test1");
    memory_corrupting_function();
    fprintf(logfile, "test2");
}

Первый fprintf снова проходит гладко, а второй вызывает SIGSEGV.Итак, перефразируя мой первоначальный вопрос: если я не вызываю fprintf снова, то, как программы продолжают нормально работать, почему произошла ошибка, когда я вызываю fprintf, а не когда пишу в недопустимую область памяти?

Есть ли даже ответ или это просто неопределенное поведение, которое меняется в разных реализациях?

1 Ответ

0 голосов
/ 26 ноября 2018

Вы не можете вернуть адрес локальной переменной.Скорее всего, logfile будет существовать в стеке.После возврата foo очень велика вероятность того, что любое значение logfile будет перезаписано.Указатель файла, возвращаемый fopen(), останется действительным, пока вы не fclose() файл.Но переменная logfile выйдет из области видимости, как только функция вернется.

Просто верните значение logfile:

FILE * foo() {
    FILE *logfile = NULL;
    //....
    return logfile;
}

void bar() {
    FILE *alsoLogfile = foo();
    // ...
}

В своем вопросе вы заявите: ЕслиЯ вызываю bar из foo непосредственно после указанного выше сегмента кода, все работает гладко. Скорее всего, это происходит потому, что часть стека, в которой существовал logfile, еще не была перезаписана.Использование адреса вне области действия - неопределенное поведение, поэтому может произойти все что угодно.

...