【问题标题】:getline returns -1, EOF not set, errno not set, when given very large input当给定非常大的输入时,getline 返回 -1,未设置 EOF,未设置 errno
【发布时间】:2017-09-20 03:53:38
【问题描述】:

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

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】:

    显示问题的测试工具。

    此代码与 OP 的不同之处在于始终显示errno 的状态。


    当内存不足时,errno 被设置,但getline() 返回的长度超过了capacity - 很奇怪 - 而不是 -1。

    如果这符合与否,我留给您对以下内容的解释。 IMO 不符合要求。如果由于getline() 以及ferror() 之外的任何原因设置了errno,我希望返回值为-1,或者在文件末尾没有读取。

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

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

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

    错误
    EINVAL 错误参数(n 或 lineptr 为 NULL,或流无效)。
    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

    【讨论】:

    • 打印 GLIBC 版本比 GCC 版本更重要。
    • @o11c 也许是“ldd (cygwin) 2.9.0”。
    • 哦。 cygwin 使用了一些奇怪的 libc,它总是有问题。
    • OP 刚刚承认他的 小程序 是跨 5 个文件的 500 行,并且显然需要一些时间来生成 SSCCE。由于 OP 对生成 SSCCE 的态度(相信 这需要一些时间),我更倾向于这是一个应该关闭的内存损坏错误...
    • @chux OP 承认他没有 MCVE,所以我不太确定...他写了 "@Sebivor 它仍然是 500 行跨 5 个文件,这需要一些是时候建立一个SSCCE了,特别是因为我已经注意到,当我以看似微不足道的方式扰乱输入时,它会改变结果。所以在那之前我会过得很危险”...内存损坏,不是吗?我认同!我确信这是“OP 在误导我们,甚至没有意识到”的情况之一,在这些情况下,我们应该始终努力推动 MCVE。
    猜你喜欢
    • 1970-01-01
    • 2014-05-01
    • 2010-10-18
    • 1970-01-01
    • 2015-10-03
    • 2018-11-12
    • 2016-03-24
    • 2021-09-29
    • 1970-01-01
    相关资源
    最近更新 更多