我正在读取.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");
一切正常,但是我担心输入文件太大,以至于发生不好的情况。
限制缓冲区溢出的方法是仔细控制写入任何缓冲区的内存量。
如果您说(用伪代码):
filesize = compute_file_size(filename);
buffer = malloc(filesize);
read_entire_file_into(buffer, filename);
那么您就会遇到一个巨大的,巨大的,潜在的缓冲区溢出问题。根本的问题不是您分配的缓冲区恰好与文件的大小完全匹配(尽管这可能是一个问题)。问题不在于您事先计算了文件的大小(尽管这可能是一个问题)。不,根本的问题是在假设的调用中
read_entire_file_into(buffer, filename);
您没有告诉read_entire_file_into
函数缓冲区有多大。这可能是read_entire_file_into
函数的问题,而不是您的问题,但最重要的是,将任意数量的数据写入固定大小的缓冲区而不允许指定该缓冲区的大小的函数正在等待灾难的发生。这就是为什么臭名昭著的gets()
功能已从C标准中删除的原因。这就是为什么不建议使用该strcpy
功能,并且只能在经过仔细控制的情况下使用(如果有的话)的原因。这就是不建议使用%s
和%[...]
格式说明符scanf
的原因。
另一方面,如果您的代码看起来像这样:
filesize = compute_file_size(filename);
buffer = malloc(some_random_number);
read_entire_file_into_with_limit(buffer, some_random_number, filename);
-关键是要再次read_entire_file_into_with_limit
告知(假设的)函数缓冲区的大小-在这种情况下,即使compute_file_size
函数得到了错误的答案,即使您使用完全不同的大小buffer
,您也可以ve确保不会溢出缓冲区。
从假设的伪代码转变为真实的实际代码:您没有显示代码中实际从文件中读取内容的部分。如果您正在调用fread
或fgets
读取文件,并且正确地将fileSize
变量作为大小传递给这些函数buffer
,那么您已充分保护自己免受缓冲区溢出的侵害。但是,另一方面,如果您正在调用gets
,或getc
循环调用并向其中写入字符,buffer
直到到达为止EOF
(但不检查反对的字符数fileSize
),那么您确实有很大的潜在缓冲区溢出问题,并且您需要重新考虑策略并重新编写代码。
您的代码存在第二个问题,即您将缓冲区作为可变长度数组(VLA)分配到堆栈上(可以这么说)。但是真正的大型堆栈分配数组将失败-不是因为缓冲区溢出,而是因为它们实际上太大了。因此,如果您实际上想将整个文件读入内存,则肯定要使用malloc
,而不是VLA。(而且,如果您不介意依赖于操作系统的解决方案,则可能需要研究内存映射文件技术,例如mmap
调用。)
您已更新代码,因此现在我可以更新此答案。您发布的文件读取循环很危险-实际上,这正是我写这篇文章时所想到的
调用
getc
循环并将字符写入缓冲区,直到到达为止EOF
(但不检查根据读取的字符数fileSize
)
您应该用以下任一代码替换该代码
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++;
}
或者,您可以采用完全不同的方法。大多数时候,不需要一次将整个文件读入内存。在大多数情况下,一次读取一行,一次读取一个块,甚至一次读取一个字符,处理并写出每个片段然后再进行下一个片段就足够了。这样,您可以处理任何大小的文件,而无需事先弄清楚文件的大小,也不需要分配大的缓冲区,也不需要担心溢出该缓冲区。
我今天没有时间向您展示如何执行此操作,但是在其他一些答案中也有提示和建议。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句