diff --git a/glibc-RHEL-119434-1.patch b/glibc-RHEL-119434-1.patch new file mode 100644 index 0000000..979034d --- /dev/null +++ b/glibc-RHEL-119434-1.patch @@ -0,0 +1,71 @@ +commit 04b1eb161fdc8b88876bf78b34d2bb92584aba45 +Author: Siddhesh Poyarekar +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 + Reviewed-by: Adhemerval Zanella + +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 ++ . */ ++ ++#include ++#include ++ ++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); diff --git a/glibc-RHEL-119434-2.patch b/glibc-RHEL-119434-2.patch new file mode 100644 index 0000000..26477a0 --- /dev/null +++ b/glibc-RHEL-119434-2.patch @@ -0,0 +1,119 @@ +commit 377e9733b50ce41e496c467ddcc112f73c88f3bd +Author: Joseph Myers +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 ++ . */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++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 diff --git a/glibc-RHEL-119434-3.patch b/glibc-RHEL-119434-3.patch new file mode 100644 index 0000000..37cd353 --- /dev/null +++ b/glibc-RHEL-119434-3.patch @@ -0,0 +1,355 @@ +commit be6818be31e756398e45f70e2819d78be0961223 +Author: Joseph Myers +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 ++ . */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++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 diff --git a/glibc-RHEL-119434-4.patch b/glibc-RHEL-119434-4.patch new file mode 100644 index 0000000..0e0b8d1 --- /dev/null +++ b/glibc-RHEL-119434-4.patch @@ -0,0 +1,150 @@ +commit 94251ae99edaa911f4cb8056748dca0874ea268c +Author: Joseph Myers +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 ++ . */ ++ ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++ ++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 diff --git a/glibc-RHEL-119434-5.patch b/glibc-RHEL-119434-5.patch new file mode 100644 index 0000000..59c5844 --- /dev/null +++ b/glibc-RHEL-119434-5.patch @@ -0,0 +1,115 @@ +commit 0dcc0b2f63051863187dc678964eb17761b1a820 +Author: Joseph Myers +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 ++ . */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++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 diff --git a/glibc-RHEL-119434-6.patch b/glibc-RHEL-119434-6.patch new file mode 100644 index 0000000..7bda950 --- /dev/null +++ b/glibc-RHEL-119434-6.patch @@ -0,0 +1,110 @@ +commit 3ff3b9997cfef891ba33a14f1dcba0310d96369c +Author: Joseph Myers +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 ++ . */ ++ ++#include ++ ++#include ++#include ++#include ++#include ++ ++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 diff --git a/glibc-RHEL-119434-7.patch b/glibc-RHEL-119434-7.patch new file mode 100644 index 0000000..74b111d --- /dev/null +++ b/glibc-RHEL-119434-7.patch @@ -0,0 +1,595 @@ +commit 203452a460143c2b0bf80e0e92027e0fd6e19fa4 +Author: Joseph Myers +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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++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 diff --git a/glibc-RHEL-119434-8.patch b/glibc-RHEL-119434-8.patch new file mode 100644 index 0000000..57ed9d1 --- /dev/null +++ b/glibc-RHEL-119434-8.patch @@ -0,0 +1,107 @@ +commit 7b47b3dd214c8ff2c699f13efe5533941be53635 +Author: Florian Weimer +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 + . + + Fixes commit be6818be31e756398e45f70e2819d78be0961223 ("Make fclose + seek input file to right offset (bug 12724)"). + + Reviewed-by: Frédéric Bérat + +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 ++ . */ ++ ++#include ++#include ++#include ++ ++#include ++#include ++ ++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 diff --git a/glibc.spec b/glibc.spec index 593545d..494c91e 100644 --- a/glibc.spec +++ b/glibc.spec @@ -2349,7 +2349,7 @@ update_gconv_modules_cache () %endif %changelog -* Thu Oct 23 2025 Eduard Abdullin - 2.39-79.alma.1 +* Fri Oct 24 2025 Eduard Abdullin - 2.39-80.alma.1 - Overwrite target for x86_64_v2 - Update patch-git.lua to handle AlmaLinux branches correctly