fprintf上的Segfault具有(应该是)有效的FILE指针

时间:2018-11-26 13:44:53

标签: c file-io segmentation-fault

因此在这个非常大的源文件中,我有以下部分,假设在函数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

也许我导致了fprintf调用之外的段错误,并且gdb给了我错误的行号,但是我用gcc -O0和-g进行了编译,并且当我移动print语句时,错误指示。

我还尝试将logfile声明为全局变量,并且没有将其传递给bar,这无济于事。感谢您的帮助。

编辑:

我进行了一些挖掘和* drum roll *-问题是由无符号整数下溢引起的,该溢出导致超出范围的数组写入。更具体地说:

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

第一个fprintf再次平稳运行,第二个触发SIGSEGV。因此,重新解释一下我的原始问题:如果不再次调用fprintf,看程序如何继续正常运行,为什么在我调用fprintf时而不是在我写到无效的内存位置?

甚至有答案吗?还是只是不确定的行为会在实现之间发生变化?

1 个答案:

答案 0 :(得分:1)

您不能返回局部变量的地址。 logfile很可能会存在于堆栈中。一旦foo返回,很有可能会将logfile持有的任何值覆盖。 fopen()返回的文件指针在您fclose()之前将一直有效。但是一旦函数返回,变量logfile将超出范围。

只需返回logfile的值:

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

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

在您的问题中,您声明:如果我在上述代码段之后直接从foo调用bar,则一切运行都将顺利进行。这很可能是因为堆栈的一部分位于logfile存在尚未被覆盖。使用范围外变量的地址是未定义的行为,因此任何事情都可能发生。