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.
This commit is contained in:
Siddhesh Poyarekar 2014-12-04 08:13:28 +05:30
parent 61b4f792e0
commit fe8b4d98e9
5 changed files with 68 additions and 11 deletions

View File

@ -1,5 +1,15 @@
2914-12-04 Siddhesh Poyarekar <siddhesh@redhat.com>
[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.

2
NEWS
View File

@ -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

View File

@ -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;

View File

@ -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);

View File

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