getline()与fgets():控制内存分配

时间:2019-05-03 12:48:41

标签: c posix

要从文件中读取行,有getline()fgets() POSIX函数(忽略可怕的gets())。通常,getline()fgets()更为可取,因为它会根据需要分配行缓冲区。

我的问题是:这样危险吗?如果某人出于偶然或出于恶意目的创建了一个100GB的文件而没有'\n'字节怎么办–会使我的getline()调用分配大量的内存不会吗?

6 个答案:

答案 0 :(得分:15)

  

我的问题是:这样危险吗?如果是偶然或恶意怎么办   意图有人创建一个100GB的文件,其中没有'\ n'字节-不会   使得我的getline()调用分配了大量的内存?

是的,您所描述的是一个合理的风险。但是,

  • 如果程序需要立即将整行加载到内存中,那么让getline()尝试执行此操作本质上不会比编写自己的代码来fgets()来冒险;和
  • 如果您的程序具有这样的漏洞,则可以通过使用setrlimit()限制它可以保留的(虚拟)内存总量来减轻风险。这可能导致失败,而不是成功分配足够的内存来干扰系统的其余部分。

我认为最好的总体方法是首先编写不需要以全行(一次)输入的代码,但是这种方法有其自身的复杂性。

答案 1 :(得分:8)

可能会很危险,是的。不知道在其他计算机上如何工作,但是运行下面的代码会使我的计算机冻结,需要硬重置:

/* DANGEROUS CODE */

#include <stdio.h>

int main(void)
{
    FILE *f;
    char *s;
    size_t n = 0;

    f = fopen("/dev/zero", "r");
    getline(&s, &n, f);

    return 0;
}

答案 2 :(得分:3)

getline函数在内部使用mallocrealloc,如果失败则返回-1,因此结果与尝试调用malloc(100000000000)的结果相同。即,errno设置为ENOMEM,而getline返回-1。

因此,无论您使用getline还是尝试使用fgets和手动内存分配来确保读取整行内容,都将遇到相同的问题。

答案 3 :(得分:1)

某些编码准则(例如MISRA C)可能会阻止您使用动态内存分配(例如getline())。这是有原因的,例如避免内存泄漏。

如果您知道所有可接受的行的最大大小,则可以使用fgets()而不是getline()来避免内存分配,因此可以消除一个潜在的内存泄漏点。

答案 4 :(得分:0)

getline()为您重新分配缓冲区,以减轻程序中的内存管理。

但是实际上,这可能会导致分配大量内存。如果这是一个问题,那么您应该采取额外的步骤来使用不会隐式分配内存的函数。

答案 5 :(得分:0)

这实际上取决于您要如何处理过长的行。

具有适当大小的缓冲区的

fgets通常可以正常工作,并且您可以检测到它已“失败”-缓冲区末尾没有换行符。可以避免始终执行strlen()来确认缓冲区是否溢出,但这是另一个问题。

也许您的策略是简单地跳过无法处理的行,或者行的其余部分仅仅是您无论如何都会忽略的注释,在这种情况下,可以很容易地将fgets放入一个循环,丢弃该行的其余部分,而没有分配损失。

如果您确实想阅读整行内容,那么getline可能是您更好的策略。恶意用户将需要大量磁盘空间来导致您描述的不良行为,或者可能传递/ dev / random或类似的名称作为输入文件名。

同样,如果getline无法重新分配,它将以一种可以从中恢复的方式失败,尽管如果您将缓冲区重新用于多行读取,则您可能希望释放它确实具有的缓冲区在尝试读取更多内容之前发生错误,因为它仍在分配中,并且可能会增加到失败之前的最大程度。