getline возвращает -1, EOF не установлен, errno не установлен при очень большом вводе

Я не думал, что эти три условия могут произойти одновременно. У меня есть:

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 символов (в качестве стресс-теста, который обнаружил это поведение).

Скомпилировано с помощью gcc C99 в Linux.

Изменить

Несколько дней спустя я приложил немало усилий, чтобы воспроизвести это. Я использовал git, так что очень легко создать еще одну ветку или тег с воспроизводимым кодом, но я не наблюдал такой хорошей практики. Работая с тем, что, как мне кажется, было ошибочным коммитом, моя ОС. только что начал убивать мою программу с помощью rc 137.

Но

Тестовая программа chux показывает связанную с этим проблему, которая может быть воспроизведена, и я думаю, что этот вопрос остается в теме.


person djechlin    schedule 20.09.2017    source источник


Ответы (1)


Тестовый жгут, показывающий проблему.

Разница между этим кодом и OP заключается в том, что всегда отображается статус errno.


Когда происходит нехватка памяти, устанавливается errno, но getline() возвращает длину, превышающую capacity, что очень странно, а не -1.

Если это соответствует или нет, я оставляю на вашу интерпретацию ниже. ИМО не соответствует. Я ожидаю возвращаемого значения -1, если errno установлено из-за getline() по какой-либо причине, помимо ferror() или отсутствия чтения в конце файла.

Я подозреваю, что проблема связана с отложенным выделением памяти.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
В случае успеха 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

person chux - Reinstate Monica    schedule 20.09.2017
comment
Печать версии GLIBC более важна, чем версия GCC. - person o11c; 21.09.2017
comment
@ o11c Возможно, ldd (cygwin) 2.9.0. - person chux - Reinstate Monica; 21.09.2017
comment
Ой. cygwin использует какую-то странную libc, которая всегда глючит. - person o11c; 21.09.2017
comment
OP только что признал, что его небольшая программа состоит из 500 строк в 5 файлах, и, по-видимому, для создания SSCCE потребуется некоторое время. Из-за отношения OP к созданию SSCCE (полагая, что это займет некоторое время), я больше склоняюсь к тому, что это ошибка повреждения памяти, которую следует закрыть... - person autistic; 21.09.2017
comment
@Sebivor Согласитесь, код OP, демонстрирующий проблему, был бы полезен. Обратите внимание, что код здесь, по-видимому, показывает ту же ошибку, а не повреждение памяти — по крайней мере, из-за самого кода. Я подозреваю ошибку кода в библиотеке gcc/cygwin. - person chux - Reinstate Monica; 21.09.2017
comment
@chux OP признал, что у него нет MCVE, и поэтому я бы не был так уверен ... Он написал @Sebivor, что это все еще 500 строк в 5 файлах, и настройка SSCCE займет некоторое время, особенно потому, что Я уже заметил, что когда я возмущаю входные данные, казалось бы, незначительными способами, это меняет результат. Так что до тех пор я буду жить опасно... Повреждение памяти, нет? Я думаю так! Я уверен, что это один из тех случаев, когда ОП вводит нас в заблуждение, даже не осознавая этого, и мы должны всегда настаивать настойчиво на MCVE в этих случаях. - person autistic; 21.09.2017
comment
Я пробую этот код, что мне нужно в test.bin? (Я использую linux fyi, в частности cc версии 4.9.2 (Debian 4.9.2-10)) - person djechlin; 22.09.2017
comment
@djechlin Я не знаком с этим сайтом. Вы можете скомпилировать/запустить этот код на своем компьютере и сообщить о результатах. - person chux - Reinstate Monica; 22.09.2017
comment
В вашем коде есть `ФАЙЛ *stream = fopen(c:\\tmp\test.bin, wb);` Я не знаю, что в этом файле, и я работаю в Linux, поэтому мне нужно кое-что реорганизовать. - person djechlin; 23.09.2017
comment
@djechlin Этот код создает "test.bin". Он заполнен многими 'a' и, наконец, концом строки. - person chux - Reinstate Monica; 23.09.2017