diff options
-rw-r--r-- | ChangeLog | 10 | ||||
-rw-r--r-- | NEWS | 2 | ||||
-rw-r--r-- | libio/fileops.c | 7 | ||||
-rw-r--r-- | libio/tst-ftell-active-handler.c | 90 | ||||
-rw-r--r-- | libio/wfileops.c | 9 |
5 files changed, 108 insertions, 10 deletions
diff --git a/ChangeLog b/ChangeLog index b064598548..83048e1c34 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,13 @@ +2914-12-04 Siddhesh Poyarekar <siddhesh@redhat.com> + + [BZ #17647] + * libio/fileops.c (do_ftell): Seek only when there are + unflushed writes. + * libio/wfileops.c (do_ftell_wide): Likewise. + * libio/tst-ftell-active-handler.c (do_ftruncate_test): New + test case. + (do_one_test): Call it. + 2014-12-03 Joseph Myers <joseph@codesourcery.com> * conform/list-header-symbols.pl (%extra_syms): Add getdate_err diff --git a/NEWS b/NEWS index 581bc8b54c..7312b47eb1 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, 17664, 17665, 17668. + 17601, 17608, 17616, 17625, 17633, 17647, 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 e0d7b76eda..dca359179f 100644 --- a/libio/fileops.c +++ b/libio/fileops.c @@ -943,15 +943,14 @@ do_ftell (_IO_FILE *fp) yet. */ if (fp->_IO_buf_base != NULL) { - bool was_writing = (fp->_IO_write_ptr > fp->_IO_write_base - || _IO_in_put_mode (fp)); + bool unflushed_writes = fp->_IO_write_ptr > fp->_IO_write_base; bool append_mode = (fp->_flags & _IO_IS_APPENDING) == _IO_IS_APPENDING; /* When we have unflushed writes in append mode, seek to the end of the file and record that offset. This is the only time we change the file stream state and it is safe since the file handle is active. */ - if (was_writing && append_mode) + if (unflushed_writes && append_mode) { result = _IO_SYSSEEK (fp, 0, _IO_seek_end); if (result == _IO_pos_BAD) @@ -961,7 +960,7 @@ do_ftell (_IO_FILE *fp) } /* Adjust for unflushed data. */ - if (!was_writing) + if (!unflushed_writes) offset -= fp->_IO_read_end - fp->_IO_read_ptr; /* We don't trust _IO_read_end to represent the current file offset when writing in append mode because the value would have to be shifted to diff --git a/libio/tst-ftell-active-handler.c b/libio/tst-ftell-active-handler.c index e9dc7b3d52..9f23c55072 100644 --- a/libio/tst-ftell-active-handler.c +++ b/libio/tst-ftell-active-handler.c @@ -88,6 +88,95 @@ static size_t file_len; typedef int (*fputs_func_t) (const void *data, FILE *fp); fputs_func_t fputs_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 + offset on truncation and hence, SEEK_CUR should continue to point to the + old offset and not be changed to the new offset. */ +static int +do_ftruncate_test (const char *filename) +{ + FILE *fp = NULL; + int fd; + int ret = 0; + struct test + { + const char *mode; + int fd_mode; + } test_modes[] = { + {"r+", O_RDWR}, + {"w", O_WRONLY}, + {"w+", O_RDWR}, + {"a", O_WRONLY}, + {"a+", O_RDWR} + }; + + for (int j = 0; j < 2; j++) + { + for (int i = 0; i < sizeof (test_modes) / sizeof (struct test); i++) + { + int fileret; + printf ("\tftruncate: %s (file, \"%s\"): ", + j == 0 ? "fopen" : "fdopen", + test_modes[i].mode); + + if (j == 0) + fileret = get_handles_fopen (filename, fd, fp, test_modes[i].mode); + else + fileret = get_handles_fdopen (filename, fd, fp, + test_modes[i].fd_mode, + test_modes[i].mode); + + if (fileret != 0) + return fileret; + + /* Write some data. */ + size_t written = fputs_func (data, fp); + + if (written == EOF) + { + printf ("fputs[1] failed to write data\n"); + ret |= 1; + } + + /* Record the offset. */ + long offset = ftell (fp); + + /* Flush data to allow switching active handles. */ + if (fflush (fp)) + { + printf ("Flush failed: %m\n"); + ret |= 1; + } + + /* Now truncate the file. */ + if (ftruncate (fd, 0) != 0) + { + printf ("Failed to truncate file: %m\n"); + ret |= 1; + } + + /* ftruncate does not change the offset, so there is no need to call + anything to be able to switch active handles. */ + long new_offset = ftell (fp); + + /* The offset should remain unchanged since ftruncate does not update + it. */ + if (offset != new_offset) + { + printf ("Incorrect offset. Expected %zu, but got %ld\n", + offset, new_offset); + + ret |= 1; + } + else + printf ("offset = %ld\n", offset); + + fclose (fp); + } + } + + return ret; +} /* Test that ftell output after a rewind is correct. */ static int do_rewind_test (const char *filename) @@ -481,6 +570,7 @@ do_one_test (const char *filename) ret |= do_write_test (filename); ret |= do_append_test (filename); ret |= do_rewind_test (filename); + ret |= do_ftruncate_test (filename); return ret; } diff --git a/libio/wfileops.c b/libio/wfileops.c index 6a088b1c15..71281c1e5d 100644 --- a/libio/wfileops.c +++ b/libio/wfileops.c @@ -626,16 +626,15 @@ do_ftell_wide (_IO_FILE *fp) const wchar_t *wide_read_base; const wchar_t *wide_read_ptr; const wchar_t *wide_read_end; - bool was_writing = ((fp->_wide_data->_IO_write_ptr - > fp->_wide_data->_IO_write_base) - || _IO_in_put_mode (fp)); + bool unflushed_writes = (fp->_wide_data->_IO_write_ptr + > fp->_wide_data->_IO_write_base); bool append_mode = (fp->_flags & _IO_IS_APPENDING) == _IO_IS_APPENDING; /* When we have unflushed writes in append mode, seek to the end of the file and record that offset. This is the only time we change the file stream state and it is safe since the file handle is active. */ - if (was_writing && append_mode) + if (unflushed_writes && append_mode) { result = _IO_SYSSEEK (fp, 0, _IO_seek_end); if (result == _IO_pos_BAD) @@ -674,7 +673,7 @@ do_ftell_wide (_IO_FILE *fp) struct _IO_codecvt *cv = fp->_codecvt; int clen = (*cv->__codecvt_do_encoding) (cv); - if (!was_writing) + if (!unflushed_writes) { if (clen > 0) { |