From 5e0f6b30b8c981d41ae201d2a03e8d7e77aad8e3 Mon Sep 17 00:00:00 2001 From: Arjun Shankar Date: Wed, 6 Aug 2025 15:02:01 +0200 Subject: [PATCH] Improve test coverage (RHEL-106562) Resolves: RHEL-106562 --- glibc-RHEL-106562-1.patch | 69 +++ glibc-RHEL-106562-10.patch | 20 + glibc-RHEL-106562-11.patch | 288 ++++++++++ glibc-RHEL-106562-12.patch | 90 +++ glibc-RHEL-106562-13.patch | 301 ++++++++++ glibc-RHEL-106562-14.patch | 18 + glibc-RHEL-106562-15.patch | 218 ++++++++ glibc-RHEL-106562-16.patch | 65 +++ glibc-RHEL-106562-17.patch | 20 + glibc-RHEL-106562-18.patch | 111 ++++ glibc-RHEL-106562-19.patch | 575 +++++++++++++++++++ glibc-RHEL-106562-2.patch | 529 ++++++++++++++++++ glibc-RHEL-106562-20.patch | 151 +++++ glibc-RHEL-106562-21.patch | 62 +++ glibc-RHEL-106562-22.patch | 121 ++++ glibc-RHEL-106562-23.patch | 1084 ++++++++++++++++++++++++++++++++++++ glibc-RHEL-106562-24.patch | 112 ++++ glibc-RHEL-106562-3.patch | 60 ++ glibc-RHEL-106562-4.patch | 29 + glibc-RHEL-106562-5.patch | 78 +++ glibc-RHEL-106562-6.patch | 391 +++++++++++++ glibc-RHEL-106562-7.patch | 415 ++++++++++++++ glibc-RHEL-106562-8.patch | 148 +++++ glibc-RHEL-106562-9.patch | 79 +++ glibc.spec | 29 +- 25 files changed, 5062 insertions(+), 1 deletion(-) create mode 100644 glibc-RHEL-106562-1.patch create mode 100644 glibc-RHEL-106562-10.patch create mode 100644 glibc-RHEL-106562-11.patch create mode 100644 glibc-RHEL-106562-12.patch create mode 100644 glibc-RHEL-106562-13.patch create mode 100644 glibc-RHEL-106562-14.patch create mode 100644 glibc-RHEL-106562-15.patch create mode 100644 glibc-RHEL-106562-16.patch create mode 100644 glibc-RHEL-106562-17.patch create mode 100644 glibc-RHEL-106562-18.patch create mode 100644 glibc-RHEL-106562-19.patch create mode 100644 glibc-RHEL-106562-2.patch create mode 100644 glibc-RHEL-106562-20.patch create mode 100644 glibc-RHEL-106562-21.patch create mode 100644 glibc-RHEL-106562-22.patch create mode 100644 glibc-RHEL-106562-23.patch create mode 100644 glibc-RHEL-106562-24.patch create mode 100644 glibc-RHEL-106562-3.patch create mode 100644 glibc-RHEL-106562-4.patch create mode 100644 glibc-RHEL-106562-5.patch create mode 100644 glibc-RHEL-106562-6.patch create mode 100644 glibc-RHEL-106562-7.patch create mode 100644 glibc-RHEL-106562-8.patch create mode 100644 glibc-RHEL-106562-9.patch diff --git a/glibc-RHEL-106562-1.patch b/glibc-RHEL-106562-1.patch new file mode 100644 index 0000000..d9838ff --- /dev/null +++ b/glibc-RHEL-106562-1.patch @@ -0,0 +1,69 @@ +commit f942a732d37a96217ef828116ebe64a644db18d7 +Author: Joe Talbott +Date: Tue May 14 14:39:38 2024 +0000 + + math: Add GLIBC_TEST_LIBM_VERBOSE environment variable support. + + Allow the libm-test-driver based tests to have their verbosity set based + on the GLIBC_TEST_LIBM_VERBOSE environment variable. This allows the entire + testsuite to be run with a non-default verbosity. + + While here check the conversion for the verbose option as well. + + Reviewed-by: Carlos O'Donell + +diff --git a/math/libm-test-support.c b/math/libm-test-support.c +index 1d60ac783be6fb65..0cae545f86ac1352 100644 +--- a/math/libm-test-support.c ++++ b/math/libm-test-support.c +@@ -130,7 +130,7 @@ static int noTests; /* number of tests (without testing exceptions) */ + static int noExcTests; /* number of tests for exception flags */ + static int noErrnoTests;/* number of tests for errno values */ + +-static int verbose; ++static unsigned int verbose; + static int output_max_error; /* Should the maximal errors printed? */ + static int output_points; /* Should the single function results printed? */ + static int ignore_max_ulp; /* Should we ignore max_ulp? */ +@@ -1057,7 +1057,14 @@ parse_opt (int key, char *arg, struct argp_state *state) + break; + case 'v': + if (optarg) +- verbose = (unsigned int) strtoul (optarg, NULL, 0); ++ { ++ char *optstr_conv = optarg; ++ unsigned int opt_verbose; ++ ++ opt_verbose = (unsigned int) strtoul (optarg, &optstr_conv, 0); ++ if (*optstr_conv == '\0' && optstr_conv != optarg) ++ verbose = opt_verbose; ++ } + else + verbose = 3; + break; +@@ -1139,6 +1146,7 @@ libm_test_init (int argc, char **argv) + int remaining; + char *ulps_file_path; + size_t dir_len = 0; ++ char *envstr_verbose; + + verbose = 1; + output_ulps = 0; +@@ -1148,6 +1156,17 @@ libm_test_init (int argc, char **argv) + /* XXX set to 0 for releases. */ + ignore_max_ulp = 0; + ++ envstr_verbose = getenv("GLIBC_TEST_LIBM_VERBOSE"); ++ if (envstr_verbose != NULL) ++ { ++ char *envstr_conv = envstr_verbose; ++ unsigned int env_verbose; ++ ++ env_verbose = (unsigned int) strtoul (envstr_verbose, &envstr_conv, 0); ++ if (*envstr_conv == '\0' && envstr_conv != envstr_verbose) ++ verbose = env_verbose; ++ } ++ + /* Parse and process arguments. */ + argp_parse (&argp, argc, argv, 0, &remaining, NULL); + diff --git a/glibc-RHEL-106562-10.patch b/glibc-RHEL-106562-10.patch new file mode 100644 index 0000000..e3c1b91 --- /dev/null +++ b/glibc-RHEL-106562-10.patch @@ -0,0 +1,20 @@ +commit 79f44e1a47e87907fb8e97bbd098e01c4adc26a5 +Author: Florian Weimer +Date: Mon Aug 26 16:45:31 2024 +0200 + + inet: Avoid label at end of compound statement in tst-if_nameindex + + This fails to compile with GCC 8. + +diff --git a/inet/tst-if_nameindex.c b/inet/tst-if_nameindex.c +index b025cdb3a7c6b68c..5b905601245bef34 100644 +--- a/inet/tst-if_nameindex.c ++++ b/inet/tst-if_nameindex.c +@@ -105,6 +105,7 @@ do_test (void) + TEST_VERIFY (errno == ENODEV); + + not_this_one: ++ ; + } + + diff --git a/glibc-RHEL-106562-11.patch b/glibc-RHEL-106562-11.patch new file mode 100644 index 0000000..ef95c71 --- /dev/null +++ b/glibc-RHEL-106562-11.patch @@ -0,0 +1,288 @@ +commit 4945ffc88a8ad49280bae64165683ddfd12b2390 +Author: DJ Delorie +Date: Wed Aug 7 16:55:16 2024 -0400 + + fgets: more tests + + Add more tests for unusual situations fgets() might see: + + * zero size file + * zero sized buffer + * NULL buffer + * NUL data + * writable stream + * closed stream + + Reviewed-by: Florian Weimer + +diff --git a/stdio-common/Makefile b/stdio-common/Makefile +index c822434293b7e809..6b52fd3e0d818960 100644 +--- a/stdio-common/Makefile ++++ b/stdio-common/Makefile +@@ -209,6 +209,7 @@ tests := \ + tst-fdopen2 \ + tst-ferror \ + tst-fgets \ ++ tst-fgets2 \ + tst-fileno \ + tst-fmemopen \ + tst-fmemopen2 \ +diff --git a/stdio-common/tst-fgets2.c b/stdio-common/tst-fgets2.c +new file mode 100644 +index 0000000000000000..5b78447ea9aa3cbe +--- /dev/null ++++ b/stdio-common/tst-fgets2.c +@@ -0,0 +1,253 @@ ++/* Test for additional fgets error 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++/* This avoids compiler warnings about passing NULL where a valid ++ pointer is expected. */ ++static void *volatile null = NULL; ++ ++/* Implementation of our FILE stream backend. */ ++ ++static int bytes_read; ++static int cookie_valid = 0; ++struct Cookie { ++ const char *buffer; ++ int bufptr; ++ int bufsz; ++}; ++ ++#define VALIDATE_COOKIE() if (! cookie_valid) { \ ++ FAIL ("call to %s after file closed", __FUNCTION__); \ ++ return -1; \ ++ } ++ ++static ssize_t ++io_read (void *vcookie, char *buf, size_t size) ++{ ++ struct Cookie *cookie = (struct Cookie *) vcookie; ++ ++ VALIDATE_COOKIE (); ++ ++ if (size > cookie->bufsz - cookie->bufptr) ++ size = cookie->bufsz - cookie->bufptr; ++ ++ memcpy (buf, cookie->buffer + cookie->bufptr, size); ++ cookie->bufptr += size; ++ bytes_read += size; ++ return size; ++} ++ ++static ssize_t ++io_write (void *vcookie, const char *buf, size_t size) ++{ ++ VALIDATE_COOKIE (); ++ FAIL_EXIT1 ("io_write called"); ++} ++ ++static int ++io_seek (void *vcookie, off64_t *position, int whence) ++{ ++ VALIDATE_COOKIE (); ++ FAIL_EXIT1 ("io_seek called"); ++} ++ ++static int ++io_clean (void *vcookie) ++{ ++ struct Cookie *cookie = (struct Cookie *) vcookie; ++ ++ VALIDATE_COOKIE (); ++ ++ cookie->buffer = NULL; ++ cookie->bufsz = 0; ++ cookie->bufptr = 0; ++ ++ cookie_valid = 0; ++ free (cookie); ++ return 0; ++} ++ ++cookie_io_functions_t io_funcs = { ++ .read = io_read, ++ .write = io_write, ++ .seek = io_seek, ++ .close = io_clean ++}; ++ ++FILE * ++io_open (const char *buffer, int buflen, const char *mode, void **vcookie) ++{ ++ FILE *f; ++ struct Cookie *cookie; ++ ++ cookie = (struct Cookie *) xcalloc (1, sizeof (struct Cookie)); ++ *vcookie = cookie; ++ cookie_valid = 1; ++ ++ cookie->buffer = buffer; ++ cookie->bufsz = buflen; ++ bytes_read = 0; ++ ++ f = fopencookie (cookie, mode, io_funcs); ++ if (f == NULL) ++ FAIL_EXIT1 ("fopencookie failed"); ++ ++ clearerr (f); ++ return f; ++} ++ ++/* The test cases. */ ++ ++#define my_open(s,l,m) io_open (s, l, m, (void *) &cookie) ++ ++#define TEST_COMPARE_0x11(buf, len) \ ++ TEST_COMPARE_BLOB (buf + (len), sizeof (buf) - (len), \ ++ buf2, sizeof (buf) - (len)); ++ ++#define check_flags(f, expected_eof, expected_err) \ ++ { \ ++ if (expected_eof) \ ++ TEST_VERIFY (feof (f) != 0); \ ++ else \ ++ TEST_VERIFY (feof (f) == 0); \ ++ if (expected_err) \ ++ TEST_VERIFY (ferror (f) != 0); \ ++ else \ ++ TEST_VERIFY (ferror (f) == 0); \ ++ } ++ ++static int ++do_test (void) ++{ ++ FILE *f; ++ struct Cookie *cookie; ++ char buf [10]; ++ char buf2 [10]; ++ char *returned_string; ++ ++ memset (buf2, 0x11, sizeof (buf2)); ++ ++ printf ("testing base operation...\n"); ++ f = my_open ("hello\n", 6, "r"); ++ memset (buf, 0x11, sizeof (buf)); ++ returned_string = fgets (buf, sizeof (buf) - 1, f); ++ TEST_VERIFY (returned_string == buf); ++ TEST_COMPARE_BLOB (buf, bytes_read + 1, "hello\n\0", 7); ++ TEST_COMPARE_0x11 (buf, bytes_read + 1); ++ check_flags (f, 0, 0); ++ ++ fclose (f); ++ ++ printf ("testing zero size file...\n"); ++ f = my_open ("hello\n", 0, "r"); ++ memset (buf, 0x11, sizeof (buf)); ++ returned_string = fgets (buf, sizeof (buf) - 1, f); ++ TEST_VERIFY (returned_string == NULL); ++ TEST_VERIFY (bytes_read == 0); ++ check_flags (f, 1, 0); ++ fclose (f); ++ ++ printf ("testing zero size buffer...\n"); ++ f = my_open ("hello\n", 6, "r"); ++ memset (buf, 0x11, sizeof (buf)); ++ returned_string = fgets (buf, 0, f); ++ TEST_VERIFY (returned_string == NULL); ++ TEST_VERIFY (bytes_read == 0); ++ check_flags (f, 0, 0); ++ fclose (f); ++ ++ printf ("testing NULL buffer with empty stream...\n"); ++ f = my_open ("hello\n", 0, "r"); ++ memset (buf, 0x11, sizeof (buf)); ++ ++ returned_string = fgets (null, sizeof (buf), f); ++ ++ TEST_VERIFY (returned_string == NULL); ++ TEST_VERIFY (bytes_read == 0); ++ check_flags (f, 1, 0); ++ fclose (f); ++ ++ printf ("testing embedded NUL...\n"); ++ f = my_open ("hel\0lo\n", 7, "r"); ++ memset (buf, 0x11, sizeof (buf)); ++ returned_string = fgets (buf, sizeof (buf) - 1, f); ++ TEST_VERIFY (returned_string == buf); ++ TEST_COMPARE_BLOB (buf, bytes_read + 1, "hel\0lo\n\0", 8); ++ TEST_COMPARE_0x11 (buf, bytes_read + 1); ++ check_flags (f, 0, 0); ++ fclose (f); ++ ++ printf ("testing writable stream...\n"); ++ f = my_open ("hel\0lo\n", 7, "w"); ++ memset (buf, 0x11, sizeof (buf)); ++ returned_string = fgets (buf, sizeof (buf) - 1, f); ++ TEST_VERIFY (returned_string == NULL); ++ TEST_VERIFY (bytes_read == 0); ++ check_flags (f, 0, 1); ++ fclose (f); ++ ++ printf ("testing closed fd stream...\n"); ++ int fd = open ("/dev/null", O_RDONLY); ++ f = fdopen (fd, "r"); ++ close (fd); ++ memset (buf, 0x11, sizeof (buf)); ++ returned_string = fgets (buf, sizeof (buf) - 1, f); ++ TEST_VERIFY (returned_string == NULL); ++ TEST_VERIFY (bytes_read == 0); ++ check_flags (f, 0, 1); ++ fclose (f); ++ ++#ifdef IO_DEBUG ++ /* These tests only pass if glibc is built with -DIO_DEBUG, but are ++ included for reference. */ ++ ++ printf ("testing NULL descriptor...\n"); ++ memset (buf, 0x11, sizeof (buf)); ++ returned_string = fgets (buf, sizeof (buf) - 1, null); ++ TEST_VERIFY (returned_string == NULL); ++ TEST_VERIFY (bytes_read == 0); ++ ++ printf ("testing closed descriptor...\n"); ++ f = my_open ("hello\n", 7, "r"); ++ fclose (f); ++ memset (buf, 0x11, sizeof (buf)); ++ returned_string = fgets (buf, sizeof (buf) - 1, f); ++ TEST_VERIFY (returned_string == NULL); ++ TEST_VERIFY (bytes_read == 0); ++#endif ++ ++ return 0; ++} ++ ++#include diff --git a/glibc-RHEL-106562-12.patch b/glibc-RHEL-106562-12.patch new file mode 100644 index 0000000..a5d4fb5 --- /dev/null +++ b/glibc-RHEL-106562-12.patch @@ -0,0 +1,90 @@ +commit 83fd4149ffdae86c8864a6828f39dd942956636f +Author: Aaron Merey +Date: Thu Sep 19 11:11:39 2024 -0400 + + Test that errno is set to 0 at program startup + + Add new testcase elf/tst-startup-errno.c which tests that errno is set + to 0 at first ELF constructor execution and at the start of the + program's main function. + + Tested for x86_64 + + Reviewed-by: Carlos O'Donell + +diff --git a/elf/Makefile b/elf/Makefile +index a46c4f69d98553f7..92da608da1ebc175 100644 +--- a/elf/Makefile ++++ b/elf/Makefile +@@ -457,6 +457,7 @@ tests += \ + tst-single_threaded-pthread \ + tst-sonamemove-dlopen \ + tst-sonamemove-link \ ++ tst-startup-errno \ + tst-thrlock \ + tst-tls-dlinfo \ + tst-tls-ie \ +diff --git a/elf/tst-startup-errno.c b/elf/tst-startup-errno.c +new file mode 100644 +index 0000000000000000..59a1005fb674a5c3 +--- /dev/null ++++ b/elf/tst-startup-errno.c +@@ -0,0 +1,58 @@ ++/* Test the value of errno at program startup. ++ 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 ++ . */ ++ ++#include ++#include ++#include ++ ++/* Verify that errno is 0 at first ELF constructor execution and at ++ the start of main. */ ++ ++static void set_ctor_errno (void) __attribute__((constructor)); ++static int ctor_errno = -1; ++ ++static void ++set_ctor_errno (void) ++{ ++ ctor_errno = errno; ++} ++ ++static int ++get_ctor_errno (void) ++{ ++ return ctor_errno; ++} ++ ++int ++main (void) ++{ ++ if (errno != 0) ++ { ++ printf ("At start of main errno set to %d != 0\n", errno); ++ exit (1); ++ } ++ ++ if (get_ctor_errno () != 0) ++ { ++ printf ("At ctor exec errno set to %d != 0\n", get_ctor_errno ()); ++ exit (1); ++ } ++ ++ return 0; ++} ++ diff --git a/glibc-RHEL-106562-13.patch b/glibc-RHEL-106562-13.patch new file mode 100644 index 0000000..84b92cf --- /dev/null +++ b/glibc-RHEL-106562-13.patch @@ -0,0 +1,301 @@ +commit cfb35f5f7f32cec8fa4e16b99e35b7d70fa13f1f +Author: DJ Delorie +Date: Tue Sep 17 22:52:37 2024 -0400 + + rt: more clock_nanosleep tests + + Test that clock_nanosleep rejects out of range time values. + + Test that clock_nanosleep actually sleeps for at least the + requested time relative to the requested clock. + + Reviewed-by: Adhemerval Zanella + +diff --git a/rt/Makefile b/rt/Makefile +index 7b50c64f7664c07f..bc5f28c6d03722a3 100644 +--- a/rt/Makefile ++++ b/rt/Makefile +@@ -77,6 +77,7 @@ tests := tst-shm tst-timer tst-timer2 \ + tst-bz28213 \ + tst-timer3 tst-timer4 tst-timer5 \ + tst-cpuclock2 tst-cputimer1 tst-cputimer2 tst-cputimer3 \ ++ tst-clock_nanosleep2 \ + tst-shm-cancel \ + tst-mqueue10 + tests-internal := tst-timer-sigmask +@@ -84,6 +85,7 @@ tests-internal := tst-timer-sigmask + tests-time64 := \ + tst-aio6-time64 \ + tst-cpuclock2-time64 \ ++ tst-clock_nanosleep2-time64 \ + tst-mqueue1-time64 \ + tst-mqueue2-time64 \ + tst-mqueue4-time64 \ +diff --git a/rt/tst-clock_nanosleep2-time64.c b/rt/tst-clock_nanosleep2-time64.c +new file mode 100644 +index 0000000000000000..8deb4201f38b094a +--- /dev/null ++++ b/rt/tst-clock_nanosleep2-time64.c +@@ -0,0 +1 @@ ++#include "tst-clock_nanosleep2.c" +diff --git a/rt/tst-clock_nanosleep2.c b/rt/tst-clock_nanosleep2.c +new file mode 100644 +index 0000000000000000..10c822fd54668531 +--- /dev/null ++++ b/rt/tst-clock_nanosleep2.c +@@ -0,0 +1,255 @@ ++/* Test program for process CPU clocks - invalid inputs, minimum time ++ 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 ++ . */ ++ ++/* This test has two primary goals - first, to validate that invalid ++ inputs to clock_nanosleep are caught, and second, to validate that ++ clock_nanosleep sleeps for at least the amount of time requested. ++ It is assumed that the system may sleep for an arbitrary additional ++ amount of time beyond the requested time. */ ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++/* This is 1 ms per test, we have 10 tests, so this file runs in on ++ the order of 0.01 seconds. */ ++#define TEST_NSEC 1000000 ++ ++/* Nanoseconds per second. */ ++#define NSECMAX 1000000000L ++ ++static pthread_barrier_t barrier; ++ ++/* This function is intended to rack up both user and system time. */ ++static void * ++chew_cpu (void *arg) ++{ ++ pthread_barrier_wait (&barrier); ++ ++ while (1) ++ { ++ static volatile char buf[4096]; ++ for (int i = 0; i < 100; ++i) ++ for (size_t j = 0; j < sizeof buf; ++j) ++ buf[j] = 0xaa; ++ int nullfd = xopen ("/dev/null", O_WRONLY, 0); ++ for (int i = 0; i < 100; ++i) ++ for (size_t j = 0; j < sizeof buf; ++j) ++ buf[j] = 0xbb; ++ xwrite (nullfd, (char *) buf, sizeof buf); ++ close (nullfd); ++ } ++ ++ return NULL; ++} ++ ++static void ++ptime_1 (const char *n, struct timespec t) ++{ ++ /* This is only for debugging failed test cases. */ ++ printf ("%12s: %lld.%09lld\n", n, (long long int) t.tv_sec, ++ (long long int) t.tv_nsec); ++} ++#define ptime(t) ptime_1 (#t, t) ++ ++static void ++test_interval_1 (const char *n_clock, clockid_t t_clock) ++{ ++ struct timespec me_before, me_after, quantum, me_sleep, me_slept; ++ long long int slept, min_slept; ++ ++ /* Arbitrary to ensure our time period is sufficiently bigger than ++ the time step. */ ++ TEST_VERIFY (clock_getres (t_clock, &quantum) == 0); ++ printf("Clock quantum: %lld ns, test time: %lld ns\n", ++ (long long int) quantum.tv_nsec, (long long int) TEST_NSEC); ++ TEST_VERIFY (quantum.tv_nsec <= TEST_NSEC / 10); ++ ++ min_slept = TEST_NSEC; ++ ++ me_sleep = make_timespec (0, min_slept); ++ ++ printf ("test clock %s for %lld.%09lld sec relative\n", ++ n_clock, (long long int) me_sleep.tv_sec, ++ (long long int) me_sleep.tv_nsec); ++ ++ TEST_COMPARE (clock_gettime (t_clock, &me_before), 0); ++ TEST_COMPARE (clock_nanosleep (t_clock, 0, &me_sleep, NULL), 0); ++ TEST_COMPARE (clock_gettime (t_clock, &me_after), 0); ++ ++ me_slept = timespec_sub (me_after, me_before); ++ slept = support_timespec_ns (me_slept); ++ ++ ptime (me_before); ++ ptime (me_after); ++ ptime (me_sleep); ++ ptime (me_slept); ++ printf ("test slept %lld nsec >= asked for %lld ?\n", slept, min_slept); ++ ++ /* This is the important part - verify that the time slept is at ++ least as much as the time requested. */ ++ TEST_VERIFY (slept >= min_slept); ++} ++ ++static void ++test_abs_1 (const char *n_clock, clockid_t t_clock) ++{ ++ struct timespec me_before, me_after, quantum, me_sleep; ++ ++ /* Arbitrary to ensure our time period is sufficiently bigger than ++ the time step. */ ++ TEST_VERIFY (clock_getres (t_clock, &quantum) == 0); ++ printf("Clock quantum: %lld ns, test time: %lld ns\n", ++ (long long int) quantum.tv_nsec, (long long int) TEST_NSEC); ++ TEST_VERIFY (quantum.tv_nsec <= TEST_NSEC / 10); ++ ++ me_sleep = make_timespec (0, TEST_NSEC); ++ ++ printf ("test clock %s for %lld.%09lld sec absolute\n", ++ n_clock, (long long int) me_sleep.tv_sec, ++ (long long int) me_sleep.tv_nsec); ++ ++ TEST_COMPARE (clock_gettime (t_clock, &me_before), 0); ++ me_sleep = timespec_add (me_sleep, me_before); ++ TEST_COMPARE (clock_nanosleep (t_clock, TIMER_ABSTIME, &me_sleep, NULL), 0); ++ TEST_COMPARE (clock_gettime (t_clock, &me_after), 0); ++ ++ ptime (me_before); ++ ptime (me_sleep); ++ ptime (me_after); ++ ++ printf("test slept until %lld.%09lld after requested %lld.%09lld ?\n", ++ (long long int) me_after.tv_sec, (long long int) me_after.tv_nsec, ++ (long long int) me_sleep.tv_sec, (long long int) me_sleep.tv_nsec); ++ ++ /* This is the important part - verify that the time slept is at ++ least as much as the time requested. */ ++ TEST_TIMESPEC_EQUAL_OR_AFTER (me_after, me_sleep); ++} ++ ++static void ++test_invalids_1 (const char *the_clock_name, int the_clock, ++ const char *flags_name, int flags) ++{ ++ struct timespec me_before; ++ ++ /* Note: do not use make_timespec() in case that function tries to ++ normalize the fields. */ ++ ++ printf ("%s: %s: test tv 0, 0\n", the_clock_name, flags_name); ++ me_before.tv_sec = 0; ++ me_before.tv_nsec = 0; ++ TEST_COMPARE (clock_nanosleep (the_clock, 0, &me_before, NULL), 0); ++ ++ printf ("%s: %s: test tv -1, 0\n", the_clock_name, flags_name); ++ me_before.tv_sec = -1; ++ me_before.tv_nsec = 0; ++ TEST_COMPARE (clock_nanosleep (the_clock, 0, &me_before, NULL), EINVAL); ++ ++ printf ("%s: %s: test tv 0, -1\n", the_clock_name, flags_name); ++ me_before.tv_sec = 0; ++ me_before.tv_nsec = -1; ++ TEST_COMPARE (clock_nanosleep (the_clock, 0, &me_before, NULL), EINVAL); ++ ++ printf ("%s: %s: test tv -1, -1\n", the_clock_name, flags_name); ++ me_before.tv_sec = -1; ++ me_before.tv_nsec = -1; ++ TEST_COMPARE (clock_nanosleep (the_clock, 0, &me_before, NULL), EINVAL); ++ ++ printf ("%s: %s: test tv 0, MAX\n", the_clock_name, flags_name); ++ me_before.tv_sec = 0; ++ me_before.tv_nsec = NSECMAX; ++ TEST_COMPARE (clock_nanosleep (the_clock, 0, &me_before, NULL), EINVAL); ++} ++ ++static int ++do_test (void) ++{ ++ pthread_t th; ++ ++ pthread_barrier_init (&barrier, NULL, 2); ++ ++ /* Test for proper error detection. */ ++ ++#define test_invalids(c, f) test_invalids_1 (#c, c, #f, f) ++ test_invalids (CLOCK_REALTIME, 0); ++#ifdef CLOCK_TAI ++ test_invalids (CLOCK_TAI, 0); ++#endif ++ test_invalids (CLOCK_MONOTONIC, 0); ++#ifdef CLOCK_BOOTTIME ++ test_invalids (CLOCK_BOOTTIME, 0); ++#endif ++ test_invalids (CLOCK_PROCESS_CPUTIME_ID, 0); ++ test_invalids (CLOCK_REALTIME, TIMER_ABSTIME); ++#ifdef CLOCK_TAI ++ test_invalids (CLOCK_TAI, TIMER_ABSTIME); ++#endif ++ test_invalids (CLOCK_MONOTONIC, TIMER_ABSTIME); ++#ifdef CLOCK_BOOTTIME ++ test_invalids (CLOCK_BOOTTIME, TIMER_ABSTIME); ++#endif ++ test_invalids (CLOCK_PROCESS_CPUTIME_ID, TIMER_ABSTIME); ++ ++ /* Test for various clocks "working". */ ++ ++#define test_interval(c) test_interval_1 (#c, c) ++ test_interval (CLOCK_REALTIME); ++#ifdef CLOCK_TAI ++ test_interval (CLOCK_TAI); ++#endif ++ test_interval (CLOCK_MONOTONIC); ++#ifdef CLOCK_BOOTTIME ++ test_interval (CLOCK_BOOTTIME); ++#endif ++ ++ th = xpthread_create (NULL, chew_cpu, NULL); ++ xpthread_barrier_wait (&barrier); ++ test_interval (CLOCK_PROCESS_CPUTIME_ID); ++ xpthread_cancel (th); ++ ++#define test_abs(c) test_abs_1 (#c, c) ++ test_abs (CLOCK_REALTIME); ++#ifdef CLOCK_TAI ++ test_abs (CLOCK_TAI); ++#endif ++ test_abs (CLOCK_MONOTONIC); ++#ifdef CLOCK_BOOTTIME ++ test_abs (CLOCK_BOOTTIME); ++#endif ++ ++ th = xpthread_create (NULL, chew_cpu, NULL); ++ xpthread_barrier_wait (&barrier); ++ test_abs (CLOCK_PROCESS_CPUTIME_ID); ++ xpthread_cancel (th); ++ ++ return 0; ++} ++ ++#include diff --git a/glibc-RHEL-106562-14.patch b/glibc-RHEL-106562-14.patch new file mode 100644 index 0000000..813c7ff --- /dev/null +++ b/glibc-RHEL-106562-14.patch @@ -0,0 +1,18 @@ +commit 1895a35e7092713b224166d36b9bc26e8eb3371f +Author: DJ Delorie +Date: Tue Oct 8 14:30:21 2024 -0400 + + rt: more clock_nanosleep tests addendum + + Forgot to change the first-line description. + +diff --git a/rt/tst-clock_nanosleep2.c b/rt/tst-clock_nanosleep2.c +index 10c822fd54668531..e9b2a2716d6e9016 100644 +--- a/rt/tst-clock_nanosleep2.c ++++ b/rt/tst-clock_nanosleep2.c +@@ -1,4 +1,4 @@ +-/* Test program for process CPU clocks - invalid inputs, minimum time ++/* Test for clock_nanosleep parameter checks and sleep duration. + Copyright (C) 2024 Free Software Foundation, Inc. + This file is part of the GNU C Library. + diff --git a/glibc-RHEL-106562-15.patch b/glibc-RHEL-106562-15.patch new file mode 100644 index 0000000..5df1459 --- /dev/null +++ b/glibc-RHEL-106562-15.patch @@ -0,0 +1,218 @@ +commit 99671e72bb27a3cb98860bdc4c0e25961ce96b3e +Author: Joseph Myers +Date: Fri Nov 22 16:58:51 2024 +0000 + + Add multithreaded test of sem_getvalue + + Test coverage of sem_getvalue is fairly limited. Add a test that runs + it on threads on each CPU. For this purpose I adapted + tst-skeleton-thread-affinity.c; it didn't seem very suitable to use + as-is or include directly in a different test doing things per-CPU, + but did seem a suitable starting point (thus sharing + tst-skeleton-affinity.c) for such testing. + + Tested for x86_64. + +diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile +index eee91c7b64d79fe7..47bf050759bd2b4e 100644 +--- a/sysdeps/unix/sysv/linux/Makefile ++++ b/sysdeps/unix/sysv/linux/Makefile +@@ -653,6 +653,7 @@ ifeq ($(subdir),nptl) + tests += \ + tst-align-clone \ + tst-getpid1 \ ++ tst-sem_getvalue-affinity \ + # tests + + # tst-rseq-nptl is an internal test because it requires a definition of +diff --git a/sysdeps/unix/sysv/linux/tst-sem_getvalue-affinity.c b/sysdeps/unix/sysv/linux/tst-sem_getvalue-affinity.c +new file mode 100644 +index 0000000000000000..4176f67533357909 +--- /dev/null ++++ b/sysdeps/unix/sysv/linux/tst-sem_getvalue-affinity.c +@@ -0,0 +1,185 @@ ++/* Test sem_getvalue across CPUs. Based on tst-skeleton-thread-affinity.c. ++ Copyright (C) 2015-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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++struct conf; ++static bool early_test (struct conf *); ++ ++static int ++setaffinity (size_t size, const cpu_set_t *set) ++{ ++ int ret = pthread_setaffinity_np (pthread_self (), size, set); ++ if (ret != 0) ++ { ++ errno = ret; ++ return -1; ++ } ++ return 0; ++} ++ ++static int ++getaffinity (size_t size, cpu_set_t *set) ++{ ++ int ret = pthread_getaffinity_np (pthread_self (), size, set); ++ if (ret != 0) ++ { ++ errno = ret; ++ return -1; ++ } ++ return 0; ++} ++ ++#include "tst-skeleton-affinity.c" ++ ++static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; ++static sem_t sem; ++ ++static void * ++tf (void *arg) ++{ ++ void *ret = NULL; ++ xpthread_mutex_lock (&lock); ++ int semval; ++ if (sem_getvalue (&sem, &semval) != 0) ++ { ++ printf ("sem_getvalue failed: %m\n"); ++ ret = (void *) 1; ++ } ++ else if (semval != 12345) ++ { ++ printf ("sem_getvalue returned %d not 12345\n", semval); ++ ret = (void *) 1; ++ } ++ xpthread_mutex_unlock (&lock); ++ return ret; ++} ++ ++static int ++stop_and_join_threads (struct conf *conf, cpu_set_t *set, ++ pthread_t *pinned_first, pthread_t *pinned_last) ++{ ++ int failed = 0; ++ for (pthread_t *p = pinned_first; p < pinned_last; ++p) ++ { ++ int cpu = p - pinned_first; ++ if (!CPU_ISSET_S (cpu, CPU_ALLOC_SIZE (conf->set_size), set)) ++ continue; ++ ++ void *retval = (void *) 1; ++ int ret = pthread_join (*p, &retval); ++ if (ret != 0) ++ { ++ printf ("error: Failed to join thread %d: %s\n", cpu, strerror (ret)); ++ fflush (stdout); ++ /* Cannot shut down cleanly with threads still running. */ ++ abort (); ++ } ++ if (retval != NULL) ++ failed = 1; ++ } ++ return failed; ++} ++ ++static bool ++early_test (struct conf *conf) ++{ ++ int ret; ++ ret = sem_init (&sem, 0, 12345); ++ if (ret != 0) ++ { ++ printf ("error: sem_init failed: %m\n"); ++ return false; ++ } ++ xpthread_mutex_lock (&lock); ++ pthread_t *pinned_threads ++ = calloc (conf->last_cpu + 1, sizeof (*pinned_threads)); ++ cpu_set_t *initial_set = CPU_ALLOC (conf->set_size); ++ cpu_set_t *scratch_set = CPU_ALLOC (conf->set_size); ++ ++ if (pinned_threads == NULL || initial_set == NULL || scratch_set == NULL) ++ { ++ puts ("error: Memory allocation failure"); ++ return false; ++ } ++ if (getaffinity (CPU_ALLOC_SIZE (conf->set_size), initial_set) < 0) ++ { ++ printf ("error: pthread_getaffinity_np failed: %m\n"); ++ return false; ++ } ++ ++ pthread_attr_t attr; ++ ret = pthread_attr_init (&attr); ++ if (ret != 0) ++ { ++ printf ("error: pthread_attr_init failed: %s\n", strerror (ret)); ++ return false; ++ } ++ support_set_small_thread_stack_size (&attr); ++ ++ /* Spawn a thread pinned to each available CPU. */ ++ for (int cpu = 0; cpu <= conf->last_cpu; ++cpu) ++ { ++ if (!CPU_ISSET_S (cpu, CPU_ALLOC_SIZE (conf->set_size), initial_set)) ++ continue; ++ CPU_ZERO_S (CPU_ALLOC_SIZE (conf->set_size), scratch_set); ++ CPU_SET_S (cpu, CPU_ALLOC_SIZE (conf->set_size), scratch_set); ++ ret = pthread_attr_setaffinity_np ++ (&attr, CPU_ALLOC_SIZE (conf->set_size), scratch_set); ++ if (ret != 0) ++ { ++ printf ("error: pthread_attr_setaffinity_np for CPU %d failed: %s\n", ++ cpu, strerror (ret)); ++ stop_and_join_threads (conf, initial_set, ++ pinned_threads, pinned_threads + cpu); ++ return false; ++ } ++ ret = pthread_create (pinned_threads + cpu, &attr, ++ tf, (void *) (uintptr_t) cpu); ++ if (ret != 0) ++ { ++ printf ("error: pthread_create for CPU %d failed: %s\n", ++ cpu, strerror (ret)); ++ stop_and_join_threads (conf, initial_set, ++ pinned_threads, pinned_threads + cpu); ++ return false; ++ } ++ } ++ ++ /* Main thread. */ ++ xpthread_mutex_unlock (&lock); ++ int failed = stop_and_join_threads (conf, initial_set, ++ pinned_threads, ++ pinned_threads + conf->last_cpu + 1); ++ ++ printf ("info: Main thread ran on %d CPU(s) of %d available CPU(s)\n", ++ CPU_COUNT_S (CPU_ALLOC_SIZE (conf->set_size), scratch_set), ++ CPU_COUNT_S (CPU_ALLOC_SIZE (conf->set_size), initial_set)); ++ ++ pthread_attr_destroy (&attr); ++ CPU_FREE (scratch_set); ++ CPU_FREE (initial_set); ++ free (pinned_threads); ++ return failed == 0; ++} diff --git a/glibc-RHEL-106562-16.patch b/glibc-RHEL-106562-16.patch new file mode 100644 index 0000000..e6ee947 --- /dev/null +++ b/glibc-RHEL-106562-16.patch @@ -0,0 +1,65 @@ +commit 4b7cfcc3fbfab55a1bbb32a2da69c048060739d6 +Author: Florian Weimer +Date: Mon Nov 25 17:32:54 2024 +0100 + + debug: Wire up tst-longjmp_chk3 + + The test was added in commit ac8cc9e300a002228eb7e660df3e7b333d9a7414 + without all the required Makefile scaffolding. Tweak the test + so that it actually builds (including with dynamic SIGSTKSZ). + + Reviewed-by: Adhemerval Zanella + +diff --git a/debug/Makefile b/debug/Makefile +index 3903cc97a3706354..76c311d2845df9c1 100644 +--- a/debug/Makefile ++++ b/debug/Makefile +@@ -287,6 +287,7 @@ tests = \ + tst-fortify-wide \ + tst-longjmp_chk \ + tst-longjmp_chk2 \ ++ tst-longjmp_chk3 \ + tst-realpath-chk \ + tst-sprintf-fortify-rdonly \ + tst-sprintf-fortify-unchecked \ +diff --git a/debug/tst-longjmp_chk3.c b/debug/tst-longjmp_chk3.c +index 9ff99772075926ce..7bf1646b354fd2fe 100644 +--- a/debug/tst-longjmp_chk3.c ++++ b/debug/tst-longjmp_chk3.c +@@ -18,9 +18,12 @@ + + #include + #include ++#include + #include + +-static char buf[SIGSTKSZ * 4]; ++#include ++ ++static char *buf; + static jmp_buf jb; + + static void +@@ -49,8 +52,10 @@ do_test (void) + set_fortify_handler (handler); + + /* Create a valid signal stack and enable it. */ ++ size_t bufsize = SIGSTKSZ * 4; ++ buf = xmalloc (bufsize); + ss.ss_sp = buf; +- ss.ss_size = sizeof (buf); ++ ss.ss_size = bufsize; + ss.ss_flags = 0; + if (sigaltstack (&ss, NULL) < 0) + { +@@ -65,8 +70,8 @@ do_test (void) + + /* Shrink the signal stack so the jmpbuf is now invalid. + We adjust the start & end to handle stacks that grow up & down. */ +- ss.ss_sp = buf + sizeof (buf) / 2; +- ss.ss_size = sizeof (buf) / 4; ++ ss.ss_sp = buf + bufsize / 2; ++ ss.ss_size = bufsize / 4; + if (sigaltstack (&ss, NULL) < 0) + { + printf ("second sigaltstack failed: %m\n"); diff --git a/glibc-RHEL-106562-17.patch b/glibc-RHEL-106562-17.patch new file mode 100644 index 0000000..a6e68a1 --- /dev/null +++ b/glibc-RHEL-106562-17.patch @@ -0,0 +1,20 @@ +commit 4836a9af89f1b4d482e6c72ff67e36226d36434c +Author: Florian Weimer +Date: Tue Nov 26 19:26:13 2024 +0100 + + debug: Fix tst-longjmp_chk3 build failure on Hurd + + Explicitly include for _exit and getpid. + +diff --git a/debug/tst-longjmp_chk3.c b/debug/tst-longjmp_chk3.c +index 7bf1646b354fd2fe..9b9db3b9e9e37d54 100644 +--- a/debug/tst-longjmp_chk3.c ++++ b/debug/tst-longjmp_chk3.c +@@ -20,6 +20,7 @@ + #include + #include + #include ++#include + + #include + diff --git a/glibc-RHEL-106562-18.patch b/glibc-RHEL-106562-18.patch new file mode 100644 index 0000000..134781a --- /dev/null +++ b/glibc-RHEL-106562-18.patch @@ -0,0 +1,111 @@ +commit bde47662b74b883149c3001e2c052dea5d3cd92f +Author: Sergey Kolosov +Date: Wed Nov 6 15:24:06 2024 +0100 + + nptl: Add new test for pthread_spin_trylock + + Add a threaded test for pthread_spin_trylock attempting to lock already + acquired spin lock and checking for correct return code. + + Reviewed-by: Florian Weimer + +diff --git a/sysdeps/pthread/Makefile b/sysdeps/pthread/Makefile +index 04ea56559ef3a79b..8c80b7a606da4c94 100644 +--- a/sysdeps/pthread/Makefile ++++ b/sysdeps/pthread/Makefile +@@ -265,6 +265,7 @@ tests += \ + tst-spin2 \ + tst-spin3 \ + tst-spin4 \ ++ tst-spin5 \ + tst-stack1 \ + tst-stdio1 \ + tst-stdio2 \ +diff --git a/sysdeps/pthread/tst-spin5.c b/sysdeps/pthread/tst-spin5.c +new file mode 100644 +index 0000000000000000..5c23bd48ef27b3ce +--- /dev/null ++++ b/sysdeps/pthread/tst-spin5.c +@@ -0,0 +1,82 @@ ++/* Threaded test the pthread_spin_trylock function. ++ 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++pthread_spinlock_t lock; ++ ++void * ++thread (void *arg) ++{ ++ int ret; ++ int thr_id = *(int *) arg; ++ ++ ret = pthread_spin_trylock (&lock); ++ if (thr_id == 1) ++ /* thread with already acquired lock. */ ++ { ++ if (ret != EBUSY) ++ { ++ FAIL_EXIT1 ("pthread_spin_trylock should fail with EBUSY"); ++ } ++ } ++ else if (thr_id == 2) ++ /* thread with released spin lock. */ ++ { ++ if (ret != 0) ++ { ++ FAIL_EXIT1 ("pthread_spin_trylock should be able to acquire lock"); ++ } ++ } ++ return NULL; ++} ++ ++static int ++do_test (void) ++{ ++ pthread_t thr1, thr2; ++ int ret; ++ int thr1_id = 1, thr2_id = 2; ++ ++ pthread_spin_init (&lock, PTHREAD_PROCESS_PRIVATE); ++ /* lock spin in main thread. */ ++ ret = pthread_spin_trylock (&lock); ++ if (ret != 0) ++ { ++ FAIL_EXIT1 ("Main thread should be able to acquire spin lock"); ++ } ++ ++ /* create first thread to try locking already acquired spin lock. */ ++ thr1 = xpthread_create (NULL, thread, &thr1_id); ++ xpthread_join (thr1); ++ ++ /* release spin lock and create thread to acquire released spin lock. */ ++ pthread_spin_unlock (&lock); ++ thr2 = xpthread_create (NULL, thread, &thr2_id); ++ xpthread_join (thr2); ++ ++ pthread_spin_destroy (&lock); ++ return 0; ++} ++ ++#include diff --git a/glibc-RHEL-106562-19.patch b/glibc-RHEL-106562-19.patch new file mode 100644 index 0000000..0502db0 --- /dev/null +++ b/glibc-RHEL-106562-19.patch @@ -0,0 +1,575 @@ +commit 45c42b65c29422b773ac94771aa71165e245f8f8 +Author: Martin Coufal +Date: Thu Jan 23 13:04:06 2025 +0100 + + Add new tests for fopen + + Adding some basic tests for fopen, testing different modes, stream + positioning and concurrent read/write operation on files. + Reviewed-by: DJ Delorie + +diff --git a/stdio-common/Makefile b/stdio-common/Makefile +index 6b52fd3e0d818960..f44562df75cb98bc 100644 +--- a/stdio-common/Makefile ++++ b/stdio-common/Makefile +@@ -215,6 +215,7 @@ tests := \ + tst-fmemopen2 \ + tst-fmemopen3 \ + tst-fmemopen4 \ ++ tst-fopen \ + tst-fphex \ + tst-fphex-wide \ + tst-fread \ +diff --git a/stdio-common/tst-fopen.c b/stdio-common/tst-fopen.c +new file mode 100644 +index 0000000000000000..8c1fefd116f9f581 +--- /dev/null ++++ b/stdio-common/tst-fopen.c +@@ -0,0 +1,279 @@ ++/* Basic test for fopen. ++ 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 ++ ++#define APPENDED_TEXT "This is appended text. " ++#define DEFAULT_TEXT "Lorem ipsum dolor sit amet, consectetur " \ ++ "adipiscing elit, sed do eiusmod tempor incididunt ut labore et " \ ++ "dolore magna aliqua." ++#define MAX_BUFFER_SIZE 300 ++ ++ ++static int ++do_test (void) ++{ ++ char *temp_file; ++ FILE *fd_file = NULL; ++ char read_buffer[MAX_BUFFER_SIZE] = ""; ++ size_t ret; ++ ++ /* Prepare files. */ ++ int fd = create_temp_file ("tst-fopen.", &temp_file); ++ TEST_VERIFY_EXIT (fd != -1); ++ fd_file = fdopen (fd, "w"); ++ ret = fwrite (DEFAULT_TEXT, sizeof (char), strlen (DEFAULT_TEXT), fd_file); ++ TEST_COMPARE (ret, strlen (DEFAULT_TEXT)); ++ xfclose (fd_file); ++ ++ /* Test 1: This checks for fopen with mode "r". Open text file for ++ reading. The stream is positioned at the beginning of the file. */ ++ printf ("Test 1: This checks for fopen with mode \"r\".\n"); ++ fd_file = fopen (temp_file, "r"); ++ TEST_COMPARE (ferror (fd_file), 0); ++ TEST_VERIFY (fd_file != NULL); ++ TEST_COMPARE (ftell (fd_file), 0); ++ /* Read should succeed. */ ++ ret = fread (read_buffer, sizeof (char), MAX_BUFFER_SIZE, fd_file); ++ TEST_COMPARE (ferror (fd_file), 0); ++ TEST_COMPARE (ret, strlen (DEFAULT_TEXT)); ++ TEST_VERIFY (strcmp (read_buffer, DEFAULT_TEXT) == 0); ++ /* Write should fail. */ ++ errno = 0; ++ ret = fwrite (DEFAULT_TEXT, sizeof (char), strlen (DEFAULT_TEXT), fd_file); ++ TEST_VERIFY (ferror (fd_file) != 0); ++ TEST_COMPARE (errno, EBADF); ++ TEST_COMPARE (ret, 0); ++ clearerr (fd_file); ++ /* Opening non-existent file should fail. */ ++ xfclose (fd_file); ++ errno = 0; ++ fd_file = fopen ("file-that-does-not-exist", "r"); ++ TEST_VERIFY (fd_file == NULL); ++ TEST_COMPARE (errno, ENOENT); ++ TEST_VERIFY (fd_file == NULL); ++ ++ memset (read_buffer, 0, MAX_BUFFER_SIZE); ++ ++ /* Test 2: This checks for fopen with mode "r+". Open for reading and ++ writing. The stream is positioned at the beginning of the file. */ ++ printf ("Test 2: This checks for fopen with mode \"r+\".\n"); ++ fd_file = fopen (temp_file, "r+"); ++ TEST_COMPARE (ferror (fd_file), 0); ++ TEST_VERIFY (fd_file != NULL); ++ TEST_COMPARE (ftell (fd_file), 0); ++ /* Read should succeed. */ ++ ret = fread (read_buffer, sizeof (char), MAX_BUFFER_SIZE, fd_file); ++ TEST_COMPARE (ferror (fd_file), 0); ++ TEST_COMPARE (ret, strlen (DEFAULT_TEXT)); ++ TEST_VERIFY (strcmp (read_buffer, DEFAULT_TEXT) == 0); ++ fflush (fd_file); ++ /* File position indicator expected at 0 + read bytes. */ ++ TEST_COMPARE (ftell (fd_file), ret); ++ /* Write should succeed. */ ++ ret = fwrite (DEFAULT_TEXT, sizeof (char), strlen (DEFAULT_TEXT), fd_file); ++ TEST_COMPARE (ferror (fd_file), 0); ++ TEST_COMPARE (ret, strlen (DEFAULT_TEXT)); ++ /* Opening non-existent file should fail. */ ++ xfclose (fd_file); ++ errno = 0; ++ fd_file = fopen ("file-that-does-not-exist", "r+"); ++ TEST_VERIFY (fd_file == NULL); ++ TEST_COMPARE (errno, ENOENT); ++ TEST_VERIFY (fd_file == NULL); ++ ++ memset (read_buffer, 0, MAX_BUFFER_SIZE); ++ ++ /* Test 3: This checks for fopen with mode "w". Truncate file to zero ++ length or create text file for writing. The stream is positioned ++ at the beginning of the file. */ ++ printf ("Test 3: This checks for fopen with mode \"w\".\n"); ++ fd_file = fopen (temp_file, "w"); ++ TEST_COMPARE (ferror (fd_file), 0); ++ TEST_VERIFY (fd_file != NULL); ++ TEST_COMPARE (ftell (fd_file), 0); ++ /* Read should fail. */ ++ errno = 0; ++ ret = fread (read_buffer, sizeof (char), MAX_BUFFER_SIZE, fd_file); ++ TEST_VERIFY (ferror (fd_file) != 0); ++ TEST_COMPARE (errno, EBADF); ++ TEST_COMPARE (ret, 0); ++ clearerr (fd_file); ++ /* Write should succeed. */ ++ ret = fwrite (DEFAULT_TEXT, sizeof (char), strlen (DEFAULT_TEXT), fd_file); ++ TEST_COMPARE (ferror (fd_file), 0); ++ TEST_COMPARE (ret, strlen (DEFAULT_TEXT)); ++ /* Opening non-existent file should succeed. */ ++ xfclose (fd_file); ++ fd_file = fopen ("/tmp/file-that-does-not-exist", "w"); ++ TEST_COMPARE (ferror (fd_file), 0); ++ TEST_VERIFY (fd_file != NULL); ++ TEST_COMPARE (ftell (fd_file), 0); ++ ++ xfclose (fd_file); ++ remove ("/tmp/file-that-does-not-exist"); ++ memset (read_buffer, 0, MAX_BUFFER_SIZE); ++ ++ /* Test 4: This checks for fopen with mode "w+". Open for reading and ++ writing. The file is created if it does not exist, otherwise it is ++ truncated. The stream is positioned at the beginning of the file. ++ */ ++ printf ("Test 4: This checks for fopen with mode \"w+\".\n"); ++ fd_file = fopen (temp_file, "w+"); ++ TEST_COMPARE (ferror (fd_file), 0); ++ TEST_VERIFY (fd_file != NULL); ++ TEST_COMPARE (ftell (fd_file), 0); ++ /* Read should succeed. */ ++ ret = fread (read_buffer, sizeof (char), MAX_BUFFER_SIZE, fd_file); ++ TEST_COMPARE (ferror (fd_file), 0); ++ TEST_COMPARE (ret, 0); ++ TEST_VERIFY (read_buffer[0] == '\0'); ++ /* Write should succeed. */ ++ ret = fwrite (DEFAULT_TEXT, sizeof (char), strlen (DEFAULT_TEXT), fd_file); ++ TEST_COMPARE (ferror (fd_file), 0); ++ TEST_COMPARE (ret, strlen (DEFAULT_TEXT)); ++ /* Opening non-existent file should succeed. */ ++ xfclose (fd_file); ++ fd_file = fopen ("/tmp/file-that-does-not-exist", "w+"); ++ TEST_COMPARE (ferror (fd_file), 0); ++ TEST_VERIFY (fd_file != NULL); ++ TEST_COMPARE (ftell (fd_file), 0); ++ ++ xfclose (fd_file); ++ remove ("/tmp/file-that-does-not-exist"); ++ memset (read_buffer, 0, MAX_BUFFER_SIZE); ++ ++ /* Test 5: This checks for fopen with mode "a". Open for appending ++ (writing at end of file). The file is created if it does not ++ exist. The stream is positioned at the end of the file. */ ++ printf ("Test 5: This checks for fopen with mode \"a\".\n"); ++ fd_file = fopen (temp_file, "a"); ++ TEST_COMPARE (ferror (fd_file), 0); ++ TEST_VERIFY (fd_file != NULL); ++ TEST_COMPARE (ftell (fd_file), strlen (DEFAULT_TEXT)); ++ /* Read should fail. */ ++ errno = 0; ++ ret = fread (read_buffer, sizeof (char), MAX_BUFFER_SIZE, fd_file); ++ TEST_VERIFY (ferror (fd_file) != 0); ++ TEST_COMPARE (errno, EBADF); ++ TEST_COMPARE (ret, 0); ++ clearerr (fd_file); ++ /* Write should succeed. */ ++ ret = fwrite (APPENDED_TEXT, sizeof (char), strlen (APPENDED_TEXT), fd_file); ++ TEST_COMPARE (ferror (fd_file), 0); ++ TEST_COMPARE (ret, strlen (APPENDED_TEXT)); ++ /* The file position indicator for the stream is advanced by the ++ * number of bytes successfully read or written. */ ++ TEST_COMPARE (ftell (fd_file), strlen (DEFAULT_TEXT) + ret); ++ /* Opening non-existent file should succeed. */ ++ xfclose (fd_file); ++ fd_file = fopen ("/tmp/file-that-does-not-exist", "a"); ++ TEST_COMPARE (ferror (fd_file), 0); ++ TEST_VERIFY (fd_file != NULL); ++ TEST_COMPARE (ftell (fd_file), 0); ++ ++ xfclose (fd_file); ++ remove ("/tmp/file-that-does-not-exist"); ++ memset (read_buffer, 0, MAX_BUFFER_SIZE); ++ ++ /* Test 6: This checks for fopen with mode "a+". Open for reading and ++ appending (writing at end of file). The file is created if it does ++ not exist. Output is always appended to the end of the file. The ++ initial file position for reading is at the beginning of the file, ++ but it is advanced to the end prior to each write. */ ++ printf ("Test 6: This checks for fopen with mode \"a+\".\n"); ++ errno = 0; ++ fd_file = fopen (temp_file, "a+"); ++ TEST_COMPARE (errno, 0); ++ TEST_VERIFY (fd_file != NULL); ++ TEST_COMPARE (ftell (fd_file), 0); ++ /* Read should succeed. */ ++ ret = fread (read_buffer, sizeof (char), MAX_BUFFER_SIZE, fd_file); ++ TEST_COMPARE (ferror (fd_file), 0); ++ TEST_COMPARE (ret, strlen (DEFAULT_TEXT) + strlen (APPENDED_TEXT)); ++ TEST_VERIFY (strcmp (read_buffer, DEFAULT_TEXT APPENDED_TEXT) == 0); ++ /* Write should succeed. */ ++ const char* SECOND_APPEND = "This is second append."; ++ ret = fwrite (SECOND_APPEND, sizeof (char), strlen (SECOND_APPEND), fd_file); ++ TEST_COMPARE (ferror (fd_file), 0); ++ TEST_COMPARE (ret, strlen (SECOND_APPEND)); ++ /* The file position indicator for the stream is advanced by the ++ number of bytes successfully read or written. */ ++ TEST_COMPARE (ftell (fd_file), ++ strlen (DEFAULT_TEXT) + strlen (APPENDED_TEXT) + ret); ++ /* Opening non-existent file should succeed. */ ++ xfclose (fd_file); ++ fd_file = fopen ("/tmp/file-that-does-not-exist", "a+"); ++ TEST_COMPARE (ferror (fd_file), 0); ++ TEST_VERIFY (fd_file != NULL); ++ TEST_COMPARE (ftell (fd_file), 0); ++ ++ xfclose (fd_file); ++ remove ("/tmp/file-that-does-not-exist"); ++ memset (read_buffer, 0, MAX_BUFFER_SIZE); ++ ++ /* Test 7: This checks for fopen with other valid modes set, such as ++ "rc", "we" or "am". The test calls fopen with these modes and ++ checks that no errors appear. */ ++ printf ("Test 7: This checks for fopen with other valid modes set, " ++ "such as \"rc\", \"we\" or \"am\".\n"); ++ /* These modes all operate correctly with the file already present. */ ++ static const char *valid_modes[] = ++ { "rc", "we", "am", "r+x", "wb+", "ab", 0 }; ++ const char **p = valid_modes; ++ while (*p != 0) ++ { ++ fd_file = fopen (temp_file, *p); ++ TEST_COMPARE (ferror (fd_file), 0); ++ TEST_VERIFY (fd_file != NULL); ++ xfclose (fd_file); ++ ++p; ++ } ++ ++ /* Test 8: This checks for fopen with invalid modes. The test calls ++ fopen with these modes and checks that opening existing files with ++ invalid mode fails and that opening non-existing files with invalid ++ mode doesn't create a new file. */ ++ printf ("Test 8: This checks for fopen with invalid modes.\n"); ++ static const char *invalid_modes[] = { "0", "tr", "z", "x", " ", 0 }; ++ p = invalid_modes; ++ while (*p != 0) ++ { ++ errno = 0; ++ fd_file = fopen (temp_file, *p); ++ TEST_VERIFY (fd_file == NULL); ++ TEST_COMPARE (errno, EINVAL); ++ errno = 0; ++ fd_file = fopen ("/tmp/file-that-does-not-exist", *p); ++ TEST_VERIFY (fd_file == NULL); ++ TEST_COMPARE (errno, EINVAL); ++ ++p; ++ TEST_VERIFY (access ("/tmp/file-that-does-not-exist", F_OK) == -1); ++ } ++ ++ return 0; ++} ++ ++#include +diff --git a/sysdeps/pthread/Makefile b/sysdeps/pthread/Makefile +index 8c80b7a606da4c94..ef3035cea8e1344b 100644 +--- a/sysdeps/pthread/Makefile ++++ b/sysdeps/pthread/Makefile +@@ -154,6 +154,7 @@ tests += \ + tst-exit3 \ + tst-flock1 \ + tst-flock2 \ ++ tst-fopen-threaded \ + tst-fork1 \ + tst-fork2 \ + tst-fork3 \ +diff --git a/sysdeps/pthread/tst-fopen-threaded.c b/sysdeps/pthread/tst-fopen-threaded.c +new file mode 100644 +index 0000000000000000..5c792c93e3711621 +--- /dev/null ++++ b/sysdeps/pthread/tst-fopen-threaded.c +@@ -0,0 +1,250 @@ ++/* Test for fread and fwrite with multiple threads. ++ 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 ++ . */ ++ ++/* Description of test intent. ++ The test creates NUM_THREADS threads for reading and writing to the ++ prepared file. The prepared file contains 'NUM_THREADS - 1' bytes ++ where each byte is unique number from 0 to 'NUM_THREADS - 2'. If all ++ operations are correctly multi-threaded safe then all concurent read ++ operations should succeed and read a unique 1 byte value. The last ++ thread to read should get an EOF. In concurrent write, all write ++ operations should succeed and the file should contain all unique 1 ++ byte values from 0 to 'NUM_THREADS - 1'. Both concurrent read and ++ concurrent write tests are repeated ITERS times to increase ++ the probability of detecting concurrency issues. */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++ ++#define NUM_THREADS 100 ++#define ITERS 10 ++ ++char *temp_file; ++pthread_barrier_t barrier; ++ ++struct thread_data ++{ ++ FILE *fd; ++ /* Read value or value to be written. */ ++ unsigned char value; ++ bool eof; ++}; ++ ++static void * ++threadReadRoutine (void *argv) ++{ ++ struct thread_data *my_data; ++ unsigned char read_buffer; ++ int ret = 0; ++ my_data = (struct thread_data *) argv; ++ /* Wait for all threads to be ready to read. */ ++ xpthread_barrier_wait (&barrier); ++ ++ ret = ++ fread (&read_buffer, sizeof (char), sizeof (read_buffer), my_data->fd); ++ if (feof (my_data->fd) != 0) ++ { ++ clearerr (my_data->fd); ++ my_data->eof = true; ++ } ++ else ++ { ++ TEST_COMPARE (ret, 1); ++ /* Save the read value. */ ++ my_data->value = read_buffer; ++ } ++ TEST_COMPARE (ferror (my_data->fd), 0); ++ return NULL; ++} ++ ++void * ++threadWriteRoutine (void *argv) ++{ ++ struct thread_data *my_data; ++ int ret = 0; ++ my_data = (struct thread_data *) argv; ++ /* Wait for all threads to be ready to write. */ ++ xpthread_barrier_wait (&barrier); ++ ++ ret = fwrite (&my_data->value, sizeof (unsigned char), 1, my_data->fd); ++ TEST_COMPARE (ferror (my_data->fd), 0); ++ TEST_COMPARE (feof (my_data->fd), 0); ++ TEST_COMPARE (ret, 1); ++ return NULL; ++} ++ ++void * ++threadOpenCloseRoutine (void *argv) ++{ ++ /* Wait for all threads to be ready to call fopen and fclose. */ ++ xpthread_barrier_wait (&barrier); ++ ++ FILE *fd = xfopen ("/tmp/openclosetest", "w+"); ++ xfclose (fd); ++ return NULL; ++} ++ ++static int ++do_test (void) ++{ ++ FILE *fd_file = NULL; ++ unsigned char buffer[NUM_THREADS] = "0"; ++ size_t ret = 0; ++ pthread_t threads[NUM_THREADS]; ++ struct thread_data thread_data_array[NUM_THREADS]; ++ bool present_values[NUM_THREADS] = { false }; ++ ++ /* Prepare files. */ ++ for (int i = 0; i < NUM_THREADS; i++) ++ buffer[i] = i; ++ int fd = create_temp_file ("tst-fopen.", &temp_file); ++ TEST_VERIFY_EXIT (fd != -1); ++ fd_file = fdopen (fd, "w"); ++ /* NUM_THREADS - 1: last thread will read EOF */ ++ ret = fwrite (buffer, sizeof (unsigned char), NUM_THREADS - 1, fd_file); ++ TEST_COMPARE (ret, NUM_THREADS - 1); ++ xfclose (fd_file); ++ ++ /* Test 1: Concurrent read. */ ++ for (int reps = 1; reps <= ITERS; reps++) ++ { ++ fd_file = xfopen (temp_file, "r"); ++ xpthread_barrier_init (&barrier, NULL, NUM_THREADS); ++ for (int i = 0; i < NUM_THREADS; i++) ++ { ++ thread_data_array[i].fd = fd_file; ++ /* Initialize with highest possible value so it's easier to debug if ++ anything goes wrong. */ ++ thread_data_array[i].value = 255; ++ thread_data_array[i].eof = false; ++ ++ threads[i] = ++ xpthread_create (support_small_stack_thread_attribute (), ++ threadReadRoutine, ++ (void *) &thread_data_array[i]); ++ } ++ ++ for (int i = 0; i < NUM_THREADS; i++) ++ { ++ xpthread_join (threads[i]); ++ } ++ xpthread_barrier_destroy (&barrier); ++ xfclose (fd_file); ++ ++ /* Verify read values. */ ++ int eof_cnt = 0; ++ for (int i = 0; i < NUM_THREADS; i++) ++ present_values[i] = false; ++ for (int i = 0; i < NUM_THREADS; i++) ++ { ++ if (thread_data_array[i].eof) ++ { ++ /* EOF was read. */ ++ present_values[NUM_THREADS - 1] = true; ++ eof_cnt++; ++ } ++ else ++ { ++ /* The same value shouldn't be read twice. */ ++ TEST_VERIFY (!present_values[thread_data_array[i].value]); ++ present_values[thread_data_array[i].value] = true; ++ } ++ } ++ /* EOF is read exactly once. */ ++ TEST_COMPARE (eof_cnt, 1); ++ for (int i = 0; i < NUM_THREADS; i++) ++ { ++ /* All values should be read. */ ++ TEST_VERIFY (present_values[i]); ++ } ++ } ++ ++ /* Test 2: Concurrent write. */ ++ for (int reps = 1; reps <= ITERS; reps++) ++ { ++ fd_file = xfopen (temp_file, "w"); ++ xpthread_barrier_init (&barrier, NULL, NUM_THREADS); ++ for (int i = 0; i < NUM_THREADS; i++) ++ { ++ thread_data_array[i].fd = fd_file; ++ thread_data_array[i].value = i; ++ ++ threads[i] = ++ xpthread_create (support_small_stack_thread_attribute (), ++ threadWriteRoutine, ++ (void *) &thread_data_array[i]); ++ } ++ ++ for (int i = 0; i < NUM_THREADS; i++) ++ { ++ xpthread_join (threads[i]); ++ } ++ xpthread_barrier_destroy (&barrier); ++ xfclose (fd_file); ++ ++ /* Verify written values. */ ++ for (int i = 0; i < NUM_THREADS; i++) ++ present_values[i] = false; ++ memset (buffer, 0, NUM_THREADS); ++ fd_file = xfopen (temp_file, "r"); ++ ret = fread (buffer, sizeof (unsigned char), NUM_THREADS, fd_file); ++ TEST_COMPARE (ret, NUM_THREADS); ++ for (int i = 0; i < NUM_THREADS; i++) ++ { ++ /* The same value shouldn't be written twice. */ ++ TEST_VERIFY (!present_values[buffer[i]]); ++ present_values[buffer[i]] = true; ++ } ++ for (int i = 0; i < NUM_THREADS; i++) ++ { ++ /* All values should be written. */ ++ TEST_VERIFY (present_values[i]); ++ } ++ xfclose (fd_file); ++ } ++ ++ /* Test 3: Concurrent open/close. */ ++ for (int reps = 1; reps <= ITERS; reps++) ++ { ++ xpthread_barrier_init (&barrier, NULL, NUM_THREADS); ++ for (int i = 0; i < NUM_THREADS; i++) ++ { ++ threads[i] = ++ xpthread_create (support_small_stack_thread_attribute (), ++ threadOpenCloseRoutine, NULL); ++ } ++ for (int i = 0; i < NUM_THREADS; i++) ++ { ++ xpthread_join (threads[i]); ++ } ++ xpthread_barrier_destroy (&barrier); ++ } ++ ++ return 0; ++} ++ ++#include diff --git a/glibc-RHEL-106562-2.patch b/glibc-RHEL-106562-2.patch new file mode 100644 index 0000000..1ea5b60 --- /dev/null +++ b/glibc-RHEL-106562-2.patch @@ -0,0 +1,529 @@ +commit ae18044f95271ed422ed847bd8d8c6d8e84674ce +Author: Joe Simmons-Talbott +Date: Mon May 20 14:09:35 2024 +0000 + + math: Add more details to the test driver output. + + Add start and end indicators that identify the test being run in the + verbose output. Better identify the tests for max errors in the + summary output. Count each exception checked for each test. Remove + double counting of tests for the check_ functions other than + check_float_internal. Rename print_max_error and + print_complex_max_error to check_max_error and check_complex_max_error + respectively since they have side effects. + + Co-Authored-By: Carlos O'Donell + Reviewed-By: Joseph Myers + +diff --git a/math/libm-test-driver.c b/math/libm-test-driver.c +index 3356f9b10d7f364e..dfb56e8cde13519a 100644 +--- a/math/libm-test-driver.c ++++ b/math/libm-test-driver.c +@@ -1083,9 +1083,9 @@ struct test_Ff_b1_data + = STR_CON3 (FUN, SUFF, TEST_SUFF) TEST_SUFF_STR; \ + init_max_error (this_func, EXACT, TEST_COND_any_ibm128) + #define END \ +- print_max_error (this_func) ++ check_max_error (this_func) + #define END_COMPLEX \ +- print_complex_max_error (this_func) ++ check_complex_max_error (this_func) + + /* Run tests for a given function in all rounding modes. */ + #define ALL_RM_TEST(FUNC, EXACT, ARRAY, LOOP_MACRO, END_MACRO, ...) \ +diff --git a/math/libm-test-support.c b/math/libm-test-support.c +index 0cae545f86ac1352..0796f9d4956e3818 100644 +--- a/math/libm-test-support.c ++++ b/math/libm-test-support.c +@@ -112,6 +112,7 @@ + #include + #include + #include ++#include + + /* This header defines func_ulps, func_real_ulps and func_imag_ulps + arrays. */ +@@ -125,10 +126,13 @@ static FILE *ulps_file; /* File to document difference. */ + static int output_ulps; /* Should ulps printed? */ + static char *output_dir; /* Directory where generated files will be written. */ + +-static int noErrors; /* number of errors */ +-static int noTests; /* number of tests (without testing exceptions) */ +-static int noExcTests; /* number of tests for exception flags */ +-static int noErrnoTests;/* number of tests for errno values */ ++#define TEST_INPUT 1 ++#define TEST_MAXERROR 2 ++static int noErrors; /* number of errors */ ++static int noTests; /* number of tests (without testing exceptions) */ ++static int noMaxErrorTests; /* number of max error tests */ ++static int noExcTests; /* number of tests for exception flags */ ++static int noErrnoTests; /* number of tests for errno values */ + + static unsigned int verbose; + static int output_max_error; /* Should the maximal errors printed? */ +@@ -299,9 +303,19 @@ print_screen_max_error (int ok) + + /* Update statistic counters. */ + static void +-update_stats (int ok) ++update_stats (int ok, int testType) + { +- ++noTests; ++ switch (testType) ++ { ++ case TEST_INPUT: ++ ++noTests; ++ break; ++ case TEST_MAXERROR: ++ ++noMaxErrorTests; ++ break; ++ default: ++ abort(); ++ } + if (!ok) + ++noErrors; + } +@@ -367,11 +381,30 @@ fpstack_test (const char *test_name) + #endif + } + ++static void ++print_test_start (int test_num, const char *test_name, int test_type) ++{ ++ if (print_screen (1)) ++ printf ("--- Start of%s test # %d, named \"%s\" ---\n", ++ test_type == TEST_MAXERROR ? " max error" : "", test_num, test_name); ++} + ++static void ++print_test_end (int test_num, const char *test_name, int test_type) ++{ ++ if (print_screen (1)) ++ printf ("--- End of%s test # %d, named \"%s\" ---\n", ++ test_type == TEST_MAXERROR ? " max error" : "", test_num, test_name); ++} ++ ++/* This is a builtin test of overall max error. */ + void +-print_max_error (const char *func_name) ++check_max_error (const char *func_name) + { + int ok = 0; ++ int thisTest = noMaxErrorTests; ++ ++ print_test_start (thisTest, func_name, TEST_MAXERROR); + + if (max_error == 0.0 || (max_error <= prev_max_error && !ignore_max_ulp)) + { +@@ -392,14 +425,19 @@ print_max_error (const char *func_name) + printf (" accepted: %s ulp\n", pmestr); + } + +- update_stats (ok); +-} ++ update_stats (ok, TEST_MAXERROR); + ++ print_test_end (thisTest, func_name, TEST_MAXERROR); ++} + ++/* This is a builtin test of overall max error. */ + void +-print_complex_max_error (const char *func_name) ++check_complex_max_error (const char *func_name) + { + int real_ok = 0, imag_ok = 0, ok; ++ int thisTest = noMaxErrorTests; ++ ++ print_test_start (thisTest, func_name, TEST_MAXERROR); + + if (real_max_error == 0 + || (real_max_error <= prev_real_max_error && !ignore_max_ulp)) +@@ -436,7 +474,8 @@ print_complex_max_error (const char *func_name) + printf (" accepted: %s ulp\n", pimestr); + } + +- update_stats (ok); ++ update_stats (ok, TEST_MAXERROR); ++ print_test_end (thisTest, func_name, TEST_MAXERROR); + } + + +@@ -477,12 +516,13 @@ test_single_exception (const char *test_name, + else + { + if (print_screen (1)) +- printf ("%s: Exception \"%s\" not set\n", test_name, ++ printf ("Pass: %s: Exception \"%s\" not set\n", test_name, + flag_name); + } + } + if (!ok) + ++noErrors; ++ ++noExcTests; + } + #endif + +@@ -494,23 +534,32 @@ test_exceptions (const char *test_name, int exception) + { + if (flag_test_exceptions && EXCEPTION_TESTS (FLOAT)) + { +- ++noExcTests; ++ int ran = 0; + #ifdef FE_DIVBYZERO + if ((exception & DIVIDE_BY_ZERO_EXCEPTION_OK) == 0) +- test_single_exception (test_name, exception, +- DIVIDE_BY_ZERO_EXCEPTION, FE_DIVBYZERO, +- "Divide by zero"); ++ { ++ test_single_exception (test_name, exception, ++ DIVIDE_BY_ZERO_EXCEPTION, FE_DIVBYZERO, ++ "Divide by zero"); ++ ran = 1; ++ } + #endif + #ifdef FE_INVALID + if ((exception & INVALID_EXCEPTION_OK) == 0) +- test_single_exception (test_name, exception, +- INVALID_EXCEPTION, FE_INVALID, +- "Invalid operation"); ++ { ++ test_single_exception (test_name, exception, ++ INVALID_EXCEPTION, FE_INVALID, ++ "Invalid operation"); ++ ran = 1; ++ } + #endif + #ifdef FE_OVERFLOW + if ((exception & OVERFLOW_EXCEPTION_OK) == 0) +- test_single_exception (test_name, exception, OVERFLOW_EXCEPTION, +- FE_OVERFLOW, "Overflow"); ++ { ++ test_single_exception (test_name, exception, OVERFLOW_EXCEPTION, ++ FE_OVERFLOW, "Overflow"); ++ ran = 1; ++ } + #endif + /* Spurious "underflow" and "inexact" exceptions are always + allowed for IBM long double, in line with the underlying +@@ -519,17 +568,30 @@ test_exceptions (const char *test_name, int exception) + if ((exception & UNDERFLOW_EXCEPTION_OK) == 0 + && !(test_ibm128 + && (exception & UNDERFLOW_EXCEPTION) == 0)) +- test_single_exception (test_name, exception, UNDERFLOW_EXCEPTION, +- FE_UNDERFLOW, "Underflow"); ++ { ++ test_single_exception (test_name, exception, UNDERFLOW_EXCEPTION, ++ FE_UNDERFLOW, "Underflow"); ++ ran = 1; ++ } ++ + #endif + #ifdef FE_INEXACT + if ((exception & (INEXACT_EXCEPTION | NO_INEXACT_EXCEPTION)) != 0 + && !(test_ibm128 + && (exception & NO_INEXACT_EXCEPTION) != 0)) +- test_single_exception (test_name, exception, INEXACT_EXCEPTION, +- FE_INEXACT, "Inexact"); ++ { ++ test_single_exception (test_name, exception, INEXACT_EXCEPTION, ++ FE_INEXACT, "Inexact"); ++ ran = 1; ++ } + #endif ++ assert (ran == 1); + } ++ else ++ { ++ if (print_screen (1)) ++ printf ("Info: %s: No exceptions tested\n", test_name); ++ } + feclearexcept (FE_ALL_EXCEPT); + } + +@@ -552,6 +614,7 @@ test_single_errno (const char *test_name, int errno_value, + printf ("Failure: %s: errno set to %d, expected %d (%s)\n", + test_name, errno_value, expected_value, expected_name); + } ++ ++noErrnoTests; + } + + /* Test whether errno (value ERRNO_VALUE) has been for TEST_NAME set +@@ -561,13 +624,39 @@ test_errno (const char *test_name, int errno_value, int exceptions) + { + if (flag_test_errno) + { +- ++noErrnoTests; ++ int ran = 0; ++ ++ if ((exceptions & (ERRNO_UNCHANGED|ERRNO_EDOM|ERRNO_ERANGE)) == 0) ++ { ++ if (print_screen (1)) ++ printf ("Info: %s: The value of errno was not tested\n", ++ test_name); ++ return; ++ } ++ ++ + if (exceptions & ERRNO_UNCHANGED) +- test_single_errno (test_name, errno_value, 0, "unchanged"); ++ { ++ test_single_errno (test_name, errno_value, 0, "unchanged"); ++ ran = 1; ++ } + if (exceptions & ERRNO_EDOM) +- test_single_errno (test_name, errno_value, EDOM, "EDOM"); ++ { ++ test_single_errno (test_name, errno_value, EDOM, "EDOM"); ++ ran = 1; ++ } + if (exceptions & ERRNO_ERANGE) +- test_single_errno (test_name, errno_value, ERANGE, "ERANGE"); ++ { ++ test_single_errno (test_name, errno_value, ERANGE, "ERANGE"); ++ ran = 1; ++ } ++ ++ assert (ran == 1); ++ } ++ else ++ { ++ if (print_screen (1)) ++ printf ("Info: %s: No errno tests\n", test_name); + } + } + +@@ -619,6 +708,9 @@ check_float_internal (const char *test_name, FLOAT computed, FLOAT expected, + FLOAT diff = 0; + FLOAT ulps = 0; + int errno_value = errno; ++ int thisTest = noTests; ++ ++ print_test_start (thisTest, test_name, TEST_INPUT); + + test_exceptions (test_name, exceptions); + test_errno (test_name, errno_value, exceptions); +@@ -716,12 +808,13 @@ check_float_internal (const char *test_name, FLOAT computed, FLOAT expected, + printf (" max.ulp : %s\n", mustrn); + } + } +- update_stats (ok); ++ update_stats (ok, TEST_INPUT); + + out: + fpstack_test (test_name); + feclearexcept (FE_ALL_EXCEPT); + errno = 0; ++ print_test_end (thisTest, test_name, TEST_INPUT); + } + + +@@ -776,12 +869,14 @@ check_int (const char *test_name, int computed, int expected, + { + int ok = 0; + int errno_value = errno; ++ int thisTest = noTests; ++ ++ print_test_start (thisTest, test_name, TEST_INPUT); + + test_exceptions (test_name, exceptions); + test_errno (test_name, errno_value, exceptions); + if (exceptions & IGNORE_RESULT) + goto out; +- noTests++; + if (computed == expected) + ok = 1; + +@@ -795,11 +890,12 @@ check_int (const char *test_name, int computed, int expected, + printf (" should be: %d\n", expected); + } + +- update_stats (ok); ++ update_stats (ok, TEST_INPUT); + out: + fpstack_test (test_name); + feclearexcept (FE_ALL_EXCEPT); + errno = 0; ++ print_test_end (thisTest, test_name, TEST_INPUT); + } + + +@@ -810,12 +906,14 @@ check_long (const char *test_name, long int computed, long int expected, + { + int ok = 0; + int errno_value = errno; ++ int thisTest = noTests; ++ ++ print_test_start (thisTest, test_name, TEST_INPUT); + + test_exceptions (test_name, exceptions); + test_errno (test_name, errno_value, exceptions); + if (exceptions & IGNORE_RESULT) + goto out; +- noTests++; + if (computed == expected) + ok = 1; + +@@ -829,11 +927,12 @@ check_long (const char *test_name, long int computed, long int expected, + printf (" should be: %ld\n", expected); + } + +- update_stats (ok); ++ update_stats (ok, TEST_INPUT); + out: + fpstack_test (test_name); + feclearexcept (FE_ALL_EXCEPT); + errno = 0; ++ print_test_end (thisTest, test_name, TEST_INPUT); + } + + +@@ -844,12 +943,14 @@ check_bool (const char *test_name, int computed, int expected, + { + int ok = 0; + int errno_value = errno; ++ int thisTest = noTests; ++ ++ print_test_start (thisTest, test_name, TEST_INPUT); + + test_exceptions (test_name, exceptions); + test_errno (test_name, errno_value, exceptions); + if (exceptions & IGNORE_RESULT) + goto out; +- noTests++; + if ((computed == 0) == (expected == 0)) + ok = 1; + +@@ -863,11 +964,12 @@ check_bool (const char *test_name, int computed, int expected, + printf (" should be: %d\n", expected); + } + +- update_stats (ok); ++ update_stats (ok, TEST_INPUT); + out: + fpstack_test (test_name); + feclearexcept (FE_ALL_EXCEPT); + errno = 0; ++ print_test_end (thisTest, test_name, TEST_INPUT); + } + + +@@ -879,12 +981,14 @@ check_longlong (const char *test_name, long long int computed, + { + int ok = 0; + int errno_value = errno; ++ int thisTest = noTests; ++ ++ print_test_start (thisTest, test_name, TEST_INPUT); + + test_exceptions (test_name, exceptions); + test_errno (test_name, errno_value, exceptions); + if (exceptions & IGNORE_RESULT) + goto out; +- noTests++; + if (computed == expected) + ok = 1; + +@@ -898,11 +1002,12 @@ check_longlong (const char *test_name, long long int computed, + printf (" should be: %lld\n", expected); + } + +- update_stats (ok); ++ update_stats (ok, TEST_INPUT); + out: + fpstack_test (test_name); + feclearexcept (FE_ALL_EXCEPT); + errno = 0; ++ print_test_end (thisTest, test_name, TEST_INPUT); + } + + +@@ -913,12 +1018,14 @@ check_intmax_t (const char *test_name, intmax_t computed, + { + int ok = 0; + int errno_value = errno; ++ int thisTest = noTests; ++ ++ print_test_start (thisTest, test_name, TEST_INPUT); + + test_exceptions (test_name, exceptions); + test_errno (test_name, errno_value, exceptions); + if (exceptions & IGNORE_RESULT) + goto out; +- noTests++; + if (computed == expected) + ok = 1; + +@@ -932,11 +1039,12 @@ check_intmax_t (const char *test_name, intmax_t computed, + printf (" should be: %jd\n", expected); + } + +- update_stats (ok); ++ update_stats (ok, TEST_INPUT); + out: + fpstack_test (test_name); + feclearexcept (FE_ALL_EXCEPT); + errno = 0; ++ print_test_end (thisTest, test_name, TEST_INPUT); + } + + +@@ -947,12 +1055,14 @@ check_uintmax_t (const char *test_name, uintmax_t computed, + { + int ok = 0; + int errno_value = errno; ++ int thisTest = noTests; ++ ++ print_test_start (thisTest, test_name, TEST_INPUT); + + test_exceptions (test_name, exceptions); + test_errno (test_name, errno_value, exceptions); + if (exceptions & IGNORE_RESULT) + goto out; +- noTests++; + if (computed == expected) + ok = 1; + +@@ -966,11 +1076,12 @@ check_uintmax_t (const char *test_name, uintmax_t computed, + printf (" should be: %ju\n", expected); + } + +- update_stats (ok); ++ update_stats (ok, TEST_INPUT); + out: + fpstack_test (test_name); + feclearexcept (FE_ALL_EXCEPT); + errno = 0; ++ print_test_end (thisTest, test_name, TEST_INPUT); + } + + /* Return whether a test with flags EXCEPTIONS should be run. */ +@@ -1211,9 +1322,11 @@ libm_test_finish (void) + fclose (ulps_file); + + printf ("\nTest suite completed:\n"); +- printf (" %d test cases plus %d tests for exception flags and\n" +- " %d tests for errno executed.\n", +- noTests, noExcTests, noErrnoTests); ++ printf (" %d max error test cases,\n", noMaxErrorTests); ++ printf (" %d input tests,\n", noTests); ++ printf (" - with %d tests for exception flags,\n", noExcTests); ++ printf (" - with %d tests for errno executed.\n", noErrnoTests); ++ + if (noErrors) + { + printf (" %d errors occurred.\n", noErrors); +diff --git a/math/libm-test-support.h b/math/libm-test-support.h +index 8baf7e1817157b2a..efb9523e9e91f64f 100644 +--- a/math/libm-test-support.h ++++ b/math/libm-test-support.h +@@ -170,8 +170,8 @@ extern const char doc[]; + + int enable_test (int); + void init_max_error (const char *, int, int); +-void print_max_error (const char *); +-void print_complex_max_error (const char *); ++void check_max_error (const char *); ++void check_complex_max_error (const char *); + void check_float (const char *, FLOAT, FLOAT, int); + void check_complex (const char *, CFLOAT, CFLOAT, int); + void check_int (const char *, int, int, int); diff --git a/glibc-RHEL-106562-20.patch b/glibc-RHEL-106562-20.patch new file mode 100644 index 0000000..e4ca16c --- /dev/null +++ b/glibc-RHEL-106562-20.patch @@ -0,0 +1,151 @@ +commit a9017caff3b77032d04e2e439f7c04a63241e63e +Author: Sergey Kolosov +Date: Tue Jan 28 23:56:26 2025 +0100 + + nptl: extend test coverage for sched_yield + + We add sched_yield() API testing to the existing thread affinity + test case because it allows us to test sched_yield() operation + in the following scenarios: + + * On a main thread. + * On multiple threads simultaneously. + * On every CPU the system reports simultaneously. + + The ensures we exercise sched_yield() in as many scenarios as + we would exercise calls to the affinity functions. + + Additionally, the test is improved by adding a semaphore to coordinate + all the threads running, so that an early starter thread won't consume + cpu resources that could be used to start the other threads. + + Co-authored-by: DJ Delorie + Reviewed-by: Carlos O'Donell + +diff --git a/sysdeps/unix/sysv/linux/tst-skeleton-affinity.c b/sysdeps/unix/sysv/linux/tst-skeleton-affinity.c +index 2f921ed397a1a4d9..7276fd8fef06d620 100644 +--- a/sysdeps/unix/sysv/linux/tst-skeleton-affinity.c ++++ b/sysdeps/unix/sysv/linux/tst-skeleton-affinity.c +@@ -38,6 +38,7 @@ + #include + #include + #include ++#include + + /* CPU set configuration determined. Can be used from early_test. */ + struct conf +@@ -253,12 +254,12 @@ do_test (void) + if (getaffinity (sizeof (set), &set) < 0 && errno == ENOSYS) + { + puts ("warning: getaffinity not supported, test cannot run"); +- return 0; ++ return EXIT_UNSUPPORTED; + } + if (sched_getcpu () < 0 && errno == ENOSYS) + { + puts ("warning: sched_getcpu not supported, test cannot run"); +- return 0; ++ return EXIT_UNSUPPORTED; + } + } + +diff --git a/sysdeps/unix/sysv/linux/tst-skeleton-thread-affinity.c b/sysdeps/unix/sysv/linux/tst-skeleton-thread-affinity.c +index 5a1e84431a30132d..507c5c94ba89e47b 100644 +--- a/sysdeps/unix/sysv/linux/tst-skeleton-thread-affinity.c ++++ b/sysdeps/unix/sysv/linux/tst-skeleton-thread-affinity.c +@@ -45,10 +45,14 @@ static int still_running; + /* 0 if no scheduling failures, 1 if failures are encountered. */ + static int failed; + ++/* Used to synchronize the threads. */ ++static pthread_barrier_t barrier; ++ + static void * + thread_burn_one_cpu (void *closure) + { + int cpu = (uintptr_t) closure; ++ xpthread_barrier_wait (&barrier); + while (__atomic_load_n (&still_running, __ATOMIC_RELAXED) == 0) + { + int current = sched_getcpu (); +@@ -61,6 +65,11 @@ thread_burn_one_cpu (void *closure) + __atomic_store_n (&still_running, 1, __ATOMIC_RELAXED); + } + } ++ if (sched_yield () != 0) ++ { ++ printf ("error: sched_yield() failed for cpu %d\n", cpu); ++ __atomic_store_n (&failed, 1, __ATOMIC_RELAXED); ++ } + return NULL; + } + +@@ -78,6 +87,7 @@ thread_burn_any_cpu (void *closure) + { + struct burn_thread *param = closure; + ++ xpthread_barrier_wait (&barrier); + /* Schedule this thread around a bit to see if it lands on another + CPU. Run this for 2 seconds, once with sched_yield, once + without. */ +@@ -99,7 +109,11 @@ thread_burn_any_cpu (void *closure) + CPU_SET_S (cpu, CPU_ALLOC_SIZE (param->conf->set_size), + param->seen_set); + if (pass == 1) +- sched_yield (); ++ if (sched_yield () != 0) ++ { ++ printf ("error: sched_yield() failed for cpu %d\n", cpu); ++ __atomic_store_n (&failed, 1, __ATOMIC_RELAXED); ++ } + } + } + return NULL; +@@ -156,6 +170,7 @@ early_test (struct conf *conf) + = calloc (conf->last_cpu + 1, sizeof (*other_threads)); + cpu_set_t *initial_set = CPU_ALLOC (conf->set_size); + cpu_set_t *scratch_set = CPU_ALLOC (conf->set_size); ++ int num_available_cpus = 0; + + if (pinned_threads == NULL || other_threads == NULL + || initial_set == NULL || scratch_set == NULL) +@@ -172,6 +187,7 @@ early_test (struct conf *conf) + { + if (!CPU_ISSET_S (cpu, CPU_ALLOC_SIZE (conf->set_size), initial_set)) + continue; ++ num_available_cpus ++; + other_threads[cpu].conf = conf; + other_threads[cpu].initial_set = initial_set; + other_threads[cpu].thread = cpu; +@@ -194,6 +210,15 @@ early_test (struct conf *conf) + } + support_set_small_thread_stack_size (&attr); + ++ /* This count assumes that all the threads below are created ++ successfully, and call pthread_barrier_wait(). If any threads ++ fail to be created, this function will return FALSE (failure) and ++ the waiting threads will eventually time out the whole test. ++ This is acceptable because we're not testing thread creation and ++ assume all threads will be created, and failure here implies a ++ failure outside the test's scope. */ ++ xpthread_barrier_init (&barrier, NULL, num_available_cpus * 2 + 1); ++ + /* Spawn a thread pinned to each available CPU. */ + for (int cpu = 0; cpu <= conf->last_cpu; ++cpu) + { +@@ -245,6 +270,15 @@ early_test (struct conf *conf) + } + } + ++ /* Test that sched_yield() works correctly in the main thread. This ++ also gives the kernel an opportunity to run the other threads, ++ randomizing thread startup a bit. */ ++ if (sched_yield () != 0) ++ { ++ printf ("error: sched_yield() failed for main thread\n"); ++ __atomic_store_n (&failed, 1, __ATOMIC_RELAXED); ++ } ++ + /* Main thread. */ + struct burn_thread main_thread; + main_thread.conf = conf; diff --git a/glibc-RHEL-106562-21.patch b/glibc-RHEL-106562-21.patch new file mode 100644 index 0000000..8ae0eae --- /dev/null +++ b/glibc-RHEL-106562-21.patch @@ -0,0 +1,62 @@ +commit 10af00f7a135c85796a9c4c75228358b8898da5c +Author: Siddhesh Poyarekar +Date: Fri Mar 14 10:18:21 2025 -0400 + + tst-fopen-threaded: Only check EOF for failing read + + The fread race checker looks for EOF in every thread, which is incorrect + since threads calling fread successfully could lag behind and read the + EOF condition, resulting in multiple threads thinking that they + encountered an EOF. + + Only look for EOF condition if fread fails to read a char. Also drop + the clearerr() since it could mask the failure of another reader, thus + hiding a test failure. + + Finally, also check for error in the stream for completeness. + + Signed-off-by: Siddhesh Poyarekar + Reviewed-by: Florian Weimer + +diff --git a/sysdeps/pthread/tst-fopen-threaded.c b/sysdeps/pthread/tst-fopen-threaded.c +index 5c792c93e3711621..ade58ad19eb209d1 100644 +--- a/sysdeps/pthread/tst-fopen-threaded.c ++++ b/sysdeps/pthread/tst-fopen-threaded.c +@@ -64,19 +64,27 @@ threadReadRoutine (void *argv) + /* Wait for all threads to be ready to read. */ + xpthread_barrier_wait (&barrier); + +- ret = +- fread (&read_buffer, sizeof (char), sizeof (read_buffer), my_data->fd); +- if (feof (my_data->fd) != 0) ++ ret = fread (&read_buffer, 1, sizeof (read_buffer), my_data->fd); ++ /* If no data is returned (we read only 1 byte, so there's no short read ++ situation here), look for EOF flag and record it in MY_DATA. The EOF flag ++ is not cleared because that could result in a test failure being masked ++ when two threads fail to read and one of them clears error/EOF flags ++ before the second one has the chance to observe it. ++ ++ Successful readers could still see the EOF if they fall behind the failing ++ read when calling feof(), which could result in a false test failure. To ++ avoid this race, we only make the failing reader check for EOF or ++ error. */ ++ if (ret == 0) + { +- clearerr (my_data->fd); +- my_data->eof = true; ++ if (feof (my_data->fd) != 0) ++ my_data->eof = true; ++ else ++ FAIL_EXIT1 ("fread failed (ferror: %d): %m", ferror (my_data->fd)); + } + else +- { +- TEST_COMPARE (ret, 1); +- /* Save the read value. */ +- my_data->value = read_buffer; +- } ++ /* Save the read value. */ ++ my_data->value = read_buffer; + TEST_COMPARE (ferror (my_data->fd), 0); + return NULL; + } diff --git a/glibc-RHEL-106562-22.patch b/glibc-RHEL-106562-22.patch new file mode 100644 index 0000000..c5058ab --- /dev/null +++ b/glibc-RHEL-106562-22.patch @@ -0,0 +1,121 @@ +commit 81e74c8676479811601b5894d72bb3d7e05f68dd +Author: DJ Delorie +Date: Fri Mar 14 16:08:12 2025 -0400 + + add ptmx support to test-container + +diff --git a/support/Makefile b/support/Makefile +index 6e3c55394fa212b6..23ce2ccec89743bb 100644 +--- a/support/Makefile ++++ b/support/Makefile +@@ -313,6 +313,7 @@ tests = \ + README-testing \ + tst-support-namespace \ + tst-support-open-dev-null-range \ ++ tst-support-openpty \ + tst-support-process_state \ + tst-support_blob_repeat \ + tst-support_capture_subprocess \ +@@ -331,6 +332,10 @@ tests = \ + tst-xsigstack \ + # tests + ++tests-container = \ ++ tst-support-openpty-c \ ++ # tests-container ++ + ifeq ($(run-built-tests),yes) + tests-special = \ + $(objpfx)tst-support_record_failure-2.out +diff --git a/support/test-container.c b/support/test-container.c +index adf2b30215273936..1696d676fd101d42 100644 +--- a/support/test-container.c ++++ b/support/test-container.c +@@ -1148,6 +1148,9 @@ main (int argc, char **argv) + devmount (new_root_path, "null"); + devmount (new_root_path, "zero"); + devmount (new_root_path, "urandom"); ++#ifdef __linux__ ++ devmount (new_root_path, "ptmx"); ++#endif + + /* We're done with the "old" root, switch to the new one. */ + if (chroot (new_root_path) < 0) +@@ -1214,6 +1217,14 @@ main (int argc, char **argv) + + maybe_xmkdir ("/tmp", 0755); + ++#ifdef __linux__ ++ maybe_xmkdir ("/dev/pts", 0777); ++ if (mount ("/dev/pts", "/dev/pts", "devpts", 0, "newinstance,ptmxmode=0666,mode=0666") < 0) ++ FAIL_EXIT1 ("can't mount /dev/pts: %m\n"); ++ if (mount ("/dev/pts/ptmx", "/dev/ptmx", "", MS_BIND | MS_REC, NULL) < 0) ++ FAIL_EXIT1 ("can't mount /dev/ptmx\n"); ++#endif ++ + if (require_pidns) + { + /* Now that we're pid 1 (effectively "root") we can mount /proc */ +diff --git a/support/tst-support-openpty-c.c b/support/tst-support-openpty-c.c +new file mode 100644 +index 0000000000000000..0a6a428fc3cd3400 +--- /dev/null ++++ b/support/tst-support-openpty-c.c +@@ -0,0 +1,2 @@ ++/* Same test, but in a test-container. */ ++#include "tst-support-openpty.c" +diff --git a/support/tst-support-openpty.c b/support/tst-support-openpty.c +new file mode 100644 +index 0000000000000000..1222d7018f9b224f +--- /dev/null ++++ b/support/tst-support-openpty.c +@@ -0,0 +1,49 @@ ++/* Basic test for support_openpty support in test-container. ++ 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 ++ ++/* Note: the purpose of this test isn't to test if ptys function ++ correctly, but only to verify that test-container's support for ++ them is correct. The many checks in support_openpty.c are ++ sufficient for this. */ ++ ++int ++do_test (void) ++{ ++ int outer, inner; ++ char *name; ++ struct termios term; ++ struct winsize win; ++ ++ cfmakeraw (&term); ++ win.ws_row = 24; ++ win.ws_col = 80; ++ ++ support_openpty (&outer, &inner, &name, &term, &win); ++ ++ return 0; ++} ++ ++#include diff --git a/glibc-RHEL-106562-23.patch b/glibc-RHEL-106562-23.patch new file mode 100644 index 0000000..17be8ef --- /dev/null +++ b/glibc-RHEL-106562-23.patch @@ -0,0 +1,1084 @@ +commit 95b780c1d0549678c0a244c6e2112ec97edf0839 +Author: DJ Delorie +Date: Fri Mar 14 16:08:35 2025 -0400 + + stdio: Add more setvbuf tests + +diff --git a/stdio-common/Makefile b/stdio-common/Makefile +index f44562df75cb98bc..74512f20d39f8fec 100644 +--- a/stdio-common/Makefile ++++ b/stdio-common/Makefile +@@ -276,7 +276,9 @@ tests := \ + # tests + + tests-container += \ +- tst-popen3 ++ tst-popen3 \ ++ tst-setvbuf2 \ ++ tst-setvbuf2-ind + # tests-container + + generated += \ +@@ -288,6 +290,8 @@ generated += \ + + tests-internal = \ + tst-grouping_iterator \ ++ tst-setvbuf2 \ ++ tst-setvbuf2-ind \ + # tests-internal + + test-srcs = \ +@@ -524,5 +528,9 @@ $(objpfx)tst-setvbuf1-cmp.out: tst-setvbuf1.expect $(objpfx)tst-setvbuf1.out + cmp $^ > $@; \ + $(evaluate-test) + ++CFLAGS-tst-setvbuf2.c += -DIND_PROC=\"$(objpfx)tst-setvbuf2-ind\" ++$(objpfx)tst-setvbuf2-ind : $(objpfx)tst-setvbuf2-ind.o ++$(objpfx)tst-setvbuf2.out: $(objpfx)tst-setvbuf2-ind ++ + $(objpfx)tst-printf-round: $(libm) + $(objpfx)tst-scanf-round: $(libm) +diff --git a/stdio-common/tst-setvbuf2-ind.c b/stdio-common/tst-setvbuf2-ind.c +new file mode 100644 +index 0000000000000000..fda2942c241961f6 +--- /dev/null ++++ b/stdio-common/tst-setvbuf2-ind.c +@@ -0,0 +1,2 @@ ++#define INDEPENDENT_PART 1 ++#include "tst-setvbuf2.c" +diff --git a/stdio-common/tst-setvbuf2.c b/stdio-common/tst-setvbuf2.c +new file mode 100644 +index 0000000000000000..6cc83355f391afab +--- /dev/null ++++ b/stdio-common/tst-setvbuf2.c +@@ -0,0 +1,1030 @@ ++/* Test setvbuf under various conditions. ++ 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 ++ . */ ++ ++/* This file is used twice, once as the test itself (where do_test ++ is defined) and once as a subprocess we spawn to test stdin et all ++ (where main is defined). INDEPENDENT_PART is defined for the ++ latter. ++ ++ Note also that the purpose of this test is to test setvbuf, not the ++ underlying buffering code. */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++/* Dear future developer: If you are reading this, you are likely ++ trying to change or understand this test. In that case, these ++ debug/dump macros will be helpful. */ ++#if 0 ++# define debug printf ("\033[3%dm%s:%d\033[0m\n", \ ++ (__LINE__ % 6) + 1, __FUNCTION__, __LINE__); ++ ++static void ++dumpfp (FILE *fp) ++{ ++ char f[10], *p=f; ++ ++ if (fp->_flags & _IO_UNBUFFERED) ++ *p++ = 'N'; ++ if (fp->_flags & _IO_LINE_BUF) ++ *p++ = 'L'; ++ if (p == f) ++ *p++ = 'B'; ++ *p = 0; ++ ++ printf ("FILE %p flags %s" ++ " read %p \033[%dm%+ld \033[%dm%+ld\033[0m" ++ " write %p \033[%dm%+ld \033[%dm%+ld\033[0m %ld" ++ " buf %p \033[%dm%+ld\033[0m sz %ld pend %ld\n", ++ fp, f, ++ ++ fp->_IO_read_base, ++ fp->_IO_read_ptr == fp->_IO_read_base ? 33 : 32, ++ fp->_IO_read_ptr - fp->_IO_read_base, ++ fp->_IO_read_end == fp->_IO_read_base ? 33 : 36, ++ fp->_IO_read_end - fp->_IO_read_base, ++ ++ fp->_IO_write_base, ++ fp->_IO_write_ptr == fp->_IO_write_base ? 33 : 32, ++ fp->_IO_write_ptr - fp->_IO_write_base, ++ fp->_IO_write_end == fp->_IO_write_base ? 33 : 36, ++ fp->_IO_write_end - fp->_IO_write_base, ++ fp->_IO_write_end - fp->_IO_write_base, ++ ++ fp->_IO_buf_base, ++ fp->_IO_buf_end == fp->_IO_buf_base ? 33 : 35, ++ fp->_IO_buf_end - fp->_IO_buf_base, ++ __fbufsize (fp), __fpending (fp) ++ ); ++} ++#else ++# define debug ++# define dumpfp(FP) ++#endif ++ ++#ifndef INDEPENDENT_PART ++/* st_blksize value for that file, or BUFSIZ if out of range. */ ++static int blksize = BUFSIZ; ++#endif ++ ++/* Our test buffer. */ ++#define TEST_BUFSIZE 42 ++static int bufsize = TEST_BUFSIZE < BUFSIZ ? TEST_BUFSIZE : BUFSIZ; ++static char *buffer; ++ ++/* Test data, both written to that file and used as an in-memory ++ stream. */ ++char test_data[2 * BUFSIZ]; ++ ++#define TEST_STRING "abcdef\n" ++ ++enum test_source_case ++ { ++ test_source_file, ++ test_source_pipe, ++ test_source_fifo, ++ test_source_pseudo_terminal, ++ test_source_dev_null, ++ test_source_count, ++ }; ++ ++static const char *const test_source_name[test_source_count] = ++ { ++ "regular file", ++ "pipe", ++ "fifo", ++ "pseudo_terminal", ++ "dev_null" ++ }; ++ ++enum test_stream_case ++ { ++ test_stream_stdin, ++ test_stream_stdout, ++ test_stream_stderr, ++ test_stream_fopen_r, ++ test_stream_fdopen_r, ++ test_stream_fopen_w, ++ test_stream_fdopen_w, ++ test_stream_count ++ }; ++ ++static bool test_stream_reads[test_stream_count] = ++ { ++ true, ++ false, ++ false, ++ true, ++ true, ++ false, ++ false ++ }; ++ ++static const char *const test_stream_name[test_stream_count] = ++ { ++ "stdin", ++ "stdout", ++ "stderr", ++ "fopen (read)", ++ "fdopen (read)", ++ "fopen (write)", ++ "fdopen (write)" ++ }; ++ ++enum test_config_case ++ { ++ test_config_none, ++ test_config_unbuffered, ++ test_config_line, ++ test_config_fully, ++ test_config_count ++ }; ++ ++static const char *const test_config_name[test_config_count] = ++ { ++ "no change", ++ "unbuffered", ++ "line buffered", ++ "fully buffered" ++ }; ++ ++FILE *test_stream; ++ ++char *test_file_name = NULL; ++int pty_fd; ++char *test_pipe_name = NULL; ++int test_pipe[2]; ++ ++/* This is either -1 or represents a pre-opened file descriptor for ++ the test as returned by prepare_test_file. */ ++int test_fd; ++ ++/*------------------------------------------------------------*/ ++ ++/* Note that throughout this test we reopen, remove, and change ++ to/from a fifo, the test file. This would normally cause a race ++ condition, except that we're in a test container. No other process ++ can run in the test container simultaneously. */ ++ ++void ++prepare_test_data (void) ++{ ++ buffer = (char *) xmalloc (bufsize); ++ ++#ifndef INDEPENDENT_PART ++ /* Both file and pipe need this. */ ++ if (test_file_name == NULL) ++ { ++ debug; ++ int fd = create_temp_file ("tst-setvbuf2", &test_file_name); ++ TEST_VERIFY_EXIT (fd != -1); ++ struct stat64 st; ++ xfstat64 (fd, &st); ++ if (st.st_blksize > 0 && st.st_blksize < BUFSIZ) ++ blksize = st.st_blksize; ++ xclose (fd); ++ } ++#endif ++ ++ for (size_t i = 0; i < 2 * BUFSIZ; i++) ++ { ++ unsigned char c = TEST_STRING[i % strlen (TEST_STRING)]; ++ test_data[i] = c; ++ } ++} ++ ++#ifndef INDEPENDENT_PART ++ ++/* These functions provide a source/sink for the "other" side of any ++ pipe-style descriptor we're using for test. */ ++ ++static pthread_t writer_thread_tid = 0; ++static pthread_t reader_thread_tid = 0; ++ ++typedef struct { ++ int fd; ++ const char *fname; ++} ThreadData; ++/* It's OK if this is static, we only run one at a time. */ ++ThreadData thread_data; ++ ++static void * ++writer_thread_proc (void *closure) ++{ ++ ThreadData *td = (ThreadData *) closure; ++ int fd; ++ int i; ++ ssize_t wn; ++ debug; ++ ++ if (td->fname) ++ td->fd = xopen (td->fname, O_WRONLY, 0777); ++ fd = td->fd; ++ ++ while (1) ++ { ++ i = 0; ++ while (i < BUFSIZ) ++ { ++ wn = write (fd, test_data + i, BUFSIZ - i); ++ if (wn <= 0) ++ break; ++ i += wn; ++ } ++ } ++ return NULL; ++} ++ ++static void * ++reader_thread_proc (void *closure) ++{ ++ ThreadData *td = (ThreadData *) closure; ++ int fd; ++ ssize_t rn; ++ int n = 0; ++ debug; ++ ++ if (td->fname) ++ td->fd = xopen (td->fname, O_RDONLY, 0777); ++ fd = td->fd; ++ ++ while (1) ++ { ++ char buf[BUFSIZ]; ++ rn = read (fd, buf, BUFSIZ); ++ if (rn <= 0) ++ break; ++ TEST_COMPARE_BLOB (buf, rn, test_data+n, rn); ++ n += rn; ++ } ++ return NULL; ++} ++ ++static void ++start_writer_thread (int fd) ++{ ++ debug; ++ thread_data.fd = fd; ++ thread_data.fname = NULL; ++ writer_thread_tid = xpthread_create (NULL, writer_thread_proc, ++ (void *)&thread_data); ++} ++ ++static void ++start_writer_thread_n (const char *fname) ++{ ++ debug; ++ thread_data.fd = 0; ++ thread_data.fname = fname; ++ writer_thread_tid = xpthread_create (NULL, writer_thread_proc, ++ (void *)&thread_data); ++} ++ ++static void ++end_writer_thread (void) ++{ ++ debug; ++ if (writer_thread_tid) ++ { ++ pthread_cancel (writer_thread_tid); ++ xpthread_join (writer_thread_tid); ++ xclose (thread_data.fd); ++ writer_thread_tid = 0; ++ } ++} ++ ++static void ++start_reader_thread (int fd) ++{ ++ debug; ++ thread_data.fd = fd; ++ thread_data.fname = NULL; ++ reader_thread_tid = xpthread_create (NULL, reader_thread_proc, ++ (void *)&thread_data); ++} ++ ++static void ++start_reader_thread_n (const char *fname) ++{ ++ debug; ++ thread_data.fd = 0; ++ thread_data.fname = fname; ++ reader_thread_tid = xpthread_create (NULL, reader_thread_proc, ++ (void *)&thread_data); ++} ++ ++static void ++end_reader_thread (void) ++{ ++ debug; ++ if (reader_thread_tid) ++ { ++ pthread_cancel (reader_thread_tid); ++ xpthread_join (reader_thread_tid); ++ xclose (thread_data.fd); ++ reader_thread_tid = 0; ++ } ++} ++ ++/*------------------------------------------------------------*/ ++ ++/* These two functions are reponsible for choosing a file to be tested ++ against, typically by returning a filename but in a few cases also ++ providing a file descriptor (i.e. for fdopen). */ ++ ++static const char * ++prepare_test_file (enum test_source_case f, enum test_stream_case s) ++{ ++ debug; ++ ++ test_fd = -1; ++ ++ switch (f) ++ { ++ case test_source_file: ++ { ++ if (test_stream_reads[f]) ++ { ++ debug; ++ FILE *fp = xfopen (test_file_name, "w"); ++ TEST_VERIFY_EXIT (fwrite (test_data, 1, 2 * BUFSIZ, fp) ++ == 2 * BUFSIZ); ++ xfclose (fp); ++ } ++ debug; ++ return test_file_name; ++ } ++ ++ case test_source_pipe: ++ { ++ debug; ++ xpipe (test_pipe); ++ if (test_stream_reads[s]) ++ { ++ start_writer_thread (test_pipe[1]); ++ test_fd = test_pipe[0]; ++ } ++ else ++ { ++ start_reader_thread (test_pipe[0]); ++ test_fd = test_pipe[1]; ++ } ++ test_pipe_name = xasprintf ("/proc/self/fd/%d", test_fd); ++ debug; ++ return test_pipe_name; ++ } ++ ++ case test_source_fifo: ++ { ++ /* We do not want to fail/exit if the file doesn't exist. */ ++ unlink (test_file_name); ++ xmkfifo (test_file_name, 0600); ++ debug; ++ if (test_stream_reads[s]) ++ start_writer_thread_n (test_file_name); ++ else ++ start_reader_thread_n (test_file_name); ++ debug; ++ return test_file_name; ++ } ++ ++ case test_source_pseudo_terminal: ++ { ++ support_openpty (&pty_fd, &test_fd, &test_pipe_name, NULL, NULL); ++ ++ debug; ++ if (test_stream_reads[s]) ++ start_writer_thread (pty_fd); ++ else ++ start_reader_thread (pty_fd); ++ ++ debug; ++ return test_pipe_name; ++ } ++ ++ case test_source_dev_null: ++ debug; ++ return "/dev/null"; ++ ++ default: ++ abort (); ++ } ++} ++ ++static void ++unprepare_test_file (FILE *fp, ++ enum test_source_case f, ++ enum test_stream_case s) ++{ ++ debug; ++ switch (f) ++ { ++ case test_source_file: ++ break; ++ ++ case test_source_pipe: ++ free (test_pipe_name); ++ if (test_stream_reads[s]) ++ end_writer_thread (); ++ else ++ end_reader_thread (); ++ break; ++ ++ case test_source_fifo: ++ if (test_stream_reads[s]) ++ end_writer_thread (); ++ else ++ end_reader_thread (); ++ unlink (test_file_name); ++ break; ++ ++ case test_source_pseudo_terminal: ++ free (test_pipe_name); ++ if (test_stream_reads[s]) ++ end_writer_thread (); ++ else ++ end_reader_thread (); ++ break; ++ ++ case test_source_dev_null: ++ break; ++ ++ default: ++ abort (); ++ } ++ debug; ++} ++ ++/*------------------------------------------------------------*/ ++ ++/* This function takes a filename and returns a file descriptor, ++ opened according to the method requested. */ ++ ++static FILE * ++open_test_stream (enum test_source_case f, enum test_stream_case s) ++{ ++ int fd; ++ FILE *fp; ++ const char *fname; ++ ++ debug; ++ fname = prepare_test_file (f, s); ++ if (fname == NULL) ++ return NULL; ++ ++ switch (s) ++ { ++ case test_stream_stdin: ++ fp = xfopen (fname, "r"); ++ break; ++ ++ case test_stream_stdout: ++ fp = xfopen (fname, "w"); ++ break; ++ ++ case test_stream_stderr: ++ fp = xfopen (fname, "w"); ++ break; ++ ++ case test_stream_fopen_r: ++ fp = xfopen (fname, "r"); ++ break; ++ ++ case test_stream_fdopen_r: ++ if (test_fd == -1) ++ fd = xopen (fname, O_RDONLY, 0); ++ else ++ fd = test_fd; ++ fp = fdopen (fd, "r"); ++ break; ++ ++ case test_stream_fopen_w: ++ fp = xfopen (fname, "w"); ++ break; ++ ++ case test_stream_fdopen_w: ++ fd = xopen (fname, O_WRONLY|O_CREAT|O_TRUNC, 0777); ++ fp = fdopen (fd, "w"); ++ break; ++ ++ default: ++ abort (); ++ } ++ TEST_VERIFY_EXIT (fp != NULL); ++ ++ if (f == test_source_pseudo_terminal) ++ { ++ struct termios t; ++ /* We disable the NL to CR-LF conversion so that we can compare ++ data without having to remove the extra CRs. */ ++ if (tcgetattr (fileno (fp), &t) < 0) ++ FAIL_EXIT1 ("tcgetattr failed: %m"); ++ t.c_oflag &= ~ONLCR; ++ if (tcsetattr (fileno (fp), TCSANOW, &t) < 0) ++ FAIL_EXIT1 ("tcsetattr failed: %m"); ++ } ++ ++ debug; ++ printf ("source %s stream %s file %s fd %d\n", ++ test_source_name[f], ++ test_stream_name[s], fname, fileno (fp)); ++ return fp; ++} ++ ++#endif ++ ++/*------------------------------------------------------------*/ ++ ++/* These functions do the actual testing - setting various buffering ++ options and verifying that they buffer as expected. */ ++ ++static void ++test_put_string (FILE *fp, const char *s, int count) ++{ ++ while (*s && count--) ++ { ++ fputc (*s++, fp); ++ TEST_VERIFY_EXIT (!ferror (fp)); ++ } ++} ++ ++int ++verify_fully_buffered (FILE *fp, ++ enum test_source_case f, ++ enum test_stream_case s, ++ enum test_config_case c) ++{ ++ debug; ++ if (test_stream_reads[s]) ++ { ++ char buf[10]; ++ dumpfp (fp); ++ size_t fc = fread (buf, 1, 10 - 1, fp); ++ dumpfp (fp); ++ ++ ssize_t count = fp->_IO_read_ptr - fp->_IO_read_base; ++ ++ TEST_VERIFY (fp->_IO_read_base != NULL); ++ if (f == test_source_dev_null) ++ { ++ TEST_VERIFY (fc == 0); ++ TEST_VERIFY (count == 0); ++ } ++ else if (f == test_source_pseudo_terminal) ++ { ++ TEST_VERIFY (fc == 9); ++ TEST_VERIFY (count == 3 || count == 10); ++ } ++ else ++ { ++ TEST_VERIFY (fc == 9); ++ TEST_VERIFY (count == 10); ++ } ++ ++ /* We already checked for the first character being 'a'. */ ++ if (count > 1) ++ { ++ TEST_COMPARE_BLOB (buf, count - 1, test_data + 1, count - 1); ++ TEST_COMPARE_BLOB (fp->_IO_read_base, count, test_data, count); ++ } ++ } ++ else ++ { ++ dumpfp (fp); ++ test_put_string (fp, test_data + 1, 10 - 1); ++ dumpfp (fp); ++ TEST_COMPARE (fp->_IO_write_ptr - fp->_IO_write_base, 10); ++ TEST_COMPARE_BLOB (fp->_IO_write_base, 10, test_data, 10); ++ } ++ ++ TEST_COMPARE ((fp->_flags & (_IO_UNBUFFERED | _IO_LINE_BUF)), 0); ++ if (c != test_config_none) ++ TEST_COMPARE (__fbufsize (fp), bufsize); ++ return 0; ++} ++ ++int ++verify_line_buffered (FILE *fp, ++ enum test_source_case f, ++ enum test_stream_case s, ++ enum test_config_case c) ++{ ++ debug; ++ /* "line buffered" for inputs is not really defined; what you really ++ want here is to control the device providing input. For GLIBC a ++ line-buffered input is treated as fully buffered. */ ++ if (test_stream_reads[s]) ++ { ++ char buf[10]; ++ dumpfp (fp); ++ size_t fc = fread (buf, 1, 10 - 1, fp); ++ dumpfp (fp); ++ ++ ssize_t count = fp->_IO_read_ptr - fp->_IO_read_base; ++ ++ TEST_VERIFY (fp->_IO_read_base != NULL); ++ if (f == test_source_dev_null) ++ { ++ TEST_VERIFY (fc == 0); ++ TEST_VERIFY (count == 0); ++ } ++ else if (f == test_source_pseudo_terminal) ++ { ++ TEST_VERIFY (fc == 9); ++ TEST_VERIFY (count == 3 || count == 10); ++ } ++ else ++ { ++ TEST_VERIFY (fc == 9); ++ TEST_VERIFY (count == 10); ++ } ++ ++ /* We already checked for the first character being 'a'. */ ++ if (count > 1) ++ { ++ TEST_COMPARE_BLOB (buf, count - 1, test_data + 1, count - 1); ++ TEST_COMPARE_BLOB (fp->_IO_read_base, count, test_data, count); ++ } ++ } ++ else ++ { ++ dumpfp (fp); ++ test_put_string (fp, test_data + 1, 10 - 1); ++ dumpfp (fp); ++ TEST_COMPARE (fp->_IO_write_ptr - fp->_IO_write_base, 3); ++ /* The first "abcdef\n" got flushed, leaving "abc". */ ++ TEST_COMPARE_BLOB (fp->_IO_write_base, 3, test_data + 7, 3); ++ } ++ ++ TEST_COMPARE ((fp->_flags & (_IO_UNBUFFERED | _IO_LINE_BUF)), _IO_LINE_BUF); ++ if (c != test_config_none) ++ TEST_COMPARE (__fbufsize (fp), bufsize); ++ return 0; ++} ++ ++int ++verify_unbuffered (FILE *fp, ++ enum test_source_case f, ++ enum test_stream_case s, ++ enum test_config_case c) ++{ ++ debug; ++ if (test_stream_reads[s]) ++ { ++ /* We've already read one byte. */ ++ dumpfp (fp); ++ TEST_VERIFY (fp->_IO_read_base != NULL); ++ if (f == test_source_dev_null) ++ TEST_COMPARE (fp->_IO_read_ptr - fp->_IO_read_base, 0); ++ else ++ { ++ TEST_COMPARE (fp->_IO_read_ptr - fp->_IO_read_base, 1); ++ TEST_COMPARE (fp->_IO_read_base[0], test_data[0]); ++ TEST_VERIFY (fp->_IO_read_ptr == fp->_IO_read_end); ++ } ++ } ++ else ++ { ++ dumpfp (fp); ++ fputc (test_data[1], fp); ++ dumpfp (fp); ++ TEST_COMPARE (fp->_IO_write_ptr - fp->_IO_write_base, 0); ++ TEST_COMPARE (fp->_IO_write_base[0], test_data[1]); ++ TEST_VERIFY (fp->_IO_write_end == fp->_IO_write_base); ++ } ++ TEST_COMPARE ((fp->_flags & (_IO_UNBUFFERED | _IO_LINE_BUF)), ++ _IO_UNBUFFERED); ++ TEST_COMPARE (__fbufsize (fp), 1); ++ return 0; ++} ++ ++static int ++do_setvbuf (FILE *fp, void *buf, int flags, int size, ++ enum test_stream_case s) ++{ ++ if (s != test_stream_stdout) ++ printf ("SETVBUF %p %p %s %d\n", ++ fp, buf, ++ flags == _IONBF ? "_IONBF" ++ : flags == _IOLBF ? "_IOLBF" ++ : flags == _IOFBF ? "_IOFBF" ++ : "???", size); ++ if (setvbuf (fp, buf, flags, size)) ++ { ++ perror ("setvbuf"); ++ return 1; ++ } ++ return 0; ++} ++ ++int ++do_second_part (FILE *fp, ++ enum test_source_case f, ++ enum test_stream_case s, ++ enum test_config_case c) ++{ ++ /* At this point, FP is the stream to test according to the other ++ parameters. */ ++ ++ int rv = 0; ++ int flags_before; ++ int flags_after; ++ ++ debug; ++ ++ flags_before = fp->_flags & (_IO_UNBUFFERED | _IO_LINE_BUF); ++ ++ /* This is where we do the thing we're testing for. */ ++ switch (c) ++ { ++ case test_config_none: ++ /* Buffering is unchanged. */ ++ break; ++ ++ case test_config_unbuffered: ++ do_setvbuf (fp, NULL, _IONBF, 0, s); ++ break; ++ ++ case test_config_line: ++ do_setvbuf (fp, buffer, _IOLBF, bufsize, s); ++ break; ++ ++ case test_config_fully: ++ do_setvbuf (fp, buffer, _IOFBF, bufsize, s); ++ break; ++ ++ default: ++ abort (); ++ } ++ ++ flags_after = fp->_flags & (_IO_UNBUFFERED | _IO_LINE_BUF); ++ ++ /* Check the buffer mode after we touch it, if we touched it. */ ++ switch (c) ++ { ++ case test_config_none: ++ /* Buffering is unchanged, but may change on the first read/write. */ ++ TEST_COMPARE (flags_after, flags_before); ++ break; ++ ++ case test_config_unbuffered: ++ TEST_COMPARE (flags_after, _IO_UNBUFFERED); ++ break; ++ ++ case test_config_line: ++ TEST_COMPARE (flags_after, _IO_LINE_BUF); ++ break; ++ ++ case test_config_fully: ++ TEST_COMPARE (flags_after, 0); ++ break; ++ ++ default: ++ abort (); ++ } ++ ++ /* Glibc defers calculating the appropriate buffering mechanism ++ until it reads from or writes to the device. So we read one ++ character here, and account for that in the tests. */ ++ if (test_stream_reads[s]) ++ { ++ dumpfp (fp); ++ int c = fgetc (fp); ++ if (c != TEST_STRING[0] && f != test_source_dev_null) ++ FAIL ("first char read is %c not %c", c, TEST_STRING[0]); ++ dumpfp (fp); ++ } ++ else ++ { ++ dumpfp (fp); ++ fputc (TEST_STRING[0], fp); ++ dumpfp (fp); ++ } ++ ++ switch (fp->_flags & (_IO_UNBUFFERED | _IO_LINE_BUF)) ++ { ++ case _IO_LINE_BUF: ++ rv += verify_line_buffered (fp, f, s, c); ++ break; ++ ++ case _IO_UNBUFFERED: ++ rv += verify_unbuffered (fp, f, s, c); ++ break; ++ ++ case 0: /* Fully buffered. */ ++ rv += verify_fully_buffered (fp, f, s, c); ++ break; ++ ++ default: ++ abort (); ++ } ++ ++ ++ fclose (fp); ++ return rv; ++} ++ ++/*------------------------------------------------------------*/ ++ ++#ifdef INDEPENDENT_PART ++/* This part is the independent sub-process we call to test stdin et ++ al. */ ++ ++int ++main (int argc, char **argv) ++{ ++ /* This is one of the subprocesses we created to test stdin et ++ al. */ ++ FILE *fp; ++ ++ /* If we're called as a regular test, instead of as a sub-process, ++ don't complain. */ ++ if (argc == 1) ++ return 0; ++ ++ if (argc != 4) ++ { ++ int i; ++ for (i = 0; i <= argc; i ++) ++ printf ("argv[%d] = `%s'\n", i, argv[i] ?: "(null)"); ++ FAIL_EXIT1 ("sub-process called wrong"); ++ } ++ ++ prepare_test_data (); ++ ++ enum test_source_case f = atoi (argv[1]); ++ enum test_stream_case s = atoi (argv[2]); ++ enum test_config_case c = atoi (argv[3]); ++ ++ if (s != test_stream_stdout) ++ printf ("\n\033[41mRunning test %s : %s : %s\033[0m\n", ++ test_source_name[f], ++ test_stream_name[s], ++ test_config_name[c]); ++ ++ switch (s) ++ { ++ case test_stream_stdin: ++ fp = stdin; ++ break; ++ case test_stream_stdout: ++ fp = stdout; ++ break; ++ case test_stream_stderr: ++ fp = stderr; ++ break; ++ default: ++ abort (); ++ } ++ ++ return do_second_part (fp, f, s, c); ++} ++ ++#else ++/* This part is the standard test process. */ ++ ++/* Spawn an independent sub-process with std* redirected. */ ++int ++recurse (FILE *fp, ++ enum test_source_case f, ++ enum test_stream_case s, ++ enum test_config_case c) ++{ ++ /* We need to test stdin, stdout, or stderr, which means creating a ++ subprocess with one of those redirected from FP. */ ++ debug; ++ ++ pid_t pid; ++ int status; ++ ++ pid = fork (); ++ ++ switch (pid) ++ { ++ case -1: /* error */ ++ perror ("fork"); ++ return 1; ++ break; ++ ++ default: /* parent */ ++ fclose (fp); ++ xwaitpid (pid, &status, 0); ++ if (WIFEXITED (status) ++ && WEXITSTATUS (status) == 0) ++ return 0; ++ return 1; ++ ++ case 0: /* child */ ++ switch (s) ++ { ++ case test_stream_stdin: ++ xclose (0); ++ dup2 (fileno (fp), 0); ++ break; ++ case test_stream_stdout: ++ xclose (1); ++ dup2 (fileno (fp), 1); ++ break; ++ case test_stream_stderr: ++ xclose (2); ++ dup2 (fileno (fp), 2); ++ break; ++ default: ++ abort (); ++ } ++ fclose (fp); ++ ++ /* At this point, we have to run a program... which is tricky to ++ properly support for remote targets or crosses, because of ++ glibc versions etc. Hence we run in a test-container. */ ++ ++ char fs[10], ss[10], cs[10]; ++ sprintf (fs, "%d", f); ++ sprintf (ss, "%d", s); ++ sprintf (cs, "%d", c); ++ execl (IND_PROC, IND_PROC, fs, ss, cs, NULL); ++ if (s == test_stream_stdout) ++ fprintf (stderr, "execl (%s) failed, ", IND_PROC); ++ else ++ printf ("execl (%s) failed, ", IND_PROC); ++ perror ("The error was"); ++ exit (1); ++ break; ++ } ++ ++ return 0; ++} ++ ++int ++do_test (void) ++{ ++ int rv = 0; ++ ++ signal (SIGPIPE, SIG_IGN); ++ ++ prepare_test_data (); ++ ++ for (enum test_source_case f = 0; f < test_source_count; ++f) ++ for (enum test_stream_case s = 0; s < test_stream_count; ++s) ++ for (enum test_config_case c = 0; c < test_config_count; ++c) ++ { ++ printf ("\n\033[43mRunning test %s : %s : %s\033[0m\n", ++ test_source_name[f], ++ test_stream_name[s], ++ test_config_name[c]); ++ ++ FILE *fp = open_test_stream (f, s); ++ ++ if (fp) ++ { ++ ++ if (s <= test_stream_stderr) ++ rv += recurse (fp, f, s, c); ++ else ++ rv += do_second_part (fp, f, s, c); ++ ++ unprepare_test_file (fp, f, s); ++ } ++ } ++ ++ free (buffer); ++ ++ printf ("return %d\n", rv); ++ return rv; ++} ++ ++# include ++#endif ++ diff --git a/glibc-RHEL-106562-24.patch b/glibc-RHEL-106562-24.patch new file mode 100644 index 0000000..fc5ce1b --- /dev/null +++ b/glibc-RHEL-106562-24.patch @@ -0,0 +1,112 @@ +commit 4fa959d13d21b8f56a43aa0a416100303736c55c +Author: Florian Weimer +Date: Tue Apr 8 10:39:44 2025 +0200 + + stdio-common: In tst-setvbuf2, close helper thread descriptor only if opened + + The helper thread may get canceled before the open system + call succeds. Then ThreadData.fd remains zero, and eventually + the xclose call in end_reader_thread fails because descriptor 0 + is not open. + + Instead, initialize the fd member to -1 (not a valid descriptor) + and close the descriptor only if valid. Do this in a new end_thread + helper routine. + + Also add more error checking to close operations. + + Fixes commit 95b780c1d0549678c0a244c6e2112ec97edf0839 ("stdio: Add + more setvbuf tests"). + +diff --git a/stdio-common/tst-setvbuf2.c b/stdio-common/tst-setvbuf2.c +index 6cc83355f391afab..84d8b43a5811b4be 100644 +--- a/stdio-common/tst-setvbuf2.c ++++ b/stdio-common/tst-setvbuf2.c +@@ -240,6 +240,21 @@ typedef struct { + /* It's OK if this is static, we only run one at a time. */ + ThreadData thread_data; + ++static void ++end_thread (pthread_t *ptid) ++{ ++ if (*ptid) ++ { ++ pthread_cancel (*ptid); ++ xpthread_join (*ptid); ++ /* The descriptor was passed in, or the helper thread made ++ sufficient progress and opened the file. */ ++ if (thread_data.fd >= 0) ++ xclose (thread_data.fd); ++ *ptid = 0; ++ } ++} ++ + static void * + writer_thread_proc (void *closure) + { +@@ -306,7 +321,7 @@ static void + start_writer_thread_n (const char *fname) + { + debug; +- thread_data.fd = 0; ++ thread_data.fd = -1; + thread_data.fname = fname; + writer_thread_tid = xpthread_create (NULL, writer_thread_proc, + (void *)&thread_data); +@@ -316,13 +331,7 @@ static void + end_writer_thread (void) + { + debug; +- if (writer_thread_tid) +- { +- pthread_cancel (writer_thread_tid); +- xpthread_join (writer_thread_tid); +- xclose (thread_data.fd); +- writer_thread_tid = 0; +- } ++ end_thread (&writer_thread_tid); + } + + static void +@@ -339,7 +348,7 @@ static void + start_reader_thread_n (const char *fname) + { + debug; +- thread_data.fd = 0; ++ thread_data.fd = -1; + thread_data.fname = fname; + reader_thread_tid = xpthread_create (NULL, reader_thread_proc, + (void *)&thread_data); +@@ -349,13 +358,7 @@ static void + end_reader_thread (void) + { + debug; +- if (reader_thread_tid) +- { +- pthread_cancel (reader_thread_tid); +- xpthread_join (reader_thread_tid); +- xclose (thread_data.fd); +- reader_thread_tid = 0; +- } ++ end_thread (&reader_thread_tid); + } + + /*------------------------------------------------------------*/ +@@ -852,7 +855,7 @@ do_second_part (FILE *fp, + } + + +- fclose (fp); ++ xfclose (fp); + return rv; + } + +@@ -939,7 +942,7 @@ recurse (FILE *fp, + break; + + default: /* parent */ +- fclose (fp); ++ xfclose (fp); + xwaitpid (pid, &status, 0); + if (WIFEXITED (status) + && WEXITSTATUS (status) == 0) diff --git a/glibc-RHEL-106562-3.patch b/glibc-RHEL-106562-3.patch new file mode 100644 index 0000000..dcdbff0 --- /dev/null +++ b/glibc-RHEL-106562-3.patch @@ -0,0 +1,60 @@ +commit 3f54e459a633b4247be91b9d0f68a7e08720b8d8 +Author: Frédéric Bérat +Date: Tue Aug 13 12:01:26 2024 +0200 + + libio/tst-getdelim: Add new test covering NUL as a delimiter + + Add a new test to getdelim to verify that '\0' can be set as a + delimiter. + + Reviewed-by: Florian Weimer + +diff --git a/libio/tst-getdelim.c b/libio/tst-getdelim.c +index e6dd964b4918b02a..db15bf92855ee9e1 100644 +--- a/libio/tst-getdelim.c ++++ b/libio/tst-getdelim.c +@@ -1,4 +1,6 @@ +-/* Check that getdelim sets error indicator on error (BZ #29917) ++/* Test getdelim conforming to POSIX specifications. ++ ++ Note: Most getdelim use cases are covered by stdio-common/tst-getline. + + Copyright (C) 2023-2024 Free Software Foundation, Inc. + This file is part of the GNU C Library. +@@ -18,18 +20,36 @@ + . */ + + #include ++#include + #include + + #include ++#include ++#include + + static int + do_test (void) + { ++ /* Check that getdelim sets error indicator on error (BZ #29917) */ + clearerr (stdin); + TEST_VERIFY (getdelim (0, 0, '\n', stdin) == -1); + TEST_VERIFY (ferror (stdin) != 0); + TEST_VERIFY (errno == EINVAL); + ++ /* Test getdelim with NUL as delimiter */ ++ verbose_printf ("Testing NUL delimiter\n"); ++ char *lineptr = NULL; ++ size_t linelen = 0; ++ char membuf[] = "abc\0d\nef\0"; ++ FILE *memstream = fmemopen (membuf, sizeof (membuf), "r"); ++ TEST_VERIFY_EXIT (memstream != NULL); ++ TEST_VERIFY (getdelim (&lineptr, &linelen, '\0', memstream) != -1); ++ TEST_COMPARE_BLOB (lineptr, 4, "abc\0", 4); ++ TEST_VERIFY (getdelim (&lineptr, &linelen, '\0', memstream) != -1); ++ TEST_COMPARE_BLOB (lineptr, 5, "d\nef\0", 5); ++ fclose (memstream); ++ free (lineptr); ++ + return 0; + } + diff --git a/glibc-RHEL-106562-4.patch b/glibc-RHEL-106562-4.patch new file mode 100644 index 0000000..86c0c97 --- /dev/null +++ b/glibc-RHEL-106562-4.patch @@ -0,0 +1,29 @@ +commit b22923abb046311ac9097a36b97b9b97342bac44 +Author: Carlos O'Donell +Date: Thu Aug 15 08:12:35 2024 -0400 + + Report error if setaffinity wrapper fails (Bug 32040) + + Previously if the setaffinity wrapper failed the rest of the subtest + would not execute and the current subtest would be reported as passing. + Now if the setaffinity wrapper fails the subtest is correctly reported + as faling. Tested manually by changing the conditions of the affinity + call including setting size to zero, or checking the wrong condition. + + No regressions on x86_64. + + Reviewed-by: Florian Weimer + +diff --git a/sysdeps/unix/sysv/linux/tst-skeleton-affinity.c b/sysdeps/unix/sysv/linux/tst-skeleton-affinity.c +index 31a15b3ad789a287..2f921ed397a1a4d9 100644 +--- a/sysdeps/unix/sysv/linux/tst-skeleton-affinity.c ++++ b/sysdeps/unix/sysv/linux/tst-skeleton-affinity.c +@@ -157,7 +157,7 @@ test_size (const struct conf *conf, size_t size) + if (setaffinity (kernel_size, initial_set) < 0) + { + printf ("error: size %zu: setaffinity: %m\n", size); +- return true; ++ return false; + } + + /* Use one-CPU set to test switching between CPUs. */ diff --git a/glibc-RHEL-106562-5.patch b/glibc-RHEL-106562-5.patch new file mode 100644 index 0000000..d97d8ff --- /dev/null +++ b/glibc-RHEL-106562-5.patch @@ -0,0 +1,78 @@ +commit 921690512946d73bf66a8c495baff31316e4489f +Author: Florian Weimer +Date: Fri Aug 16 16:05:19 2024 +0200 + + support: Add the xstatx function + + Reviewed-by: Adhemerval Zanella + +diff --git a/support/Makefile b/support/Makefile +index aa57207bdccc852d..5b1c96a49e9410f4 100644 +--- a/support/Makefile ++++ b/support/Makefile +@@ -210,6 +210,7 @@ libsupport-routines = \ + xsignal \ + xsigstack \ + xsocket \ ++ xstatx \ + xstrdup \ + xstrndup \ + xsymlink \ +diff --git a/support/xstatx.c b/support/xstatx.c +new file mode 100644 +index 0000000000000000..621f2440f83b598d +--- /dev/null ++++ b/support/xstatx.c +@@ -0,0 +1,32 @@ ++/* Error-checking wrapper for statx. ++ 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 ++ . */ ++ ++#include ++ ++#include ++#include ++#include ++ ++void ++xstatx (int fd, const char *path, int flags, unsigned int mask, ++ struct statx *stx) ++{ ++ if (statx (fd, path, flags, mask, stx) != 0) ++ FAIL_EXIT1 ("statx (AT_FDCWD, \"%s\", 0x%x, 0x%x): %m", ++ path, (unsigned int) flags, mask); ++} +diff --git a/support/xunistd.h b/support/xunistd.h +index 13be9a46a3e6b07b..cc74c4fad07dd088 100644 +--- a/support/xunistd.h ++++ b/support/xunistd.h +@@ -30,6 +30,7 @@ + __BEGIN_DECLS + + struct stat64; ++struct statx; + + pid_t xfork (void); + pid_t xwaitpid (pid_t, int *status, int flags); +@@ -51,6 +52,7 @@ void __REDIRECT (xstat, (const char *path, struct stat *), xstat_time64); + void __REDIRECT (xlstat, (const char *path, struct stat *), xlstat_time64); + void __REDIRECT (xfstat, (int fd, struct stat *), xfstat_time64); + #endif ++void xstatx (int, const char *, int, unsigned int, struct statx *); + void xmkdir (const char *path, mode_t); + void xchroot (const char *path); + void xunlink (const char *path); diff --git a/glibc-RHEL-106562-6.patch b/glibc-RHEL-106562-6.patch new file mode 100644 index 0000000..959730e --- /dev/null +++ b/glibc-RHEL-106562-6.patch @@ -0,0 +1,391 @@ +commit bf2927484152e12996af60ea439cf94b66fcd81d +Author: Florian Weimer +Date: Fri Aug 16 16:05:20 2024 +0200 + + io: Use struct statx and xstatx in tests + + This avoids the need to define struct_statx to an appropriate + struct stat type variant because struct statx does not change + based on time/file offset flags. + + Reviewed-by: Adhemerval Zanella + +diff --git a/io/tst-futimens-time64.c b/io/tst-futimens-time64.c +index 88fcb38489b160de..71204a6166eaba56 100644 +--- a/io/tst-futimens-time64.c ++++ b/io/tst-futimens-time64.c +@@ -1,2 +1 @@ +-#define struct_stat struct stat + #include "tst-futimens.c" +diff --git a/io/tst-futimens.c b/io/tst-futimens.c +index 6204befedd869bb6..075ca42b9378d98f 100644 +--- a/io/tst-futimens.c ++++ b/io/tst-futimens.c +@@ -18,26 +18,23 @@ + + #include + #include ++#include + #include + +-#ifndef struct_stat +-# define struct_stat struct stat64 +-#endif +- + static int + test_futimens_helper (const char *file, int fd, const struct timespec *ts) + { + int result = futimens (fd, ts); + TEST_VERIFY_EXIT (result == 0); + +- struct_stat st; +- xfstat (fd, &st); ++ struct statx st; ++ xstatx (fd, "", AT_EMPTY_PATH, STATX_BASIC_STATS, &st); + + /* Check if seconds for atime match */ +- TEST_COMPARE (st.st_atime, ts[0].tv_sec); ++ TEST_COMPARE (st.stx_atime.tv_sec, ts[0].tv_sec); + + /* Check if seconds for mtime match */ +- TEST_COMPARE (st.st_mtime, ts[1].tv_sec); ++ TEST_COMPARE (st.stx_mtime.tv_sec, ts[1].tv_sec); + + return 0; + } +diff --git a/io/tst-futimes-time64.c b/io/tst-futimes-time64.c +index d489c265d1964196..eeb4bed7c4f5fb12 100644 +--- a/io/tst-futimes-time64.c ++++ b/io/tst-futimes-time64.c +@@ -1,2 +1 @@ +-#define struct_stat struct stat + #include "tst-futimes.c" +diff --git a/io/tst-futimes.c b/io/tst-futimes.c +index d21acf6a24715a10..612fe460cf505547 100644 +--- a/io/tst-futimes.c ++++ b/io/tst-futimes.c +@@ -18,27 +18,24 @@ + + #include + #include ++#include + #include + #include + +-#ifndef struct_stat +-# define struct_stat struct stat64 +-#endif +- + static int + test_futimens_helper (const char *file, int fd, const struct timeval *tv) + { + int r = futimes (fd, tv); + TEST_VERIFY_EXIT (r == 0); + +- struct_stat st; +- xfstat (fd, &st); ++ struct statx st; ++ xstatx (fd, "", AT_EMPTY_PATH, STATX_BASIC_STATS, &st); + + /* Check if seconds for atime match */ +- TEST_COMPARE (st.st_atime, tv[0].tv_sec); ++ TEST_COMPARE (st.stx_atime.tv_sec, tv[0].tv_sec); + + /* Check if seconds for mtime match */ +- TEST_COMPARE (st.st_mtime, tv[1].tv_sec); ++ TEST_COMPARE (st.stx_mtime.tv_sec, tv[1].tv_sec); + + return 0; + } +diff --git a/io/tst-futimesat-time64.c b/io/tst-futimesat-time64.c +index f6c0500eefb9c98b..15853175796e59dd 100644 +--- a/io/tst-futimesat-time64.c ++++ b/io/tst-futimesat-time64.c +@@ -1,4 +1 @@ +-#define struct_stat struct stat +-#define fstat fstat +-#define fstatat fstatat + #include "io/tst-futimesat.c" +diff --git a/io/tst-futimesat.c b/io/tst-futimesat.c +index 67a8551bebf23190..feae4e7aa76ddee7 100644 +--- a/io/tst-futimesat.c ++++ b/io/tst-futimesat.c +@@ -30,12 +30,6 @@ + #include + #include + +-#ifndef struct_stat +-# define struct_stat struct stat64 +-# define fstat fstat64 +-# define fstatat fstatat64 +-#endif +- + static int dir_fd; + + static void +@@ -118,19 +112,15 @@ do_test (void) + xwrite (fd, "hello", 5); + puts ("file created"); + +- struct_stat st1; +- if (fstat (fd, &st1) != 0) +- { +- puts ("fstat64 failed"); +- return 1; +- } ++ struct statx st1; ++ xstatx (fd, "", AT_EMPTY_PATH, STATX_BASIC_STATS, &st1); + + close (fd); + + struct timeval tv[2]; +- tv[0].tv_sec = st1.st_atime + 1; ++ tv[0].tv_sec = st1.stx_atime.tv_sec + 1; + tv[0].tv_usec = 0; +- tv[1].tv_sec = st1.st_mtime + 1; ++ tv[1].tv_sec = st1.stx_mtime.tv_sec + 1; + tv[1].tv_usec = 0; + if (futimesat (dir_fd, "some-file", tv) != 0) + { +@@ -138,16 +128,12 @@ do_test (void) + return 1; + } + +- struct_stat st2; +- if (fstatat (dir_fd, "some-file", &st2, 0) != 0) +- { +- puts ("fstatat64 failed"); +- return 1; +- } ++ struct statx st2; ++ xstatx (dir_fd, "some-file", 0, STATX_BASIC_STATS, &st2); + +- if (st2.st_mtime != tv[1].tv_sec ++ if (st2.stx_mtime.tv_sec != tv[1].tv_sec + #ifdef _STATBUF_ST_NSEC +- || st2.st_mtim.tv_nsec != 0 ++ || st2.stx_mtime.tv_nsec != 0 + #endif + ) + { +diff --git a/io/tst-lutimes-time64.c b/io/tst-lutimes-time64.c +index 06caec0a91d91b6c..c5bea965dabe6cd7 100644 +--- a/io/tst-lutimes-time64.c ++++ b/io/tst-lutimes-time64.c +@@ -1,2 +1 @@ +-#define struct_stat struct stat + #include "tst-lutimes.c" +diff --git a/io/tst-lutimes.c b/io/tst-lutimes.c +index edef5ab90e8681c7..78bcc58291fdcdc8 100644 +--- a/io/tst-lutimes.c ++++ b/io/tst-lutimes.c +@@ -18,34 +18,32 @@ + + #include + #include ++#include + #include + #include + +-#ifndef struct_stat +-# define struct_stat struct stat64 +-#endif +- + static int + test_lutimes_helper (const char *testfile, int fd, const char *testlink, + const struct timeval *tv) + { +- struct_stat stfile_orig; +- xlstat (testfile, &stfile_orig); ++ struct statx stfile_orig; ++ xstatx (AT_FDCWD, testfile, AT_SYMLINK_NOFOLLOW, STATX_BASIC_STATS, ++ &stfile_orig); + + TEST_VERIFY_EXIT (lutimes (testlink, tv) == 0); + +- struct_stat stlink; +- xlstat (testlink, &stlink); ++ struct statx stlink; ++ xstatx (AT_FDCWD, testlink, AT_SYMLINK_NOFOLLOW, STATX_BASIC_STATS, &stlink); + +- TEST_COMPARE (stlink.st_atime, tv[0].tv_sec); +- TEST_COMPARE (stlink.st_mtime, tv[1].tv_sec); ++ TEST_COMPARE (stlink.stx_atime.tv_sec, tv[0].tv_sec); ++ TEST_COMPARE (stlink.stx_mtime.tv_sec, tv[1].tv_sec); + + /* Check if the timestamp from original file is not changed. */ +- struct_stat stfile; +- xlstat (testfile, &stfile); ++ struct statx stfile; ++ xstatx (AT_FDCWD, testfile, AT_SYMLINK_NOFOLLOW, STATX_BASIC_STATS, &stfile); + +- TEST_COMPARE (stfile_orig.st_atime, stfile.st_atime); +- TEST_COMPARE (stfile_orig.st_mtime, stfile.st_mtime); ++ TEST_COMPARE (stfile_orig.stx_atime.tv_sec, stfile.stx_atime.tv_sec); ++ TEST_COMPARE (stfile_orig.stx_mtime.tv_sec, stfile.stx_mtime.tv_sec); + + return 0; + } +diff --git a/io/tst-utime-time64.c b/io/tst-utime-time64.c +index eb62f59126564896..8894592a1570a366 100644 +--- a/io/tst-utime-time64.c ++++ b/io/tst-utime-time64.c +@@ -1,2 +1 @@ +-#define struct_stat struct stat + #include "tst-utime.c" +diff --git a/io/tst-utime.c b/io/tst-utime.c +index e2e6dcd04cebb7fc..f32935828923a47f 100644 +--- a/io/tst-utime.c ++++ b/io/tst-utime.c +@@ -19,26 +19,23 @@ + #include + #include + #include ++#include + #include + +-#ifndef struct_stat +-# define struct_stat struct stat64 +-#endif +- + static int + test_utime_helper (const char *file, int fd, const struct utimbuf *ut) + { + int result = utime (file, ut); + TEST_VERIFY_EXIT (result == 0); + +- struct_stat st; +- xfstat (fd, &st); ++ struct statx st; ++ xstatx (fd, "", AT_EMPTY_PATH, STATX_BASIC_STATS, &st); + + /* Check if seconds for actime match */ +- TEST_COMPARE (st.st_atime, ut->actime); ++ TEST_COMPARE (st.stx_atime.tv_sec, ut->actime); + + /* Check if seconds for modtime match */ +- TEST_COMPARE (st.st_mtime, ut->modtime); ++ TEST_COMPARE (st.stx_mtime.tv_sec, ut->modtime); + + return 0; + } +diff --git a/io/tst-utimensat-time64.c b/io/tst-utimensat-time64.c +index 7ac7d8df1d48f0a0..5d60fce8818f7545 100644 +--- a/io/tst-utimensat-time64.c ++++ b/io/tst-utimensat-time64.c +@@ -1,2 +1 @@ +-#define struct_stat struct stat + #include "tst-utimensat.c" +diff --git a/io/tst-utimensat.c b/io/tst-utimensat.c +index 3d9a72c4719cc6c9..2a756d7b07b6b07f 100644 +--- a/io/tst-utimensat.c ++++ b/io/tst-utimensat.c +@@ -22,10 +22,6 @@ + #include + #include + +-#ifndef struct_stat +-# define struct_stat struct stat64 +-#endif +- + static int + test_utimesat_helper (const char *testfile, int fd, const char *testlink, + const struct timespec *ts) +@@ -33,35 +29,38 @@ test_utimesat_helper (const char *testfile, int fd, const char *testlink, + { + TEST_VERIFY_EXIT (utimensat (fd, testfile, ts, 0) == 0); + +- struct_stat st; +- xfstat (fd, &st); ++ struct statx st; ++ xstatx (fd, "", AT_EMPTY_PATH, STATX_BASIC_STATS, &st); + + /* Check if seconds for atime match */ +- TEST_COMPARE (st.st_atime, ts[0].tv_sec); ++ TEST_COMPARE (st.stx_atime.tv_sec, ts[0].tv_sec); + + /* Check if seconds for mtime match */ +- TEST_COMPARE (st.st_mtime, ts[1].tv_sec); ++ TEST_COMPARE (st.stx_mtime.tv_sec, ts[1].tv_sec); + } + + { +- struct_stat stfile_orig; +- xlstat (testfile, &stfile_orig); ++ struct statx stfile_orig; ++ xstatx (AT_FDCWD, testfile, AT_SYMLINK_NOFOLLOW, STATX_BASIC_STATS, ++ &stfile_orig); + + TEST_VERIFY_EXIT (utimensat (0 /* ignored */, testlink, ts, + AT_SYMLINK_NOFOLLOW) + == 0); +- struct_stat stlink; +- xlstat (testlink, &stlink); ++ struct statx stlink; ++ xstatx (AT_FDCWD, testlink, AT_SYMLINK_NOFOLLOW, STATX_BASIC_STATS, ++ &stlink); + +- TEST_COMPARE (stlink.st_atime, ts[0].tv_sec); +- TEST_COMPARE (stlink.st_mtime, ts[1].tv_sec); ++ TEST_COMPARE (stlink.stx_atime.tv_sec, ts[0].tv_sec); ++ TEST_COMPARE (stlink.stx_mtime.tv_sec, ts[1].tv_sec); + + /* Check if the timestamp from original file is not changed. */ +- struct_stat stfile; +- xlstat (testfile, &stfile); ++ struct statx stfile; ++ xstatx (AT_FDCWD, testfile, AT_SYMLINK_NOFOLLOW, STATX_BASIC_STATS, ++ &stfile); + +- TEST_COMPARE (stfile_orig.st_atime, stfile.st_atime); +- TEST_COMPARE (stfile_orig.st_mtime, stfile.st_mtime); ++ TEST_COMPARE (stfile_orig.stx_atime.tv_sec, stfile.stx_atime.tv_sec); ++ TEST_COMPARE (stfile_orig.stx_mtime.tv_sec, stfile.stx_mtime.tv_sec); + } + + return 0; +diff --git a/io/tst-utimes-time64.c b/io/tst-utimes-time64.c +index 234ec02541032017..026ef5f78dd4616c 100644 +--- a/io/tst-utimes-time64.c ++++ b/io/tst-utimes-time64.c +@@ -1,2 +1 @@ +-#define struct_stat struct stat + #include "tst-utimes.c" +diff --git a/io/tst-utimes.c b/io/tst-utimes.c +index 8edcfabebfbd978d..6cd436c5a0f94d84 100644 +--- a/io/tst-utimes.c ++++ b/io/tst-utimes.c +@@ -18,28 +18,25 @@ + + #include + #include ++#include + #include + #include + #include + +-#ifndef struct_stat +-# define struct_stat struct stat64 +-#endif +- + static int + test_utimes_helper (const char *file, int fd, const struct timeval *tv) + { + int result = utimes (file, tv); + TEST_VERIFY_EXIT (result == 0); + +- struct_stat st; +- xfstat (fd, &st); ++ struct statx st; ++ xstatx (fd, "", AT_EMPTY_PATH, STATX_BASIC_STATS, &st); + + /* Check if seconds for atime match */ +- TEST_COMPARE (st.st_atime, tv[0].tv_sec); ++ TEST_COMPARE (st.stx_atime.tv_sec, tv[0].tv_sec); + + /* Check if seconds for mtime match */ +- TEST_COMPARE (st.st_mtime, tv[1].tv_sec); ++ TEST_COMPARE (st.stx_mtime.tv_sec, tv[1].tv_sec); + + return 0; + } diff --git a/glibc-RHEL-106562-7.patch b/glibc-RHEL-106562-7.patch new file mode 100644 index 0000000..569a715 --- /dev/null +++ b/glibc-RHEL-106562-7.patch @@ -0,0 +1,415 @@ +commit e7c14e542d8d858b824b5df4f4e3dc93695e6171 +Author: Florian Weimer +Date: Fri Aug 16 16:05:20 2024 +0200 + + support: Use macros for *stat wrappers + + Macros will automatically use the correct types, without + having to fiddle with internal glibc macros. It's also + impossible to get the types wrong due to aliasing because + support_check_stat_fd and support_check_stat_path do not + depend on the struct stat* types. + + The changes reveal some inconsistencies in tests. + + Reviewed-by: Adhemerval Zanella + +diff --git a/elf/tst-ldconfig-bad-aux-cache.c b/elf/tst-ldconfig-bad-aux-cache.c +index 7f1fbb52523ac1ac..8c2e62ecc2c54c04 100644 +--- a/elf/tst-ldconfig-bad-aux-cache.c ++++ b/elf/tst-ldconfig-bad-aux-cache.c +@@ -85,7 +85,7 @@ do_test (void) + support_capture_subprocess_check (&result, "execv", 0, sc_allow_none); + support_capture_subprocess_free (&result); + +- xstat (path, &fs); ++ xstat64 (path, &fs); + + size = fs.st_size; + /* Run 3 tests, each truncating aux-cache shorter and shorter. */ +diff --git a/io/tst-copy_file_range.c b/io/tst-copy_file_range.c +index 9837b7c3395725a5..3d7b0aa90160190a 100644 +--- a/io/tst-copy_file_range.c ++++ b/io/tst-copy_file_range.c +@@ -117,7 +117,7 @@ simple_file_copy (void) + TEST_COMPARE (xlseek (outfd, 0, SEEK_CUR), 6 + length); + + struct stat64 st; +- xfstat (outfd, &st); ++ xfstat64 (outfd, &st); + if (length > 0) + TEST_COMPARE (st.st_size, out_skipped + length); + else +diff --git a/io/tst-statx.c b/io/tst-statx.c +index d84568859eb5c604..685924ae76245676 100644 +--- a/io/tst-statx.c ++++ b/io/tst-statx.c +@@ -78,7 +78,7 @@ both_implementations_tests (statx_function impl, const char *path, int fd) + struct statx stx = { 0, }; + TEST_COMPARE (statx (fd, "", AT_EMPTY_PATH, STATX_BASIC_STATS, &stx), 0); + struct stat64 st; +- xfstat (fd, &st); ++ xfstat64 (fd, &st); + TEST_COMPARE (stx.stx_mode, st.st_mode); + TEST_COMPARE (stx.stx_dev_major, major (st.st_dev)); + TEST_COMPARE (stx.stx_dev_minor, minor (st.st_dev)); +@@ -88,7 +88,7 @@ both_implementations_tests (statx_function impl, const char *path, int fd) + TEST_COMPARE (statx (AT_FDCWD, "/dev/null", 0, STATX_BASIC_STATS, &stx), + 0); + struct stat64 st; +- xstat ("/dev/null", &st); ++ xstat64 ("/dev/null", &st); + TEST_COMPARE (stx.stx_mode, st.st_mode); + TEST_COMPARE (stx.stx_dev_major, major (st.st_dev)); + TEST_COMPARE (stx.stx_dev_minor, minor (st.st_dev)); +diff --git a/locale/tst-localedef-path-norm.c b/locale/tst-localedef-path-norm.c +index ffe8cbd46732386f..f592b9a9605bff94 100644 +--- a/locale/tst-localedef-path-norm.c ++++ b/locale/tst-localedef-path-norm.c +@@ -84,7 +84,7 @@ run_test (void *closure) + support_capture_subprocess_free (&result); + + /* Verify path is present and is a directory. */ +- xstat (path, &fs); ++ xstat64 (path, &fs); + if (!S_ISDIR (fs.st_mode)) + { + support_record_failure (); +diff --git a/localedata/tst-localedef-hardlinks.c b/localedata/tst-localedef-hardlinks.c +index e88215a1507bb30a..23927b462fc2224a 100644 +--- a/localedata/tst-localedef-hardlinks.c ++++ b/localedata/tst-localedef-hardlinks.c +@@ -62,7 +62,7 @@ check_link (struct test_data step) + char *output; + + output = xasprintf ("%s/%s", support_complocaledir_prefix, step.output); +- xstat (output, &locale); ++ xstat64 (output, &locale); + free (output); + TEST_COMPARE (locale.st_nlink, step.st_nlink); + } +diff --git a/posix/tst-execveat.c b/posix/tst-execveat.c +index 4565d6b19f41ca63..dde034a9f1356453 100644 +--- a/posix/tst-execveat.c ++++ b/posix/tst-execveat.c +@@ -155,7 +155,7 @@ do_test (void) + tmp_sh = xasprintf ("%s/tmp_sh", tmp_dir); + add_temp_file (tmp_sh); + fd_out = xopen (symlink_name, O_CREAT | O_WRONLY, 0); +- xstat ("/bin/sh", &st); ++ xstat64 ("/bin/sh", &st); + fd = xopen ("/bin/sh", O_RDONLY, 0); + xcopy_file_range (fd, 0, fd_out, 0, st.st_size, 0); + xfchmod (fd_out, 0700); +diff --git a/stdio-common/tst-renameat2.c b/stdio-common/tst-renameat2.c +index b65afed75ec65ca4..7f4345f716182552 100644 +--- a/stdio-common/tst-renameat2.c ++++ b/stdio-common/tst-renameat2.c +@@ -82,7 +82,7 @@ static void + check_size (const char *path, off64_t expected_size) + { + struct stat64 st; +- xstat (path, &st); ++ xstat64 (path, &st); + if (st.st_size != expected_size) + FAIL_EXIT1 ("file \"%s\": expected size %lld, actual size %lld", + path, (unsigned long long int) expected_size, +diff --git a/stdlib/tst-system.c b/stdlib/tst-system.c +index 47c742f963a1b6b4..b5b630a41b9e4663 100644 +--- a/stdlib/tst-system.c ++++ b/stdlib/tst-system.c +@@ -169,7 +169,7 @@ do_test (void) + + { + struct stat64 st; +- xstat (_PATH_BSHELL, &st); ++ xstat64 (_PATH_BSHELL, &st); + mode_t mode = st.st_mode; + xchmod (_PATH_BSHELL, mode & ~(S_IXUSR | S_IXGRP | S_IXOTH)); + +diff --git a/support/Makefile b/support/Makefile +index 5b1c96a49e9410f4..6e3c55394fa212b6 100644 +--- a/support/Makefile ++++ b/support/Makefile +@@ -42,14 +42,12 @@ libsupport-routines = \ + resolv_test \ + set_fortify_handler \ + support-open-dev-null-range \ +- support-xfstat \ +- support-xfstat-time64 \ +- support-xstat \ +- support-xstat-time64 \ + support_become_root \ + support_can_chroot \ + support_capture_subprocess \ + support_capture_subprocess_check \ ++ support_check_stat_fd \ ++ support_check_stat_path \ + support_chroot \ + support_copy_file \ + support_copy_file_range \ +@@ -135,8 +133,6 @@ libsupport-routines = \ + xgetsockname \ + xlisten \ + xlseek \ +- xlstat \ +- xlstat-time64 \ + xmalloc \ + xmemstream \ + xmkdir \ +diff --git a/support/support-xfstat-time64.c b/support/support-xfstat-time64.c +deleted file mode 100644 +index 589a69bb3e04857d..0000000000000000 +--- a/support/support-xfstat-time64.c ++++ /dev/null +@@ -1,32 +0,0 @@ +-/* 64-bit time_t stat with error checking. +- Copyright (C) 2021-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 +- . */ +- +-/* NB: Non-standard file name to avoid sysdeps override for xstat. */ +- +-#include +-#include +-#include +- +-#if __TIMESIZE != 64 +-void +-xfstat_time64 (int fd, struct __stat64_t64 *result) +-{ +- if (__fstat64_time64 (fd, result) != 0) +- FAIL_EXIT1 ("__fstat64_time64 (%d): %m", fd); +-} +-#endif +diff --git a/support/support-xstat-time64.c b/support/support-xstat-time64.c +deleted file mode 100644 +index 451948734ae0de0f..0000000000000000 +--- a/support/support-xstat-time64.c ++++ /dev/null +@@ -1,32 +0,0 @@ +-/* 64-bit time_t stat with error checking. +- Copyright (C) 2021-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 +- . */ +- +-/* NB: Non-standard file name to avoid sysdeps override for xstat. */ +- +-#include +-#include +-#include +- +-#if __TIMESIZE != 64 +-void +-xstat_time64 (const char *path, struct __stat64_t64 *result) +-{ +- if (__stat64_time64 (path, result) != 0) +- FAIL_EXIT1 ("__stat64_time64 (\"%s\"): %m", path); +-} +-#endif +diff --git a/support/support-xstat.c b/support/support-xstat.c +deleted file mode 100644 +index ce866f74d242c07a..0000000000000000 +--- a/support/support-xstat.c ++++ /dev/null +@@ -1,30 +0,0 @@ +-/* stat64 with error checking. +- Copyright (C) 2017-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 +- . */ +- +-/* NB: Non-standard file name to avoid sysdeps override for xstat. */ +- +-#include +-#include +-#include +- +-void +-xstat (const char *path, struct stat64 *result) +-{ +- if (stat64 (path, result) != 0) +- FAIL_EXIT1 ("stat64 (\"%s\"): %m", path); +-} +diff --git a/support/xlstat.c b/support/support_check_stat_fd.c +similarity index 76% +rename from support/xlstat.c +rename to support/support_check_stat_fd.c +index 87df988879094d5b..4c12adf6b7f68d0c 100644 +--- a/support/xlstat.c ++++ b/support/support_check_stat_fd.c +@@ -1,5 +1,5 @@ +-/* lstat64 with error checking. +- Copyright (C) 2017-2024 Free Software Foundation, Inc. ++/* Error checking for descriptor-based stat functions. ++ 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 +@@ -18,11 +18,10 @@ + + #include + #include +-#include + + void +-xlstat (const char *path, struct stat64 *result) ++support_check_stat_fd (const char *name, int fd, int result) + { +- if (lstat64 (path, result) != 0) +- FAIL_EXIT1 ("lstat64 (\"%s\"): %m", path); ++ if (result != 0) ++ FAIL_EXIT1 ("%s (%d): %m", name, fd); + } +diff --git a/support/support-xfstat.c b/support/support_check_stat_path.c +similarity index 81% +rename from support/support-xfstat.c +rename to support/support_check_stat_path.c +index ab4b01c97d7b5f2f..3cf72afe5901dcd5 100644 +--- a/support/support-xfstat.c ++++ b/support/support_check_stat_path.c +@@ -1,4 +1,4 @@ +-/* fstat64 with error checking. ++/* Error checking for path-based stat functions. + Copyright (C) 2017-2024 Free Software Foundation, Inc. + This file is part of the GNU C Library. + +@@ -18,11 +18,10 @@ + + #include + #include +-#include + + void +-xfstat (int fd, struct stat64 *result) ++support_check_stat_path (const char *name, const char *path, int result) + { +- if (fstat64 (fd, result) != 0) +- FAIL_EXIT1 ("fstat64 (%d): %m", fd); ++ if (result != 0) ++ FAIL_EXIT1 ("%s (\"%s\"): %m", name, path); + } +diff --git a/support/xlstat-time64.c b/support/xlstat-time64.c +deleted file mode 100644 +index 2bc3ca6593bd397d..0000000000000000 +--- a/support/xlstat-time64.c ++++ /dev/null +@@ -1,32 +0,0 @@ +-/* 64-bit time_t stat with error checking. +- Copyright (C) 2021-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 +- . */ +- +-/* NB: Non-standard file name to avoid sysdeps override for xstat. */ +- +-#include +-#include +-#include +- +-#if __TIMESIZE != 64 +-void +-xlstat_time64 (const char *path, struct __stat64_t64 *result) +-{ +- if (__lstat64_time64 (path, result) != 0) +- FAIL_EXIT1 ("__lstat64_time64 (\"%s\"): %m", path); +-} +-#endif +diff --git a/support/xunistd.h b/support/xunistd.h +index cc74c4fad07dd088..204951bce75f576b 100644 +--- a/support/xunistd.h ++++ b/support/xunistd.h +@@ -29,7 +29,6 @@ + + __BEGIN_DECLS + +-struct stat64; + struct statx; + + pid_t xfork (void); +@@ -37,21 +36,20 @@ pid_t xwaitpid (pid_t, int *status, int flags); + void xpipe (int[2]); + void xdup2 (int, int); + int xopen (const char *path, int flags, mode_t); +-#ifndef __USE_TIME64_REDIRECTS +-# ifdef __USE_FILE_OFFSET64 +-void xstat (const char *path, struct stat *); +-void xlstat (const char *path, struct stat *); +-void xfstat (int fd, struct stat *); +-# else +-void xstat (const char *path, struct stat64 *); +-void xlstat (const char *path, struct stat64 *); +-void xfstat (int fd, struct stat64 *); +-# endif +-#else +-void __REDIRECT (xstat, (const char *path, struct stat *), xstat_time64); +-void __REDIRECT (xlstat, (const char *path, struct stat *), xlstat_time64); +-void __REDIRECT (xfstat, (int fd, struct stat *), xfstat_time64); +-#endif ++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); ++#define xstat(path, st) \ ++ (support_check_stat_path ("stat", (path), stat ((path), (st)))) ++#define xfstat(fd, st) \ ++ (support_check_stat_fd ("fstat", (fd), fstat ((fd), (st)))) ++#define xlstat(path, st) \ ++ (support_check_stat_path ("lstat", (path), lstat ((path), (st)))) ++#define xstat64(path, st) \ ++ (support_check_stat_path ("stat64", (path), stat64 ((path), (st)))) ++#define xfstat64(fd, st) \ ++ (support_check_stat_fd ("fstat64", (fd), fstat64 ((fd), (st)))) ++#define xlstat64(path, st) \ ++ (support_check_stat_path ("lstat64", (path), lstat64 ((path), (st)))) + void xstatx (int, const char *, int, unsigned int, struct statx *); + void xmkdir (const char *path, mode_t); + void xchroot (const char *path); diff --git a/glibc-RHEL-106562-8.patch b/glibc-RHEL-106562-8.patch new file mode 100644 index 0000000..2eb8c05 --- /dev/null +++ b/glibc-RHEL-106562-8.patch @@ -0,0 +1,148 @@ +commit 2eee835eca960c9d4119279804214b7a1ed5d156 +Author: DJ Delorie +Date: Thu Aug 8 22:44:56 2024 -0400 + + inet: test if_nametoindex and if_indextoname + + Tests for if_nameindex, if_name2index, and if_index2name + + Tests that valid results are consistent. + + Tests that invalid parameters fail correctly. + + Reviewed-by: Florian Weimer + +diff --git a/inet/Makefile b/inet/Makefile +index 2f03e6f7ee211525..cb97b45f0f9d223f 100644 +--- a/inet/Makefile ++++ b/inet/Makefile +@@ -91,6 +91,7 @@ tests := \ + tst-getni1 \ + tst-getni2 \ + tst-if_index-long \ ++ tst-if_nameindex \ + tst-inet6_rth \ + tst-network \ + tst-ntoa \ +diff --git a/inet/tst-if_nameindex.c b/inet/tst-if_nameindex.c +new file mode 100644 +index 0000000000000000..b025cdb3a7c6b68c +--- /dev/null ++++ b/inet/tst-if_nameindex.c +@@ -0,0 +1,116 @@ ++/* Tests for if_nameindex et al. ++ 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 ++ . */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static char *buffer; ++ ++static const char *test_names[] = { ++ "testing", ++ "invalid", ++ "dont-match", ++ "", ++ "\001\001\001\177", ++ NULL ++}; ++ ++static void ++checki (int i) ++{ ++ char *ifname; ++ ++ /* Test that a known-invalid index returns NULL. */ ++ /* BUFFER should not be accessed. */ ++ ++ printf ("Testing if_indextoname (%d) == NULL\n", i); ++ ifname = if_indextoname (i, NULL); ++ TEST_VERIFY (ifname == NULL); ++ TEST_VERIFY (errno == ENXIO); ++} ++ ++static int ++do_test (void) ++{ ++ struct if_nameindex *if_ni, *ifp; ++ int min_idx, max_idx, buflen = 0; ++ int i; ++ ++ if_ni = if_nameindex (); ++ TEST_VERIFY (if_ni != NULL); ++ ++ min_idx = max_idx = if_ni->if_index; ++ ++ for (ifp = if_ni; !(ifp->if_index == 0 && ifp->if_name == NULL); ifp++) ++ { ++ printf ("%u: %s\n", ifp->if_index, ifp->if_name); ++ if (ifp->if_index < min_idx) ++ min_idx = ifp->if_index; ++ if (ifp->if_index > max_idx) ++ max_idx = ifp->if_index; ++ if (strlen (ifp->if_name) + 1 > buflen) ++ buflen = strlen (ifp->if_name) + 1; ++ } ++ buffer = (char *) xmalloc (buflen); ++ ++ /* Check normal operation. */ ++ for (ifp = if_ni; !(ifp->if_index == 0 && ifp->if_name == NULL); ifp++) ++ { ++ unsigned int idx = if_nametoindex (ifp->if_name); ++ TEST_VERIFY (idx == ifp->if_index); ++ ++ char *fn = if_indextoname (ifp->if_index, buffer); ++ TEST_VERIFY (strcmp (fn, ifp->if_name) == 0); ++ } ++ ++ for (i=-2; iif_index == 0 && ifp->if_name == NULL); ifp++) ++ if (strcmp (test_names[i], ifp->if_name) == 0) ++ goto not_this_one; ++ ++ printf ("Testing if_nametoindex (%s) == 0\n", test_names[i]); ++ ++ unsigned int idx = if_nametoindex (test_names[i]); ++ TEST_VERIFY (idx == 0); ++ TEST_VERIFY (errno == ENODEV); ++ ++ not_this_one: ++ } ++ ++ ++ if_freenameindex (if_ni); ++ ++ return 0; ++} ++ ++#include diff --git a/glibc-RHEL-106562-9.patch b/glibc-RHEL-106562-9.patch new file mode 100644 index 0000000..e7b1bda --- /dev/null +++ b/glibc-RHEL-106562-9.patch @@ -0,0 +1,79 @@ +commit 55cd51d971b84fbb2cc0fe8140cc8581f98582c7 +Author: Joseph Myers +Date: Thu Aug 22 11:25:14 2024 +0000 + + Test mkdirat use of mode argument + + The test io/tst-mkdirat doesn't verify the permissions on the created + directory (thus, doesn't verify at all anything about how mkdirat uses + the mode argument). Add checks of this to the existing test. + + Tested for x86_64. + +diff --git a/io/tst-mkdirat.c b/io/tst-mkdirat.c +index 605e51ef1e966b42..b97bc3ca6d0cdf23 100644 +--- a/io/tst-mkdirat.c ++++ b/io/tst-mkdirat.c +@@ -53,6 +53,10 @@ prepare (void) + static int + do_test (void) + { ++ /* Find the current umask. */ ++ mode_t mask = umask (022); ++ umask (mask); ++ + /* fdopendir takes over the descriptor, make a copy. */ + int dupfd = dup (dir_fd); + if (dupfd == -1) +@@ -107,6 +111,13 @@ do_test (void) + puts ("mkdirat did not create a directory"); + return 1; + } ++ if ((st1.st_mode & 01777) != (~mask & 0777)) ++ { ++ printf ("mkdirat created directory with wrong mode %o, expected %o\n", ++ (unsigned int) (st1.st_mode & 01777), ++ (unsigned int) (~mask & 0777)); ++ return 1; ++ } + + dupfd = dup (dir_fd); + if (dupfd == -1) +@@ -156,6 +167,37 @@ do_test (void) + return 1; + } + ++ /* Test again with a different mode. */ ++ umask (0); ++ e = mkdirat (dir_fd, "some-dir", 01755); ++ umask (mask); ++ if (e == -1) ++ { ++ puts ("directory creation (different mode) failed"); ++ return 1; ++ } ++ if (fstatat64 (dir_fd, "some-dir", &st1, 0) != 0) ++ { ++ puts ("fstat64 (different mode) failed"); ++ return 1; ++ } ++ if (!S_ISDIR (st1.st_mode)) ++ { ++ puts ("mkdirat (different mode) did not create a directory"); ++ return 1; ++ } ++ if ((st1.st_mode & 01777) != 01755) ++ { ++ printf ("mkdirat (different mode) created directory with wrong mode %o\n", ++ (unsigned int) (st1.st_mode & 01777)); ++ return 1; ++ } ++ if (unlinkat (dir_fd, "some-dir", AT_REMOVEDIR) != 0) ++ { ++ puts ("unlinkat (different mode) failed"); ++ return 1; ++ } ++ + close (dir_fd); + + return 0; diff --git a/glibc.spec b/glibc.spec index c810d91..686d803 100644 --- a/glibc.spec +++ b/glibc.spec @@ -145,7 +145,7 @@ Version: %{glibcversion} # - It allows using the Release number without the %%dist tag in the dependency # generator to make the generated requires interchangeable between Rawhide # and ELN (.elnYY < .fcXX). -%global baserelease 49 +%global baserelease 50 Release: %{baserelease}%{?dist} # Licenses: @@ -591,6 +591,30 @@ Patch267: glibc-RHEL-72564-2.patch Patch268: glibc-RHEL-107540-1.patch Patch269: glibc-RHEL-107540-2.patch Patch270: glibc-RHEL-107540-3.patch +Patch271: glibc-RHEL-106562-1.patch +Patch272: glibc-RHEL-106562-2.patch +Patch273: glibc-RHEL-106562-3.patch +Patch274: glibc-RHEL-106562-4.patch +Patch275: glibc-RHEL-106562-5.patch +Patch276: glibc-RHEL-106562-6.patch +Patch277: glibc-RHEL-106562-7.patch +Patch278: glibc-RHEL-106562-8.patch +Patch279: glibc-RHEL-106562-9.patch +Patch280: glibc-RHEL-106562-10.patch +Patch281: glibc-RHEL-106562-11.patch +Patch282: glibc-RHEL-106562-12.patch +Patch283: glibc-RHEL-106562-13.patch +Patch284: glibc-RHEL-106562-14.patch +Patch285: glibc-RHEL-106562-15.patch +Patch286: glibc-RHEL-106562-16.patch +Patch287: glibc-RHEL-106562-17.patch +Patch288: glibc-RHEL-106562-18.patch +Patch289: glibc-RHEL-106562-19.patch +Patch290: glibc-RHEL-106562-20.patch +Patch291: glibc-RHEL-106562-21.patch +Patch292: glibc-RHEL-106562-22.patch +Patch293: glibc-RHEL-106562-23.patch +Patch294: glibc-RHEL-106562-24.patch ############################################################################## # Continued list of core "glibc" package information: @@ -2603,6 +2627,9 @@ update_gconv_modules_cache () %endif %changelog +* Wed Aug 06 2025 Arjun Shankar - 2.39-50 +- Improve test coverage (RHEL-106562) + * Tue Aug 05 2025 Florian Weimer - 2.39-49 - x86_64, aarch64: More CPU output in ld.so --list-diagnostics (RHEL-107540)