使用fseek和ftell确定文件大小是否存在漏洞?

| 我读过一些文章,展示了如何使用fseek和ftell来确定文件的大小。
FILE *fp;
long file_size;
char *buffer;

fp = fopen(\"foo.bin\", \"r\");
if (NULL == fp) {
 /* Handle Error */
}

if (fseek(fp, 0 , SEEK_END) != 0) {
  /* Handle Error */
}

file_size = ftell(fp);
buffer = (char*)malloc(file_size);
if (NULL == buffer){
  /* handle error */
}
我本打算使用此技术,但随后遇到了描述潜在漏洞的链接。 该链接建议改为使用fstat。有人可以对此发表评论吗?     
已邀请:
该链接是CERT提供的许多荒谬的C编码建议之一。它们的合理性基于C标准允许实现的自由,但是POSIX不允许这样做,因此在所有带有ѭ1的情况下都是不相关的。 POSIX要求:
fopen
\"b\"
修饰符无效,即文本和二进制模式的行为相同。这意味着他们对在文本文件上调用UB的担心是胡说八道。 文件具有通过写操作和截断操作设置的字节分辨率大小。这意味着他们对文件末尾的空字节随机数的担心是胡说八道。 遗憾的是,他们发布的所有此类废话,都很难知道哪些CERT出版物需要认真对待。真可惜,因为其中很多都是严肃的。     
如果您的目标是查找文件的大小,则绝对应该使用
fstat()
或它的朋友。这是一种更直接和更具表现力的方法-实际上,您是在要求系统告诉您文件的统计信息,而不是更加round回的fseek / ftell方法。 一个额外的提示:如果您只想知道文件是否可用,请使用
access()
,而不要打开文件甚至对其进行统计。这是许多程序员都不知道的甚至更简单的操作。     
不使用use1ѭ的原因是
fstat
是POSIX,而
fopen
ftell
fseek
是C标准的一部分。 可能有一个系统实现了C标准,但没有实现POSIX。在这样的系统上,ѭ1根本不起作用。     
我倾向于同意他们的基本结论,即您通常不应该直接在代码的主流中使用
fseek
/
ftell
代码-但您也可能不应该使用
fstat
。如果需要文件的大小,则大多数代码应使用带有清晰,直接名称的名称,例如
filesize
。 现在,最好在可用的地方使用ѭ1,而在Windows上(例如,通常不提供
fstat
的最明显的平台)上使用
FindFirstFile
来实现。 故事的另一面是,对于实际上源自CP / M的二进制文件,在ѭ10上有很多(大部分)限制,这些限制没有明确地在任何地方存储文件的大小。文本文件的结尾由控件Z发出信号。但是,对于二进制文件,您真正了解的只是用于存储文件的扇区。在最后一个扇区中,您有一些经常(但不总是)为零的未使用数据。不幸的是,可能有零是有意义的,和/或非零值是没有意义的。 如果整个C标准是在批准之前就编写的(例如,如果它于1988年开始并于1989年完成),则它们可能会完全忽略了CP / M。不管是好是坏,他们在大约1982年左右就开始了C标准的工作,当时CP / M仍被广泛使用,因此不能忽视。在CP / M消失时,许多决定已经做出,我怀疑有人想重新审视它们。 但是,对于当今的大多数人来说,这是没有意义的-没有大量工作,大多数代码都不会移植到CP / M。这是要处理的相对较小的问题之一。使现代程序仅在48K(或大约)内存中运行以用于代码和数据,这是一个更为严重的问题(要使海量存储具有最大兆字节左右的存储容量将是另一个严重的问题)。 CERT确实有一个好处:您可能不应该(通常这样做)找到文件的大小,分配那么多的空间,然后假定文件的内容适合该文件。即使fseek / ftell可以为您提供现代系统正确的大小,但在实际读取数据时该数据可能已过时,因此无论如何您可能会溢出缓冲区。     
根据C标准,第7.21.3节:   将文件位置指示符设置为文件末尾(如ѭ20),对于二进制流有未定义的行为(因为   可能结尾的空字符)或任何具有   状态相关的编码,不能确保以初始形式结束   转移状态。 一个法律上的家伙可能会认为可以通过使用以下方法计算文件大小来避免此UB:
fseek(file, -1, SEEK_END);
size = ftell(file) + 1;
但是C标准也这样说:   二进制流不需要使用   值SEEK_END。 因此,对于fseek / SEEK_END,我们无能为力。尽管如此,我还是更喜欢fseek / ftell而不是特定于操作系统的API调用。     

要回复问题请先登录注册