C 如何在读取文件时阻止堆栈缓冲区溢出?

C 如何在读取文件时阻止堆栈缓冲区溢出?,c,C,我从一个.txt文件中读取数据,将其保存到一个char数组中,数组大小与文件本身相同。这足以阻止不受控制的堆栈缓冲区溢出发生吗 我已经尝试过使用固定大小的缓冲区,但现在我明白了这正是发生溢出的原因 FILE *inputFP = NULL; inputFP = fopen(input_file, "r"); if (inputFP == NULL) return 1; fseek(inputFP, 0, SEEK_END); long fileSize = ftell(inputFP)

我从一个
.txt
文件中读取数据,将其保存到一个
char
数组中,数组大小与文件本身相同。这足以阻止不受控制的堆栈缓冲区溢出发生吗

我已经尝试过使用固定大小的缓冲区,但现在我明白了这正是发生溢出的原因

FILE *inputFP = NULL;

inputFP = fopen(input_file, "r");
if (inputFP == NULL)
    return 1;
fseek(inputFP, 0, SEEK_END);
long fileSize = ftell(inputFP);
fseek(inputFP, 0, SEEK_SET);
char buffer[fileSize+20];

while ((ch = fgetc(inputFP)) != EOF) 
{
     buffer[i] = ch;
     i++;
}

fprintf(outputFP, buffer, "%s");
一切正常,但我担心输入文件太大,会发生不好的事情

我从一个.txt文件中读取数据,将其保存到与文件大小相同的字符数组中。这足以阻止不受控制的堆栈缓冲区溢出发生吗

通过避免在数组外写入,可以防止缓冲区溢出。它们是非常糟糕的东西

当耗尽线程/进程/程序中为堆栈分配的可用页面时,会发生堆栈溢出。通常,堆栈的大小非常小(按1 MiB的顺序考虑)。这些也很糟糕,但它们只会使你的程序崩溃

这是一个可变长度数组(VLA)。它分配动态(编译时未知)堆栈空间。如果正确使用,则不会出现缓冲区溢出,但会出现堆栈溢出,因为文件大小是无限的



您应该做的不是使用VLA,而是使用固定大小的缓冲区并读取文件的块,而不是整个文件。如果确实需要将整个文件存储在内存中,可以尝试为其分配堆内存(
malloc
),或者将其映射到内存(
mmap
)。

如注释中所述,
malloc()
可以防止缓冲区溢出


作为旁注,始终尝试逐步读取文件,不要将其完全加载到内存中。在大文件的情况下,您将遇到麻烦,因为您的进程将无法分配那个数量的内存。例如,完全在内存中加载10GB视频文件几乎是不可能的。此外,通常情况下,每个大文件的数据都是结构化的,因此您可以以小块的形式逐步读取。

限制缓冲区溢出的方法是仔细控制写入任何缓冲区的内存量

如果你说(用伪代码):

然后就有了一个巨大的、巨大的、潜在的缓冲区溢出问题。根本的问题不是您分配的缓冲区恰好与文件大小匹配(尽管这可能是个问题)。问题不在于您提前计算了文件的大小(尽管这可能是个问题)。不,根本的问题是在假设通话中

read_entire_file_into(buffer, filename);
您没有告诉
将整个文件读入
函数缓冲区有多大。这可能是
将整个文件读入
函数的问题,而不是您的问题,但底线是,将任意数量的数据写入固定大小的缓冲区而不允许指定缓冲区大小的函数正在等待灾难的发生。这就是为什么臭名昭著的
get()
函数已从C标准中删除。这就是为什么strcpy
strcpy
函数不被推荐的原因,并且只能在仔细控制的情况下使用(如果有的话)。这就是为什么不推荐使用
%s
%[…]
格式说明符到
scanf

另一方面,如果您的代码看起来更像这样:

filesize = compute_file_size(filename);
buffer = malloc(some_random_number);
read_entire_file_into_with_limit(buffer, some_random_number, filename);
--这里的要点是(同样是假设的)
将整个文件读入带限制的函数中可以知道缓冲区有多大——在这种情况下,即使
计算文件大小
函数得到错误的答案,即使您对
缓冲区使用完全不同的大小
,您已确保不会使缓冲区溢出

从假想的伪代码转移到真实的代码:您没有显示代码中实际从文件中读取内容的部分。如果您正在调用
fread
fgets
来读取文件,并且如果您正确地将
fileSize
变量作为
缓冲区的大小传递给这些函数,那么您已经充分地保护了自己,防止了缓冲区溢出。但另一方面,如果您在循环中调用
gets
,或调用
getc
,并将字符写入
缓冲区
,直到达到
EOF
(但不检查针对
文件大小
)读取的字符数),则确实存在巨大的潜在缓冲区溢出问题,你需要重新思考你的策略,重写你的代码


代码中还有一个次要问题,就是将缓冲区分配为堆栈上的可变长度数组(VLA)(可以这么说)。但真正的大堆栈分配数组将失败——不是因为缓冲区溢出,而是因为它们确实太大了。因此,如果您真的想将整个文件读入内存,那么您肯定要使用
malloc
,而不是VLA。(如果您不介意依赖于操作系统的解决方案,您可能需要研究内存映射文件技术,例如
mmap
调用。)


你已经更新了你的代码,所以现在我可以更新这个答案了。你发布的文件读取循环是危险的——事实上,这正是我在写这篇文章时的想法

在循环中调用
getc
,并将字符写入缓冲区,直到到达
EOF
(但不根据
文件大小检查读取的字符数)

您应该将该代码替换为

while ((ch = getc(inputFP)) != EOF) 
{
     if(i >= fileSize) {
         fprintf(stderr, "buffer overflow!\n");
         break;
     }

     buffer[i] = ch;
     i++;
}

while((ch=getc(inputFP))!=EOF&&i

或者,你可以采取完全不同的方法。大多数情况下,不需要一次将整个文件读入内存。大多数情况下,一次读取一行文件、一次读取一个块、甚至一次读取一个字符,在移动前处理并写出每一个片段,这就足够了
filesize = compute_file_size(filename);
buffer = malloc(some_random_number);
read_entire_file_into_with_limit(buffer, some_random_number, filename);
while ((ch = getc(inputFP)) != EOF) 
{
     if(i >= fileSize) {
         fprintf(stderr, "buffer overflow!\n");
         break;
     }

     buffer[i] = ch;
     i++;
}
while ((ch = getc(inputFP)) != EOF && i < fileSize) 
{
     buffer[i] = ch;
     i++;
}