|
Message-ID:
<PR0P264MB0794F199A69A5E139FA0B5CCC05F9@PR0P264MB0794.FRAP264.PROD.OUTLOOK.COM>
Date: Fri, 7 Oct 2022 13:12:27 +0000
From: Pascal Cuoq <cuoq@...st-in-soft.com>
To: "libc-coord@...ts.openwall.com" <libc-coord@...ts.openwall.com>
Subject: fgets behavior for n<=0 (and =1)
Dear all,
the function fgets is defined in the C standard. In C17 7.21.7.2 (https://cigix.me/c17#7.21.7.2 ):
Synopsis
1
#include <stdio.h>
char *fgets(char * restrict s, int n,
FILE * restrict stream);
Description
2
The fgets function reads at most one less than the number of characters specified by n from the stream pointed to by stream into the array pointed to by s. No additional characters are read after a new-line character (which is retained) or after end-of-file. A null character is written immediately after the last character read into the array.
Returns
3
The fgets function returns s if successful. If end-of-file is encountered and no characters have been read into the array, the contents of the array remain unchanged and a null pointer is returned. If a read error occurs during the operation, the array contents are indeterminate and a null pointer is returned.
___
It has come to our attention that it is possible to interpret the informal specification above in several ways for n <= 0, and that indeed several interpretations co-exist. All links are provided “as of this writing”, with snippets illustrating the interesting lines for clarity and against obsolescence of this message.
A/ For n <= 0, return a null pointer without setting errno.
Examples:
if (n <= 0) /* sanity check */
return (NULL);
https://opensource.apple.com/source/Libc/Libc-594.9.5/stdio/FreeBSD/fgets.c.auto.html
if (n <= 0) return NULL;
http://www.jbox.dk/sanos/source/lib/stdio.c.html
B/ Same as A but also do not write anything for n=1 (arguably incorrect)
if (n < 2) /* sanity check */
return 0;
https://github.com/eblot/newlib/blob/master/newlib/libc/stdio/fgets.c
C/ Have undefined behavior for n=INT_MIN
Since undefined behavior can be anything and worse, and the C standard's description does not explicitly allow “anything and worse” for any value of n, this is arguably incorrect.
if (n--<=1) {
https://git.musl-libc.org/cgit/musl/tree/src/stdio/fgets.c
D/ Same as C (and rather a worse kind of UB in practice) but also write a nul character for n=0 (which is also arguably incorrect in itself)
for (p = dst, max--; max > 0; max--) {
if ((c = fgetc (fp)) == EOF)
break;
*p++ = c;
if (c == '\n')
break;
}
*p = 0;
http://mirror.fsf.org/pmon2000/pmon2000/src/lib/libc/fgets.c
E/ Document implementation-dependent result for n=1 and set errno to EINVAL for n<=0 in addition to returning a null pointer
From https://man.openbsd.org/fgets.3 :
> Whether fgets() can possibly fail with a size argument of 1 is implementation-dependent. On OpenBSD, fgets() will never return NULL when size is 1.
>
> ERRORS
> …
> [EINVAL]
> The given size is less than or equal to 0.
Implementation:
if (n <= 0) { /* sanity check */
errno = EINVAL;
return (NULL);
}
https://github.com/openbsd/src/blob/2207c4325726fdc5c4bcd0011af0fdf7d3dab137/lib/libc/stdio/fgets.c#L52-L55
Does anyone have any remarks about what the consensual behavior of fgets should be or how to get closer to a point where the consensual behavior is more widely implemented?
Pascal
Content of type "text/html" skipped
Powered by blists - more mailing lists
Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.