2

I did not think these three conditions can happen at the same time. I have:

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"
    }
}

The getline documentation claims

Both functions return -1 on failure to read a line (including end-of-file condition). In the event of an error, errno is set to indicate the cause.

So I have a non-EOF non-error condition?

This is a small, single-threaded program. This error happened when it was fed a 2^30 character string as input (as a stress test, which found this behavior).

Compiled with gcc C99 on Linux.

Edit

I put a fair amount of work into repro-ing this a few days later. I was using git so it's very easy to create another branch or tag with the repro-ing code, but I didn't observe that good practice. While working with what I believe was the buggy commit, my O.S. just took to killing my program with rc 137.

But

chux's test harness shows a related problem which is repro-able and I guess keeps this question on topic.

djechlin
  • 59,258
  • 35
  • 162
  • 290

1 Answers1

1

A test harness that shows the problem.

A difference between this code and OP's is that the status of errno is always shown.


When out-of-memory occurs, errno is set, but getline() returned the length exceeding the capacity - very strange - and not -1.

If this is compliant or not, I leave to your interpretation of the below. IMO it is not compliant. I would expect a return value of -1, if errno is set due to getline() for any reason in addition to ferror() or no read on end-of-file.

I suspect the issue due to a deferred memory allocation.

RETURN VALUE
On success, getline() and getdelim() return the number of characters read, including the delimiter character, but not including the terminating null byte ('\0'). This value can be used to handle embedded null bytes in the line read.

Both functions return -1 on failure to read a line (including end-of-file condition). In the event of an error, errno is set to indicate the cause.

ERRORS
EINVAL Bad arguments (n or lineptr is NULL, or stream is not valid).
ENOMEM Allocation or reallocation of the line buffer failed.


Code also experiments with CR/LF vs LF, yet that did not appear relevant.


#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;
}

Output

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) version 6.4.0

Community
  • 1
  • 1
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
  • Printing the GLIBC version is more important that the GCC version. – o11c Sep 20 '17 at 23:47
  • @o11c Perhaps "ldd (cygwin) 2.9.0". – chux - Reinstate Monica Sep 21 '17 at 01:56
  • Oh. cygwin uses some weird libc that is *always* buggy. – o11c Sep 21 '17 at 06:37
  • OP has just admitted that his *small program* is 500 lines across 5 files and *it would take some time* to produce an SSCCE, apparently. Due to OPs attitude towards producing an SSCCE (believing *it would take some time*), I'm leaning more towards this being a memory corruption bug which should be closed... – autistic Sep 21 '17 at 07:32
  • @Sebivor Agree OP code that demos the problem would be useful. Note that the code here apparently shows the same bug and not memory corruption - at least due to the code itself. I suspect a code bug in the gcc/cygwin library. – chux - Reinstate Monica Sep 21 '17 at 14:11
  • 1
    @chux OP admitted he has no MCVE and so I wouldn't be so sure... He wrote *"@Sebivor it's still 500 lines across 5 files, and it'll take some time to set up an SSCCE, especially because I've already noticed that when I perturb the inputs in seemingly insignificant ways it changes the result. So I'll live dangerously until then"*... Memory corruption, no? I think so! I'm certain this is one of those cases of "the OP is misleading us, without even realising", and we should *always* push **hard** for an MCVE in these cases. – autistic Sep 21 '17 at 14:16
  • I'm trying this code, what do I need in test.bin? (I'm on linux fyi, specifically cc version 4.9.2 (Debian 4.9.2-10) ) – djechlin Sep 22 '17 at 00:43
  • @djechlin I am unfamiliar with that site. You can compile/run this code on your own machine and report the results. – chux - Reinstate Monica Sep 22 '17 at 01:18
  • Your code has ` FILE *stream = fopen("c:\\tmp\test.bin", "wb");` I don't know what's in that file, and I'm on Linux so I have to reorganize stuff some. – djechlin Sep 23 '17 at 16:29
  • @djechlin This code _creates_ `"test.bin"`. It is filled with many `'a'` and finally an end-of-line. – chux - Reinstate Monica Sep 23 '17 at 19:59