Summary: | fgets goes into infinite loop when called with size set to less than 2 bytes | ||
---|---|---|---|
Product: | Gentoo Linux | Reporter: | Ryan H. <scaevoli> |
Component: | [OLD] Core system | Assignee: | Gentoo Toolchain Maintainers <toolchain> |
Status: | RESOLVED INVALID | ||
Severity: | minor | CC: | kfm, scaevoli |
Priority: | High | ||
Version: | unspecified | ||
Hardware: | All | ||
OS: | Linux | ||
URL: | http://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=190560 | ||
Whiteboard: | |||
Package list: | Runtime testing required: | --- | |
Attachments: |
Test case demonstrating correct behaviour.
*Correct* test case demonstrating correct behaviour. |
Description
Ryan H.
2007-06-17 03:13:36 UTC
Bits of `emerge --info glibc` on my system, where this code fails Portage 2.1.2.7 (default-linux/amd64/2007.0, gcc-4.1.2, glibc-2.5-r3, 2.6.20-gentoo-r8 x86_64) ================================================================= System Settings ================================================================= System uname: 2.6.20-gentoo-r8 x86_64 AMD Athlon(tm) 64 Processor 3800+ Gentoo Base System release 1.12.9 Timestamp of tree: Sat, 16 Jun 2007 23:50:01 +0000 ccache version 2.4 [enabled] dev-lang/python: 2.4.4-r4 dev-python/pycrypto: 2.0.1-r5 dev-util/ccache: 2.4-r7 sys-apps/sandbox: 1.2.17 sys-devel/autoconf: 2.13, 2.61 sys-devel/automake: 1.4_p6, 1.5, 1.6.3, 1.7.9-r1, 1.8.5-r3, 1.9.6-r2, 1.10 sys-devel/binutils: 2.16.1-r3 sys-devel/gcc-config: 1.3.16 sys-devel/libtool: 1.5.22 virtual/os-headers: 2.6.17-r2 ACCEPT_KEYWORDS="amd64" AUTOCLEAN="yes" CBUILD="x86_64-pc-linux-gnu" ... sys-libs/glibc-2.5-r3 was built with the following: CFLAGS="-O2 -fno-strict-aliasing -march=athlon64 -mtune=k8 -pipe" CXXFLAGS="-O2 -fno-strict-aliasing -march=athlon64 -mtune=k8 -pipe" The code is flawed. The size in bytes of /tmp/null_file is 1, and this is stored in file_size. With fgets(line, file_size, fp) -- file_size is 1. fgets reads file_size-1 bytes, hence 0. Thus, end of file is never reached, since you're never reading any data! After a conversation in #gentoo and some testing, it was observed that the program works on glibc-2.3.5 because on this library, fgets(buffer, 1, fp) returns NULL - without setting either the end-of-file or error indicators on the stream. This appears to have changed in more recent glibc, where zero bytes are quite happily "read" and a zero-length string is written to the buffer. The C standard has the following to say about fgets: char *fgets(char *restrict s, int n, FILE *restrict stream) Description 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 The fgets function returns s if successful. If end-of-fïle 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. footnote: An end-of-file and a read error can be distinguished by use of the feof and ferror functions. This makes no indication that fgets should fail under such a read. I would conclude that the bug lies in the old behaviour of glibc, rather than the new. Of course, a request to read 0 bytes makes little sense in the first place, and is unlikely to be encountered in practise. Created attachment 122293 [details]
Test case demonstrating correct behaviour.
This program demonstrates correct use of fgets, works on files of length 0, 1, and >1 bytes, and with a small modification (described in comments), will demonstrate the wierd behaviour on glibc-2.3.5.
Make sure to create /tmp/null_file before running.
Created attachment 122294 [details]
*Correct* test case demonstrating correct behaviour.
Whoops, I accidentally posted the file with the mentioned "small modification".
Jakub is correct, calling fgets() with size less than 2 is invalid, but i'm not sure that constitutes an infinite loop http://www.opengroup.org/onlinepubs/009695399/functions/fgets.html actually, i dont see the trouble here ... the infinite loop is in the testcase, not in glibc if (n <= 0), you get NULL ... since the stream does not have an error nor is it an eof, neither gets set if (n == 1), you get a NUL string, which is correct |