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

C getline()与fgets():控制内存分配,c,posix,C,Posix,要从文件中读取行,有getline()和fgets()POSIX函数(忽略可怕的gets())。常识是,getline()优于fgets(),因为它根据需要分配行缓冲区 我的问题是:这不是很危险吗?如果由于意外或恶意,有人创建了一个100GB的文件,其中没有'\n'字节,那会不会使我的getline()调用分配了大量内存 我的问题是:这不是很危险吗?如果是意外或恶意的呢 意图有人创建一个100GB文件,其中没有“\n”字节–不会 这使得我的getline()调用分配了大量内存 是的,你所描述的是

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

我的问题是:这不是很危险吗?如果由于意外或恶意,有人创建了一个100GB的文件,其中没有
'\n'
字节,那会不会使我的
getline()
调用分配了大量内存

我的问题是:这不是很危险吗?如果是意外或恶意的呢 意图有人创建一个100GB文件,其中没有“\n”字节–不会 这使得我的getline()调用分配了大量内存

是的,你所描述的是一个似是而非的风险。但是,

  • 如果程序需要一次将整行加载到内存中,那么允许
    getline()
    尝试这样做并不比使用
    fgets()编写自己的代码更危险;及
  • 如果您的程序存在这样的漏洞,那么您可以通过使用
    setrlimit()
    限制它可以保留的(虚拟)内存总量来降低风险。这可以用来导致它失败,而不是成功地分配足够的内存来干扰系统的其余部分
我认为,总体而言,最好的方法是首先编写不需要以整行为单位输入(一次全部输入)的代码,但这种方法有其自身的复杂性。

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


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

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

/* 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;
}
/*危险代码*/
#包括
内部主(空)
{
文件*f;
char*s;
尺寸n=0;
f=fopen(“/dev/zero”,“r”);
getline(s&n,f);
返回0;
}

函数
getline
在内部使用
malloc
realloc
并在失败时返回-1,因此结果与尝试调用
malloc(10000000000)
时没有什么不同。也就是说,
errno
设置为
ENOMEM
,而
getline
返回-1


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

实际上,这取决于您希望如何处理过长的行

fgets
具有适当大小的缓冲区通常可以工作,并且您可以检测到它已“失败”-缓冲区端没有换行符。可以避免总是使用strlen()来确认缓冲区是否溢出,但这是另一个问题

也许您的策略是跳过无法处理的行,或者可能该行的其余部分只是一条您无论如何都会忽略的注释,在这种情况下,很容易将
fgets
放入一个循环中,以丢弃该行的其余部分,而不会产生分配惩罚

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

同样,如果
getline
无法realloc,它将以一种您可以恢复的方式失败,尽管如果您将缓冲区重新用于多行读取,您可能希望在尝试读取更多内容之前释放错误后的缓冲区,因为它仍然被分配,并且在失败之前可能已经增长到最大。

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


若您知道所有可接受行的最大大小,那个么您可以通过使用
fgets()
而不是
getline()
,来避免内存分配,从而删除一个潜在的内存泄漏点。

这不取决于实现吗?当然。我担心的是,我的系统可能会切换到停滞的地步。使用
fgets()
这是不可能的。我想知道我是否误解了什么,或者是否知道
getline()
的这种风险。
内存量太大了
-100GB内存量太大了吗?是100KB吗?是1PB吗?告诉20年后的某个人。。。。有多少记忆是疯狂的,有多少不是
getline()
是上世纪90年代的一个函数。它的存在是为了“让生活更轻松”,而不是“处理用户想要的所有疯狂案例”。编写具有最大限制的
getline()
实现并不是那么难。@KamilCuk分别是、否和是。20年后,答案将是否定、否定和肯定。在以后的几年里,构成疯狂内存量的确切阈值肯定会增加,但潜在的问题是(存在导致内存不足或抖动的线条)将存在于任何有限内存系统上。@KamilCuk:一个包含100GB记录但没有长度前缀的文件的概念似乎有点疯狂,不管目标计算机有多少内存。除了
fgets()
和手动分配您可以在进程的整个内存耗尽之前选择退出。实际上,这种行为与只调用
malloc(100000000000)
非常不同,因为
getline()
将首先分配一个小的大小,然后逐步重新分配到它找到新行的位置,文件或内存lim的结尾