getline 返回 -1,EOF 未设置,errno 未设置,当给定非常大的输入时

getline returns -1, EOF not set, errno not set, when given very large input

提问人:djechlin 提问时间:9/20/2017 最后编辑:djechlin 更新时间:2/10/2021 访问量:1518

问:

我不认为这三个条件可以同时发生。我有:

char* line = NULL;
size_t capacity = 0;
ssize_t n = getline(&line, &capacity, stdin);
if (n == -1) {
    int err = errno; // preserve it
    if (feof(stdin) == 0) { // means not EOF
        printf(strerror(err)); // "Success"
    }
}

文件索赔getline

这两个函数在读取行失败时(包括文件结束条件)都返回 -1。如果发生错误,则设置 errno 以指示原因。

所以我有一个非EOF非错误条件?

这是一个小型的单线程程序。当它被输入一个 2^30 个字符的字符串作为输入时,就会发生此错误(作为压力测试,它发现了这种行为)。

在 Linux 上使用 gcc C99 编译。

编辑

几天后,我投入了相当多的工作来重现这一点。我使用的是 git,所以使用复制代码创建另一个分支或标签非常容易,但我没有观察到这种好的做法。在处理我认为有缺陷的提交时,我的操作系统刚刚用 rc 137 杀死了我的程序。

chux 的测试工具显示了一个可重现的相关问题,我想这个问题一直保持在主题上。

c gcc getline errno feof

评论


答:

1赞 chux - Reinstate Monica 9/21/2017 #1

显示问题的测试工具。

此代码与 OP 的区别在于始终显示 的状态。errno


当发生内存不足时,设置了,但返回的长度超过了 - 非常奇怪 - 而不是 -1。errnogetline()capacity

如果这是否合规,我留给您对以下内容的解释。IMO 它不合规。如果由于任何原因而设置了 -1,除了文件末尾或没有读取之外,我预计返回值为 -1。errnogetline()ferror()

我怀疑问题是由于延迟的内存分配造成的。

RETURN VALUE
成功时,并返回读取的字符数,包括分隔符字符,但不包括终止 null 字节 ('\0')。此值可用于处理行读取中嵌入的 null 字节。
getline()getdelim()

这两个函数在读取行失败时(包括文件结束条件)都返回 -1。如果发生错误,则设置 errno 以指示原因。

ERRORS
EINVAL 参数错误(n 或 lineptr 为 NULL,或者 stream 无效)。
ENOMEM 线路缓冲区的分配或重新分配失败。


代码还对 CR/LF 与 LF 进行了实验,但这似乎无关紧要。


#include <errno.h>
#include <stdint.h>
#include <stdio.h>

int djtest(unsigned sh, unsigned long long a, bool crflag) {
  printf("sh:%2u a:%12llu crlf:%d   ", sh, a, crflag);
  FILE *stream = fopen("test.bin", "wb");
  if (stream == NULL) {
    return printf("error fopen(wb)\n");
  }
  char buf[1024 * 1204];
  memset(buf, 'a', sizeof buf);
  while (a > 0) {
    unsigned long long m = a;
    if (m > sizeof buf) m = sizeof buf;
    size_t y = fwrite(buf, sizeof *buf, (size_t) m, stream);
    if (y != m) {
      return printf("error fwrite\n");
    }
    a -= m;
  }
  if (fputs(crflag ? "\r\n" : "\n", stream)) {
    return printf("error fputs\n");
  }
  if (fclose(stream)) {
    return printf("error fclose\n");
  }


  stream = fopen("test.bin", "r");
  if (stream == NULL) {
    return printf("error fopen(r)\n");
  }
  char* line = NULL;
  size_t capacity = 0;
  ssize_t n = getline(&line, &capacity, stream);
  int err = errno;
  printf("cap:%12zu ssize_t:%12lld feof:%d ferror:%d errno:%2d line:%p %s   ", //
      capacity, (long long)n, feof(stream), ferror(stream), err, (void*)line, strerror(err));

  free(line);

  if (fclose(stream)) {
    return printf("error fclose\n");
  }

  return printf("Fin\n");
}

int main() {
  for (unsigned sh = 28; sh < 31; sh++) {
    unsigned long long a = 1ull << sh;
    djtest(sh, a, 0);
    djtest(sh, a, 1);
    fflush(stdout);
    a *= 2;
  }
  printf("All done\n");
  return 0;
}

输出

sh:28 a:   268435456 crlf:0   cap:   536870912 ssize_t:   268435457 feof:0 ferror:0 errno: 0 line:0xbff20008 No error   Fin
sh:28 a:   268435456 crlf:1   cap:   536870912 ssize_t:   268435458 feof:0 ferror:0 errno: 0 line:0xbff20008 No error   Fin
sh:29 a:   536870912 crlf:0   cap:  1073741824 ssize_t:   536870913 feof:0 ferror:0 errno: 0 line:0x1ff80008 No error   Fin
sh:29 a:   536870912 crlf:1   cap:  1073741824 ssize_t:   536870914 feof:0 ferror:0 errno: 0 line:0x1ff80008 No error   Fin
sh:30 a:  1073741824 crlf:0   cap:  1073741824 ssize_t:  1610088455 feof:0 ferror:0 errno:12 line:0x1ff80008 Cannot allocate memory   Fin
sh:30 a:  1073741824 crlf:1   cap:  1073741824 ssize_t:  1610088455 feof:0 ferror:0 errno:12 line:0x1ff80008 Cannot allocate memory   Fin
All done

GNU C11 (GCC) 版本 6.4.0

评论

0赞 o11c 9/21/2017
打印 GLIBC 版本比打印 GCC 版本更重要。
0赞 chux - Reinstate Monica 9/21/2017
@o11c 也许是“ldd (cygwin) 2.9.0”。
0赞 o11c 9/21/2017
哦。Cygwin 使用了一些奇怪的 libc,这些 libc 总是有问题。
0赞 autistic 9/21/2017
OP 刚刚承认,他的小程序是 500 个文件的 500 行,显然需要一些时间才能生成 SSCCE。由于 OP 对生成 SSCCE 的态度(相信这需要一些时间),我更倾向于这是一个应该关闭的内存损坏错误......
1赞 autistic 9/21/2017
@chux OP 承认他没有 MCVE,所以我不太确定......他写道:“@Sebivor它仍然是 500 个文件的 500 行,并且设置 SSCCE 需要一些时间,特别是因为我已经注意到,当我以看似微不足道的方式扰乱输入时,它会改变结果。所以在那之前我会危险地生活“......内存损坏,不是吗?我认为如此!我敢肯定,这是“OP在不知不觉中误导我们”的案例之一,在这些情况下,我们应该始终努力推动MCVE。