Add more tests for freopen (RHEL-46736)

Resolves: RHEL-46736
This commit is contained in:
Florian Weimer 2024-11-07 19:51:26 +01:00
parent a2d1e68858
commit 9c1694b310
12 changed files with 2298 additions and 1 deletions

67
glibc-RHEL-46736-1.patch Normal file
View File

@ -0,0 +1,67 @@
commit e3fdbe9f39747206b9c3fbb0219f29fd5b35d020
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Date: Thu Apr 8 17:36:07 2021 -0300
support: Add xmkfifo
Wrapper support mkfifo.
diff --git a/support/Makefile b/support/Makefile
index 25e8bbefd78c2268..0273c0f6306720c9 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -137,6 +137,7 @@ libsupport-routines = \
xmemstream \
xmkdir \
xmkdirp \
+ xmkfifo \
xmmap \
xmprotect \
xmunmap \
diff --git a/support/xmkfifo.c b/support/xmkfifo.c
new file mode 100644
index 0000000000000000..a8e196dbc209a47d
--- /dev/null
+++ b/support/xmkfifo.c
@@ -0,0 +1,29 @@
+/* mkfifo with error checking.
+ Copyright (C) 2022 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 <support/check.h>
+#include <support/xunistd.h>
+#include <sys/stat.h>
+
+void
+xmkfifo (const char *pathname, mode_t mode)
+{
+ int r = mkfifo (pathname, mode);
+ if (r < 0)
+ FAIL_EXIT1 ("mkfifo (%s, 0%o): %m", pathname, mode);
+}
diff --git a/support/xunistd.h b/support/xunistd.h
index a25ecf1cf38ef328..94b1e1eb1b1e8253 100644
--- a/support/xunistd.h
+++ b/support/xunistd.h
@@ -61,6 +61,7 @@ void xsymlink (const char *target, const char *linkpath);
void xchdir (const char *path);
void xfchmod (int fd, mode_t mode);
void xchmod (const char *pathname, mode_t mode);
+void xmkfifo (const char *pathname, mode_t mode);
/* Equivalent of "mkdir -p". */
void xmkdirp (const char *, mode_t);

30
glibc-RHEL-46736-10.patch Normal file
View File

@ -0,0 +1,30 @@
commit 6948ee4edf0c57c556f8d5f394d9191216d05780
Author: Florian Weimer <fweimer@redhat.com>
Date: Sat Sep 28 21:06:11 2024 +0200
stdio-common: Fix memory leak in tst-freopen4* tests on UNSUPPORTED
The temp_dir allocation leaks if support_can_chroot returns false.
diff --git a/stdio-common/tst-freopen4-main.c b/stdio-common/tst-freopen4-main.c
index e169442cf4df2e9d..7284677a97e10af6 100644
--- a/stdio-common/tst-freopen4-main.c
+++ b/stdio-common/tst-freopen4-main.c
@@ -33,7 +33,7 @@ int
do_test (void)
{
mtrace ();
- char *temp_dir = support_create_temp_directory ("tst-freopen4");
+ char *temp_dir;
FILE *fp;
int ret;
@@ -45,6 +45,8 @@ do_test (void)
support_become_root ();
if (!support_can_chroot ())
return EXIT_UNSUPPORTED;
+
+ temp_dir = support_create_temp_directory ("tst-freopen4");
xchroot (temp_dir);
/* Test freopen with NULL, renamed file. This verifies that

216
glibc-RHEL-46736-11.patch Normal file
View File

@ -0,0 +1,216 @@
commit 42c810c2cf3554afbdd60885b7da6bb4e702466f
Author: Joseph Myers <josmyers@redhat.com>
Date: Mon Oct 7 19:44:25 2024 +0000
Add freopen special-case tests: thread cancellation
Add tests of freopen adding or removing "c" (non-cancelling I/O) from
the mode string (so completing my planned tests of freopen with
different features used in the mode strings). Note that it's in the
nature of the uncertain time at which cancellation might act (possibly
during freopen, possibly during subsequent reads) that these can leak
memory or file descriptors, so these do not include leak tests.
Tested for x86_64.
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index c920f55ed2119900..09d3622823203f74 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -180,10 +180,12 @@ tests := \
tst-freopen4 \
tst-freopen5 \
tst-freopen6 \
+ tst-freopen7 \
tst-freopen64-2 \
tst-freopen64-3 \
tst-freopen64-4 \
tst-freopen64-6 \
+ tst-freopen64-7 \
tst-fseek \
tst-fwrite \
tst-getline \
@@ -480,3 +482,6 @@ $(objpfx)tst-setvbuf1-cmp.out: tst-setvbuf1.expect $(objpfx)tst-setvbuf1.out
$(objpfx)tst-printf-round: $(libm)
$(objpfx)tst-scanf-round: $(libm)
+
+$(objpfx)tst-freopen7: $(shared-thread-library)
+$(objpfx)tst-freopen64-7: $(shared-thread-library)
diff --git a/stdio-common/tst-freopen64-7.c b/stdio-common/tst-freopen64-7.c
new file mode 100644
index 0000000000000000..f34c2805210079b9
--- /dev/null
+++ b/stdio-common/tst-freopen64-7.c
@@ -0,0 +1,2 @@
+#define FREOPEN freopen64
+#include <tst-freopen7-main.c>
diff --git a/stdio-common/tst-freopen7-main.c b/stdio-common/tst-freopen7-main.c
new file mode 100644
index 0000000000000000..965e0b4adce750cc
--- /dev/null
+++ b/stdio-common/tst-freopen7-main.c
@@ -0,0 +1,155 @@
+/* Test freopen cancellation handling.
+ Copyright (C) 2024 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 <fcntl.h>
+#include <mcheck.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <wchar.h>
+
+#include <support/check.h>
+#include <support/file_contents.h>
+#include <support/support.h>
+#include <support/temp_file.h>
+#include <support/test-driver.h>
+#include <support/xstdio.h>
+#include <support/xthread.h>
+#include <support/xunistd.h>
+
+char *file1, *file2, *file3, *fifo;
+
+sem_t sem;
+
+void *
+test_rc_to_r (void *p)
+{
+ int ret;
+ FILE *fp, *fp2;
+ ret = sem_post (&sem);
+ TEST_VERIFY_EXIT (ret == 0);
+ fp = xfopen (file1, "rc");
+ for (int i = 0; i < 1000000; i++)
+ {
+ fgetc (fp);
+ fseek (fp, 0, SEEK_SET);
+ }
+ fp2 = xfopen (file3, "wc");
+ fputs ("rc_to_r got to freopen", fp2);
+ xfclose (fp2);
+ /* Cancellation should occur at some point from here onwards
+ (possibly leaking memory and file descriptors associated with the
+ FILE). */
+ fp = FREOPEN (file2, "r", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ for (;;)
+ {
+ fgetc (fp);
+ fseek (fp, 0, SEEK_SET);
+ }
+}
+
+void *
+test_r_to_rc (void *p)
+{
+ int ret;
+ FILE *fp;
+ fp = xfopen (file1, "r");
+ fp = FREOPEN (fifo, "rc", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = sem_post (&sem);
+ TEST_VERIFY_EXIT (ret == 0);
+ /* No cancellation should occur for I/O on fifo. */
+ ret = fgetc (fp);
+ /* At this point, the other thread has called pthread_cancel and
+ then written a byte to the fifo, so this thread is cancelled at
+ the next cancellation point. */
+ TEST_VERIFY (ret == 'x');
+ xfclose (fp);
+ fp = xfopen (file3, "wc");
+ fputs ("r_to_rc got to fclose", fp);
+ xfclose (fp);
+ pthread_testcancel ();
+ FAIL_EXIT1 ("test_r_to_rc not cancelled\n");
+}
+
+int
+do_test (void)
+{
+ char *temp_dir = support_create_temp_directory ("tst-freopen-cancel");
+ file1 = xasprintf ("%s/file1", temp_dir);
+ support_write_file_string (file1, "file1");
+ add_temp_file (file1);
+ file2 = xasprintf ("%s/file2", temp_dir);
+ support_write_file_string (file2, "file2");
+ add_temp_file (file2);
+ file3 = xasprintf ("%s/file3", temp_dir);
+ support_write_file_string (file3, "file3");
+ add_temp_file (file3);
+ fifo = xasprintf ("%s/fifo", temp_dir);
+ xmkfifo (fifo, 0666);
+ add_temp_file (fifo);
+ int ret;
+ pthread_t thr;
+ void *retval;
+
+ /* Test changing to/from c (cancellation disabled). */
+
+ verbose_printf ("Testing rc -> r\n");
+ ret = sem_init (&sem, 0, 0);
+ TEST_VERIFY_EXIT (ret == 0);
+ thr = xpthread_create (NULL, test_rc_to_r, NULL);
+ ret = sem_wait (&sem);
+ TEST_VERIFY_EXIT (ret == 0);
+ xpthread_cancel (thr);
+ ret = pthread_join (thr, &retval);
+ TEST_COMPARE (ret, 0);
+ TEST_VERIFY (retval == PTHREAD_CANCELED);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file3, "rc_to_r got to freopen");
+
+ verbose_printf ("Testing r -> rc\n");
+ ret = sem_init (&sem, 0, 0);
+ TEST_VERIFY_EXIT (ret == 0);
+ thr = xpthread_create (NULL, test_r_to_rc, NULL);
+ FILE *fp = xfopen (fifo, "w");
+ ret = sem_wait (&sem);
+ TEST_VERIFY_EXIT (ret == 0);
+ /* This call happens while, or before, the other thread is waiting
+ to read a character from the fifo. It thus verifies that
+ cancellation does not occur from the fgetc call in that thread
+ (it should instead occur only in pthread_testcancel call),
+ because the expected string is only written to file3 after that
+ thread closes the fifo. */
+ xpthread_cancel (thr);
+ fputc ('x', fp);
+ xfclose (fp);
+ ret = pthread_join (thr, &retval);
+ TEST_COMPARE (ret, 0);
+ TEST_VERIFY (retval == PTHREAD_CANCELED);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file3, "r_to_rc got to fclose");
+
+ free (temp_dir);
+ free (file1);
+ free (file2);
+ free (file3);
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/stdio-common/tst-freopen7.c b/stdio-common/tst-freopen7.c
new file mode 100644
index 0000000000000000..03d0de798e3d2616
--- /dev/null
+++ b/stdio-common/tst-freopen7.c
@@ -0,0 +1,2 @@
+#define FREOPEN freopen
+#include <tst-freopen7-main.c>

168
glibc-RHEL-46736-2.patch Normal file
View File

@ -0,0 +1,168 @@
commit da55fae9e277a0c138d4395fee505e5d2f8b2b84
Author: Carlos O'Donell <carlos@redhat.com>
Date: Tue May 23 08:34:56 2023 -0400
support: Reformat Makefile.
Add list end markers.
Sort text using scripts/sort-makefile-lines.py.
No code generation changes observed in non-test binary artifacts.
No regressions on x86_64 and i686.
Conflicts:
support/Makefile
(different backport order)
diff --git a/support/Makefile b/support/Makefile
index 0273c0f6306720c9..17a4157563f9ecd6 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -41,7 +41,7 @@ libsupport-routines = \
resolv_response_context_free \
resolv_test \
set_fortify_handler \
- support_stack_alloc \
+ support-open-dev-null-range \
support_become_root \
support_can_chroot \
support_capture_subprocess \
@@ -65,12 +65,11 @@ libsupport-routines = \
support_fuse \
support_isolate_in_subprocess \
support_need_proc \
+ support_openpty \
support_path_support_time64 \
+ support_paths \
support_process_state \
support_ptrace \
- support-open-dev-null-range \
- support_openpty \
- support_paths \
support_quote_blob \
support_quote_blob_wide \
support_quote_string \
@@ -84,6 +83,8 @@ libsupport-routines = \
support_shared_allocate \
support_small_stack_thread_attribute \
support_socket_so_timestamp_time64 \
+ support_stack_alloc \
+ support_stack_alloc \
support_stat_nanoseconds \
support_subprocess \
support_test_compare_blob \
@@ -96,11 +97,11 @@ libsupport-routines = \
support_write_file_string \
temp_file \
timespec \
- timespec-time64 \
timespec-add \
timespec-add-time64 \
timespec-sub \
timespec-sub-time64 \
+ timespec-time64 \
write_message \
xaccept \
xaccept4 \
@@ -108,6 +109,8 @@ libsupport-routines = \
xbind \
xcalloc \
xchdir \
+ xchmod \
+ xchmod \
xchroot \
xclock_gettime \
xclock_gettime_time64 \
@@ -116,7 +119,6 @@ libsupport-routines = \
xclone \
xclose \
xclosedir \
- xchmod \
xconnect \
xcopy_file_range \
xdlfcn \
@@ -147,11 +149,17 @@ libsupport-routines = \
xpipe \
xpoll \
xposix_memalign \
+ xposix_spawn \
+ xposix_spawn \
+ xposix_spawn_file_actions_addclose \
+ xposix_spawn_file_actions_addclose \
+ xposix_spawn_file_actions_adddup2 \
+ xposix_spawn_file_actions_adddup2 \
xpthread_attr_destroy \
xpthread_attr_init \
+ xpthread_attr_setaffinity_np \
xpthread_attr_setdetachstate \
xpthread_attr_setguardsize \
- xpthread_attr_setaffinity_np \
xpthread_attr_setstack \
xpthread_attr_setstacksize \
xpthread_barrier_destroy \
@@ -162,8 +170,8 @@ libsupport-routines = \
xpthread_barrierattr_setpshared \
xpthread_cancel \
xpthread_check_return \
- xpthread_cond_wait \
xpthread_cond_signal \
+ xpthread_cond_wait \
xpthread_create \
xpthread_detach \
xpthread_join \
@@ -182,8 +190,8 @@ libsupport-routines = \
xpthread_mutexattr_setrobust \
xpthread_mutexattr_settype \
xpthread_once \
- xpthread_rwlock_init \
xpthread_rwlock_destroy \
+ xpthread_rwlock_init \
xpthread_rwlock_rdlock \
xpthread_rwlock_unlock \
xpthread_rwlock_wrlock \
@@ -204,9 +212,6 @@ libsupport-routines = \
xsigstack \
xsocket \
xstatx \
- xposix_spawn \
- xposix_spawn_file_actions_addclose \
- xposix_spawn_file_actions_adddup2 \
xstrdup \
xstrndup \
xsymlink \
@@ -215,6 +220,7 @@ libsupport-routines = \
xuselocale \
xwaitpid \
xwrite \
+ # libsupport-routines
libsupport-static-only-routines := $(libsupport-routines)
# Only build one variant of the library.
@@ -278,8 +284,16 @@ LDLIBS-test-container = $(libsupport)
others += test-container
others-noinstall += test-container
-others += shell-container echo-container true-container
-others-noinstall += shell-container echo-container true-container
+others += \
+ echo-container \
+ shell-container \
+ true-container \
+ # others
+others-noinstall += \
+ echo-container \
+ shell-container \
+ true-container \
+ # others-noinstall
others += $(LINKS_DSO_PROGRAM)
others-noinstall += $(LINKS_DSO_PROGRAM)
@@ -317,6 +331,7 @@ tests = \
tst-xdirent \
tst-xreadlink \
tst-xsigstack \
+ # tests
ifeq ($(run-built-tests),yes)
tests-special = \

261
glibc-RHEL-46736-3.patch Normal file
View File

@ -0,0 +1,261 @@
commit 96d0bf98cafd0b63721f369ca21ec64590551d47
Author: Joseph Myers <josmyers@redhat.com>
Date: Tue Sep 3 13:53:01 2024 +0000
Add support/ code for checking file contents
For use in freopen tests, add various support/ helper interfaces for
use in checking file contents.
Tested for x86_64.
diff --git a/support/Makefile b/support/Makefile
index 38ad266a0dec8e36..e70322cea06f137b 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -48,6 +48,8 @@ libsupport-routines = \
support_check_stat_fd \
support_check_stat_path \
support_chroot \
+ support_compare_file_bytes \
+ support_compare_file_string \
support_copy_file \
support_copy_file_range \
support_create_timer \
@@ -64,6 +66,8 @@ libsupport-routines = \
support_fuse \
support_isolate_in_subprocess \
support_need_proc \
+ support_open_and_compare_file_bytes \
+ support_open_and_compare_file_string \
support_openpty \
support_path_support_time64 \
support_paths \
diff --git a/support/file_contents.h b/support/file_contents.h
new file mode 100644
index 0000000000000000..9b2d750aae8a885a
--- /dev/null
+++ b/support/file_contents.h
@@ -0,0 +1,63 @@
+/* Functionality for checking file contents.
+ Copyright (C) 2024 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/>. */
+
+#ifndef SUPPORT_FILE_CONTENTS_H
+#define SUPPORT_FILE_CONTENTS_H
+
+#include <support/check.h>
+#include <stdio.h>
+
+__BEGIN_DECLS
+
+/* Check that an already-open file has exactly the given bytes,
+ starting at the current location in the file. The file position
+ indicator is updated to point after the bytes compared. Return 0
+ if equal, 1 otherwise or on read error. */
+int support_compare_file_bytes (FILE *fp, const char *contents, size_t length);
+
+/* Check that an already-open file has exactly the given string as
+ contents, starting at the current offset. The file position
+ indicator is updated to point after the bytes compared. Return 0
+ if equal, 1 otherwise or on read error. */
+int support_compare_file_string (FILE *fp, const char *contents);
+
+/* Check that a not-currently-open file has exactly the given bytes.
+ Return 0 if equal, 1 otherwise or on read error. */
+int support_open_and_compare_file_bytes (const char *file,
+ const char *contents,
+ size_t length);
+
+/* Check that a not-currently-open file has exactly the given string
+ as contents, starting at the current offset. Return 0 if equal, 1
+ otherwise or on read error. */
+int support_open_and_compare_file_string (const char *file,
+ const char *contents);
+
+/* Compare bytes read from an open file with the given string. The
+ file position indicator is updated to point after the bytes
+ compared. */
+#define TEST_COMPARE_FILE_STRING(FP, CONTENTS) \
+ TEST_COMPARE (support_compare_file_string (FP, CONTENTS), 0)
+
+/* Read a file and compare bytes read from it with the given string. */
+#define TEST_OPEN_AND_COMPARE_FILE_STRING(FILE, CONTENTS) \
+ TEST_COMPARE (support_open_and_compare_file_string (FILE, CONTENTS), 0)
+
+__END_DECLS
+
+#endif /* SUPPORT_FILE_CONTENTS_H */
diff --git a/support/support_compare_file_bytes.c b/support/support_compare_file_bytes.c
new file mode 100644
index 0000000000000000..e261e1da8f7b02b2
--- /dev/null
+++ b/support/support_compare_file_bytes.c
@@ -0,0 +1,42 @@
+/* Compare bytes from an open file.
+ Copyright (C) 2024 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/file_contents.h>
+
+/* Check that an already-open file has exactly the given bytes,
+ starting at the current offset. */
+
+int
+support_compare_file_bytes (FILE *fp, const char *contents, size_t length)
+{
+ int c;
+ while (length > 0)
+ {
+ c = getc (fp);
+ if (c == EOF || (unsigned char) c != (unsigned char) contents[0])
+ return 1;
+ contents++;
+ length--;
+ }
+ c = getc (fp);
+ if (c != EOF || ferror (fp))
+ return 1;
+ return 0;
+}
diff --git a/support/support_compare_file_string.c b/support/support_compare_file_string.c
new file mode 100644
index 0000000000000000..04513c3af197037d
--- /dev/null
+++ b/support/support_compare_file_string.c
@@ -0,0 +1,28 @@
+/* Compare string from an open file.
+ Copyright (C) 2024 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 <string.h>
+
+#include <support/file_contents.h>
+
+int
+support_compare_file_string (FILE *fp, const char *contents)
+{
+ return support_compare_file_bytes (fp, contents, strlen (contents));
+}
diff --git a/support/support_open_and_compare_file_bytes.c b/support/support_open_and_compare_file_bytes.c
new file mode 100644
index 0000000000000000..f804ed8e460d82f0
--- /dev/null
+++ b/support/support_open_and_compare_file_bytes.c
@@ -0,0 +1,33 @@
+/* Compare bytes from a file.
+ Copyright (C) 2024 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 <support/file_contents.h>
+#include <support/xstdio.h>
+
+/* Check that a not-currently-open file has exactly the given
+ bytes. */
+
+int
+support_open_and_compare_file_bytes (const char *file, const char *contents,
+ size_t length)
+{
+ FILE *fp = xfopen (file, "r");
+ int ret = support_compare_file_bytes (fp, contents, length);
+ xfclose (fp);
+ return ret;
+}
diff --git a/support/support_open_and_compare_file_string.c b/support/support_open_and_compare_file_string.c
new file mode 100644
index 0000000000000000..2b596d4c88b697f2
--- /dev/null
+++ b/support/support_open_and_compare_file_string.c
@@ -0,0 +1,32 @@
+/* Compare string from a file.
+ Copyright (C) 2024 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 <string.h>
+
+#include <support/file_contents.h>
+#include <support/xstdio.h>
+
+/* Check that a not-currently-open file has exactly the given string
+ as contents, starting at the current offset. */
+
+int
+support_open_and_compare_file_string (const char *file, const char *contents)
+{
+ return support_open_and_compare_file_bytes (file, contents,
+ strlen (contents));
+}

739
glibc-RHEL-46736-4.patch Normal file
View File

@ -0,0 +1,739 @@
commit ed4bb289cf739f537deb735eaa01be531df084b9
Author: Joseph Myers <josmyers@redhat.com>
Date: Wed Sep 4 16:32:21 2024 +0000
Add more thorough tests of freopen
freopen is rather minimally tested in libio/tst-freopen and
libio/test-freopen. Add some more thorough tests, covering different
cases for change of mode in particular. The tests are run for both
freopen and freopen64 (given that those functions have two separate
copies of much of the code, so any bug fix directly in the freopen
code would probably need applying in both places).
Note that there are two parts of the tests disabled because of bugs
discovered through running the tests, with bug numbers given in
comments. I expect to address those separately. The tests also don't
cover changes to cancellation ("c" in mode); I think that will better
be handled through a separate test. Also to handle separately:
testing on stdin / stdout / stderr; documenting lack of support for
streams opened with popen / fmemopen / open_memstream / fopencookie;
maybe also a chroot test without /proc; maybe also more thorough tests
for large file handling on 32-bit systems (freopen64).
Tested for x86_64.
Conflicts:
stdio-common/Makefile
(tst-fread already backported)
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index ea8598bbe3a6dfdd..5eddc4bfbf4e7fb9 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -175,6 +175,10 @@ tests := \
tst-fphex \
tst-fphex-wide \
tst-fread \
+ tst-freopen2 \
+ tst-freopen3 \
+ tst-freopen64-2 \
+ tst-freopen64-3 \
tst-fseek \
tst-fwrite \
tst-getline \
@@ -246,6 +250,8 @@ tests-special += \
ifeq (yes,$(build-shared))
ifneq ($(PERL),no)
tests-special += \
+ $(objpfx)tst-freopen2-mem.out \
+ $(objpfx)tst-freopen64-2-mem.out \
$(objpfx)tst-getline-enomem-mem.out \
$(objpfx)tst-getline-mem.out \
$(objpfx)tst-printf-bz18872-mem.out \
@@ -256,6 +262,10 @@ tests-special += \
# tests-special
generated += \
+ tst-freopen2-mem.out \
+ tst-freopen2.mtrace \
+ tst-freopen64-2-mem.out \
+ tst-freopen64-2.mtrace \
tst-getline-enomem-mem.out \
tst-getline-enomem.mtrace \
tst-getline-mem.out \
@@ -328,6 +338,12 @@ tst-getline-ENV = \
tst-getline-enomem-ENV = \
MALLOC_TRACE=$(objpfx)tst-getline-enomem.mtrace \
LD_PRELOAD=$(common-objpfx)malloc/libc_malloc_debug.so
+tst-freopen2-ENV = \
+ MALLOC_TRACE=$(objpfx)tst-freopen2.mtrace \
+ LD_PRELOAD=$(common-objpfx)malloc/libc_malloc_debug.so
+tst-freopen64-2-ENV = \
+ MALLOC_TRACE=$(objpfx)tst-freopen64-2.mtrace \
+ LD_PRELOAD=$(common-objpfx)malloc/libc_malloc_debug.so
$(objpfx)tst-unbputc.out: tst-unbputc.sh $(objpfx)tst-unbputc
$(SHELL) $< $(common-objpfx) '$(test-program-prefix)'; \
diff --git a/stdio-common/tst-freopen2-main.c b/stdio-common/tst-freopen2-main.c
new file mode 100644
index 0000000000000000..22b21afebf709563
--- /dev/null
+++ b/stdio-common/tst-freopen2-main.c
@@ -0,0 +1,526 @@
+/* Test freopen.
+ Copyright (C) 2024 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 <fcntl.h>
+#include <mcheck.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <wchar.h>
+
+#include <support/check.h>
+#include <support/descriptors.h>
+#include <support/file_contents.h>
+#include <support/support.h>
+#include <support/temp_file.h>
+#include <support/test-driver.h>
+#include <support/xstdio.h>
+
+#define START_TEST(DESC) \
+ do \
+ { \
+ fds = support_descriptors_list (); \
+ verbose_printf (DESC); \
+ } \
+ while (0)
+
+#define END_TEST \
+ do \
+ { \
+ support_descriptors_check (fds); \
+ support_descriptors_free (fds); \
+ } \
+ while (0)
+
+int
+do_test (void)
+{
+ mtrace ();
+ struct support_descriptors *fds;
+ char *temp_dir = support_create_temp_directory ("tst-freopen2");
+ char *file1 = xasprintf ("%s/file1", temp_dir);
+ support_write_file_string (file1, "file1");
+ add_temp_file (file1);
+ char *file2 = xasprintf ("%s/file2", temp_dir);
+ support_write_file_string (file2, "file2");
+ add_temp_file (file2);
+ char *file3 = xasprintf ("%s/file3", temp_dir);
+ char *file4 = xasprintf ("%s/file4", temp_dir);
+ char *file1a = xasprintf ("%s/file1a", temp_dir);
+ FILE *fp;
+ int ret;
+ wint_t wc;
+
+ /* Test each pair of old and new modes from r w a. */
+
+ START_TEST ("Testing r -> r\n");
+ fp = xfopen (file1, "r");
+ fp = FREOPEN (file2, "r", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "file2");
+ xfclose (fp);
+ END_TEST;
+
+ START_TEST ("Testing r -> w\n");
+ fp = xfopen (file1, "r");
+ fp = FREOPEN (file2, "w", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fputs ("File2new", fp);
+ TEST_VERIFY (ret >= 0);
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file1, "file1");
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "File2new");
+ END_TEST;
+
+ START_TEST ("Testing r -> a\n");
+ fp = xfopen (file1, "r");
+ fp = FREOPEN (file2, "a", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fputs ("3", fp);
+ TEST_VERIFY (ret >= 0);
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "File2new3");
+ END_TEST;
+
+ START_TEST ("Testing w -> r\n");
+ fp = xfopen (file1, "w");
+ fp = FREOPEN (file2, "r", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "File2new3");
+ xfclose (fp);
+ END_TEST;
+
+ START_TEST ("Testing w -> w\n");
+ fp = xfopen (file1, "w");
+ fp = FREOPEN (file2, "w", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fputs ("next", fp);
+ TEST_VERIFY (ret >= 0);
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file1, "");
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "next");
+ END_TEST;
+
+ START_TEST ("Testing w -> a\n");
+ fp = xfopen (file1, "w");
+ fp = FREOPEN (file2, "a", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fputs ("4", fp);
+ TEST_VERIFY (ret >= 0);
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "next4");
+ END_TEST;
+
+ START_TEST ("Testing a -> r\n");
+ fp = xfopen (file1, "a");
+ fp = FREOPEN (file2, "r", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "next4");
+ xfclose (fp);
+ END_TEST;
+
+ START_TEST ("Testing a -> w\n");
+ fp = xfopen (file1, "a");
+ fp = FREOPEN (file2, "w", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fputs ("another", fp);
+ TEST_VERIFY (ret >= 0);
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "another");
+ END_TEST;
+
+ START_TEST ("Testing a -> a\n");
+ fp = xfopen (file1, "a");
+ fp = FREOPEN (file2, "a", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fputs ("5", fp);
+ TEST_VERIFY (ret >= 0);
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "another5");
+ END_TEST;
+
+ /* Test for file originally opened with fopen64. */
+ START_TEST ("Testing fopen64 a -> a\n");
+ fp = fopen64 (file1, "a");
+ TEST_VERIFY_EXIT (fp != NULL);
+ fp = FREOPEN (file2, "a", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fputs ("64", fp);
+ TEST_VERIFY (ret >= 0);
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "another564");
+ END_TEST;
+
+ /* Test calling freopen more than once on the same FILE *. */
+
+ START_TEST ("Testing r -> w -> r\n");
+ fp = xfopen (file1, "r");
+ fp = FREOPEN (file2, "w", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fputs ("freopen-twice", fp);
+ TEST_VERIFY (ret >= 0);
+ fp = FREOPEN (file2, "r", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "freopen-twice");
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "freopen-twice");
+ END_TEST;
+
+ START_TEST ("Testing r -> w -> r (exactly one freopen64)\n");
+ fp = xfopen (file1, "r");
+ fp = OTHER_FREOPEN (file2, "w", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fputs ("freopen-twice64", fp);
+ TEST_VERIFY (ret >= 0);
+ fp = FREOPEN (file2, "r", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "freopen-twice64");
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "freopen-twice64");
+ END_TEST;
+
+ /* Test changing to/from b (binary, no-op). */
+
+ START_TEST ("Testing rb -> r\n");
+ fp = xfopen (file1, "rb");
+ fp = FREOPEN (file2, "r", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "freopen-twice64");
+ xfclose (fp);
+ END_TEST;
+
+ START_TEST ("Testing r -> rb\n");
+ fp = xfopen (file1, "r");
+ fp = FREOPEN (file2, "rb", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "freopen-twice64");
+ xfclose (fp);
+ END_TEST;
+
+ /* Test changing to/from + (read-and-write). */
+
+ START_TEST ("Testing r -> w+\n");
+ fp = xfopen (file1, "r");
+ fp = FREOPEN (file2, "w+", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fputs ("latest", fp);
+ TEST_VERIFY (ret >= 0);
+ ret = fseek (fp, 0, SEEK_SET);
+ TEST_COMPARE (ret, 0);
+ TEST_COMPARE_FILE_STRING (fp, "latest");
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "latest");
+ END_TEST;
+
+ START_TEST ("Testing w -> a+\n");
+ fp = xfopen (file1, "w");
+ fp = FREOPEN (file2, "a+", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fputs ("suffix", fp);
+ TEST_VERIFY (ret >= 0);
+ ret = fseek (fp, 0, SEEK_SET);
+ TEST_COMPARE (ret, 0);
+ TEST_COMPARE_FILE_STRING (fp, "latestsuffix");
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "latestsuffix");
+ END_TEST;
+
+ START_TEST ("Testing a -> r+\n");
+ fp = xfopen (file1, "a");
+ fp = FREOPEN (file2, "r+", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "latestsuffix");
+ ret = fseek (fp, 0, SEEK_SET);
+ TEST_COMPARE (ret, 0);
+ ret = fputs ("new", fp);
+ TEST_VERIFY (ret >= 0);
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "newestsuffix");
+ END_TEST;
+
+ START_TEST ("Testing r+ -> w\n");
+ fp = xfopen (file1, "r+");
+ fp = FREOPEN (file2, "w", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fputs ("plusto", fp);
+ TEST_VERIFY (ret >= 0);
+ ret = fseek (fp, 0, SEEK_SET);
+ TEST_COMPARE (ret, 0);
+ errno = 0;
+ TEST_COMPARE (fgetc (fp), EOF);
+ TEST_COMPARE (errno, EBADF);
+ clearerr (fp);
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "plusto");
+ END_TEST;
+
+ START_TEST ("Testing w+ -> a\n");
+ fp = xfopen (file1, "w+");
+ fp = FREOPEN (file2, "a", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fputs ("more", fp);
+ TEST_VERIFY (ret >= 0);
+ ret = fseek (fp, 0, SEEK_SET);
+ TEST_COMPARE (ret, 0);
+ errno = 0;
+ TEST_COMPARE (fgetc (fp), EOF);
+ TEST_COMPARE (errno, EBADF);
+ clearerr (fp);
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "plustomore");
+ END_TEST;
+
+ START_TEST ("Testing a+ -> r\n");
+ fp = xfopen (file1, "a+");
+ fp = FREOPEN (file2, "rr", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "plustomore");
+ ret = fputs ("2", fp);
+ TEST_COMPARE (ret, EOF);
+ clearerr (fp);
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "plustomore");
+ END_TEST;
+
+ /* Test changing to/from e (FD_CLOEXEC). */
+
+ START_TEST ("Testing re -> r\n");
+ fp = xfopen (file1, "re");
+ ret = fcntl (fileno (fp), F_GETFD);
+ TEST_VERIFY (ret != -1);
+ TEST_COMPARE (ret & FD_CLOEXEC, FD_CLOEXEC);
+ fp = FREOPEN (file2, "r", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fcntl (fileno (fp), F_GETFD);
+ TEST_VERIFY (ret != -1);
+#if 0 /* Fails to clear FD_CLOEXEC (bug 32134). */
+ TEST_COMPARE (ret & FD_CLOEXEC, 0);
+#endif
+ TEST_COMPARE_FILE_STRING (fp, "plustomore");
+ xfclose (fp);
+ END_TEST;
+
+ START_TEST ("Testing r -> re\n");
+ fp = xfopen (file1, "r");
+ ret = fcntl (fileno (fp), F_GETFD);
+ TEST_VERIFY (ret != -1);
+ TEST_COMPARE (ret & FD_CLOEXEC, 0);
+ fp = FREOPEN (file2, "re", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fcntl (fileno (fp), F_GETFD);
+ TEST_VERIFY (ret != -1);
+ TEST_COMPARE (ret & FD_CLOEXEC, FD_CLOEXEC);
+ TEST_COMPARE_FILE_STRING (fp, "plustomore");
+ xfclose (fp);
+ END_TEST;
+
+ /* Test changing to/from m (mmap) (a no-op as far as testing
+ semantics is concerned). */
+
+ START_TEST ("Testing rm -> r\n");
+ fp = xfopen (file1, "rm");
+ fp = FREOPEN (file2, "r", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "plustomore");
+ xfclose (fp);
+ END_TEST;
+
+ START_TEST ("Testing r -> rm\n");
+ fp = xfopen (file1, "r");
+ fp = FREOPEN (file2, "rm", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "plustomore");
+ xfclose (fp);
+ END_TEST;
+
+ /* Test changing to/from x (O_EXCL). */
+
+ START_TEST ("Testing wx -> w\n");
+ fp = xfopen (file3, "wx");
+ add_temp_file (file3);
+ fp = FREOPEN (file2, "w", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ ret = fputs ("wxtow", fp);
+ TEST_VERIFY (ret >= 0);
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file2, "wxtow");
+ END_TEST;
+
+ START_TEST ("Testing w -> wx (file does not exist)\n");
+ fp = xfopen (file1, "w");
+ fp = FREOPEN (file4, "wx", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ add_temp_file (file4);
+ ret = fputs ("wtowx", fp);
+ TEST_VERIFY (ret >= 0);
+ xfclose (fp);
+ TEST_OPEN_AND_COMPARE_FILE_STRING (file4, "wtowx");
+ END_TEST;
+
+ /* Test with ,ccs=CHARSET. */
+
+ START_TEST ("testing w,ccs=utf-8 -> r\n");
+ fp = xfopen (file1, "w,ccs=utf-8");
+ ret = fputws (L"\xc0\xc1", fp);
+ TEST_VERIFY (ret >= 0);
+ fp = FREOPEN (file2, "r", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "wxtow");
+ xfclose (fp);
+ END_TEST;
+
+ START_TEST ("testing w,ccs=iso-8859-1 -> r,ccs=utf-8\n");
+ fp = xfopen (file2, "w,ccs=iso-8859-1");
+ ret = fputws (L"\xc0\xc1", fp);
+ TEST_VERIFY (ret >= 0);
+#if 0 /* Doesn't work (bug 23675). */
+ fp = FREOPEN (file1, "r,ccs=utf-8", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+#else /* Works instead. */
+ xfclose (fp);
+ fp = xfopen (file1, "r,ccs=utf-8");
+#endif
+ wc = fgetwc (fp);
+ TEST_COMPARE (wc, (wint_t) 0xc0);
+ wc = fgetwc (fp);
+ TEST_COMPARE (wc, (wint_t) 0xc1);
+ wc = fgetwc (fp);
+ TEST_COMPARE (wc, WEOF);
+ xfclose (fp);
+ END_TEST;
+
+ START_TEST ("testing r,ccs=utf-8 -> r\n");
+ fp = xfopen (file1, "r,ccs=utf-8");
+ fp = FREOPEN (file1, "r", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "\u00c0\u00c1");
+ xfclose (fp);
+ END_TEST;
+
+ /* Test that errors closing the old file are ignored. */
+
+ START_TEST ("testing errors closing old file ignored\n");
+ fp = xfopen ("/dev/full", "w");
+ fputc ('x', fp);
+ fp = FREOPEN (file1, "r", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "\u00c0\u00c1");
+ xfclose (fp);
+ END_TEST;
+
+ /* Test that error / EOF state from the old file are cleared. */
+
+ START_TEST ("testing error state from old file cleared\n");
+ fp = xfopen ("/dev/full", "w");
+ fputc ('x', fp);
+ fflush (fp);
+ TEST_VERIFY (ferror (fp));
+ TEST_VERIFY (!feof (fp));
+ fp = FREOPEN (file2, "w", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_VERIFY (!ferror (fp));
+ TEST_VERIFY (!feof (fp));
+ xfclose (fp);
+ END_TEST;
+
+ START_TEST ("testing EOF state from old file cleared\n");
+ fp = xfopen ("/dev/null", "r");
+ fgetc (fp);
+ TEST_VERIFY (!ferror (fp));
+ TEST_VERIFY (feof (fp));
+ fp = FREOPEN (file2, "r", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_VERIFY (!ferror (fp));
+ TEST_VERIFY (!feof (fp));
+ xfclose (fp);
+ END_TEST;
+
+ /* Test freopen with NULL, same mode (should flush content and reset
+ file offset). */
+
+ START_TEST ("testing freopen with NULL, same mode\n");
+ fp = xfopen (file1, "r+");
+ ret = fputs ("same mode", fp);
+ TEST_VERIFY (ret >= 0);
+ fp = FREOPEN (NULL, "r+", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "same mode");
+ xfclose (fp);
+ END_TEST;
+
+ /* Test freopen with NULL, different mode. */
+
+ START_TEST ("testing freopen with NULL, different mode\n");
+ fp = xfopen (file1, "w");
+ ret = fputs ("different mode", fp);
+ TEST_VERIFY (ret >= 0);
+ fp = FREOPEN (NULL, "r", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "different mode");
+ xfclose (fp);
+ END_TEST;
+
+ /* Test freopen with NULL, renamed file. This verifies that
+ reopening succeeds (and resets the file position indicator to
+ start of file) even when the original path could no longer be
+ opened. */
+
+ START_TEST ("testing freopen with NULL, renamed file\n");
+ fp = xfopen (file1, "r+");
+ ret = fputs ("file has been renamed", fp);
+ TEST_VERIFY (ret >= 0);
+ ret = rename (file1, file1a);
+ TEST_COMPARE (ret, 0);
+ fp = FREOPEN (NULL, "r+", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "file has been renamed");
+ xfclose (fp);
+ ret = rename (file1a, file1);
+ TEST_COMPARE (ret, 0);
+ END_TEST;
+
+ /* Test freopen with NULL, deleted file. This verifies that
+ reopening succeeds (and resets the file position indicator to
+ start of file) even when the original path could no longer be
+ opened. */
+
+ START_TEST ("testing freopen with NULL, deleted file\n");
+ fp = xfopen (file1, "r+");
+ ret = fputs ("file has now been deleted", fp);
+ TEST_VERIFY (ret >= 0);
+ ret = remove (file1);
+ TEST_COMPARE (ret, 0);
+ fp = FREOPEN (NULL, "r+", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ TEST_COMPARE_FILE_STRING (fp, "file has now been deleted");
+ xfclose (fp);
+ /* Recreate the file so it is present when expected for temporary
+ file deletion. */
+ support_write_file_string (file1, "file1");
+ END_TEST;
+
+ free (temp_dir);
+ free (file1);
+ free (file2);
+ free (file3);
+ free (file4);
+ free (file1a);
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/stdio-common/tst-freopen2.c b/stdio-common/tst-freopen2.c
new file mode 100644
index 0000000000000000..11ec7a9783b7caa3
--- /dev/null
+++ b/stdio-common/tst-freopen2.c
@@ -0,0 +1,3 @@
+#define FREOPEN freopen
+#define OTHER_FREOPEN freopen64
+#include <tst-freopen2-main.c>
diff --git a/stdio-common/tst-freopen3-main.c b/stdio-common/tst-freopen3-main.c
new file mode 100644
index 0000000000000000..5107e1f98e189e4b
--- /dev/null
+++ b/stdio-common/tst-freopen3-main.c
@@ -0,0 +1,90 @@
+/* Test freopen failure.
+ Copyright (C) 2024 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 <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <support/check.h>
+#include <support/descriptors.h>
+#include <support/file_contents.h>
+#include <support/support.h>
+#include <support/temp_file.h>
+#include <support/test-driver.h>
+#include <support/xstdio.h>
+
+#define START_TEST(DESC) \
+ do \
+ { \
+ fds = support_descriptors_list (); \
+ verbose_printf (DESC); \
+ } \
+ while (0)
+
+#define END_TEST \
+ do \
+ { \
+ support_descriptors_check (fds); \
+ support_descriptors_free (fds); \
+ } \
+ while (0)
+
+int
+do_test (void)
+{
+ struct support_descriptors *fds;
+ char *temp_dir = support_create_temp_directory ("tst-freopen3");
+ char *file1 = xasprintf ("%s/file1", temp_dir);
+ support_write_file_string (file1, "file1");
+ add_temp_file (file1);
+ char *file2 = xasprintf ("%s/file2", temp_dir);
+ support_write_file_string (file2, "file2");
+ add_temp_file (file2);
+ char *file_nodir = xasprintf ("%s/nodir/file", temp_dir);
+ FILE *fp;
+ int ret;
+ int fd;
+
+ START_TEST ("Testing w -> wx (file exists)\n");
+ fp = xfopen (file1, "w");
+ fp = FREOPEN (file2, "wx", fp);
+ TEST_VERIFY (fp == NULL);
+ END_TEST;
+
+ /* Test old file is closed even when opening the new file fails. */
+
+ START_TEST ("testing r -> r (opening new file fails)\n");
+ fp = xfopen (file1, "r");
+ fd = fileno (fp);
+ fp = FREOPEN (file_nodir, "r", fp);
+ TEST_VERIFY (fp == NULL);
+ errno = 0;
+ ret = fcntl (fd, F_GETFL);
+ TEST_COMPARE (ret, -1);
+ TEST_COMPARE (errno, EBADF);
+ END_TEST;
+
+ free (temp_dir);
+ free (file1);
+ free (file2);
+ free (file_nodir);
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/stdio-common/tst-freopen3.c b/stdio-common/tst-freopen3.c
new file mode 100644
index 0000000000000000..5041b6b2332c8af1
--- /dev/null
+++ b/stdio-common/tst-freopen3.c
@@ -0,0 +1,2 @@
+#define FREOPEN freopen
+#include <tst-freopen3-main.c>
diff --git a/stdio-common/tst-freopen64-2.c b/stdio-common/tst-freopen64-2.c
new file mode 100644
index 0000000000000000..88fdc64d8c6548f5
--- /dev/null
+++ b/stdio-common/tst-freopen64-2.c
@@ -0,0 +1,3 @@
+#define FREOPEN freopen64
+#define OTHER_FREOPEN freopen
+#include <tst-freopen2-main.c>
diff --git a/stdio-common/tst-freopen64-3.c b/stdio-common/tst-freopen64-3.c
new file mode 100644
index 0000000000000000..b91b6d2c033a1a79
--- /dev/null
+++ b/stdio-common/tst-freopen64-3.c
@@ -0,0 +1,2 @@
+#define FREOPEN freopen64
+#include <tst-freopen3-main.c>

69
glibc-RHEL-46736-5.patch Normal file
View File

@ -0,0 +1,69 @@
commit f512634ddef242ef0ff025ddeba64ce51035040f
Author: Joseph Myers <josmyers@redhat.com>
Date: Thu Sep 5 11:15:29 2024 +0000
Clear flags2 flags set from mode in freopen (bug 32134)
As reported in bug 32134, freopen does not clear the flags set in
fp->_flags2 by the "e", "m" or "c" mode characters. Clear these so
that they can be set or not as appropriate from the mode string passed
to freopen. The relevant test for "e" in tst-freopen2-main.c is
enabled accordingly; "c" is expected to be covered in a separately
written test (and while tst-freopen2-main.c does include transitions
to and from "m", that's not really a semantic flag intended to result
in behaving in an observably different way).
Tested for x86_64.
diff --git a/libio/freopen.c b/libio/freopen.c
index c947a5aecfde3c80..bed034d89441f200 100644
--- a/libio/freopen.c
+++ b/libio/freopen.c
@@ -63,6 +63,9 @@ freopen (const char *filename, const char *mode, FILE *fp)
up here. */
_IO_old_file_close_it (fp);
_IO_JUMPS_FUNC_UPDATE (fp, &_IO_old_file_jumps);
+ fp->_flags2 &= ~(_IO_FLAGS2_MMAP
+ | _IO_FLAGS2_NOTCANCEL
+ | _IO_FLAGS2_CLOEXEC);
result = _IO_old_file_fopen (fp, gfilename, mode);
}
else
@@ -72,6 +75,9 @@ freopen (const char *filename, const char *mode, FILE *fp)
_IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps;
if (_IO_vtable_offset (fp) == 0 && fp->_wide_data != NULL)
fp->_wide_data->_wide_vtable = &_IO_wfile_jumps;
+ fp->_flags2 &= ~(_IO_FLAGS2_MMAP
+ | _IO_FLAGS2_NOTCANCEL
+ | _IO_FLAGS2_CLOEXEC);
result = _IO_file_fopen (fp, gfilename, mode, 1);
if (result != NULL)
result = __fopen_maybe_mmap (result);
diff --git a/libio/freopen64.c b/libio/freopen64.c
index fb02c201bd83c401..9a314c65c1d8a5a4 100644
--- a/libio/freopen64.c
+++ b/libio/freopen64.c
@@ -56,6 +56,9 @@ freopen64 (const char *filename, const char *mode, FILE *fp)
_IO_JUMPS_FILE_plus (fp) = &_IO_file_jumps;
if (_IO_vtable_offset (fp) == 0 && fp->_wide_data != NULL)
fp->_wide_data->_wide_vtable = &_IO_wfile_jumps;
+ fp->_flags2 &= ~(_IO_FLAGS2_MMAP
+ | _IO_FLAGS2_NOTCANCEL
+ | _IO_FLAGS2_CLOEXEC);
result = _IO_file_fopen (fp, gfilename, mode, 0);
fp->_flags2 &= ~_IO_FLAGS2_NOCLOSE;
if (result != NULL)
diff --git a/stdio-common/tst-freopen2-main.c b/stdio-common/tst-freopen2-main.c
index 22b21afebf709563..5dad41c76b02e6de 100644
--- a/stdio-common/tst-freopen2-main.c
+++ b/stdio-common/tst-freopen2-main.c
@@ -308,9 +308,7 @@ do_test (void)
TEST_VERIFY_EXIT (fp != NULL);
ret = fcntl (fileno (fp), F_GETFD);
TEST_VERIFY (ret != -1);
-#if 0 /* Fails to clear FD_CLOEXEC (bug 32134). */
TEST_COMPARE (ret & FD_CLOEXEC, 0);
-#endif
TEST_COMPARE_FILE_STRING (fp, "plustomore");
xfclose (fp);
END_TEST;

119
glibc-RHEL-46736-6.patch Normal file
View File

@ -0,0 +1,119 @@
commit 9c0d6f7a1046aba111e25e34ec07242853e859dc
Author: Joseph Myers <josmyers@redhat.com>
Date: Thu Sep 5 11:16:59 2024 +0000
Fix memory leak on freopen error return (bug 32140)
As reported in bug 32140, freopen leaks the FILE object when it
returns NULL: there is no valid use of the FILE * pointer (including
passing to freopen again or to fclose) after such an error return, so
the underlying object should be freed. Add code to free it.
Note 1: while I think it's clear from the relevant standards that the
object should be freed and the FILE * can't be used after the call in
this case (the stream is closed, which ends the lifetime of the FILE),
it's entirely possible that some existing code does in fact try to use
the existing FILE * in some way and could be broken by this change.
(Though the most common case for freopen may be stdin / stdout /
stderr, which _IO_deallocate_file explicitly checks for and does not
deallocate.)
Note 2: the deallocation is only done in the _IO_IS_FILEBUF case.
Other kinds of streams bypass all the freopen logic handling closing
the file, meaning a call to _IO_deallocate_file would neither be safe
(the FILE might still be linked into the list of all open FILEs) nor
sufficient (other internal memory allocations associated with the file
would not have been freed). I think the validity of freopen for any
other kind of stream will need clarifying with the Austin Group, but
if it is valid in any such case (where "valid" means "not undefined
behavior so required to close the stream" rather than "required to
successfully associate the stream with the new file in cases where
fopen would work"), more significant changes would be needed to ensure
the stream gets fully closed.
Tested for x86_64.
diff --git a/libio/freopen.c b/libio/freopen.c
index bed034d89441f200..03f8961a61b12e80 100644
--- a/libio/freopen.c
+++ b/libio/freopen.c
@@ -114,5 +114,7 @@ freopen (const char *filename, const char *mode, FILE *fp)
end:
_IO_release_lock (fp);
+ if (result == NULL && (fp->_flags & _IO_IS_FILEBUF) != 0)
+ _IO_deallocate_file (fp);
return result;
}
diff --git a/libio/freopen64.c b/libio/freopen64.c
index 9a314c65c1d8a5a4..abcbd80a5bd92e69 100644
--- a/libio/freopen64.c
+++ b/libio/freopen64.c
@@ -94,5 +94,7 @@ freopen64 (const char *filename, const char *mode, FILE *fp)
end:
_IO_release_lock (fp);
+ if (result == NULL && (fp->_flags & _IO_IS_FILEBUF) != 0)
+ _IO_deallocate_file (fp);
return result;
}
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index 5eddc4bfbf4e7fb9..bd3c785537ba0330 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -251,7 +251,9 @@ ifeq (yes,$(build-shared))
ifneq ($(PERL),no)
tests-special += \
$(objpfx)tst-freopen2-mem.out \
+ $(objpfx)tst-freopen3-mem.out \
$(objpfx)tst-freopen64-2-mem.out \
+ $(objpfx)tst-freopen64-3-mem.out \
$(objpfx)tst-getline-enomem-mem.out \
$(objpfx)tst-getline-mem.out \
$(objpfx)tst-printf-bz18872-mem.out \
@@ -264,8 +266,12 @@ tests-special += \
generated += \
tst-freopen2-mem.out \
tst-freopen2.mtrace \
+ tst-freopen3-mem.out \
+ tst-freopen3.mtrace \
tst-freopen64-2-mem.out \
tst-freopen64-2.mtrace \
+ tst-freopen64-3-mem.out \
+ tst-freopen64-3.mtrace \
tst-getline-enomem-mem.out \
tst-getline-enomem.mtrace \
tst-getline-mem.out \
@@ -344,6 +350,12 @@ tst-freopen2-ENV = \
tst-freopen64-2-ENV = \
MALLOC_TRACE=$(objpfx)tst-freopen64-2.mtrace \
LD_PRELOAD=$(common-objpfx)malloc/libc_malloc_debug.so
+tst-freopen3-ENV = \
+ MALLOC_TRACE=$(objpfx)tst-freopen3.mtrace \
+ LD_PRELOAD=$(common-objpfx)malloc/libc_malloc_debug.so
+tst-freopen64-3-ENV = \
+ MALLOC_TRACE=$(objpfx)tst-freopen64-3.mtrace \
+ LD_PRELOAD=$(common-objpfx)malloc/libc_malloc_debug.so
$(objpfx)tst-unbputc.out: tst-unbputc.sh $(objpfx)tst-unbputc
$(SHELL) $< $(common-objpfx) '$(test-program-prefix)'; \
diff --git a/stdio-common/tst-freopen3-main.c b/stdio-common/tst-freopen3-main.c
index 5107e1f98e189e4b..990a6e5921843793 100644
--- a/stdio-common/tst-freopen3-main.c
+++ b/stdio-common/tst-freopen3-main.c
@@ -18,6 +18,7 @@
#include <errno.h>
#include <fcntl.h>
+#include <mcheck.h>
#include <stdio.h>
#include <stdlib.h>
@@ -48,6 +49,7 @@
int
do_test (void)
{
+ mtrace ();
struct support_descriptors *fds;
char *temp_dir = support_create_temp_directory ("tst-freopen3");
char *file1 = xasprintf ("%s/file1", temp_dir);

86
glibc-RHEL-46736-7.patch Normal file
View File

@ -0,0 +1,86 @@
commit e44ca1c085b3bd41266c882ea1cb0fd436231635
Author: Joseph Myers <josmyers@redhat.com>
Date: Thu Sep 5 20:08:10 2024 +0000
Fix freopen handling of ,ccs= (bug 23675)
As reported in bug 23675 and shown up in the recently added tests of
different cases of freopen (relevant part of the test currently
conditioned under #if 0 to avoid a failure resulting from this bug),
freopen wrongly forces the stream to unoriented even when a mode with
,ccs= is specified, though such a mode is supposed to result in a
wide-oriented stream. Move the clearing of _mode to before the actual
reopening occurs, so that the main fopen implementation can leave a
wide-oriented stream in the ,ccs= case.
Tested for x86_64.
diff --git a/libio/freopen.c b/libio/freopen.c
index 03f8961a61b12e80..d71a4cfffdc35280 100644
--- a/libio/freopen.c
+++ b/libio/freopen.c
@@ -66,6 +66,7 @@ freopen (const char *filename, const char *mode, FILE *fp)
fp->_flags2 &= ~(_IO_FLAGS2_MMAP
| _IO_FLAGS2_NOTCANCEL
| _IO_FLAGS2_CLOEXEC);
+ fp->_mode = 0;
result = _IO_old_file_fopen (fp, gfilename, mode);
}
else
@@ -78,6 +79,7 @@ freopen (const char *filename, const char *mode, FILE *fp)
fp->_flags2 &= ~(_IO_FLAGS2_MMAP
| _IO_FLAGS2_NOTCANCEL
| _IO_FLAGS2_CLOEXEC);
+ fp->_mode = 0;
result = _IO_file_fopen (fp, gfilename, mode, 1);
if (result != NULL)
result = __fopen_maybe_mmap (result);
@@ -85,9 +87,6 @@ freopen (const char *filename, const char *mode, FILE *fp)
fp->_flags2 &= ~_IO_FLAGS2_NOCLOSE;
if (result != NULL)
{
- /* unbound stream orientation */
- result->_mode = 0;
-
if (fd != -1 && _IO_fileno (result) != fd)
{
/* At this point we have both file descriptors already allocated,
diff --git a/libio/freopen64.c b/libio/freopen64.c
index abcbd80a5bd92e69..64af2c5f7c80a3e9 100644
--- a/libio/freopen64.c
+++ b/libio/freopen64.c
@@ -59,15 +59,13 @@ freopen64 (const char *filename, const char *mode, FILE *fp)
fp->_flags2 &= ~(_IO_FLAGS2_MMAP
| _IO_FLAGS2_NOTCANCEL
| _IO_FLAGS2_CLOEXEC);
+ fp->_mode = 0;
result = _IO_file_fopen (fp, gfilename, mode, 0);
fp->_flags2 &= ~_IO_FLAGS2_NOCLOSE;
if (result != NULL)
result = __fopen_maybe_mmap (result);
if (result != NULL)
{
- /* unbound stream orientation */
- result->_mode = 0;
-
if (fd != -1 && _IO_fileno (result) != fd)
{
/* At this point we have both file descriptors already allocated,
diff --git a/stdio-common/tst-freopen2-main.c b/stdio-common/tst-freopen2-main.c
index 5dad41c76b02e6de..74c3125fca697fe3 100644
--- a/stdio-common/tst-freopen2-main.c
+++ b/stdio-common/tst-freopen2-main.c
@@ -386,13 +386,8 @@ do_test (void)
fp = xfopen (file2, "w,ccs=iso-8859-1");
ret = fputws (L"\xc0\xc1", fp);
TEST_VERIFY (ret >= 0);
-#if 0 /* Doesn't work (bug 23675). */
fp = FREOPEN (file1, "r,ccs=utf-8", fp);
TEST_VERIFY_EXIT (fp != NULL);
-#else /* Works instead. */
- xfclose (fp);
- fp = xfopen (file1, "r,ccs=utf-8");
-#endif
wc = fgetwc (fp);
TEST_COMPARE (wc, (wint_t) 0xc0);
wc = fgetwc (fp);

33
glibc-RHEL-46736-8.patch Normal file
View File

@ -0,0 +1,33 @@
commit a2509a8bc955988f01f389a1cf74db3a9da42409
Author: Joseph Myers <josmyers@redhat.com>
Date: Fri Sep 6 20:38:23 2024 +0000
Document limitations on streams passed to freopen
As recently discussed, document that freopen does not work with
streams opened with functions such as popen, fmemopen, open_memstream
or fopencookie. I've filed
<https://austingroupbugs.net/view.php?id=1855> to clarify this issue
in POSIX.
Tested with "make info" and "make html".
diff --git a/manual/stdio.texi b/manual/stdio.texi
index 60ab7e7a5d505bb6..a2d9292a787b9fa3 100644
--- a/manual/stdio.texi
+++ b/manual/stdio.texi
@@ -330,6 +330,14 @@ this ability, so using @code{freopen} is more portable.
When the sources are compiled with @code{_FILE_OFFSET_BITS == 64} on a
32 bit machine this function is in fact @code{freopen64} since the LFS
interface replaces transparently the old interface.
+
+@Theglibc{} only supports use of @code{freopen} on streams opened with
+@code{fopen} or @code{fopen64} and on the original values of the
+standard streams @code{stdin}, @code{stdout}, and @code{stderr}; such
+a stream may be reopened multiple times with @code{freopen}. If it is
+called on another kind of stream (opened with functions such as
+@code{popen}, @code{fmemopen}, @code{open_memstream}, and
+@code{fopencookie}), @code{freopen} fails and returns a null pointer.
@end deftypefun
@deftypefun {FILE *} freopen64 (const char *@var{filename}, const char *@var{opentype}, FILE *@var{stream})

495
glibc-RHEL-46736-9.patch Normal file
View File

@ -0,0 +1,495 @@
commit e0f3bf10acf4aab27752847828bfecd3fce41190
Author: Joseph Myers <josmyers@redhat.com>
Date: Fri Sep 20 23:26:31 2024 +0000
Add freopen special-case tests: chroot, EFBIG, stdin/stdout/stderr
Add tests of special cases for freopen that were omitted from the more
general tests of different modes and similar issues. The special
cases in the three tests here are logically unconnected, it was simply
convenient to put these tests in one patch.
* Test freopen with a NULL path to the new file, in a chroot. Rather
than asserting that this fails (logically, failure in this case is
an implementation detail; it's not required for freopen to rely on
/proc), verify that either it fails (without memory leaks) or that
it succeeds and behaves as expected on success. There is no check
for file descriptor leaks because the machinery for that also
depends on /proc, so can't be used in a chroot.
* Test that freopen and freopen64 are genuinely different in
configurations with 32-bit off_t by checking for an EFBIG trying to
write past 2GB in a file opened with freopen in such a configuration
but no error with 64-bit off_t or when opening with freopen64.
* Test freopen of stdin, stdout and stderr.
Tested for x86_64 and x86.
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index bd3c785537ba0330..c920f55ed2119900 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -177,8 +177,13 @@ tests := \
tst-fread \
tst-freopen2 \
tst-freopen3 \
+ tst-freopen4 \
+ tst-freopen5 \
+ tst-freopen6 \
tst-freopen64-2 \
tst-freopen64-3 \
+ tst-freopen64-4 \
+ tst-freopen64-6 \
tst-fseek \
tst-fwrite \
tst-getline \
@@ -252,8 +257,13 @@ ifneq ($(PERL),no)
tests-special += \
$(objpfx)tst-freopen2-mem.out \
$(objpfx)tst-freopen3-mem.out \
+ $(objpfx)tst-freopen4-mem.out \
+ $(objpfx)tst-freopen5-mem.out \
+ $(objpfx)tst-freopen6-mem.out \
$(objpfx)tst-freopen64-2-mem.out \
$(objpfx)tst-freopen64-3-mem.out \
+ $(objpfx)tst-freopen64-4-mem.out \
+ $(objpfx)tst-freopen64-6-mem.out \
$(objpfx)tst-getline-enomem-mem.out \
$(objpfx)tst-getline-mem.out \
$(objpfx)tst-printf-bz18872-mem.out \
@@ -268,10 +278,20 @@ generated += \
tst-freopen2.mtrace \
tst-freopen3-mem.out \
tst-freopen3.mtrace \
+ tst-freopen4-mem.out \
+ tst-freopen4.mtrace \
+ tst-freopen5-mem.out \
+ tst-freopen5.mtrace \
+ tst-freopen6-mem.out \
+ tst-freopen6.mtrace \
tst-freopen64-2-mem.out \
tst-freopen64-2.mtrace \
tst-freopen64-3-mem.out \
tst-freopen64-3.mtrace \
+ tst-freopen64-4-mem.out \
+ tst-freopen64-4.mtrace \
+ tst-freopen64-6-mem.out \
+ tst-freopen64-6.mtrace \
tst-getline-enomem-mem.out \
tst-getline-enomem.mtrace \
tst-getline-mem.out \
@@ -356,6 +376,21 @@ tst-freopen3-ENV = \
tst-freopen64-3-ENV = \
MALLOC_TRACE=$(objpfx)tst-freopen64-3.mtrace \
LD_PRELOAD=$(common-objpfx)malloc/libc_malloc_debug.so
+tst-freopen4-ENV = \
+ MALLOC_TRACE=$(objpfx)tst-freopen4.mtrace \
+ LD_PRELOAD=$(common-objpfx)malloc/libc_malloc_debug.so
+tst-freopen64-4-ENV = \
+ MALLOC_TRACE=$(objpfx)tst-freopen64-4.mtrace \
+ LD_PRELOAD=$(common-objpfx)malloc/libc_malloc_debug.so
+tst-freopen5-ENV = \
+ MALLOC_TRACE=$(objpfx)tst-freopen5.mtrace \
+ LD_PRELOAD=$(common-objpfx)malloc/libc_malloc_debug.so
+tst-freopen6-ENV = \
+ MALLOC_TRACE=$(objpfx)tst-freopen6.mtrace \
+ LD_PRELOAD=$(common-objpfx)malloc/libc_malloc_debug.so
+tst-freopen64-6-ENV = \
+ MALLOC_TRACE=$(objpfx)tst-freopen64-6.mtrace \
+ LD_PRELOAD=$(common-objpfx)malloc/libc_malloc_debug.so
$(objpfx)tst-unbputc.out: tst-unbputc.sh $(objpfx)tst-unbputc
$(SHELL) $< $(common-objpfx) '$(test-program-prefix)'; \
diff --git a/stdio-common/tst-freopen4-main.c b/stdio-common/tst-freopen4-main.c
new file mode 100644
index 0000000000000000..e169442cf4df2e9d
--- /dev/null
+++ b/stdio-common/tst-freopen4-main.c
@@ -0,0 +1,100 @@
+/* Test freopen in chroot.
+ Copyright (C) 2024 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 <mcheck.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <support/check.h>
+#include <support/file_contents.h>
+#include <support/namespace.h>
+#include <support/support.h>
+#include <support/temp_file.h>
+#include <support/test-driver.h>
+#include <support/xstdio.h>
+#include <support/xunistd.h>
+
+int
+do_test (void)
+{
+ mtrace ();
+ char *temp_dir = support_create_temp_directory ("tst-freopen4");
+ FILE *fp;
+ int ret;
+
+ /* These chroot tests verify that either reopening a renamed or
+ deleted file works even in the absence of /proc, or that it fails
+ (without memory leaks); thus, for example, such reopening does
+ not crash in the absence of /proc. */
+
+ support_become_root ();
+ if (!support_can_chroot ())
+ return EXIT_UNSUPPORTED;
+ xchroot (temp_dir);
+
+ /* Test freopen with NULL, renamed file. This verifies that
+ reopening succeeds (and resets the file position indicator to
+ start of file) even when the original path could no longer be
+ opened, or fails without a memory leak. (It is not possible to
+ use <support/descriptors.h> to test for file descriptor leaks
+ here, because that also depends on /proc.) */
+
+ verbose_printf ("testing freopen with NULL, renamed file\n");
+ fp = xfopen ("/file1", "w+");
+ ret = fputs ("file has been renamed", fp);
+ TEST_VERIFY (ret >= 0);
+ ret = rename ("/file1", "/file1a");
+ TEST_COMPARE (ret, 0);
+ fp = FREOPEN (NULL, "r+", fp);
+ if (fp != NULL)
+ {
+ puts ("freopen of renamed file succeeded");
+ TEST_COMPARE_FILE_STRING (fp, "file has been renamed");
+ xfclose (fp);
+ }
+ else
+ puts ("freopen of renamed file failed (OK)");
+ ret = rename ("/file1a", "/file1");
+ TEST_COMPARE (ret, 0);
+
+ /* Test freopen with NULL, deleted file. This verifies that
+ reopening succeeds (and resets the file position indicator to
+ start of file) even when the original path could no longer be
+ opened, or fails without a memory leak. */
+
+ verbose_printf ("testing freopen with NULL, deleted file\n");
+ fp = xfopen ("/file1", "r+");
+ ret = fputs ("file has now been deleted", fp);
+ TEST_VERIFY (ret >= 0);
+ ret = remove ("/file1");
+ TEST_COMPARE (ret, 0);
+ fp = FREOPEN (NULL, "r+", fp);
+ if (fp != NULL)
+ {
+ puts ("freopen of deleted file succeeded");
+ TEST_COMPARE_FILE_STRING (fp, "file has now been deleted");
+ xfclose (fp);
+ }
+ else
+ puts ("freopen of deleted file failed (OK)");
+
+ free (temp_dir);
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/stdio-common/tst-freopen4.c b/stdio-common/tst-freopen4.c
new file mode 100644
index 0000000000000000..f39ec0d21730879f
--- /dev/null
+++ b/stdio-common/tst-freopen4.c
@@ -0,0 +1,2 @@
+#define FREOPEN freopen
+#include <tst-freopen4-main.c>
diff --git a/stdio-common/tst-freopen5.c b/stdio-common/tst-freopen5.c
new file mode 100644
index 0000000000000000..f32626bccfe5c10a
--- /dev/null
+++ b/stdio-common/tst-freopen5.c
@@ -0,0 +1,144 @@
+/* Test freopen and freopen64 with large offsets.
+ Copyright (C) 2024 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 <mcheck.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <support/check.h>
+#include <support/descriptors.h>
+#include <support/support.h>
+#include <support/temp_file.h>
+#include <support/test-driver.h>
+#include <support/xstdio.h>
+
+#define START_TEST(DESC) \
+ do \
+ { \
+ fds = support_descriptors_list (); \
+ verbose_printf (DESC); \
+ } \
+ while (0)
+
+#define END_TEST \
+ do \
+ { \
+ support_descriptors_check (fds); \
+ support_descriptors_free (fds); \
+ } \
+ while (0)
+
+int
+do_test (void)
+{
+ mtrace ();
+ struct support_descriptors *fds;
+ FILE *fp;
+ int ret;
+
+ char *temp_dir = support_create_temp_directory ("tst-freopen5");
+ /* This file is removed at the end of each test rather than left
+ around between tests to avoid problems with subsequent tests
+ reopening it as a large (2GB + 1 byte) file. */
+ char *file1 = xasprintf ("%s/file1", temp_dir);
+
+ /* fopen with freopen64: large offsets OK. */
+ START_TEST ("testing fopen with freopen64\n");
+ fp = fopen ("/dev/null", "r");
+ TEST_VERIFY_EXIT (fp != NULL);
+ fp = freopen64 (file1, "w", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ setbuf (fp, NULL);
+ ret = fseeko64 (fp, 1LL << 32, SEEK_SET);
+ TEST_COMPARE (ret, 0);
+ ret = fputc ('x', fp);
+ TEST_COMPARE (ret, 'x');
+ xfclose (fp);
+ ret = remove (file1);
+ TEST_COMPARE (ret, 0);
+ END_TEST;
+
+ /* fopen64 with freopen64: large offsets OK. */
+ START_TEST ("testing fopen64 with freopen64\n");
+ fp = fopen64 ("/dev/null", "r");
+ TEST_VERIFY_EXIT (fp != NULL);
+ fp = freopen64 (file1, "w", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ setbuf (fp, NULL);
+ ret = fseeko64 (fp, 1LL << 32, SEEK_SET);
+ TEST_COMPARE (ret, 0);
+ ret = fputc ('x', fp);
+ TEST_COMPARE (ret, 'x');
+ xfclose (fp);
+ ret = remove (file1);
+ TEST_COMPARE (ret, 0);
+ END_TEST;
+
+ /* fopen with freopen: large offsets not OK on 32-bit systems. */
+ START_TEST ("testing fopen with freopen\n");
+ fp = fopen ("/dev/null", "r");
+ TEST_VERIFY_EXIT (fp != NULL);
+ fp = freopen (file1, "w", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ setbuf (fp, NULL);
+ ret = fseeko64 (fp, 1LL << 32, SEEK_SET);
+ TEST_COMPARE (ret, 0);
+ errno = 0;
+ ret = fputc ('x', fp);
+ if (sizeof (off_t) == 4)
+ {
+ TEST_COMPARE (ret, EOF);
+ TEST_COMPARE (errno, EFBIG);
+ }
+ else
+ TEST_COMPARE (ret, 'x');
+ fclose (fp);
+ ret = remove (file1);
+ TEST_COMPARE (ret, 0);
+ END_TEST;
+
+ /* fopen64 with freopen: large offsets not OK on 32-bit systems. */
+ START_TEST ("testing fopen64 with freopen\n");
+ fp = fopen64 ("/dev/null", "r");
+ TEST_VERIFY_EXIT (fp != NULL);
+ fp = freopen (file1, "w", fp);
+ TEST_VERIFY_EXIT (fp != NULL);
+ setbuf (fp, NULL);
+ ret = fseeko64 (fp, 1LL << 32, SEEK_SET);
+ TEST_COMPARE (ret, 0);
+ errno = 0;
+ ret = fputc ('x', fp);
+ if (sizeof (off_t) == 4)
+ {
+ TEST_COMPARE (ret, EOF);
+ TEST_COMPARE (errno, EFBIG);
+ }
+ else
+ TEST_COMPARE (ret, 'x');
+ fclose (fp);
+ ret = remove (file1);
+ TEST_COMPARE (ret, 0);
+ END_TEST;
+
+ free (temp_dir);
+ free (file1);
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/stdio-common/tst-freopen6-main.c b/stdio-common/tst-freopen6-main.c
new file mode 100644
index 0000000000000000..f493f42fd7486b72
--- /dev/null
+++ b/stdio-common/tst-freopen6-main.c
@@ -0,0 +1,98 @@
+/* Test freopen of stdin / stdout / stderr.
+ Copyright (C) 2024 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 <mcheck.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <support/check.h>
+#include <support/file_contents.h>
+#include <support/support.h>
+#include <support/temp_file.h>
+#include <support/test-driver.h>
+#include <support/xstdio.h>
+
+int
+do_test (void)
+{
+ mtrace ();
+ char *temp_dir = support_create_temp_directory ("tst-freopen6");
+ char *file1 = xasprintf ("%s/file1", temp_dir);
+ support_write_file_string (file1, "file1");
+ add_temp_file (file1);
+ FILE *fp;
+ int ret;
+
+ verbose_printf ("Testing reopening stdin\n");
+ fp = FREOPEN (file1, "r", stdin);
+ TEST_VERIFY_EXIT (fp == stdin);
+ ret = getchar ();
+ TEST_COMPARE (ret, 'f');
+ ret = getchar ();
+ TEST_COMPARE (ret, 'i');
+ ret = getchar ();
+ TEST_COMPARE (ret, 'l');
+ ret = getchar ();
+ TEST_COMPARE (ret, 'e');
+ ret = getchar ();
+ TEST_COMPARE (ret, '1');
+ ret = getchar ();
+ TEST_COMPARE (ret, EOF);
+ xfclose (fp);
+
+ verbose_printf ("Testing reopening stderr\n");
+ fp = FREOPEN (file1, "w+", stderr);
+ TEST_VERIFY_EXIT (fp == stderr);
+ errno = EINVAL;
+ perror ("test");
+ ret = fseek (fp, 0, SEEK_SET);
+ TEST_COMPARE (ret, 0);
+ TEST_COMPARE_FILE_STRING (fp, "test: Invalid argument\n");
+ xfclose (fp);
+
+ verbose_printf ("Testing reopening stdout\n");
+ /* Defer checks until the old stdout has been restored to make it
+ more likely any errors are written to the old stdout (rather than
+ the temporary file used for the redirected stdout). */
+ int old_stdout = dup (STDOUT_FILENO);
+ TEST_VERIFY_EXIT (old_stdout != -1);
+ int ret_fseek = 0;
+ int ret_compare = 0;
+ fp = FREOPEN (file1, "w+", stdout);
+ int fp_eq_stdout = fp == stdout;
+ if (fp != NULL)
+ {
+ printf ("reopened\n");
+ ret_fseek = fseek (fp, 0, SEEK_SET);
+ ret_compare = support_compare_file_string (fp, "reopened\n");
+ }
+ xfclose (fp);
+ stdout = fdopen (old_stdout, "w");
+ TEST_VERIFY (fp_eq_stdout);
+ TEST_COMPARE (ret_fseek, 0);
+ TEST_COMPARE (ret_compare, 0);
+ xfclose (stdout);
+
+ free (temp_dir);
+ free (file1);
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/stdio-common/tst-freopen6.c b/stdio-common/tst-freopen6.c
new file mode 100644
index 0000000000000000..8fd6957b54fa9bc2
--- /dev/null
+++ b/stdio-common/tst-freopen6.c
@@ -0,0 +1,2 @@
+#define FREOPEN freopen
+#include <tst-freopen6-main.c>
diff --git a/stdio-common/tst-freopen64-4.c b/stdio-common/tst-freopen64-4.c
new file mode 100644
index 0000000000000000..1411be2bfa0105c1
--- /dev/null
+++ b/stdio-common/tst-freopen64-4.c
@@ -0,0 +1,2 @@
+#define FREOPEN freopen64
+#include <tst-freopen4-main.c>
diff --git a/stdio-common/tst-freopen64-6.c b/stdio-common/tst-freopen64-6.c
new file mode 100644
index 0000000000000000..3ec509a36c2471f6
--- /dev/null
+++ b/stdio-common/tst-freopen64-6.c
@@ -0,0 +1,2 @@
+#define FREOPEN freopen64
+#include <tst-freopen6-main.c>

View File

@ -157,7 +157,7 @@ end \
Summary: The GNU libc libraries
Name: glibc
Version: %{glibcversion}
Release: 139%{?dist}
Release: 140%{?dist}
# In general, GPLv2+ is used by programs, LGPLv2+ is used for
# libraries.
@ -917,6 +917,17 @@ Patch678: glibc-RHEL-46733-1.patch
Patch679: glibc-RHEL-46733-2.patch
Patch680: glibc-RHEL-46733-3.patch
Patch681: glibc-RHEL-54413.patch
Patch682: glibc-RHEL-46736-1.patch
Patch683: glibc-RHEL-46736-2.patch
Patch684: glibc-RHEL-46736-3.patch
Patch685: glibc-RHEL-46736-4.patch
Patch686: glibc-RHEL-46736-5.patch
Patch687: glibc-RHEL-46736-6.patch
Patch688: glibc-RHEL-46736-7.patch
Patch689: glibc-RHEL-46736-8.patch
Patch690: glibc-RHEL-46736-9.patch
Patch691: glibc-RHEL-46736-10.patch
Patch692: glibc-RHEL-46736-11.patch
##############################################################################
# Continued list of core "glibc" package information:
@ -3076,6 +3087,9 @@ update_gconv_modules_cache ()
%endif
%changelog
* Thu Nov 7 2024 Florian Weimer <fweimer@redhat.com> - 2.34-140
- Add more tests for freopen (RHEL-46736)
* Thu Nov 7 2024 Florian Weimer <fweimer@redhat.com> - 2.34-139
- Add more tests of getline (RHEL-54413)