使用fseek和ftell来确定文件的大小有漏洞吗?

时间:2011-05-11 00:09:48

标签: c file fseek

我读过帖子,展示如何使用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 */
}

我正准备使用这种技术但后来遇到了描述潜在漏洞的link

该链接建议使用fstat。有人可以对此发表评论吗?

5 个答案:

答案 0 :(得分:14)

该链接是CERT的许多无意义的C编码建议之一。他们的理由是基于C标准允许实现的自由,但是POSIX 不允许这样做,因此在你fstat作为替代的所有情况下都无关紧要。

POSIX要求:

  1. "b"的{​​{1}}修饰符无效,即文本和二进制模式的行为相同。这意味着他们对在文本文件上调用UB的担忧是无稽之谈。

  2. 文件具有通过写操作和截断操作设置的字节分辨率大小。这意味着他们对文件末尾的空字节随机数的关注是无稽之谈。

  3. 可悲的是,随着他们发布的所有废话,很难知道哪些CERT出版物需要认真对待。这很遗憾,因为很多都很严重。

答案 1 :(得分:6)

如果你的目标是找到文件的大小,肯定你应该使用fstat()或其朋友。这是一种更直接,更具表现力的方法 - 你实际上要求系统告诉你文件的统计数据,而不是更环绕的fseek / ftell方法。

奖励提示:如果您只想知道文件是否可用,请使用access()而不是打开文件,甚至是对其进行统计。这是一个更简单的操作,许多程序员都不知道。

答案 2 :(得分:4)

使用fstat的原因是fstat是POSIX,但fopenftellfseek是其中的一部分C标准。

可能有一个系统实现了C标准而不是POSIX。在这样的系统上fstat根本不起作用。

答案 3 :(得分:3)

我倾向于同意他们的基本结论,即您通常不应该直接在代码的主流中使用fseek / ftell代码 - 但是您可能也不应该使用fstat。如果您想要文件的大小,那么您的大部分代码都应使用明确的直接名称,例如filesize

现在, 可能更好地使用fstat(如果可用)和(例如)FindFirstFile在Windows上实现(最明显的{{1}平台通常不可用。)

故事的另一方面是fstat关于二进制文件的许多(大多数?)限制实际上源于CP / M,它没有明确地将文件的大小存储在任何地方。文本文件的结尾由control-Z发出信号。但是,对于二进制文件,您真正知道的是用于存储文件的扇区。在最后一个扇区中,您有一些经常(但不总是)零填充的未使用数据。不幸的是,可能存在重要的零和/或非零的非零值。

如果整个C标准是在批准之前编写的(例如,如果它是在1988年开始并在1989年完成),他们可能完全忽略了CP / M.然而,无论好坏,他们开始在1982年左右的时候开始研究C标准,当时CP / M的使用范围仍然广泛,不容忽视。当CP / M消失时,许多决定已经做出,我怀疑是否有人想重新审视它们。

然而,对于今天的大多数人来说,没有任何意义 - 如果没有大量的工作,大多数代码都不会移植到CP / M;这是要处理的相对次要问题之一。使代码和数据的内存只有48K(左右)的现代程序运行是一个很多更严重的问题(对于大容量存储而言,最大的兆字节左右是另一个严重的问题)。

CERT确实有一个好处:你可能应该(通常做)查找文件大小,分配那么多空间,然后假设文件内容适合那里。尽管fseek / ftell会使用现代系统为您提供正确的大小,但是当您实际读取数据时,这些数据可能会过时,因此无论如何您都可能超出缓冲区。

答案 4 :(得分:2)

根据C standard, §7.21.3

  

将文件位置指示器设置为文件结束,与fseek(file, 0, SEEK_END)一样,具有二进制流的未定义行为(因为   可能的尾随空字符)或任何流   状态相关的编码,不能确定地在初始结束   转变状态。

通过以下方式计算文件大小,一个法律信函的人可能认为可以避免这个UB:

fseek(file, -1, SEEK_END);
size = ftell(file) + 1;

但C标准也说过:

  

二进制流不需要有意义地支持fseek调用   SEEK_END的价值。

因此,关于fseek / SEEK_END,我们无法解决这个问题。不过,我更喜欢fseek / ftell而不是特定于操作系统的API调用。