Fix stdio input stream flushing and positioning behavior (RHEL-119434)
Resolves: RHEL-119434
This commit is contained in:
parent
02111ad8a7
commit
d686f6fe8b
71
glibc-RHEL-119434-1.patch
Normal file
71
glibc-RHEL-119434-1.patch
Normal file
@ -0,0 +1,71 @@
|
||||
commit 04b1eb161fdc8b88876bf78b34d2bb92584aba45
|
||||
Author: Siddhesh Poyarekar <siddhesh@sourceware.org>
|
||||
Date: Fri Nov 8 12:33:47 2024 -0500
|
||||
|
||||
support: Add xdup
|
||||
|
||||
Add xdup as the error-checking version of dup for test cases.
|
||||
|
||||
Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
|
||||
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
|
||||
diff --git a/support/Makefile b/support/Makefile
|
||||
index af3f69f5df76e6f9..6b859e85ddb157f4 100644
|
||||
--- a/support/Makefile
|
||||
+++ b/support/Makefile
|
||||
@@ -124,6 +124,7 @@ libsupport-routines = \
|
||||
xcopy_file_range \
|
||||
xdlfcn \
|
||||
xdlmopen \
|
||||
+ xdup \
|
||||
xdup2 \
|
||||
xfchmod \
|
||||
xfclose \
|
||||
diff --git a/support/xdup.c b/support/xdup.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..1eab317354f1f353
|
||||
--- /dev/null
|
||||
+++ b/support/xdup.c
|
||||
@@ -0,0 +1,30 @@
|
||||
+/* dup with error checking.
|
||||
+ Copyright The GNU Toolchain Authors.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <support/xunistd.h>
|
||||
+#include <support/check.h>
|
||||
+
|
||||
+int
|
||||
+xdup (int from)
|
||||
+{
|
||||
+ int ret = dup (from);
|
||||
+ if (ret < 0)
|
||||
+ FAIL_EXIT1 ("dup (%d): %m", from);
|
||||
+
|
||||
+ return ret;
|
||||
+}
|
||||
diff --git a/support/xunistd.h b/support/xunistd.h
|
||||
index 204951bce75f576b..0c6d837ac073ca2b 100644
|
||||
--- a/support/xunistd.h
|
||||
+++ b/support/xunistd.h
|
||||
@@ -35,6 +35,7 @@ pid_t xfork (void);
|
||||
pid_t xwaitpid (pid_t, int *status, int flags);
|
||||
void xpipe (int[2]);
|
||||
void xdup2 (int, int);
|
||||
+int xdup (int);
|
||||
int xopen (const char *path, int flags, mode_t);
|
||||
void support_check_stat_fd (const char *name, int fd, int result);
|
||||
void support_check_stat_path (const char *name, const char *path, int result);
|
||||
119
glibc-RHEL-119434-2.patch
Normal file
119
glibc-RHEL-119434-2.patch
Normal file
@ -0,0 +1,119 @@
|
||||
commit 377e9733b50ce41e496c467ddcc112f73c88f3bd
|
||||
Author: Joseph Myers <josmyers@redhat.com>
|
||||
Date: Tue Jan 28 19:38:27 2025 +0000
|
||||
|
||||
Fix fflush after ungetc on input file (bug 5994)
|
||||
|
||||
As discussed in bug 5994 (plus duplicates), POSIX requires fflush
|
||||
after ungetc to discard pushed-back characters but preserve the file
|
||||
position indicator. For this purpose, each ungetc decrements the file
|
||||
position indicator by 1; it is unspecified after ungetc at the start
|
||||
of the file, and after ungetwc, so no special handling is needed for
|
||||
either of those cases.
|
||||
|
||||
This is fixed with appropriate logic in _IO_new_file_sync. I haven't
|
||||
made any attempt to test or change things in this area for the "old"
|
||||
functions; the case of files using mmap is addressed in a subsequent
|
||||
patch (and there seem to be no problems in this area with files opened
|
||||
with fmemopen).
|
||||
|
||||
Tested for x86_64.
|
||||
|
||||
diff --git a/libio/fileops.c b/libio/fileops.c
|
||||
index d49e489f55d3a283..715bcf69328e769e 100644
|
||||
--- a/libio/fileops.c
|
||||
+++ b/libio/fileops.c
|
||||
@@ -799,6 +799,11 @@ _IO_new_file_sync (FILE *fp)
|
||||
if (fp->_IO_write_ptr > fp->_IO_write_base)
|
||||
if (_IO_do_flush(fp)) return EOF;
|
||||
delta = fp->_IO_read_ptr - fp->_IO_read_end;
|
||||
+ if (_IO_in_backup (fp))
|
||||
+ {
|
||||
+ _IO_switch_to_main_get_area (fp);
|
||||
+ delta += fp->_IO_read_ptr - fp->_IO_read_end;
|
||||
+ }
|
||||
if (delta != 0)
|
||||
{
|
||||
off64_t new_pos = _IO_SYSSEEK (fp, delta, 1);
|
||||
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
|
||||
index 2a842488fb69541b..501b6341711c1781 100644
|
||||
--- a/stdio-common/Makefile
|
||||
+++ b/stdio-common/Makefile
|
||||
@@ -260,6 +260,7 @@ tests := \
|
||||
tst-swscanf \
|
||||
tst-tmpnam \
|
||||
tst-ungetc \
|
||||
+ tst-ungetc-fflush \
|
||||
tst-ungetc-leak \
|
||||
tst-ungetc-nomem \
|
||||
tst-unlockedio \
|
||||
diff --git a/stdio-common/tst-ungetc-fflush.c b/stdio-common/tst-ungetc-fflush.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..a86d1fdb7f8cac88
|
||||
--- /dev/null
|
||||
+++ b/stdio-common/tst-ungetc-fflush.c
|
||||
@@ -0,0 +1,64 @@
|
||||
+/* Test flushing input file after ungetc (bug 5994).
|
||||
+ Copyright (C) 2025 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <stdio.h>
|
||||
+
|
||||
+#include <support/check.h>
|
||||
+#include <support/temp_file.h>
|
||||
+#include <support/xstdio.h>
|
||||
+#include <support/xunistd.h>
|
||||
+
|
||||
+int
|
||||
+do_test (void)
|
||||
+{
|
||||
+ char *filename = NULL;
|
||||
+ int fd = create_temp_file ("tst-ungetc-fflush", &filename);
|
||||
+ TEST_VERIFY_EXIT (fd != -1);
|
||||
+ xclose (fd);
|
||||
+
|
||||
+ /* Test as in bug 5994. */
|
||||
+ FILE *fp = xfopen (filename, "w");
|
||||
+ TEST_VERIFY_EXIT (fputs ("#include", fp) >= 0);
|
||||
+ xfclose (fp);
|
||||
+ fp = xfopen (filename, "r");
|
||||
+ TEST_COMPARE (fgetc (fp), '#');
|
||||
+ TEST_COMPARE (fgetc (fp), 'i');
|
||||
+ TEST_COMPARE (ungetc ('@', fp), '@');
|
||||
+ TEST_COMPARE (fflush (fp), 0);
|
||||
+ TEST_COMPARE (lseek (fileno (fp), 0, SEEK_CUR), 1);
|
||||
+ TEST_COMPARE (fgetc (fp), 'i');
|
||||
+ TEST_COMPARE (fgetc (fp), 'n');
|
||||
+ xfclose (fp);
|
||||
+
|
||||
+ /* Test as in bug 12799 (duplicate of 5994). */
|
||||
+ fp = xfopen (filename, "w+");
|
||||
+ TEST_VERIFY_EXIT (fputs ("hello world", fp) >= 0);
|
||||
+ rewind (fp);
|
||||
+ TEST_VERIFY (fileno (fp) >= 0);
|
||||
+ char buffer[10];
|
||||
+ TEST_COMPARE (fread (buffer, 1, 5, fp), 5);
|
||||
+ TEST_COMPARE (fgetc (fp), ' ');
|
||||
+ TEST_COMPARE (ungetc ('@', fp), '@');
|
||||
+ TEST_COMPARE (fflush (fp), 0);
|
||||
+ TEST_COMPARE (fgetc (fp), ' ');
|
||||
+ xfclose (fp);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#include <support/test-driver.c>
|
||||
355
glibc-RHEL-119434-3.patch
Normal file
355
glibc-RHEL-119434-3.patch
Normal file
@ -0,0 +1,355 @@
|
||||
commit be6818be31e756398e45f70e2819d78be0961223
|
||||
Author: Joseph Myers <josmyers@redhat.com>
|
||||
Date: Tue Jan 28 20:22:56 2025 +0000
|
||||
|
||||
Make fclose seek input file to right offset (bug 12724)
|
||||
|
||||
As discussed in bug 12724 and required by POSIX, before an input file
|
||||
(based on an underlying seekable file descriptor) is closed, fclose is
|
||||
sometimes required to seek that file descriptor to the correct offset,
|
||||
so that any other file descriptors sharing the underlying open file
|
||||
description are left at that offset (as a motivating example, a script
|
||||
could call a sequence of commands each of which processes some data
|
||||
from (seekable) stdin using stdio; fclose needs to do this so that
|
||||
each successive command can read exactly the data not handled by
|
||||
previous commands), but glibc fails to do this.
|
||||
|
||||
The precise POSIX wording has changed a few times; in the 2024 edition
|
||||
it's "If the file is not already at EOF, and the file is one capable
|
||||
of seeking, the file offset of the underlying open file description
|
||||
shall be set to the file position of the stream if the stream is the
|
||||
active handle to the underlying file description.".
|
||||
|
||||
Add appropriate logic to _IO_new_file_close_it to handle this case. I
|
||||
haven't made any attempt to test or change things in this area for the
|
||||
"old" functions.
|
||||
|
||||
Note that there was a previous attempt to fix bug 12724, reverted in
|
||||
commit eb6cbd249f4465b01f428057bf6ab61f5f0c07e3. The fix version here
|
||||
addresses the original test in that bug report without breaking the
|
||||
one given in a subsequent comment in that bug report (which works with
|
||||
glibc before the patch, but maybe was broken by the original fix that
|
||||
was reverted).
|
||||
|
||||
The logic here tries to take care not to seek the file, even to its
|
||||
newly computed current offset, if at EOF / possibly not the active
|
||||
handle; even seeking to the current offset would be problematic
|
||||
because of a potential race (fclose computes the current offset,
|
||||
another thread or process with the active handle does its own seek,
|
||||
fclose does a seek (not permitted by POSIX in this case) that loses
|
||||
the effect of the seek on the active handle in another thread or
|
||||
process). There are tests included for various cases of being or not
|
||||
being the active handle, though there aren't tests for the potential
|
||||
race condition.
|
||||
|
||||
Tested for x86_64.
|
||||
|
||||
diff --git a/libio/fileops.c b/libio/fileops.c
|
||||
index 715bcf69328e769e..d96941be9e2bccd6 100644
|
||||
--- a/libio/fileops.c
|
||||
+++ b/libio/fileops.c
|
||||
@@ -127,15 +127,48 @@ _IO_new_file_init (struct _IO_FILE_plus *fp)
|
||||
int
|
||||
_IO_new_file_close_it (FILE *fp)
|
||||
{
|
||||
- int write_status;
|
||||
+ int flush_status = 0;
|
||||
if (!_IO_file_is_open (fp))
|
||||
return EOF;
|
||||
|
||||
if ((fp->_flags & _IO_NO_WRITES) == 0
|
||||
&& (fp->_flags & _IO_CURRENTLY_PUTTING) != 0)
|
||||
- write_status = _IO_do_flush (fp);
|
||||
- else
|
||||
- write_status = 0;
|
||||
+ flush_status = _IO_do_flush (fp);
|
||||
+ else if (fp->_fileno >= 0
|
||||
+ /* If this is the active handle, we must seek the
|
||||
+ underlying open file description (possibly shared with
|
||||
+ other file descriptors that remain open) to the correct
|
||||
+ offset. But if this stream is in a state such that some
|
||||
+ other handle might have become the active handle, then
|
||||
+ (a) at the time it entered that state, the underlying
|
||||
+ open file description had the correct offset, and (b)
|
||||
+ seeking the underlying open file description, even to
|
||||
+ its newly determined current offset, is not safe because
|
||||
+ it can race with operations on a different active
|
||||
+ handle. So check here for cases where it is necessary
|
||||
+ to seek, while avoiding seeking in cases where it is
|
||||
+ unsafe to do so. */
|
||||
+ && (_IO_in_backup (fp)
|
||||
+ || (fp->_mode <= 0 && fp->_IO_read_ptr < fp->_IO_read_end)
|
||||
+ || (_IO_vtable_offset (fp) == 0
|
||||
+ && fp->_mode > 0 && (fp->_wide_data->_IO_read_ptr
|
||||
+ < fp->_wide_data->_IO_read_end))))
|
||||
+ {
|
||||
+ off64_t o = _IO_SEEKOFF (fp, 0, _IO_seek_cur, 0);
|
||||
+ if (o == EOF)
|
||||
+ {
|
||||
+ if (errno != ESPIPE)
|
||||
+ flush_status = EOF;
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ if (_IO_in_backup (fp))
|
||||
+ o -= fp->_IO_save_end - fp->_IO_save_base;
|
||||
+ flush_status = (_IO_SYSSEEK (fp, o, SEEK_SET) < 0 && errno != ESPIPE
|
||||
+ ? EOF
|
||||
+ : 0);
|
||||
+ }
|
||||
+ }
|
||||
|
||||
_IO_unsave_markers (fp);
|
||||
|
||||
@@ -160,7 +193,7 @@ _IO_new_file_close_it (FILE *fp)
|
||||
fp->_fileno = -1;
|
||||
fp->_offset = _IO_pos_BAD;
|
||||
|
||||
- return close_status ? close_status : write_status;
|
||||
+ return close_status ? close_status : flush_status;
|
||||
}
|
||||
libc_hidden_ver (_IO_new_file_close_it, _IO_file_close_it)
|
||||
|
||||
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
|
||||
index 501b6341711c1781..aa4ba158e8ea9b44 100644
|
||||
--- a/stdio-common/Makefile
|
||||
+++ b/stdio-common/Makefile
|
||||
@@ -206,6 +206,7 @@ tests := \
|
||||
tst-bz11319-fortify2 \
|
||||
tst-cookie \
|
||||
tst-dprintf-length \
|
||||
+ tst-fclose-offset \
|
||||
tst-fdopen \
|
||||
tst-fdopen2 \
|
||||
tst-ferror \
|
||||
diff --git a/stdio-common/tst-fclose-offset.c b/stdio-common/tst-fclose-offset.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..a31de1117c7dfeec
|
||||
--- /dev/null
|
||||
+++ b/stdio-common/tst-fclose-offset.c
|
||||
@@ -0,0 +1,225 @@
|
||||
+/* Test offset of input file descriptor after close (bug 12724).
|
||||
+ Copyright (C) 2025 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <errno.h>
|
||||
+#include <stdio.h>
|
||||
+#include <wchar.h>
|
||||
+
|
||||
+#include <support/check.h>
|
||||
+#include <support/temp_file.h>
|
||||
+#include <support/xstdio.h>
|
||||
+#include <support/xunistd.h>
|
||||
+
|
||||
+int
|
||||
+do_test (void)
|
||||
+{
|
||||
+ char *filename = NULL;
|
||||
+ int fd = create_temp_file ("tst-fclose-offset", &filename);
|
||||
+ TEST_VERIFY_EXIT (fd != -1);
|
||||
+
|
||||
+ /* Test offset of open file description for output and input streams
|
||||
+ after fclose, case from bug 12724. */
|
||||
+
|
||||
+ const char buf[] = "hello world";
|
||||
+ xwrite (fd, buf, sizeof buf);
|
||||
+ TEST_COMPARE (lseek (fd, 1, SEEK_SET), 1);
|
||||
+ int fd2 = xdup (fd);
|
||||
+ FILE *f = fdopen (fd2, "w");
|
||||
+ TEST_VERIFY_EXIT (f != NULL);
|
||||
+ TEST_COMPARE (fputc (buf[1], f), buf[1]);
|
||||
+ xfclose (f);
|
||||
+ errno = 0;
|
||||
+ TEST_COMPARE (lseek (fd2, 0, SEEK_CUR), -1);
|
||||
+ TEST_COMPARE (errno, EBADF);
|
||||
+ TEST_COMPARE (lseek (fd, 0, SEEK_CUR), 2);
|
||||
+
|
||||
+ /* Likewise for an input stream. */
|
||||
+ fd2 = xdup (fd);
|
||||
+ f = fdopen (fd2, "r");
|
||||
+ TEST_VERIFY_EXIT (f != NULL);
|
||||
+ TEST_COMPARE (fgetc (f), buf[2]);
|
||||
+ xfclose (f);
|
||||
+ errno = 0;
|
||||
+ TEST_COMPARE (lseek (fd2, 0, SEEK_CUR), -1);
|
||||
+ TEST_COMPARE (errno, EBADF);
|
||||
+ TEST_COMPARE (lseek (fd, 0, SEEK_CUR), 3);
|
||||
+
|
||||
+ /* Test offset of open file description for output and input streams
|
||||
+ after fclose, case from comment on bug 12724 (failed after first
|
||||
+ attempt at fixing that bug). This verifies that the offset is
|
||||
+ not reset when there has been no input or output on the FILE* (in
|
||||
+ that case, the FILE* might not be the active handle). */
|
||||
+
|
||||
+ TEST_COMPARE (lseek (fd, 0, SEEK_SET), 0);
|
||||
+ xwrite (fd, buf, sizeof buf);
|
||||
+ TEST_COMPARE (lseek (fd, 1, SEEK_SET), 1);
|
||||
+ fd2 = xdup (fd);
|
||||
+ f = fdopen (fd2, "w");
|
||||
+ TEST_VERIFY_EXIT (f != NULL);
|
||||
+ TEST_COMPARE (lseek (fd, 4, SEEK_SET), 4);
|
||||
+ xfclose (f);
|
||||
+ errno = 0;
|
||||
+ TEST_COMPARE (lseek (fd2, 0, SEEK_CUR), -1);
|
||||
+ TEST_COMPARE (errno, EBADF);
|
||||
+ TEST_COMPARE (lseek (fd, 0, SEEK_CUR), 4);
|
||||
+
|
||||
+ /* Likewise for an input stream. */
|
||||
+ fd2 = xdup (fd);
|
||||
+ f = fdopen (fd2, "r");
|
||||
+ TEST_VERIFY_EXIT (f != NULL);
|
||||
+ TEST_COMPARE (lseek (fd, 4, SEEK_SET), 4);
|
||||
+ xfclose (f);
|
||||
+ errno = 0;
|
||||
+ TEST_COMPARE (lseek (fd2, 0, SEEK_CUR), -1);
|
||||
+ TEST_COMPARE (errno, EBADF);
|
||||
+ TEST_COMPARE (lseek (fd, 0, SEEK_CUR), 4);
|
||||
+
|
||||
+ /* Further cases without specific tests in bug 12724, to verify
|
||||
+ proper operation of the rules about the offset only being set
|
||||
+ when the stream is the active handle. */
|
||||
+
|
||||
+ /* Test offset set by fclose after fseek and fgetc. */
|
||||
+ TEST_COMPARE (lseek (fd, 0, SEEK_SET), 0);
|
||||
+ fd2 = xdup (fd);
|
||||
+ f = fdopen (fd2, "r");
|
||||
+ TEST_VERIFY_EXIT (f != NULL);
|
||||
+ TEST_COMPARE (fseek (f, 1, SEEK_SET), 0);
|
||||
+ TEST_COMPARE (fgetc (f), buf[1]);
|
||||
+ xfclose (f);
|
||||
+ errno = 0;
|
||||
+ TEST_COMPARE (lseek (fd2, 0, SEEK_CUR), -1);
|
||||
+ TEST_COMPARE (errno, EBADF);
|
||||
+ TEST_COMPARE (lseek (fd, 0, SEEK_CUR), 2);
|
||||
+
|
||||
+ /* Test offset not set by fclose after fseek and fgetc, if that
|
||||
+ fgetc is at EOF (in which case the active handle might have
|
||||
+ changed). */
|
||||
+ TEST_COMPARE (lseek (fd, 0, SEEK_SET), 0);
|
||||
+ fd2 = xdup (fd);
|
||||
+ f = fdopen (fd2, "r");
|
||||
+ TEST_VERIFY_EXIT (f != NULL);
|
||||
+ TEST_COMPARE (fseek (f, sizeof buf, SEEK_SET), 0);
|
||||
+ TEST_COMPARE (fgetc (f), EOF);
|
||||
+ TEST_COMPARE (lseek (fd, 4, SEEK_SET), 4);
|
||||
+ xfclose (f);
|
||||
+ errno = 0;
|
||||
+ TEST_COMPARE (lseek (fd2, 0, SEEK_CUR), -1);
|
||||
+ TEST_COMPARE (errno, EBADF);
|
||||
+ TEST_COMPARE (lseek (fd, 0, SEEK_CUR), 4);
|
||||
+
|
||||
+ /* Test offset not set by fclose after fseek and fgetc and fflush
|
||||
+ (active handle might have changed after fflush). */
|
||||
+ TEST_COMPARE (lseek (fd, 0, SEEK_SET), 0);
|
||||
+ fd2 = xdup (fd);
|
||||
+ f = fdopen (fd2, "r");
|
||||
+ TEST_VERIFY_EXIT (f != NULL);
|
||||
+ TEST_COMPARE (fseek (f, 1, SEEK_SET), 0);
|
||||
+ TEST_COMPARE (fgetc (f), buf[1]);
|
||||
+ TEST_COMPARE (fflush (f), 0);
|
||||
+ TEST_COMPARE (lseek (fd, 4, SEEK_SET), 4);
|
||||
+ xfclose (f);
|
||||
+ errno = 0;
|
||||
+ TEST_COMPARE (lseek (fd2, 0, SEEK_CUR), -1);
|
||||
+ TEST_COMPARE (errno, EBADF);
|
||||
+ TEST_COMPARE (lseek (fd, 0, SEEK_CUR), 4);
|
||||
+
|
||||
+ /* Test offset not set by fclose after fseek and fgetc, if the
|
||||
+ stream is unbuffered (active handle might change at any
|
||||
+ time). */
|
||||
+ TEST_COMPARE (lseek (fd, 0, SEEK_SET), 0);
|
||||
+ fd2 = xdup (fd);
|
||||
+ f = fdopen (fd2, "r");
|
||||
+ TEST_VERIFY_EXIT (f != NULL);
|
||||
+ setbuf (f, NULL);
|
||||
+ TEST_COMPARE (fseek (f, 1, SEEK_SET), 0);
|
||||
+ TEST_COMPARE (fgetc (f), buf[1]);
|
||||
+ TEST_COMPARE (lseek (fd, 4, SEEK_SET), 4);
|
||||
+ xfclose (f);
|
||||
+ errno = 0;
|
||||
+ TEST_COMPARE (lseek (fd2, 0, SEEK_CUR), -1);
|
||||
+ TEST_COMPARE (errno, EBADF);
|
||||
+ TEST_COMPARE (lseek (fd, 0, SEEK_CUR), 4);
|
||||
+
|
||||
+ /* Also test such cases with the stream in wide mode. */
|
||||
+
|
||||
+ /* Test offset set by fclose after fseek and fgetwc. */
|
||||
+ TEST_COMPARE (lseek (fd, 0, SEEK_SET), 0);
|
||||
+ fd2 = xdup (fd);
|
||||
+ f = fdopen (fd2, "r");
|
||||
+ TEST_VERIFY_EXIT (f != NULL);
|
||||
+ TEST_COMPARE (fseek (f, 1, SEEK_SET), 0);
|
||||
+ TEST_COMPARE (fgetwc (f), (wint_t) buf[1]);
|
||||
+ xfclose (f);
|
||||
+ errno = 0;
|
||||
+ TEST_COMPARE (lseek (fd2, 0, SEEK_CUR), -1);
|
||||
+ TEST_COMPARE (errno, EBADF);
|
||||
+ TEST_COMPARE (lseek (fd, 0, SEEK_CUR), 2);
|
||||
+
|
||||
+ /* Test offset not set by fclose after fseek and fgetwc, if that
|
||||
+ fgetwc is at EOF (in which case the active handle might have
|
||||
+ changed). */
|
||||
+ TEST_COMPARE (lseek (fd, 0, SEEK_SET), 0);
|
||||
+ fd2 = xdup (fd);
|
||||
+ f = fdopen (fd2, "r");
|
||||
+ TEST_VERIFY_EXIT (f != NULL);
|
||||
+ TEST_COMPARE (fseek (f, sizeof buf, SEEK_SET), 0);
|
||||
+ TEST_COMPARE (fgetwc (f), WEOF);
|
||||
+ TEST_COMPARE (lseek (fd, 4, SEEK_SET), 4);
|
||||
+ xfclose (f);
|
||||
+ errno = 0;
|
||||
+ TEST_COMPARE (lseek (fd2, 0, SEEK_CUR), -1);
|
||||
+ TEST_COMPARE (errno, EBADF);
|
||||
+ TEST_COMPARE (lseek (fd, 0, SEEK_CUR), 4);
|
||||
+
|
||||
+ /* Test offset not set by fclose after fseek and fgetwc and fflush
|
||||
+ (active handle might have changed after fflush). */
|
||||
+ TEST_COMPARE (lseek (fd, 0, SEEK_SET), 0);
|
||||
+ fd2 = xdup (fd);
|
||||
+ f = fdopen (fd2, "r");
|
||||
+ TEST_VERIFY_EXIT (f != NULL);
|
||||
+ TEST_COMPARE (fseek (f, 1, SEEK_SET), 0);
|
||||
+ TEST_COMPARE (fgetwc (f), (wint_t) buf[1]);
|
||||
+ TEST_COMPARE (fflush (f), 0);
|
||||
+ TEST_COMPARE (lseek (fd, 4, SEEK_SET), 4);
|
||||
+ xfclose (f);
|
||||
+ errno = 0;
|
||||
+ TEST_COMPARE (lseek (fd2, 0, SEEK_CUR), -1);
|
||||
+ TEST_COMPARE (errno, EBADF);
|
||||
+ TEST_COMPARE (lseek (fd, 0, SEEK_CUR), 4);
|
||||
+
|
||||
+ /* Test offset not set by fclose after fseek and fgetwc, if the
|
||||
+ stream is unbuffered (active handle might change at any
|
||||
+ time). */
|
||||
+ TEST_COMPARE (lseek (fd, 0, SEEK_SET), 0);
|
||||
+ fd2 = xdup (fd);
|
||||
+ f = fdopen (fd2, "r");
|
||||
+ TEST_VERIFY_EXIT (f != NULL);
|
||||
+ setbuf (f, NULL);
|
||||
+ TEST_COMPARE (fseek (f, 1, SEEK_SET), 0);
|
||||
+ TEST_COMPARE (fgetwc (f), (wint_t) buf[1]);
|
||||
+ TEST_COMPARE (lseek (fd, 4, SEEK_SET), 4);
|
||||
+ xfclose (f);
|
||||
+ errno = 0;
|
||||
+ TEST_COMPARE (lseek (fd2, 0, SEEK_CUR), -1);
|
||||
+ TEST_COMPARE (errno, EBADF);
|
||||
+ TEST_COMPARE (lseek (fd, 0, SEEK_CUR), 4);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#include <support/test-driver.c>
|
||||
150
glibc-RHEL-119434-4.patch
Normal file
150
glibc-RHEL-119434-4.patch
Normal file
@ -0,0 +1,150 @@
|
||||
commit 94251ae99edaa911f4cb8056748dca0874ea268c
|
||||
Author: Joseph Myers <josmyers@redhat.com>
|
||||
Date: Tue Jan 28 21:53:49 2025 +0000
|
||||
|
||||
Make fflush (NULL) flush input files (bug 32369)
|
||||
|
||||
As discussed in bug 32369 and required by POSIX, the POSIX feature
|
||||
fflush (NULL) should flush input files, not just output files. The
|
||||
POSIX requirement is that "fflush() shall perform this flushing action
|
||||
on all streams for which the behavior is defined above", and the
|
||||
definition for input files is for "a stream open for reading with an
|
||||
underlying file description, if the file is not already at EOF, and
|
||||
the file is one capable of seeking".
|
||||
|
||||
Implement this requirement in glibc. (The underlying flushing
|
||||
implementation is what deals with avoiding errors for seeking on an
|
||||
unseekable file.)
|
||||
|
||||
Tested for x86_64.
|
||||
|
||||
diff --git a/libio/genops.c b/libio/genops.c
|
||||
index 6b2f508f853b8969..0b5344d4cca4e6d9 100644
|
||||
--- a/libio/genops.c
|
||||
+++ b/libio/genops.c
|
||||
@@ -705,6 +705,13 @@ _IO_flush_all (void)
|
||||
)
|
||||
&& _IO_OVERFLOW (fp, EOF) == EOF)
|
||||
result = EOF;
|
||||
+ if (_IO_fileno (fp) >= 0
|
||||
+ && ((fp->_mode <= 0 && fp->_IO_read_ptr < fp->_IO_read_end)
|
||||
+ || (_IO_vtable_offset (fp) == 0
|
||||
+ && fp->_mode > 0 && (fp->_wide_data->_IO_read_ptr
|
||||
+ < fp->_wide_data->_IO_read_end)))
|
||||
+ && _IO_SYNC (fp) != 0)
|
||||
+ result = EOF;
|
||||
|
||||
_IO_funlockfile (fp);
|
||||
run_fp = NULL;
|
||||
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
|
||||
index aa4ba158e8ea9b44..c795fd752a39b1e7 100644
|
||||
--- a/stdio-common/Makefile
|
||||
+++ b/stdio-common/Makefile
|
||||
@@ -210,6 +210,7 @@ tests := \
|
||||
tst-fdopen \
|
||||
tst-fdopen2 \
|
||||
tst-ferror \
|
||||
+ tst-fflush-all-input \
|
||||
tst-fgets \
|
||||
tst-fgets2 \
|
||||
tst-fileno \
|
||||
diff --git a/stdio-common/tst-fflush-all-input.c b/stdio-common/tst-fflush-all-input.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..8e3fca3a087a1699
|
||||
--- /dev/null
|
||||
+++ b/stdio-common/tst-fflush-all-input.c
|
||||
@@ -0,0 +1,94 @@
|
||||
+/* Test fflush (NULL) flushes input files (bug 32369).
|
||||
+ Copyright (C) 2025 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <stdio.h>
|
||||
+#include <stdlib.h>
|
||||
+#include <unistd.h>
|
||||
+#include <wchar.h>
|
||||
+
|
||||
+#include <support/check.h>
|
||||
+#include <support/xstdio.h>
|
||||
+#include <support/xunistd.h>
|
||||
+
|
||||
+int
|
||||
+do_test (void)
|
||||
+{
|
||||
+ FILE *temp = tmpfile ();
|
||||
+ TEST_VERIFY_EXIT (temp != NULL);
|
||||
+ fprintf (temp, "abc");
|
||||
+ TEST_COMPARE (fflush (temp), 0);
|
||||
+ TEST_COMPARE (lseek (fileno (temp), 0, SEEK_SET), 0);
|
||||
+ TEST_COMPARE (fgetc (temp), 'a');
|
||||
+ TEST_COMPARE (fflush (NULL), 0);
|
||||
+ TEST_COMPARE (lseek (fileno (temp), 0, SEEK_CUR), 1);
|
||||
+ xfclose (temp);
|
||||
+
|
||||
+ /* Likewise, but in wide mode. */
|
||||
+ temp = tmpfile ();
|
||||
+ TEST_VERIFY_EXIT (temp != NULL);
|
||||
+ fwprintf (temp, L"abc");
|
||||
+ TEST_COMPARE (fflush (temp), 0);
|
||||
+ TEST_COMPARE (lseek (fileno (temp), 0, SEEK_SET), 0);
|
||||
+ TEST_COMPARE (fgetwc (temp), L'a');
|
||||
+ TEST_COMPARE (fflush (NULL), 0);
|
||||
+ TEST_COMPARE (lseek (fileno (temp), 0, SEEK_CUR), 1);
|
||||
+ xfclose (temp);
|
||||
+
|
||||
+ /* Similar tests, but with the flush implicitly occurring on exit
|
||||
+ (in a forked subprocess). */
|
||||
+
|
||||
+ temp = tmpfile ();
|
||||
+ TEST_VERIFY_EXIT (temp != NULL);
|
||||
+ pid_t pid = xfork ();
|
||||
+ if (pid == 0)
|
||||
+ {
|
||||
+ fprintf (temp, "abc");
|
||||
+ TEST_COMPARE (fflush (temp), 0);
|
||||
+ TEST_COMPARE (lseek (fileno (temp), 0, SEEK_SET), 0);
|
||||
+ TEST_COMPARE (fgetc (temp), 'a');
|
||||
+ exit (EXIT_SUCCESS);
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ TEST_COMPARE (xwaitpid (pid, NULL, 0), pid);
|
||||
+ TEST_COMPARE (lseek (fileno (temp), 0, SEEK_CUR), 1);
|
||||
+ xfclose (temp);
|
||||
+ }
|
||||
+
|
||||
+ temp = tmpfile ();
|
||||
+ TEST_VERIFY_EXIT (temp != NULL);
|
||||
+ pid = xfork ();
|
||||
+ if (pid == 0)
|
||||
+ {
|
||||
+ fwprintf (temp, L"abc");
|
||||
+ TEST_COMPARE (fflush (temp), 0);
|
||||
+ TEST_COMPARE (lseek (fileno (temp), 0, SEEK_SET), 0);
|
||||
+ TEST_COMPARE (fgetwc (temp), L'a');
|
||||
+ exit (EXIT_SUCCESS);
|
||||
+ }
|
||||
+ else
|
||||
+ {
|
||||
+ TEST_COMPARE (xwaitpid (pid, NULL, 0), pid);
|
||||
+ TEST_COMPARE (lseek (fileno (temp), 0, SEEK_CUR), 1);
|
||||
+ xfclose (temp);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#include <support/test-driver.c>
|
||||
115
glibc-RHEL-119434-5.patch
Normal file
115
glibc-RHEL-119434-5.patch
Normal file
@ -0,0 +1,115 @@
|
||||
commit 0dcc0b2f63051863187dc678964eb17761b1a820
|
||||
Author: Joseph Myers <josmyers@redhat.com>
|
||||
Date: Tue Jan 28 22:35:21 2025 +0000
|
||||
|
||||
Fix fseek handling for mmap files after ungetc or fflush (bug 32529)
|
||||
|
||||
As discussed in bug 32529, fseek fails on files opened for reading
|
||||
using mmap after ungetc. The implementation of fseek for such files
|
||||
has an offset computation that's also incorrect after fflush. A
|
||||
combined fix addresses both problems (with tests for both included as
|
||||
well) and it seems reasonable to consider them a single bug.
|
||||
|
||||
Tested for x86_64.
|
||||
|
||||
diff --git a/libio/fileops.c b/libio/fileops.c
|
||||
index d96941be9e2bccd6..e3967c1eb8ee6993 100644
|
||||
--- a/libio/fileops.c
|
||||
+++ b/libio/fileops.c
|
||||
@@ -1106,11 +1106,18 @@ _IO_file_seekoff_mmap (FILE *fp, off64_t offset, int dir, int mode)
|
||||
if (mode == 0)
|
||||
return fp->_offset - (fp->_IO_read_end - fp->_IO_read_ptr);
|
||||
|
||||
+ if (_IO_in_backup (fp))
|
||||
+ {
|
||||
+ if (dir == _IO_seek_cur)
|
||||
+ offset += fp->_IO_read_ptr - fp->_IO_read_end;
|
||||
+ _IO_switch_to_main_get_area (fp);
|
||||
+ }
|
||||
+
|
||||
switch (dir)
|
||||
{
|
||||
case _IO_seek_cur:
|
||||
/* Adjust for read-ahead (bytes is buffer). */
|
||||
- offset += fp->_IO_read_ptr - fp->_IO_read_base;
|
||||
+ offset += fp->_offset - (fp->_IO_read_end - fp->_IO_read_ptr);
|
||||
break;
|
||||
case _IO_seek_set:
|
||||
break;
|
||||
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
|
||||
index c795fd752a39b1e7..9a5d324495946385 100644
|
||||
--- a/stdio-common/Makefile
|
||||
+++ b/stdio-common/Makefile
|
||||
@@ -223,6 +223,7 @@ tests := \
|
||||
tst-fphex-wide \
|
||||
tst-fread \
|
||||
tst-fseek \
|
||||
+ tst-fseek-mmap \
|
||||
tst-fwrite \
|
||||
tst-getline \
|
||||
tst-getline-enomem \
|
||||
diff --git a/stdio-common/tst-fseek-mmap.c b/stdio-common/tst-fseek-mmap.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..86fa99a1a2e3e7e9
|
||||
--- /dev/null
|
||||
+++ b/stdio-common/tst-fseek-mmap.c
|
||||
@@ -0,0 +1,59 @@
|
||||
+/* Test fseek on files using mmap (bug 32529).
|
||||
+ Copyright (C) 2025 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <stdio.h>
|
||||
+
|
||||
+#include <support/check.h>
|
||||
+#include <support/temp_file.h>
|
||||
+#include <support/xstdio.h>
|
||||
+#include <support/xunistd.h>
|
||||
+
|
||||
+int
|
||||
+do_test (void)
|
||||
+{
|
||||
+ char *filename = NULL;
|
||||
+ int fd = create_temp_file ("tst-fseek-mmap", &filename);
|
||||
+ TEST_VERIFY_EXIT (fd != -1);
|
||||
+ xclose (fd);
|
||||
+
|
||||
+ /* Test fseek after ungetc (bug 32529). */
|
||||
+ FILE *fp = xfopen (filename, "w");
|
||||
+ TEST_VERIFY (0 <= fputs ("test", fp));
|
||||
+ xfclose (fp);
|
||||
+
|
||||
+ fp = xfopen (filename, "rm");
|
||||
+ TEST_COMPARE (fgetc (fp), 't');
|
||||
+ TEST_COMPARE (ungetc ('u', fp), 'u');
|
||||
+ TEST_COMPARE (fseek (fp, 0, SEEK_CUR), 0);
|
||||
+ xfclose (fp);
|
||||
+
|
||||
+ /* Test fseek positioning after fflush (another issue covered by the
|
||||
+ same fix). */
|
||||
+ fp = xfopen (filename, "rm");
|
||||
+ TEST_COMPARE (fgetc (fp), 't');
|
||||
+ TEST_COMPARE (fflush (fp), 0);
|
||||
+ TEST_COMPARE (ftell (fp), 1);
|
||||
+ TEST_COMPARE (fseek (fp, 0, SEEK_CUR), 0);
|
||||
+ TEST_COMPARE (ftell (fp), 1);
|
||||
+ TEST_COMPARE (fgetc (fp), 'e');
|
||||
+ xfclose (fp);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#include <support/test-driver.c>
|
||||
110
glibc-RHEL-119434-6.patch
Normal file
110
glibc-RHEL-119434-6.patch
Normal file
@ -0,0 +1,110 @@
|
||||
commit 3ff3b9997cfef891ba33a14f1dcba0310d96369c
|
||||
Author: Joseph Myers <josmyers@redhat.com>
|
||||
Date: Tue Jan 28 23:20:08 2025 +0000
|
||||
|
||||
Fix fflush handling for mmap files after ungetc (bug 32535)
|
||||
|
||||
As discussed in bug 32535, fflush fails on files opened for reading
|
||||
using mmap after ungetc. Fix the logic to handle this case and still
|
||||
compute the file offset correctly.
|
||||
|
||||
Tested for x86_64.
|
||||
|
||||
diff --git a/libio/fileops.c b/libio/fileops.c
|
||||
index e3967c1eb8ee6993..ac5ff9a4353d0201 100644
|
||||
--- a/libio/fileops.c
|
||||
+++ b/libio/fileops.c
|
||||
@@ -858,17 +858,21 @@ libc_hidden_ver (_IO_new_file_sync, _IO_file_sync)
|
||||
int
|
||||
_IO_file_sync_mmap (FILE *fp)
|
||||
{
|
||||
+ off64_t o = fp->_offset - (fp->_IO_read_end - fp->_IO_read_ptr);
|
||||
if (fp->_IO_read_ptr != fp->_IO_read_end)
|
||||
{
|
||||
- if (__lseek64 (fp->_fileno, fp->_IO_read_ptr - fp->_IO_buf_base,
|
||||
- SEEK_SET)
|
||||
- != fp->_IO_read_ptr - fp->_IO_buf_base)
|
||||
+ if (_IO_in_backup (fp))
|
||||
+ {
|
||||
+ _IO_switch_to_main_get_area (fp);
|
||||
+ o -= fp->_IO_read_end - fp->_IO_read_base;
|
||||
+ }
|
||||
+ if (__lseek64 (fp->_fileno, o, SEEK_SET) != o)
|
||||
{
|
||||
fp->_flags |= _IO_ERR_SEEN;
|
||||
return EOF;
|
||||
}
|
||||
}
|
||||
- fp->_offset = fp->_IO_read_ptr - fp->_IO_buf_base;
|
||||
+ fp->_offset = o;
|
||||
fp->_IO_read_end = fp->_IO_read_ptr = fp->_IO_read_base;
|
||||
return 0;
|
||||
}
|
||||
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
|
||||
index 9a5d324495946385..710120820ee4e337 100644
|
||||
--- a/stdio-common/Makefile
|
||||
+++ b/stdio-common/Makefile
|
||||
@@ -211,6 +211,7 @@ tests := \
|
||||
tst-fdopen2 \
|
||||
tst-ferror \
|
||||
tst-fflush-all-input \
|
||||
+ tst-fflush-mmap \
|
||||
tst-fgets \
|
||||
tst-fgets2 \
|
||||
tst-fileno \
|
||||
diff --git a/stdio-common/tst-fflush-mmap.c b/stdio-common/tst-fflush-mmap.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..3bddb909e072caf2
|
||||
--- /dev/null
|
||||
+++ b/stdio-common/tst-fflush-mmap.c
|
||||
@@ -0,0 +1,50 @@
|
||||
+/* Test fflush after ungetc on files using mmap (bug 32535).
|
||||
+ Copyright (C) 2025 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <stdio.h>
|
||||
+
|
||||
+#include <support/check.h>
|
||||
+#include <support/temp_file.h>
|
||||
+#include <support/xstdio.h>
|
||||
+#include <support/xunistd.h>
|
||||
+
|
||||
+int
|
||||
+do_test (void)
|
||||
+{
|
||||
+ char *filename = NULL;
|
||||
+ int fd = create_temp_file ("tst-fflush-mmap", &filename);
|
||||
+ TEST_VERIFY_EXIT (fd != -1);
|
||||
+ xclose (fd);
|
||||
+
|
||||
+ /* Test fflush after ungetc (bug 32535). */
|
||||
+ FILE *fp = xfopen (filename, "w");
|
||||
+ TEST_VERIFY (0 <= fputs ("test", fp));
|
||||
+ xfclose (fp);
|
||||
+
|
||||
+ fp = xfopen (filename, "rm");
|
||||
+ TEST_COMPARE (fgetc (fp), 't');
|
||||
+ TEST_COMPARE (ungetc ('u', fp), 'u');
|
||||
+ TEST_COMPARE (fflush (fp), 0);
|
||||
+ TEST_COMPARE (fgetc (fp), 't');
|
||||
+ TEST_COMPARE (fgetc (fp), 'e');
|
||||
+ xfclose (fp);
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#include <support/test-driver.c>
|
||||
595
glibc-RHEL-119434-7.patch
Normal file
595
glibc-RHEL-119434-7.patch
Normal file
@ -0,0 +1,595 @@
|
||||
commit 203452a460143c2b0bf80e0e92027e0fd6e19fa4
|
||||
Author: Joseph Myers <josmyers@redhat.com>
|
||||
Date: Tue Jan 28 23:39:12 2025 +0000
|
||||
|
||||
Add test of input file flushing / offset issues
|
||||
|
||||
Having fixed several bugs relating to flushing of FILE* streams (with
|
||||
fflush and other operations) and their offsets (both the file position
|
||||
indicator in the FILE*, and the offset in the underlying open file
|
||||
description), especially after ungetc but not limited to that case,
|
||||
add a test that more systematically covers different combinations of
|
||||
cases for such issues, with 57220 separate scenarios tested (which
|
||||
include examples of all the five separate fixed bugs), all of which
|
||||
pass given the five previous bug fixes.
|
||||
|
||||
Tested for x86_64.
|
||||
|
||||
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
|
||||
index 710120820ee4e337..31ff420432453dfe 100644
|
||||
--- a/stdio-common/Makefile
|
||||
+++ b/stdio-common/Makefile
|
||||
@@ -244,6 +244,7 @@ tests := \
|
||||
tst-printf-round \
|
||||
tst-printfsz \
|
||||
tst-put-error \
|
||||
+ tst-read-offset \
|
||||
tst-renameat2 \
|
||||
tst-rndseek \
|
||||
tst-scanf-binary-c11 \
|
||||
diff --git a/stdio-common/tst-read-offset.c b/stdio-common/tst-read-offset.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..b8706607fc86da99
|
||||
--- /dev/null
|
||||
+++ b/stdio-common/tst-read-offset.c
|
||||
@@ -0,0 +1,560 @@
|
||||
+/* Test offsets in files being read, in particular with ungetc.
|
||||
+ Copyright (C) 2025 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <dlfcn.h>
|
||||
+#include <stdbool.h>
|
||||
+#include <stdio.h>
|
||||
+#include <stdlib.h>
|
||||
+#include <unistd.h>
|
||||
+
|
||||
+#include <support/check.h>
|
||||
+#include <support/temp_file.h>
|
||||
+#include <support/xstdio.h>
|
||||
+#include <support/xunistd.h>
|
||||
+
|
||||
+static volatile bool fail = false;
|
||||
+
|
||||
+/* Induce a malloc failure whenever FAIL is set. */
|
||||
+void *
|
||||
+malloc (size_t sz)
|
||||
+{
|
||||
+ if (fail)
|
||||
+ return NULL;
|
||||
+
|
||||
+ static void *(*real_malloc) (size_t);
|
||||
+
|
||||
+ if (real_malloc == NULL)
|
||||
+ real_malloc = dlsym (RTLD_NEXT, "malloc");
|
||||
+
|
||||
+ return real_malloc (sz);
|
||||
+}
|
||||
+
|
||||
+/* The name of the temporary file used by all the tests. */
|
||||
+static char *filename;
|
||||
+
|
||||
+/* st_blksize value for that file, or BUFSIZ if out of range. */
|
||||
+static int blksize = BUFSIZ;
|
||||
+
|
||||
+/* Test data, both written to that file and used as an in-memory
|
||||
+ stream. */
|
||||
+char test_data[2 * BUFSIZ];
|
||||
+
|
||||
+/* Ways to open a test stream for reading (that may use different code
|
||||
+ paths in libio). */
|
||||
+enum test_open_case
|
||||
+ {
|
||||
+ test_open_fopen,
|
||||
+ test_open_fopen_m,
|
||||
+ test_open_fopen64,
|
||||
+ test_open_fopen64_m,
|
||||
+ test_open_fmemopen,
|
||||
+ test_open_max
|
||||
+ };
|
||||
+
|
||||
+static const char *const test_open_case_name[test_open_max] =
|
||||
+ {
|
||||
+ "fopen", "fopen(mmap)", "fopen64", "fopen64(mmap)", "fmemopen"
|
||||
+ };
|
||||
+
|
||||
+static FILE *
|
||||
+open_test_stream (enum test_open_case c)
|
||||
+{
|
||||
+ FILE *fp;
|
||||
+ switch (c)
|
||||
+ {
|
||||
+ case test_open_fopen:
|
||||
+ fp = fopen (filename, "r");
|
||||
+ break;
|
||||
+
|
||||
+ case test_open_fopen_m:
|
||||
+ fp = fopen (filename, "rm");
|
||||
+ break;
|
||||
+
|
||||
+ case test_open_fopen64:
|
||||
+ fp = fopen64 (filename, "r");
|
||||
+ break;
|
||||
+
|
||||
+ case test_open_fopen64_m:
|
||||
+ fp = fopen64 (filename, "rm");
|
||||
+ break;
|
||||
+
|
||||
+ case test_open_fmemopen:
|
||||
+ fp = fmemopen (test_data, 2 * BUFSIZ, "r");
|
||||
+ break;
|
||||
+
|
||||
+ default:
|
||||
+ abort ();
|
||||
+ }
|
||||
+ TEST_VERIFY_EXIT (fp != NULL);
|
||||
+ return fp;
|
||||
+}
|
||||
+
|
||||
+/* Base locations at which the main test (ungetc calls then doing
|
||||
+ something that clears ungetc characters, then checking offset)
|
||||
+ starts. */
|
||||
+enum test_base_loc
|
||||
+ {
|
||||
+ base_loc_start,
|
||||
+ base_loc_blksize,
|
||||
+ base_loc_half,
|
||||
+ base_loc_bufsiz,
|
||||
+ base_loc_eof,
|
||||
+ base_loc_max
|
||||
+ };
|
||||
+
|
||||
+static int
|
||||
+base_loc_to_bytes (enum test_base_loc loc, int offset)
|
||||
+{
|
||||
+ switch (loc)
|
||||
+ {
|
||||
+ case base_loc_start:
|
||||
+ return offset;
|
||||
+
|
||||
+ case base_loc_blksize:
|
||||
+ return blksize + offset;
|
||||
+
|
||||
+ case base_loc_half:
|
||||
+ return BUFSIZ / 2 + offset;
|
||||
+
|
||||
+ case base_loc_bufsiz:
|
||||
+ return BUFSIZ + offset;
|
||||
+
|
||||
+ case base_loc_eof:
|
||||
+ return 2 * BUFSIZ + offset;
|
||||
+
|
||||
+ default:
|
||||
+ abort ();
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/* Ways to clear data from ungetc. */
|
||||
+enum clear_ungetc_case
|
||||
+ {
|
||||
+ clear_fseek,
|
||||
+ clear_fseekm1,
|
||||
+ clear_fseekp1,
|
||||
+ clear_fseeko,
|
||||
+ clear_fseekom1,
|
||||
+ clear_fseekop1,
|
||||
+ clear_fseeko64,
|
||||
+ clear_fseeko64m1,
|
||||
+ clear_fseeko64p1,
|
||||
+ clear_fsetpos,
|
||||
+ clear_fsetposu,
|
||||
+ clear_fsetpos64,
|
||||
+ clear_fsetpos64u,
|
||||
+ clear_fflush,
|
||||
+ clear_fflush_null,
|
||||
+ clear_fclose,
|
||||
+ clear_max
|
||||
+ };
|
||||
+
|
||||
+static const char *const clear_ungetc_case_name[clear_max] =
|
||||
+ {
|
||||
+ "fseek", "fseek(-1)", "fseek(1)", "fseeko", "fseeko(-1)", "fseeko(1)",
|
||||
+ "fseeko64", "fseeko64(-1)", "fseeko64(1)", "fsetpos", "fsetpos(before)",
|
||||
+ "fsetpos64", "fsetpos64(before)", "fflush", "fflush(NULL)", "fclose"
|
||||
+ };
|
||||
+
|
||||
+static int
|
||||
+clear_offset (enum clear_ungetc_case c, int num_ungetc)
|
||||
+{
|
||||
+ switch (c)
|
||||
+ {
|
||||
+ case clear_fseekm1:
|
||||
+ case clear_fseekom1:
|
||||
+ case clear_fseeko64m1:
|
||||
+ return -1;
|
||||
+
|
||||
+ case clear_fseekp1:
|
||||
+ case clear_fseekop1:
|
||||
+ case clear_fseeko64p1:
|
||||
+ return 1;
|
||||
+
|
||||
+ case clear_fsetposu:
|
||||
+ case clear_fsetpos64u:
|
||||
+ return num_ungetc;
|
||||
+
|
||||
+ default:
|
||||
+ return 0;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/* The offsets used with fsetpos / fsetpos64. */
|
||||
+static fpos_t pos;
|
||||
+static fpos64_t pos64;
|
||||
+
|
||||
+static int
|
||||
+do_clear_ungetc (FILE *fp, enum clear_ungetc_case c, int num_ungetc)
|
||||
+{
|
||||
+ int ret;
|
||||
+ int offset = clear_offset (c, num_ungetc);
|
||||
+ switch (c)
|
||||
+ {
|
||||
+ case clear_fseek:
|
||||
+ case clear_fseekm1:
|
||||
+ case clear_fseekp1:
|
||||
+ ret = fseek (fp, offset, SEEK_CUR);
|
||||
+ break;
|
||||
+
|
||||
+ case clear_fseeko:
|
||||
+ case clear_fseekom1:
|
||||
+ case clear_fseekop1:
|
||||
+ ret = fseeko (fp, offset, SEEK_CUR);
|
||||
+ break;
|
||||
+
|
||||
+ case clear_fseeko64:
|
||||
+ case clear_fseeko64m1:
|
||||
+ case clear_fseeko64p1:
|
||||
+ ret = fseeko64 (fp, offset, SEEK_CUR);
|
||||
+ break;
|
||||
+
|
||||
+ case clear_fsetpos:
|
||||
+ case clear_fsetposu:
|
||||
+ ret = fsetpos (fp, &pos);
|
||||
+ break;
|
||||
+
|
||||
+ case clear_fsetpos64:
|
||||
+ case clear_fsetpos64u:
|
||||
+ ret = fsetpos64 (fp, &pos64);
|
||||
+ break;
|
||||
+
|
||||
+ case clear_fflush:
|
||||
+ ret = fflush (fp);
|
||||
+ break;
|
||||
+
|
||||
+ case clear_fflush_null:
|
||||
+ ret = fflush (NULL);
|
||||
+ break;
|
||||
+
|
||||
+ case clear_fclose:
|
||||
+ ret = fclose (fp);
|
||||
+ break;
|
||||
+
|
||||
+ default:
|
||||
+ abort();
|
||||
+ }
|
||||
+ TEST_COMPARE (ret, 0);
|
||||
+ return offset;
|
||||
+}
|
||||
+
|
||||
+static bool
|
||||
+clear_valid (enum test_open_case c, enum clear_ungetc_case cl)
|
||||
+{
|
||||
+ switch (c)
|
||||
+ {
|
||||
+ case test_open_fmemopen:
|
||||
+ /* fflush is not valid for input memory streams, and fclose is
|
||||
+ useless for this test for such streams because there is no
|
||||
+ underlying open file description for which an offset could be
|
||||
+ checked after fclose. */
|
||||
+ switch (cl)
|
||||
+ {
|
||||
+ case clear_fflush:
|
||||
+ case clear_fflush_null:
|
||||
+ case clear_fclose:
|
||||
+ return false;
|
||||
+
|
||||
+ default:
|
||||
+ return true;
|
||||
+ }
|
||||
+
|
||||
+ default:
|
||||
+ /* All ways of clearing ungetc state are valid for streams with
|
||||
+ an underlying file. */
|
||||
+ return true;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static bool
|
||||
+clear_closes_file (enum clear_ungetc_case cl)
|
||||
+{
|
||||
+ switch (cl)
|
||||
+ {
|
||||
+ case clear_fclose:
|
||||
+ return true;
|
||||
+
|
||||
+ default:
|
||||
+ return false;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+clear_getpos_before (FILE *fp, enum clear_ungetc_case c)
|
||||
+{
|
||||
+ switch (c)
|
||||
+ {
|
||||
+ case clear_fsetposu:
|
||||
+ TEST_COMPARE (fgetpos (fp, &pos), 0);
|
||||
+ break;
|
||||
+
|
||||
+ case clear_fsetpos64u:
|
||||
+ TEST_COMPARE (fgetpos64 (fp, &pos64), 0);
|
||||
+ break;
|
||||
+
|
||||
+ default:
|
||||
+ break;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+clear_getpos_after (FILE *fp, enum clear_ungetc_case c)
|
||||
+{
|
||||
+ switch (c)
|
||||
+ {
|
||||
+ case clear_fsetpos:
|
||||
+ TEST_COMPARE (fgetpos (fp, &pos), 0);
|
||||
+ break;
|
||||
+
|
||||
+ case clear_fsetpos64:
|
||||
+ TEST_COMPARE (fgetpos64 (fp, &pos64), 0);
|
||||
+ break;
|
||||
+
|
||||
+ default:
|
||||
+ break;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+/* Ways to verify results of clearing ungetc data. */
|
||||
+enum verify_case
|
||||
+ {
|
||||
+ verify_read,
|
||||
+ verify_ftell,
|
||||
+ verify_ftello,
|
||||
+ verify_ftello64,
|
||||
+ verify_fd,
|
||||
+ verify_max
|
||||
+ };
|
||||
+
|
||||
+static const char *const verify_case_name[verify_max] =
|
||||
+ {
|
||||
+ "read", "ftell", "ftello", "ftello64", "fd"
|
||||
+ };
|
||||
+
|
||||
+static bool
|
||||
+valid_fd_offset (enum test_open_case c, enum clear_ungetc_case cl)
|
||||
+{
|
||||
+ switch (c)
|
||||
+ {
|
||||
+ case test_open_fmemopen:
|
||||
+ /* No open file description. */
|
||||
+ return false;
|
||||
+
|
||||
+ default:
|
||||
+ /* fseek does not necessarily set the offset for the underlying
|
||||
+ open file description ("If the most recent operation, other
|
||||
+ than ftell(), on a given stream is fflush(), the file offset
|
||||
+ in the underlying open file description shall be adjusted to
|
||||
+ reflect the location specified by fseek()." in POSIX does not
|
||||
+ include the case here where getc was the last operation).
|
||||
+ Similarly, fsetpos does not necessarily set that offset
|
||||
+ either. */
|
||||
+ switch (cl)
|
||||
+ {
|
||||
+ case clear_fflush:
|
||||
+ case clear_fflush_null:
|
||||
+ case clear_fclose:
|
||||
+ return true;
|
||||
+
|
||||
+ default:
|
||||
+ return false;
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static bool
|
||||
+verify_valid (enum test_open_case c, enum clear_ungetc_case cl,
|
||||
+ enum verify_case v)
|
||||
+{
|
||||
+ switch (v)
|
||||
+ {
|
||||
+ case verify_fd:
|
||||
+ return valid_fd_offset (c, cl);
|
||||
+
|
||||
+ default:
|
||||
+ switch (cl)
|
||||
+ {
|
||||
+ case clear_fclose:
|
||||
+ return false;
|
||||
+
|
||||
+ default:
|
||||
+ return true;
|
||||
+ }
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static bool
|
||||
+verify_uses_fd (enum verify_case v)
|
||||
+{
|
||||
+ switch (v)
|
||||
+ {
|
||||
+ case verify_fd:
|
||||
+ return true;
|
||||
+
|
||||
+ default:
|
||||
+ return false;
|
||||
+ }
|
||||
+}
|
||||
+
|
||||
+static int
|
||||
+read_to_test_loc (FILE *fp, enum test_base_loc loc, int offset)
|
||||
+{
|
||||
+ int to_read = base_loc_to_bytes (loc, offset);
|
||||
+ for (int i = 0; i < to_read; i++)
|
||||
+ TEST_COMPARE (getc (fp), (unsigned char) i);
|
||||
+ return to_read;
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+setup (void)
|
||||
+{
|
||||
+ int fd = create_temp_file ("tst-read-offset", &filename);
|
||||
+ TEST_VERIFY_EXIT (fd != -1);
|
||||
+ struct stat64 st;
|
||||
+ xfstat64 (fd, &st);
|
||||
+ if (st.st_blksize > 0 && st.st_blksize < BUFSIZ)
|
||||
+ blksize = st.st_blksize;
|
||||
+ printf ("BUFSIZ = %d, blksize = %d\n", BUFSIZ, blksize);
|
||||
+ xclose (fd);
|
||||
+ FILE *fp = xfopen (filename, "w");
|
||||
+ for (size_t i = 0; i < 2 * BUFSIZ; i++)
|
||||
+ {
|
||||
+ unsigned char c = i;
|
||||
+ TEST_VERIFY_EXIT (fputc (c, fp) == c);
|
||||
+ test_data[i] = c;
|
||||
+ }
|
||||
+ xfclose (fp);
|
||||
+}
|
||||
+
|
||||
+static void
|
||||
+test_one_case (enum test_open_case c, enum test_base_loc loc, int offset,
|
||||
+ int num_ungetc, int num_ungetc_diff, bool ungetc_fallback,
|
||||
+ enum clear_ungetc_case cl, enum verify_case v)
|
||||
+{
|
||||
+ int full_offset = base_loc_to_bytes (loc, offset);
|
||||
+ printf ("Testing %s offset %d ungetc %d different %d %s%s %s\n",
|
||||
+ test_open_case_name[c], full_offset, num_ungetc, num_ungetc_diff,
|
||||
+ ungetc_fallback ? "fallback " : "", clear_ungetc_case_name[cl],
|
||||
+ verify_case_name[v]);
|
||||
+ FILE *fp = open_test_stream (c);
|
||||
+ int cur_offset = read_to_test_loc (fp, loc, offset);
|
||||
+ clear_getpos_before (fp, cl);
|
||||
+ for (int i = 0; i < num_ungetc; i++)
|
||||
+ {
|
||||
+ unsigned char c = (i >= num_ungetc - num_ungetc_diff
|
||||
+ ? cur_offset
|
||||
+ : cur_offset - 1);
|
||||
+ if (ungetc_fallback)
|
||||
+ fail = true;
|
||||
+ TEST_COMPARE (ungetc (c, fp), c);
|
||||
+ fail = false;
|
||||
+ cur_offset--;
|
||||
+ }
|
||||
+ clear_getpos_after (fp, cl);
|
||||
+ int fd = -1;
|
||||
+ bool done_dup = false;
|
||||
+ if (verify_uses_fd (v))
|
||||
+ {
|
||||
+ fd = fileno (fp);
|
||||
+ TEST_VERIFY (fd != -1);
|
||||
+ if (clear_closes_file (cl))
|
||||
+ {
|
||||
+ fd = xdup (fd);
|
||||
+ done_dup = true;
|
||||
+ }
|
||||
+ }
|
||||
+ cur_offset += do_clear_ungetc (fp, cl, num_ungetc);
|
||||
+ switch (v)
|
||||
+ {
|
||||
+ case verify_read:
|
||||
+ for (;
|
||||
+ cur_offset <= full_offset + 1 && cur_offset < 2 * BUFSIZ;
|
||||
+ cur_offset++)
|
||||
+ TEST_COMPARE (getc (fp), (unsigned char) cur_offset);
|
||||
+ break;
|
||||
+
|
||||
+ case verify_ftell:
|
||||
+ TEST_COMPARE (ftell (fp), cur_offset);
|
||||
+ break;
|
||||
+
|
||||
+ case verify_ftello:
|
||||
+ TEST_COMPARE (ftello (fp), cur_offset);
|
||||
+ break;
|
||||
+
|
||||
+ case verify_ftello64:
|
||||
+ TEST_COMPARE (ftello64 (fp), cur_offset);
|
||||
+ break;
|
||||
+
|
||||
+ case verify_fd:
|
||||
+ TEST_COMPARE (lseek (fd, 0, SEEK_CUR), cur_offset);
|
||||
+ break;
|
||||
+
|
||||
+ default:
|
||||
+ abort ();
|
||||
+ }
|
||||
+ if (! clear_closes_file (cl))
|
||||
+ {
|
||||
+ int ret = fclose (fp);
|
||||
+ TEST_COMPARE (ret, 0);
|
||||
+ }
|
||||
+ if (done_dup)
|
||||
+ xclose (fd);
|
||||
+}
|
||||
+
|
||||
+int
|
||||
+do_test (void)
|
||||
+{
|
||||
+ setup ();
|
||||
+ for (enum test_open_case c = 0; c < test_open_max; c++)
|
||||
+ for (enum test_base_loc loc = 0; loc < base_loc_max; loc++)
|
||||
+ for (int offset = -2; offset <= 3; offset++)
|
||||
+ for (int num_ungetc = 0;
|
||||
+ num_ungetc <= 2 && num_ungetc <= base_loc_to_bytes (loc, offset);
|
||||
+ num_ungetc++)
|
||||
+ for (int num_ungetc_diff = 0;
|
||||
+ num_ungetc_diff <= num_ungetc;
|
||||
+ num_ungetc_diff++)
|
||||
+ for (int ungetc_fallback = 0;
|
||||
+ ungetc_fallback <= (num_ungetc == 1 ? 1 : 0);
|
||||
+ ungetc_fallback++)
|
||||
+ for (enum clear_ungetc_case cl = 0; cl < clear_max; cl++)
|
||||
+ {
|
||||
+ if (!clear_valid (c, cl))
|
||||
+ continue;
|
||||
+ if (base_loc_to_bytes (loc, offset) > 2 * BUFSIZ)
|
||||
+ continue;
|
||||
+ if ((base_loc_to_bytes (loc, offset)
|
||||
+ - num_ungetc
|
||||
+ + clear_offset (cl, num_ungetc)) < 0)
|
||||
+ continue;
|
||||
+ if ((base_loc_to_bytes (loc, offset)
|
||||
+ - num_ungetc
|
||||
+ + clear_offset (cl, num_ungetc)) > 2 * BUFSIZ)
|
||||
+ continue;
|
||||
+ for (enum verify_case v = 0; v < verify_max; v++)
|
||||
+ {
|
||||
+ if (!verify_valid (c, cl, v))
|
||||
+ continue;
|
||||
+ test_one_case (c, loc, offset, num_ungetc,
|
||||
+ num_ungetc_diff, ungetc_fallback, cl, v);
|
||||
+ }
|
||||
+ }
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#include <support/test-driver.c>
|
||||
107
glibc-RHEL-119434-8.patch
Normal file
107
glibc-RHEL-119434-8.patch
Normal file
@ -0,0 +1,107 @@
|
||||
commit 7b47b3dd214c8ff2c699f13efe5533941be53635
|
||||
Author: Florian Weimer <fweimer@redhat.com>
|
||||
Date: Tue Apr 8 18:38:38 2025 +0200
|
||||
|
||||
libio: Synthesize ESPIPE error if lseek returns 0 after reading bytes
|
||||
|
||||
This is required so that fclose, when trying to seek to the right
|
||||
position after filling the input buffer, does not fail with EINVAL.
|
||||
This fclose code path only ignores ESPIPE errors.
|
||||
|
||||
Reported by Petr Pisar on
|
||||
<https://bugzilla.redhat.com/show_bug.cgi?id=2358265>.
|
||||
|
||||
Fixes commit be6818be31e756398e45f70e2819d78be0961223 ("Make fclose
|
||||
seek input file to right offset (bug 12724)").
|
||||
|
||||
Reviewed-by: Frédéric Bérat <fberat@redhat.com>
|
||||
|
||||
diff --git a/libio/fileops.c b/libio/fileops.c
|
||||
index ac5ff9a4353d0201..cca52c09beb11747 100644
|
||||
--- a/libio/fileops.c
|
||||
+++ b/libio/fileops.c
|
||||
@@ -928,6 +928,16 @@ do_ftell (FILE *fp)
|
||||
if (result == EOF)
|
||||
return result;
|
||||
|
||||
+ if (result == 0 && offset < 0)
|
||||
+ {
|
||||
+ /* This happens for some character devices that always report
|
||||
+ file offset 0 even after some data has been read (instead of
|
||||
+ failing with ESPIPE). The fclose path ignores this
|
||||
+ error. */
|
||||
+ __set_errno (ESPIPE);
|
||||
+ return EOF;
|
||||
+ }
|
||||
+
|
||||
result += offset;
|
||||
|
||||
if (result < 0)
|
||||
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
|
||||
index 31ff420432453dfe..cee076cb7bcff2d2 100644
|
||||
--- a/stdio-common/Makefile
|
||||
+++ b/stdio-common/Makefile
|
||||
@@ -206,6 +206,7 @@ tests := \
|
||||
tst-bz11319-fortify2 \
|
||||
tst-cookie \
|
||||
tst-dprintf-length \
|
||||
+ tst-fclose-devzero \
|
||||
tst-fclose-offset \
|
||||
tst-fdopen \
|
||||
tst-fdopen2 \
|
||||
diff --git a/stdio-common/tst-fclose-devzero.c b/stdio-common/tst-fclose-devzero.c
|
||||
new file mode 100644
|
||||
index 0000000000000000..1c7b39a3e04ba80d
|
||||
--- /dev/null
|
||||
+++ b/stdio-common/tst-fclose-devzero.c
|
||||
@@ -0,0 +1,50 @@
|
||||
+/* Test that always-zero lseek does not cause fclose failure after fread.
|
||||
+ Copyright (C) 2025 Free Software Foundation, Inc.
|
||||
+ This file is part of the GNU C Library.
|
||||
+
|
||||
+ The GNU C Library is free software; you can redistribute it and/or
|
||||
+ modify it under the terms of the GNU Lesser General Public
|
||||
+ License as published by the Free Software Foundation; either
|
||||
+ version 2.1 of the License, or (at your option) any later version.
|
||||
+
|
||||
+ The GNU C Library is distributed in the hope that it will be useful,
|
||||
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
+ Lesser General Public License for more details.
|
||||
+
|
||||
+ You should have received a copy of the GNU Lesser General Public
|
||||
+ License along with the GNU C Library; if not, see
|
||||
+ <https://www.gnu.org/licenses/>. */
|
||||
+
|
||||
+#include <errno.h>
|
||||
+#include <stdio.h>
|
||||
+#include <string.h>
|
||||
+
|
||||
+#include <support/check.h>
|
||||
+#include <support/xstdio.h>
|
||||
+
|
||||
+int
|
||||
+do_test (void)
|
||||
+{
|
||||
+ for (int do_ftello = 0; do_ftello < 2; ++do_ftello)
|
||||
+ {
|
||||
+ FILE *fp = xfopen ("/dev/zero", "r");
|
||||
+ char buf[17];
|
||||
+ memset (buf, 0xcc, sizeof (buf));
|
||||
+ xfread (buf, 1, sizeof (buf), fp);
|
||||
+ static const char zeros[sizeof (buf)] = { 0 };
|
||||
+ TEST_COMPARE_BLOB (buf, sizeof (buf), zeros, sizeof (zeros));
|
||||
+ if (do_ftello)
|
||||
+ {
|
||||
+ errno = 0;
|
||||
+ TEST_COMPARE (ftello (fp), -1);
|
||||
+ TEST_COMPARE (errno, ESPIPE);
|
||||
+ }
|
||||
+ /* Do not use xfclose because it flushes first. */
|
||||
+ TEST_COMPARE (fclose (fp), 0);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
+#include <support/test-driver.c>
|
||||
Loading…
Reference in New Issue
Block a user