From fe8b4d98e9ac371238388469cb74011cb2120343 Mon Sep 17 00:00:00 2001 From: Siddhesh Poyarekar Date: Thu, 4 Dec 2014 08:13:28 +0530 Subject: [PATCH] Reset cached offset when reading to end of stream (BZ #17653) POSIX allows applications to switch file handles when a read results in an end of file. Unset the cached offset at this point so that it is queried again. --- ChangeLog | 10 ++++++ NEWS | 2 +- libio/fileops.c | 8 ++++- libio/tst-ftell-active-handler.c | 54 +++++++++++++++++++++++++++----- libio/wfileops.c | 5 ++- 5 files changed, 68 insertions(+), 11 deletions(-) diff --git a/ChangeLog b/ChangeLog index 1bd4b23dc7..4cee7d79c6 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,15 @@ 2914-12-04 Siddhesh Poyarekar + [BZ #17653] + * libio/fileops.c (_IO_new_file_underflow): Unset cached + offset on EOF. + * libio/wfileops.c (_IO_wfile_underflow): Likewise. + * libio/tst-ftell-active-handler.c (fgets_func_t): New type. + (fgets_func): Function pointer to fgets and fgetws. + (do_ftell_test): Add test to verify ftell value after read + EOF. + (do_test): Set fgets_func. + * libio/tst-ftell-active-handler.c (do_ftruncate_test): Add O_TRUNC flag for w and w+ modes. (do_rewind_test): Likewise. diff --git a/NEWS b/NEWS index 7312b47eb1..84c1353148 100644 --- a/NEWS +++ b/NEWS @@ -13,7 +13,7 @@ Version 2.21 16619, 16740, 16857, 17192, 17266, 17344, 17363, 17370, 17371, 17411, 17460, 17475, 17485, 17501, 17506, 17508, 17522, 17555, 17570, 17571, 17572, 17573, 17574, 17581, 17582, 17583, 17584, 17585, 17589, 17594, - 17601, 17608, 17616, 17625, 17633, 17647, 17664, 17665, 17668. + 17601, 17608, 17616, 17625, 17633, 17647, 17653, 17664, 17665, 17668. * CVE-2104-7817 The wordexp function could ignore the WRDE_NOCMD flag under certain input conditions resulting in the execution of a shell for diff --git a/libio/fileops.c b/libio/fileops.c index dca359179f..ee50b72aec 100644 --- a/libio/fileops.c +++ b/libio/fileops.c @@ -615,7 +615,13 @@ _IO_new_file_underflow (fp) } fp->_IO_read_end += count; if (count == 0) - return EOF; + { + /* If a stream is read to EOF, the calling application may switch active + handles. As a result, our offset cache would no longer be valid, so + unset it. */ + fp->_offset = _IO_pos_BAD; + return EOF; + } if (fp->_offset != _IO_pos_BAD) _IO_pos_adjust (fp->_offset, count); return *(unsigned char *) fp->_IO_read_ptr; diff --git a/libio/tst-ftell-active-handler.c b/libio/tst-ftell-active-handler.c index 72066b467e..f69e16922a 100644 --- a/libio/tst-ftell-active-handler.c +++ b/libio/tst-ftell-active-handler.c @@ -86,7 +86,9 @@ static size_t data_len; static size_t file_len; typedef int (*fputs_func_t) (const void *data, FILE *fp); +typedef void *(*fgets_func_t) (void *ws, int n, FILE *fp); fputs_func_t fputs_func; +fgets_func_t fgets_func; /* This test verifies that the offset reported by ftell is correct after the file is truncated using ftruncate. ftruncate does not change the file @@ -290,20 +292,22 @@ do_ftell_test (const char *filename) int fd_mode; size_t old_off; size_t new_off; + size_t eof_off; } test_modes[] = { /* In w, w+ and r+ modes, the file position should be at the beginning of the file. After the write, the offset should be - updated to data_len. */ - {"w", O_WRONLY | O_TRUNC, 0, data_len}, - {"w+", O_RDWR | O_TRUNC, 0, data_len}, - {"r+", O_RDWR, 0, data_len}, + updated to data_len. We don't use eof_off in w and a modes since + they don't allow reading. */ + {"w", O_WRONLY | O_TRUNC, 0, data_len, 0}, + {"w+", O_RDWR | O_TRUNC, 0, data_len, 2 * data_len}, + {"r+", O_RDWR, 0, data_len, 3 * data_len}, /* For the 'a' mode, the initial file position should be the current end of file. After the write, the offset has data_len added to the old value. For a+ mode however, the initial file position is the file position of the underlying file descriptor, since it is initially assumed to be in read mode. */ - {"a", O_WRONLY, data_len, 2 * data_len}, - {"a+", O_RDWR, 0, 3 * data_len}, + {"a", O_WRONLY, 3 * data_len, 4 * data_len, 5 * data_len}, + {"a+", O_RDWR, 0, 5 * data_len, 6 * data_len}, }; for (int j = 0; j < 2; j++) { @@ -348,12 +352,44 @@ do_ftell_test (const char *filename) if (off != test_modes[i].new_off) { - printf ("Incorrect new offset. Expected %zu but got %ld\n", + printf ("Incorrect new offset. Expected %zu but got %ld", test_modes[i].new_off, off); ret |= 1; } else - printf ("new offset = %ld\n", off); + printf ("new offset = %ld", off); + + /* Read to the end, write some data to the fd and check if ftell can + see the new ofset. Do this test only for files that allow + reading. */ + if (test_modes[i].fd_mode != O_WRONLY) + { + char tmpbuf[data_len]; + + rewind (fp); + + while (fgets_func (tmpbuf, sizeof (tmpbuf), fp) && !feof (fp)); + + write_ret = write (fd, data, data_len); + if (write_ret != data_len) + { + printf ("write failed (%m)\n"); + ret |= 1; + } + off = ftell (fp); + + if (off != test_modes[i].eof_off) + { + printf (", Incorrect offset after read EOF. " + "Expected %zu but got %ld\n", + test_modes[i].eof_off, off); + ret |= 1; + } + else + printf (", offset after EOF = %ld\n", off); + } + else + putc ('\n', stdout); fclose (fp); } @@ -617,6 +653,7 @@ do_test (void) /* Tests for regular files. */ puts ("Regular mode:"); fputs_func = (fputs_func_t) fputs; + fgets_func = (fgets_func_t) fgets; data = char_data; data_len = strlen (char_data); ret |= do_one_test (filename); @@ -638,6 +675,7 @@ do_test (void) return 1; } fputs_func = (fputs_func_t) fputws; + fgets_func = (fgets_func_t) fgetws; data = wide_data; data_len = wcslen (wide_data); ret |= do_one_test (filename); diff --git a/libio/wfileops.c b/libio/wfileops.c index 71281c1e5d..2a003b368d 100644 --- a/libio/wfileops.c +++ b/libio/wfileops.c @@ -257,7 +257,10 @@ _IO_wfile_underflow (fp) if (count <= 0) { if (count == 0 && naccbuf == 0) - fp->_flags |= _IO_EOF_SEEN; + { + fp->_flags |= _IO_EOF_SEEN; + fp->_offset = _IO_pos_BAD; + } else fp->_flags |= _IO_ERR_SEEN, count = 0; }