Backport fwrite tests and a fix for BZ 29459

Resolves: RHEL-55471
This commit is contained in:
Tulio Magno Quites Machado Filho 2025-02-17 10:37:17 -03:00
parent 283b9330df
commit e87166d350
11 changed files with 1370 additions and 1 deletions

94
glibc-RHEL-55471-1.patch Normal file
View File

@ -0,0 +1,94 @@
commit 5d4ab106d4cf7d6e410d6fc3d460b090c9108682
Author: Tulio Magno Quites Machado Filho <tuliom@redhat.com>
Date: Thu Sep 5 15:34:29 2024 -0300
Add a new fwrite test for read-only streams
Ensure that fwrite() behaves correctly even when the stream is
read-only.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index 74e0edff73a9e468..4c2b820c641e8b55 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -217,6 +217,7 @@ tests := \
tst-freopen64-7 \
tst-fseek \
tst-fwrite \
+ tst-fwrite-ro \
tst-getline \
tst-getline-enomem \
tst-gets \
diff --git a/stdio-common/tst-fwrite-ro.c b/stdio-common/tst-fwrite-ro.c
new file mode 100644
index 0000000000000000..7013bee53cc494d0
--- /dev/null
+++ b/stdio-common/tst-fwrite-ro.c
@@ -0,0 +1,65 @@
+/* Test fwrite on a read-only stream.
+ Copyright (C) 2024 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <support/check.h>
+#include <support/temp_file.h>
+#include <support/xstdio.h>
+#include <support/xunistd.h>
+
+/* A small buffer size is enough to run this test. */
+#define BUFSIZE 4
+
+static int
+do_test (void)
+{
+ int fd;
+ FILE *f;
+ struct stat64 st;
+
+ /* Create a temporary file and open it in read-only mode. */
+ fd = create_temp_file ("tst-fwrite-ro", NULL);
+ TEST_VERIFY_EXIT (fd != -1);
+ f = fdopen (fd, "r");
+ TEST_VERIFY_EXIT (f != NULL);
+
+ /* Try to write to the temporary file with nmemb = 0, then check that
+ fwrite returns 0. No errors are expected from this. */
+ TEST_COMPARE (fwrite ("a", 1, 0, f), 0);
+ TEST_COMPARE (ferror (f), 0);
+
+ /* Try to write to the temporary file with size = 0, then check that
+ fwrite returns 0. No errors are expected from this. */
+ TEST_COMPARE (fwrite ("a", 0, 1, f), 0);
+ TEST_COMPARE (ferror (f), 0);
+
+ /* Try to write a single byte to the temporary file, then check that
+ fwrite returns 0. Check if an error was reported. */
+ TEST_COMPARE (fwrite ("a", 1, 1, f), 0);
+ TEST_COMPARE (ferror (f), 1);
+ clearerr (f);
+
+ xfstat64 (fd, &st);
+ TEST_COMPARE (st.st_size, 0);
+
+ xfclose (f);
+
+ return 0;
+}
+
+#include <support/test-driver.c>

39
glibc-RHEL-55471-10.patch Normal file
View File

@ -0,0 +1,39 @@
commit 4734d0f8adde573aeafe79ad0c71807833db1cae
Author: Stefan Liebler <stli@linux.ibm.com>
Date: Mon Feb 24 14:13:00 2025 +0100
Increase the amount of data tested in stdio-common/tst-fwrite-pipe.c
The number of iterations and the length of the string are not high
enough on some systems causing the test to return false-positives.
Testcase stdio-common/tst-fwrite-bz29459.c was fixed in the same way in
1b6f868625403d6b7683af840e87d2b18d5d7731
(Increase the amount of data tested in stdio-common/tst-fwrite-bz29459.c, 2025-02-14)
Testcases stdio-common/tst-fwrite-bz29459.c and stdio-common/tst-fwrite-pipe.c
were introcued in 596a61cf6b51ce2d58b8ca4e1d1f4fdfe1440dbc
(libio: Start to return errors when flushing fwrite's buffer [BZ #29459], 2025-01-28)
diff --git a/stdio-common/tst-fwrite-pipe.c b/stdio-common/tst-fwrite-pipe.c
index a6119125b25eeddb..ce1a92b384279600 100644
--- a/stdio-common/tst-fwrite-pipe.c
+++ b/stdio-common/tst-fwrite-pipe.c
@@ -27,7 +27,7 @@
/* Usually this test reproduces in a few iterations. However, keep a high
number of iterations in order to avoid return false-positives due to an
overwhelmed/slow system. */
-#define ITERATIONS 5000
+#define ITERATIONS 500000
#define BUFFERSIZE 20
@@ -71,7 +71,7 @@ do_test (void)
{
/* Ensure the string we send has a new line because we're dealing
with a lined-buffered stream. */
- const char *s = "hello\n";
+ const char *s = "hello world\n";
size_t len = strlen (s);
int i;

205
glibc-RHEL-55471-2.patch Normal file
View File

@ -0,0 +1,205 @@
commit dccc9a5161264d2f98411c24ae22495ca3a09b60
Author: Tulio Magno Quites Machado Filho <tuliom@redhat.com>
Date: Thu Aug 29 14:12:41 2024 -0300
Add a new fwrite test for memory streams
Ensure that fwrite() behaves correctly when using memory streams.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index 4c2b820c641e8b55..a483234085a6c612 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -217,6 +217,7 @@ tests := \
tst-freopen64-7 \
tst-fseek \
tst-fwrite \
+ tst-fwrite-memstrm \
tst-fwrite-ro \
tst-getline \
tst-getline-enomem \
diff --git a/stdio-common/tst-fwrite-memstrm.c b/stdio-common/tst-fwrite-memstrm.c
new file mode 100644
index 0000000000000000..7ee38314302ba794
--- /dev/null
+++ b/stdio-common/tst-fwrite-memstrm.c
@@ -0,0 +1,177 @@
+/* Test fwrite on a memory stream.
+ Copyright (C) 2024 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/temp_file.h>
+#include <support/xstdio.h>
+#include <support/xunistd.h>
+
+void
+test_ro (void)
+{
+ FILE *f;
+ char *out;
+
+ /* Try to allocate a small buffer for this test. */
+ out = malloc (2);
+ TEST_VERIFY_EXIT (out != NULL);
+
+ /* Try to open the allocated buffer as a read-only stream. */
+ f = fmemopen (out, 2, "r");
+ TEST_VERIFY_EXIT (f != NULL);
+
+ /* Try to write to the temporary file with nmemb = 0, then check that
+ fwrite returns 0. No errors are expected from this. */
+ TEST_COMPARE (fwrite ("a", 1, 0, f), 0);
+ TEST_COMPARE (ferror (f), 0);
+
+ /* Try to write to the temporary file with size = 0, then check that
+ fwrite returns 0. No errors are expected from this. */
+ TEST_COMPARE (fwrite ("a", 0, 1, f), 0);
+ TEST_COMPARE (ferror (f), 0);
+
+ /* Try to write a single byte to the temporary file, then check that
+ fwrite returns 0. Check if an error was reported. */
+ TEST_COMPARE (fwrite ("a", 1, 1, f), 0);
+ TEST_COMPARE (ferror (f), 1);
+
+ clearerr (f);
+ xfclose (f);
+ free (out);
+}
+
+/* Length of the output buffer in bytes. */
+#define RWBUF_SIZE 16 * 1024
+/* Maximum number of bytes to be written in output buffer. The rest will be
+ used to check against overflow. */
+#define RWBUF_SIZE_WRITABLE RWBUF_SIZE-2048
+
+/* Use the following byte to identify areas that should have not been
+ modified. */
+#define KNOWN_BYTE 0xaa
+
+void
+test_one_rw (const char *in, size_t size, size_t nmemb,
+ size_t expected_ret)
+{
+ FILE *f;
+ char *out, *expected_out;
+ /* Total number of bytes expected to be written. */
+ size_t expected_bytes = size * nmemb;
+
+ printf ("Testing with size = %zd, nmemb = %zd\n", size, nmemb);
+
+ TEST_VERIFY_EXIT (expected_ret <= RWBUF_SIZE_WRITABLE);
+ TEST_VERIFY_EXIT (expected_bytes <= RWBUF_SIZE_WRITABLE);
+
+ /* Try to allocate a buffer for this test and initialize it with
+ known contents. */
+ out = malloc (RWBUF_SIZE);
+ TEST_VERIFY_EXIT (out != NULL);
+ memset (out, KNOWN_BYTE, RWBUF_SIZE);
+
+ /* Try to allocate a buffer and fill it with the contents that are expected
+ to be in memory after flushing/closing the memory stream. */
+ expected_out = malloc (RWBUF_SIZE);
+ TEST_VERIFY_EXIT (expected_out != NULL);
+ if (expected_bytes > 0)
+ {
+ memcpy (expected_out, in, expected_bytes);
+ expected_out[expected_bytes] = 0;
+ memset (expected_out + expected_bytes + 1, KNOWN_BYTE,
+ RWBUF_SIZE - expected_bytes - 1);
+ }
+ else
+ {
+ /* No changes to the output are expected. */
+ memset (expected_out, KNOWN_BYTE, RWBUF_SIZE);
+ }
+
+ /* Try to open the allocated buffer as a read-write stream. */
+ f = fmemopen (out, RWBUF_SIZE, "w");
+ TEST_VERIFY_EXIT (f != NULL);
+
+ /* Try to write to the memory stream. Check if fwrite() returns the
+ expected value. No errors are expected. */
+ TEST_COMPARE (fwrite (in, size, nmemb, f), expected_ret);
+ TEST_COMPARE (ferror (f), 0);
+
+ xfclose (f);
+
+ /* Ensure the output has the expected contents. */
+ TEST_COMPARE (memcmp (out, expected_out, expected_bytes), 0);
+
+ free (expected_out);
+ free (out);
+}
+
+void
+test_rw (void)
+{
+ char * in;
+ int i, j;
+ size_t size[] = {1, 8, 11, 16, 17, 0};
+ size_t nmemb[] = {32, 83, 278, 709, 4097, RWBUF_SIZE / 2,
+ RWBUF_SIZE_WRITABLE, 0};
+ size_t n;
+
+ /* Try to write to the temporary file with nmemb = 0, then check that
+ fwrite returns 0; */
+ test_one_rw ("a", 1, 0, 0);
+
+ /* Try to write to the temporary file with size = 0, then check that
+ fwrite returns 0; */
+ test_one_rw ("a", 0, 1, 0);
+
+ /* Try to write a single byte to the temporary file, then check that
+ fwrite returns 1; */
+ test_one_rw ("a", 1, 2, 2);
+
+ in = malloc (RWBUF_SIZE);
+ TEST_VERIFY_EXIT (in != NULL);
+ for (i = 0; i < RWBUF_SIZE / 2; i++)
+ in[i] = i % 0xff;
+
+ /* Test with all posibilities of size[] x nmemb[]. */
+ for (i = 0; nmemb[i] != 0; i++)
+ {
+ for (j = 0; size[j] != 0; j++)
+ {
+ n = nmemb[i] / size[j];
+ test_one_rw (in, size[j], n, n);
+ }
+ /* Run the test with a single item of maximum size. */
+ test_one_rw (in, nmemb[i], 1, 1);
+ }
+
+ free (in);
+}
+
+static int
+do_test (void)
+{
+ test_ro ();
+ test_rw ();
+
+ return 0;
+}
+
+#include <support/test-driver.c>

166
glibc-RHEL-55471-3.patch Normal file
View File

@ -0,0 +1,166 @@
commit 97aa92263a151d12286d27d327edc35475fe521c
Author: Tulio Magno Quites Machado Filho <tuliom@redhat.com>
Date: Thu Sep 26 11:30:29 2024 -0300
Add a new fwrite test that exercises buffer overflow
Exercises fwrite's internal buffer when doing a file operation.
The new test, exercises 2 overflow behaviors:
1. Call fwrite multiple times making usage of fwrite's internal buffer.
The total number of bytes written is larger than fwrite's internal
buffer, forcing an automatic flush.
2. Call fwrite a single time with an amount of data that is larger than
fwrite's internal buffer.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index a483234085a6c612..71f6ea12d103564c 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -218,6 +218,7 @@ tests := \
tst-fseek \
tst-fwrite \
tst-fwrite-memstrm \
+ tst-fwrite-overflow \
tst-fwrite-ro \
tst-getline \
tst-getline-enomem \
diff --git a/stdio-common/tst-fwrite-overflow.c b/stdio-common/tst-fwrite-overflow.c
new file mode 100644
index 0000000000000000..fe503fd5890a4812
--- /dev/null
+++ b/stdio-common/tst-fwrite-overflow.c
@@ -0,0 +1,130 @@
+/* Test the overflow of fwrite's internal buffer.
+ Copyright (C) 2024 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+/* stdio.h provides BUFSIZ, which is the size of fwrite's internal buffer. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/temp_file.h>
+#include <support/support.h>
+#include <support/xstdio.h>
+#include <support/xunistd.h>
+
+/* Length of the buffers in bytes. */
+#define RWBUF_SIZE (2 * BUFSIZ)
+
+void
+test_one_rw (const char *in, size_t size, size_t nmemb, size_t blocks)
+{
+ int fd;
+ FILE *f;
+ char *out;
+ size_t written, to_write;
+ const size_t requested = size * nmemb;
+
+ printf ("Testing with size = %zd, nmemb = %zd, blocks = %zd\n",
+ size, nmemb, blocks);
+
+ TEST_VERIFY_EXIT (requested <= RWBUF_SIZE);
+ /* Ensure fwrite's internal buffer will overflow. */
+ TEST_VERIFY_EXIT (requested > BUFSIZ);
+
+ /* Create a temporary file and open it for reading and writing. */
+ fd = create_temp_file ("tst-fwrite-overflow", NULL);
+ TEST_VERIFY_EXIT (fd != -1);
+ f = fdopen (fd, "w+");
+ TEST_VERIFY_EXIT (f != NULL);
+
+ /* Call fwrite() as many times as needed, until all data is written,
+ limiting the amount of data written per call to block items. */
+ for (written = 0; written < nmemb; written += to_write)
+ {
+ if (written + blocks <= nmemb)
+ to_write = blocks;
+ else
+ to_write = nmemb - written;
+ /* Check if fwrite() returns the expected value. No errors are
+ expected. */
+ TEST_COMPARE (fwrite (in + size * written, size, to_write, f),
+ to_write);
+ TEST_COMPARE (ferror (f), 0);
+ }
+ TEST_VERIFY_EXIT (written == nmemb);
+
+ /* Ensure all the data is flushed to file. */
+ TEST_COMPARE (fflush (f), 0);
+
+ /* We have to check if the contents in the file are correct. Go back to
+ the beginning of the file. */
+ rewind (f);
+ /* Try to allocate a buffer and save the contents of the generated file to
+ it. */
+ out = xmalloc (RWBUF_SIZE);
+ TEST_COMPARE (fread (out, size, nmemb, f), nmemb);
+
+ /* Ensure the output has the expected contents. */
+ TEST_COMPARE (memcmp (out, in, requested), 0);
+
+ xfclose (f);
+ free (out);
+}
+
+static int
+do_test (void)
+{
+ char * in;
+ int i, j;
+ size_t nmemb[] = {BUFSIZ + 1, RWBUF_SIZE, 0};
+ /* Maximum number of items written for each fwrite call. */
+ size_t block[] = {100, 1024, 2047, 0};
+ /* The largest block must fit entirely in fwrite's buffer. */
+ _Static_assert (2047 < BUFSIZ,
+ "a block must fit in fwrite's internal buffer");
+
+ in = xmalloc (RWBUF_SIZE);
+ for (i = 0; i < RWBUF_SIZE; i++)
+ in[i] = i % 0xff;
+
+ for (i = 0; nmemb[i] != 0; i++)
+ for (j = 0; block[j] != 0; j++)
+ {
+ /* Run a test with an array of nmemb bytes. Write at most block
+ items per fwrite call. */
+ test_one_rw (in, 1, nmemb[i], block[j]);
+ /* Run a test that overflows fwrite's internal buffer in a single call
+ by writting a single item of nmemb bytes.
+ This call should not use the buffer and should be written directly
+ to the file. */
+ test_one_rw (in, nmemb[i], 1, nmemb[i]);
+ }
+
+ for (j = 0; block[j] != 0; j++)
+ {
+ /* Run a test with size=2 and the minimum nmemb value that still
+ overflows the buffer. Write at most block items per fwrite call. */
+ test_one_rw (in, 2, BUFSIZ / 2 + 1, block[j]);
+ /* Likewise, but size=3. */
+ test_one_rw (in, 3, BUFSIZ / 3 + 1, block[j]);
+ }
+
+ free (in);
+ return 0;
+}
+
+#include <support/test-driver.c>

445
glibc-RHEL-55471-4.patch Normal file
View File

@ -0,0 +1,445 @@
commit 596a61cf6b51ce2d58b8ca4e1d1f4fdfe1440dbc
Author: Tulio Magno Quites Machado Filho <tuliom@redhat.com>
Date: Tue Jan 28 15:37:44 2025 -0300
libio: Start to return errors when flushing fwrite's buffer [BZ #29459]
When an error happens, fwrite is expected to return a value that is less
than nmemb. If this error happens while flushing its internal buffer,
fwrite is in a complex scenario: all the data might have been written to
the buffer, indicating a successful copy, but the buffer is expected to
be flushed and it was not.
POSIX.1-2024 states the following about errors on fwrite:
If an error occurs, the resulting value of the file-position indicator
for the stream is unspecified.
The fwrite() function shall return the number of elements successfully
written, which may be less than nitems if a write error is encountered.
With that in mind, this commit modifies _IO_new_file_write in order to
return the total number of bytes written via the file pointer. It also
modifies fwrite in order to use the new information and return the
correct number of bytes written even when sputn returns EOF.
Add 2 tests:
1. tst-fwrite-bz29459: This test is based on the reproducer attached to
bug 29459. In order to work, it requires to pipe stdout to another
process making it hard to reuse test-driver.c. This code is more
specific to the issue reported.
2. tst-fwrite-pipe: Recreates the issue by creating a pipe that is shared
with a child process. Reuses test-driver.c. Evaluates a more generic
scenario.
Co-authored-by: Florian Weimer <fweimer@redhat.com>
Reviewed-by: DJ Delorie <dj@redhat.com>
Conflicts:
libio/bits/types/struct_FILE.h
(Downstream is missing commit 2a99e2398d9d717c034e915f7846a49e623f5450)
diff --git a/libio/bits/types/struct_FILE.h b/libio/bits/types/struct_FILE.h
index f7f756a701ce0e93..7292334a28ad3f79 100644
--- a/libio/bits/types/struct_FILE.h
+++ b/libio/bits/types/struct_FILE.h
@@ -102,8 +102,15 @@ struct _IO_FILE_complete
void *_freeres_buf;
size_t __pad5;
int _mode;
+#ifdef __LP64__
+ int _unused3;
+#endif
+ __uint64_t _total_written;
+#ifndef __LP64__
+ int _unused3;
+#endif
/* Make sure we don't get into trouble again. */
- char _unused2[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)];
+ char _unused2[12 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)];
};
/* These macros are used by bits/stdio.h and internal headers. */
diff --git a/libio/fileops.c b/libio/fileops.c
index f43ad59c5a5bca7d..b2354d42b420b80c 100644
--- a/libio/fileops.c
+++ b/libio/fileops.c
@@ -114,6 +114,7 @@ _IO_new_file_init_internal (struct _IO_FILE_plus *fp)
_IO_link_in (fp);
fp->file._fileno = -1;
+ fp->file._total_written = 0;
}
/* External version of _IO_new_file_init_internal which switches off
@@ -1185,6 +1186,7 @@ _IO_new_file_write (FILE *f, const void *data, ssize_t n)
f->_flags |= _IO_ERR_SEEN;
break;
}
+ f->_total_written += count;
to_do -= count;
data = (void *) ((char *) data + count);
}
diff --git a/libio/iofwrite.c b/libio/iofwrite.c
index 71b609c526b79071..5c648302c8fd9224 100644
--- a/libio/iofwrite.c
+++ b/libio/iofwrite.c
@@ -36,13 +36,42 @@ _IO_fwrite (const void *buf, size_t size, size_t count, FILE *fp)
return 0;
_IO_acquire_lock (fp);
if (_IO_vtable_offset (fp) != 0 || _IO_fwide (fp, -1) == -1)
- written = _IO_sputn (fp, (const char *) buf, request);
+ {
+ /* Compute actually written bytes plus pending buffer
+ contents. */
+ uint64_t original_total_written
+ = fp->_total_written + (fp->_IO_write_ptr - fp->_IO_write_base);
+ written = _IO_sputn (fp, (const char *) buf, request);
+ if (written == EOF)
+ {
+ /* An error happened and we need to find the appropriate return
+ value. There 3 possible scenarios:
+ 1. If the number of bytes written is between 0..[buffer content],
+ we need to return 0 because none of the bytes from this
+ request have been written;
+ 2. If the number of bytes written is between
+ [buffer content]+1..request-1, that means we managed to write
+ data requested in this fwrite call;
+ 3. We might have written all the requested data and got an error
+ anyway. We can't return success, which means we still have to
+ return less than request. */
+ if (fp->_total_written > original_total_written)
+ {
+ written = fp->_total_written - original_total_written;
+ /* If everything was reported as written and somehow an
+ error occurred afterwards, avoid reporting success. */
+ if (written == request)
+ --written;
+ }
+ else
+ /* Only already-pending buffer contents was written. */
+ written = 0;
+ }
+ }
_IO_release_lock (fp);
/* We have written all of the input in case the return value indicates
- this or EOF is returned. The latter is a special case where we
- simply did not manage to flush the buffer. But the data is in the
- buffer and therefore written as far as fwrite is concerned. */
- if (written == request || written == EOF)
+ this. */
+ if (written == request)
return count;
else
return written / size;
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index 71f6ea12d103564c..b4a1e62f4a388d0a 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -219,6 +219,7 @@ tests := \
tst-fwrite \
tst-fwrite-memstrm \
tst-fwrite-overflow \
+ tst-fwrite-pipe \
tst-fwrite-ro \
tst-getline \
tst-getline-enomem \
@@ -276,6 +277,7 @@ endif
test-srcs = \
$(xprintf-srcs) \
+ tst-fwrite-bz29459 \
tst-printf \
tst-printfsz-islongdouble \
tst-unbputc \
@@ -284,6 +286,7 @@ test-srcs = \
ifeq ($(run-built-tests),yes)
tests-special += \
$(foreach f,$(xprintf-stems),$(objpfx)$(f).out) \
+ $(objpfx)tst-fwrite-bz29459.out \
$(objpfx)tst-printf.out \
$(objpfx)tst-printfsz-islongdouble.out \
$(objpfx)tst-setvbuf1-cmp.out \
@@ -436,6 +439,10 @@ tst-freopen64-6-ENV = \
MALLOC_TRACE=$(objpfx)tst-freopen64-6.mtrace \
LD_PRELOAD=$(common-objpfx)malloc/libc_malloc_debug.so
+$(objpfx)tst-fwrite-bz29459.out: tst-fwrite-bz29459.sh $(objpfx)tst-fwrite-bz29459
+ $(SHELL) $< $(common-objpfx) '$(test-program-prefix)'; \
+ $(evaluate-test)
+
$(objpfx)tst-unbputc.out: tst-unbputc.sh $(objpfx)tst-unbputc
$(SHELL) $< $(common-objpfx) '$(test-program-prefix)'; \
$(evaluate-test)
diff --git a/stdio-common/tst-fwrite-bz29459.c b/stdio-common/tst-fwrite-bz29459.c
new file mode 100644
index 0000000000000000..0640faac0c3823ef
--- /dev/null
+++ b/stdio-common/tst-fwrite-bz29459.c
@@ -0,0 +1,89 @@
+/* Test fwrite against bug 29459.
+ Copyright (C) 2025 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+/* This test is based on the code attached to bug 29459.
+ It depends on stdout being redirected to a specific process via a script
+ with the same name. Because of this, we cannot use the features from
+ test_driver.c. */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <support/check.h>
+#include <support/xsignal.h>
+
+/* Usually this test reproduces in a few iterations. However, keep a high
+ number of iterations in order to avoid return false-positives due to an
+ overwhelmed/slow system. */
+#define ITERATIONS 5000
+
+/* The goal of this test is to use fwrite () on a redirected and closed
+ stdout. A script will guarantee that stdout is redirected to another
+ process that closes it during the execution. The process reading from
+ the pipe must read at least the first line in order to guarantee that
+ flag _IO_CURRENTLY_PUTTING is set in the write end of the pipe, triggering
+ important parts of the code that flushes lines from fwrite's internal
+ buffer. The underlying write () returns EPIPE, which fwrite () must
+ propagate. */
+
+int
+main (void)
+{
+ int i;
+ size_t rc;
+ /* Ensure the string we send has a new line because we're dealing
+ with a lined-buffered stream. */
+ const char *s = "hello\n";
+ const size_t len = strlen(s);
+
+ /* Ensure that fwrite buffers the output before writing to stdout. */
+ setlinebuf(stdout);
+ /* Ignore SIGPIPE in order to catch the EPIPE returned by the
+ underlying call to write(). */
+ xsignal(SIGPIPE, SIG_IGN);
+
+ for (i = 1; i <= ITERATIONS; i++)
+ {
+ /* Keep writing to stdout. The test succeeds if fwrite () returns an
+ error. */
+ if ((rc = fwrite(s, 1, len, stdout)) < len)
+ {
+ /* An error happened. Check if ferror () does return an error
+ and that it is indeed EPIPE. */
+ TEST_COMPARE (ferror (stdout), 1);
+ TEST_COMPARE (errno, EPIPE);
+ fprintf(stderr, "Success: i=%d. fwrite returned %zu < %zu "
+ "and errno=EPIPE\n",
+ i, rc, len);
+ /* The test succeeded! */
+ return 0;
+ }
+ else
+ {
+ /* fwrite () was able to write all the contents. Check if no errors
+ have been reported and try again. */
+ TEST_COMPARE (ferror (stdout), 0);
+ TEST_COMPARE (errno, 0);
+ }
+ }
+
+ fprintf(stderr, "Error: fwrite did not return an error\n");
+ return 1;
+}
diff --git a/stdio-common/tst-fwrite-bz29459.sh b/stdio-common/tst-fwrite-bz29459.sh
new file mode 100755
index 0000000000000000..164313532b91cb56
--- /dev/null
+++ b/stdio-common/tst-fwrite-bz29459.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+# Test fwrite for bug 29459.
+# Copyright (C) 2025 Free Software Foundation, Inc.
+# This file is part of the GNU C Library.
+
+# The GNU C Library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+
+# The GNU C Library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+
+# You should have received a copy of the GNU Lesser General Public
+# License along with the GNU C Library; if not, see
+# <https://www.gnu.org/licenses/>.
+
+set -e
+
+common_objpfx=$1; shift
+test_program_prefix=$1; shift
+
+status=0
+
+${test_program_prefix} \
+ ${common_objpfx}stdio-common/tst-fwrite-bz29459 \
+ 2> ${common_objpfx}stdio-common/tst-fwrite-bz29459.out \
+ | head -n1 > /dev/null
+
+grep -q Success ${common_objpfx}stdio-common/tst-fwrite-bz29459.out || status=1
+
+exit $status
diff --git a/stdio-common/tst-fwrite-pipe.c b/stdio-common/tst-fwrite-pipe.c
new file mode 100644
index 0000000000000000..a6119125b25eeddb
--- /dev/null
+++ b/stdio-common/tst-fwrite-pipe.c
@@ -0,0 +1,130 @@
+/* Test if fwrite returns EPIPE.
+ Copyright (C) 2025 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <support/check.h>
+#include <support/xstdio.h>
+#include <support/xsignal.h>
+#include <support/xunistd.h>
+
+/* Usually this test reproduces in a few iterations. However, keep a high
+ number of iterations in order to avoid return false-positives due to an
+ overwhelmed/slow system. */
+#define ITERATIONS 5000
+
+#define BUFFERSIZE 20
+
+/* When the underlying write () fails with EPIPE, fwrite () is expected to
+ return an error by returning < nmemb and keeping errno=EPIPE. */
+
+static int
+do_test (void)
+{
+ int fd[2];
+ pid_t p;
+ FILE *f;
+ size_t written;
+ int ret = 1; /* Return failure by default. */
+
+ /* Try to create a pipe. */
+ xpipe (fd);
+
+ p = xfork ();
+ if (p == 0)
+ {
+ char b[BUFFERSIZE];
+ size_t bytes;
+
+ /* Read at least the first line from the pipe before closing it.
+ This is important because it guarantees the file stream will have
+ flag _IO_CURRENTLY_PUTTING set, which triggers important parts of
+ the code that flushes lines from fwrite's internal buffer. */
+ do {
+ bytes = read (fd[0], b, BUFFERSIZE);
+ } while(bytes > 0 && memrchr (b, '\n', bytes) == NULL);
+
+ /* Child closes both ends of the pipe in order to trigger an EPIPE
+ error on the parent. */
+ xclose (fd[0]);
+ xclose (fd[1]);
+
+ return 0;
+ }
+ else
+ {
+ /* Ensure the string we send has a new line because we're dealing
+ with a lined-buffered stream. */
+ const char *s = "hello\n";
+ size_t len = strlen (s);
+ int i;
+
+ /* Parent only writes to pipe.
+ Close the unused read end of the pipe. */
+ xclose (fd[0]);
+
+ /* Ignore SIGPIPE in order to catch the EPIPE returned by the
+ underlying call to write(). */
+ xsignal(SIGPIPE, SIG_IGN);
+
+ /* Create a file stream associated with the write end of the pipe. */
+ f = fdopen (fd[1], "w");
+ TEST_VERIFY_EXIT (f != NULL);
+ /* Ensure that fwrite buffers the output before writing to the pipe. */
+ setlinebuf (f);
+
+ /* Ensure errno is not set before starting. */
+ errno = 0;
+ for (i = 1; i <= ITERATIONS; i++)
+ {
+ /* Try to write to the pipe. The first calls are expected to
+ suceeded until the child process closes the read end.
+ After that, fwrite () is expected to fail and errno should be
+ set to EPIPE. */
+ written = fwrite (s, 1, len, f);
+
+ if (written == len)
+ {
+ TEST_VERIFY_EXIT (ferror (f) == 0);
+ TEST_VERIFY_EXIT (errno == 0);
+ }
+ else
+ {
+ /* An error happened. Check if ferror () does return an error
+ and that it is indeed EPIPE. */
+ TEST_COMPARE (ferror (f), 1);
+ TEST_COMPARE (errno, EPIPE);
+ /* The test succeeded! Clear the error from the file stream and
+ return success. */
+ clearerr (f);
+ ret = 0;
+ break;
+ }
+ }
+
+ xfclose (f);
+ }
+
+ if (ret)
+ FAIL_RET ("fwrite should have returned an error, but it didn't.\n");
+
+ return ret;
+}
+
+#include <support/test-driver.c>

262
glibc-RHEL-55471-5.patch Normal file
View File

@ -0,0 +1,262 @@
commit 1515f74fd81035a79861cd9fa12053fa9450ec65
Author: Tulio Magno Quites Machado Filho <tuliom@redhat.com>
Date: Tue Jan 28 15:37:44 2025 -0300
libio: Add a new fwrite test that evaluates partial writes
Test if the file-position is correctly updated when fwrite tries to
flush its internal cache but is not able to completely write all items.
Reviewed-by: DJ Delorie <dj@redhat.com>
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index b4a1e62f4a388d0a..acf7059ba50d2ca9 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -220,6 +220,7 @@ tests := \
tst-fwrite-memstrm \
tst-fwrite-overflow \
tst-fwrite-pipe \
+ tst-fwrite-pos \
tst-fwrite-ro \
tst-getline \
tst-getline-enomem \
diff --git a/stdio-common/tst-fwrite-pos.c b/stdio-common/tst-fwrite-pos.c
new file mode 100644
index 0000000000000000..3923490d5923b4b4
--- /dev/null
+++ b/stdio-common/tst-fwrite-pos.c
@@ -0,0 +1,233 @@
+/* Test if fwrite returns consistent values on partial writes.
+ Copyright (C) 2025 Free Software Foundation, Inc.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include <errno.h>
+/* stdio.h provides BUFSIZ, which is the size of fwrite's internal buffer. */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/fuse.h>
+#include <support/support.h>
+#include <support/xstdio.h>
+#include <support/temp_file.h>
+
+/* Length of the buffer in bytes. */
+#define INBUF_SIZE (BUFSIZ)
+
+/* Amount of bytes written to fwrite's internal cache that trigger a
+ flush. */
+#define CACHE_THRESHOLD (BUFSIZ / 2)
+
+#define ITERATIONS 1000
+
+/* Maximum number of bytes written during a partial write. */
+#define PARTIAL_BYTES 4
+
+#define EXPECT_EVENT(opcode, state, expected_state) \
+ { \
+ if (state != expected_state) \
+ { \
+ char *s = support_fuse_opcode (opcode); \
+ FAIL ("unexpected event %s at state %d", s, state); \
+ free (s); \
+ } \
+ }
+
+/* The goal of this test is to check that file position of a file stream is
+ correctly updated when write () returns a partial write.
+ The file system simulates pseudorandom partial writes while the test is
+ running.
+ Meanwhile the main thread calls fwrite () with a large object first and
+ small objects later. The usage of a large enough object ensures that
+ fwrite's internal cache is full enough, without triggering a write to file.
+ Subsequent calls to fwrite are guaranteed to trigger a write to file. */
+
+static void
+fuse_thread (struct support_fuse *f, void *closure)
+{
+ struct fuse_in_header *inh;
+ int state = 0;
+ while ((inh = support_fuse_next (f)) != NULL)
+ {
+ {
+ char *opcode = support_fuse_opcode (inh->opcode);
+ printf ("info: (T) event %s(%llu) len=%u nodeid=%llu\n",
+ opcode, (unsigned long long int) inh->unique, inh->len,
+ (unsigned long long int) inh->nodeid);
+ free (opcode);
+ }
+
+ /* Handle mountpoint and basic directory operation for the root (1). */
+ if (support_fuse_handle_mountpoint (f)
+ || (inh->nodeid == 1 && support_fuse_handle_directory (f)))
+ continue;
+
+ switch (inh->opcode)
+ {
+ case FUSE_LOOKUP:
+ EXPECT_EVENT (inh->nodeid, state, 0);
+ state++;
+ support_fuse_reply_error (f, ENOENT);
+ break;
+ case FUSE_CREATE:
+ EXPECT_EVENT (inh->nodeid, state, 1);
+ state++;
+ struct fuse_entry_out *entry;
+ struct fuse_open_out *open;
+ support_fuse_prepare_create (f, 2, &entry, &open);
+ entry->attr.mode = S_IFREG | 0600;
+ support_fuse_reply_prepared (f);
+ break;
+ case FUSE_GETXATTR:
+ /* We don't need to support extended attributes in this test. */
+ support_fuse_reply_error (f, ENOSYS);
+ break;
+ case FUSE_GETATTR:
+ /* Happens after open. */
+ if (inh->nodeid == 2)
+ {
+ struct fuse_attr_out *out = support_fuse_prepare_attr (f);
+ out->attr.mode = S_IFREG | 0600;
+ out->attr.size = 0;
+ support_fuse_reply_prepared (f);
+ }
+ else
+ support_fuse_reply_error (f, ENOENT);
+ break;
+ case FUSE_WRITE:
+ if (inh->nodeid == 2)
+ {
+ struct fuse_write_out out;
+ if (state > 1 && state < ITERATIONS + 2)
+ {
+ /* The 2nd and subsequent calls to fwrite () trigger a
+ flush of fwrite's internal cache. Simulate a partial
+ write of up to PARTIAL_BYTES bytes. */
+ out.padding = 0;
+ out.size = 1 + rand () % PARTIAL_BYTES,
+ state++;
+ support_fuse_reply (f, &out, sizeof (out));
+ }
+ else if (state >= ITERATIONS + 2)
+ {
+ /* This request is expected to come from fflush (). Copy
+ all the data successfully. This may be executed more
+ than once. */
+ struct fuse_write_in *p = support_fuse_cast (WRITE, inh);
+ out.padding = 0;
+ out.size = p->size,
+ state++;
+ support_fuse_reply (f, &out, sizeof (out));
+ }
+ else
+ support_fuse_reply_error (f, EIO);
+ }
+ else
+ support_fuse_reply_error (f, EIO);
+ break;
+ case FUSE_FLUSH:
+ case FUSE_RELEASE:
+ TEST_COMPARE (inh->nodeid, 2);
+ support_fuse_reply_empty (f);
+ break;
+ default:
+ FAIL ("unexpected event %s", support_fuse_opcode (inh->opcode));
+ support_fuse_reply_error (f, EIO);
+ }
+ }
+}
+
+static int
+do_test (void)
+{
+ char *in;
+ int i;
+ size_t written;
+
+ _Static_assert (CACHE_THRESHOLD <= INBUF_SIZE,
+ "the input buffer must be larger than the cache threshold");
+ /* Avoid filling up fwrite's cache. */
+ _Static_assert (CACHE_THRESHOLD - 1 + PARTIAL_BYTES * ITERATIONS <= BUFSIZ,
+ "fwrite's cache must fit all data written");
+
+ support_fuse_init ();
+ struct support_fuse *fs = support_fuse_mount (fuse_thread, NULL);
+
+ /* Create and open a temporary file in the fuse mount point. */
+ char *fname = xasprintf ("%s/%sXXXXXX", support_fuse_mountpoint (fs),
+ "tst-fwrite-fuse");
+ int fd = mkstemp (fname);
+ TEST_VERIFY_EXIT (fd != -1);
+ FILE *f = fdopen (fd, "w");
+ TEST_VERIFY_EXIT (f != NULL);
+
+ /* Allocate an input array that will be written to the temporary file. */
+ in = xmalloc (INBUF_SIZE);
+ for (i = 0; i < INBUF_SIZE; i++)
+ in[i] = i % 0xff;
+
+ /* Ensure the file position indicator is at the beginning of the stream. */
+ TEST_COMPARE (ftell (f), 0);
+
+ /* Try to fill as most data to the cache of the file stream as possible
+ with a single large object.
+ All data is expected to be written to the cache.
+ No errors are expected from this. */
+ TEST_COMPARE (fwrite (in, CACHE_THRESHOLD - 1, 1, f), 1);
+ TEST_COMPARE (ferror (f), 0);
+ written = CACHE_THRESHOLD - 1;
+
+ /* Ensure the file position indicator advanced correctly. */
+ TEST_COMPARE (ftell (f), written);
+
+ for (i = 0; i < ITERATIONS; i++)
+ {
+ /* Write an extra object of size PARTIAL_BYTES that triggers a write to
+ disk. Our FS will write at most PARTIAL_BYTES bytes to the file
+ instead of all the data. By writting PARTIAL_BYTES, we guarantee
+ the amount of data in the cache will never decrease below
+ CACHE_THRESHOLD.
+ No errors are expected. */
+ TEST_COMPARE (fwrite (in, PARTIAL_BYTES, 1, f), 1);
+ TEST_COMPARE (ferror (f), 0);
+ written += PARTIAL_BYTES;
+
+ /* Ensure the file position indicator advanced correctly. */
+ TEST_COMPARE (ftell (f), written);
+ }
+
+ /* Flush the rest of the data. */
+ TEST_COMPARE (fflush (f), 0);
+ TEST_COMPARE (ferror (f), 0);
+
+ /* Ensure the file position indicator was not modified. */
+ TEST_COMPARE (ftell (f), written);
+
+ /* In case an unexpected error happened, clear it before exiting. */
+ if (ferror (f))
+ clearerr (f);
+
+ xfclose (f);
+ free (fname);
+ free (in);
+ support_fuse_unmount (fs);
+ return 0;
+}
+
+#include <support/test-driver.c>

39
glibc-RHEL-55471-6.patch Normal file
View File

@ -0,0 +1,39 @@
commit cdb0800022110bc68a033944f09e501be5bd72d7
Author: Tulio Magno Quites Machado Filho <tuliom@redhat.com>
Date: Thu Jan 30 15:51:01 2025 -0300
libio: Replace __LP64__ with __WORDSIZE
__LP64__ is a GCC extension and shouldn't be used in an installed
header.
Fixes: 596a61cf6b (libio: Start to return errors when flushing fwrite's buffer [BZ #29459], 2025-01-28)
Reported-by: Florian Weimer <fweimer@redhat.com>
Reviewed-by: Arjun Shankar <arjun@redhat.com>
diff --git a/libio/bits/types/struct_FILE.h b/libio/bits/types/struct_FILE.h
index 7292334a28ad3f79..59f316f8a03d9498 100644
--- a/libio/bits/types/struct_FILE.h
+++ b/libio/bits/types/struct_FILE.h
@@ -32,6 +32,7 @@
#endif
#include <bits/types.h>
+#include <bits/wordsize.h>
struct _IO_FILE;
struct _IO_marker;
@@ -102,11 +103,11 @@ struct _IO_FILE_complete
void *_freeres_buf;
size_t __pad5;
int _mode;
-#ifdef __LP64__
+#if __WORDSIZE == 64
int _unused3;
#endif
__uint64_t _total_written;
-#ifndef __LP64__
+#if __WORDSIZE == 32
int _unused3;
#endif
/* Make sure we don't get into trouble again. */

37
glibc-RHEL-55471-7.patch Normal file
View File

@ -0,0 +1,37 @@
commit 88f7ef881d1b9507aa934104c338b958c37821d7
Author: Tulio Magno Quites Machado Filho <tuliom@redhat.com>
Date: Fri Jan 31 10:26:22 2025 -0300
libio: Initialize _total_written for all kinds of streams
Move the initialization code to a general place instead of keeping it
specific to file-backed streams.
Fixes: 596a61cf6b (libio: Start to return errors when flushing fwrite's buffer [BZ #29459], 2025-01-28)
Reported-by: Florian Weimer <fweimer@redhat.com>
Reviewed-by: Arjun Shankar <arjun@redhat.com>
diff --git a/libio/fileops.c b/libio/fileops.c
index b2354d42b420b80c..d40748e0fc548fff 100644
--- a/libio/fileops.c
+++ b/libio/fileops.c
@@ -114,7 +114,6 @@ _IO_new_file_init_internal (struct _IO_FILE_plus *fp)
_IO_link_in (fp);
fp->file._fileno = -1;
- fp->file._total_written = 0;
}
/* External version of _IO_new_file_init_internal which switches off
diff --git a/libio/genops.c b/libio/genops.c
index a82c1b96767e14e0..cf6985938255e70d 100644
--- a/libio/genops.c
+++ b/libio/genops.c
@@ -586,6 +586,7 @@ _IO_no_init (FILE *fp, int flags, int orientation,
stream. */
fp->_wide_data = (struct _IO_wide_data *) -1L;
fp->_freeres_list = NULL;
+ fp->_total_written = 0;
}
int

34
glibc-RHEL-55471-8.patch Normal file
View File

@ -0,0 +1,34 @@
commit 1b6f868625403d6b7683af840e87d2b18d5d7731
Author: Tulio Magno Quites Machado Filho <tuliom@redhat.com>
Date: Wed Feb 5 17:20:34 2025 -0300
Increase the amount of data tested in stdio-common/tst-fwrite-bz29459.c
The number of iterations and the length of the string are not high
enough on some systems causing the test to return false-positives.
Fixes: 596a61cf6b (libio: Start to return errors when flushing fwrite's buffer [BZ #29459], 2025-01-28)
Reported-by: Florian Weimer <fweimer@redhat.com>
diff --git a/stdio-common/tst-fwrite-bz29459.c b/stdio-common/tst-fwrite-bz29459.c
index 0640faac0c3823ef..4fcc4c89e21d754d 100644
--- a/stdio-common/tst-fwrite-bz29459.c
+++ b/stdio-common/tst-fwrite-bz29459.c
@@ -32,7 +32,7 @@
/* Usually this test reproduces in a few iterations. However, keep a high
number of iterations in order to avoid return false-positives due to an
overwhelmed/slow system. */
-#define ITERATIONS 5000
+#define ITERATIONS 500000
/* The goal of this test is to use fwrite () on a redirected and closed
stdout. A script will guarantee that stdout is redirected to another
@@ -50,7 +50,7 @@ main (void)
size_t rc;
/* Ensure the string we send has a new line because we're dealing
with a lined-buffered stream. */
- const char *s = "hello\n";
+ const char *s = "hello world\n";
const size_t len = strlen(s);
/* Ensure that fwrite buffers the output before writing to stdout. */

35
glibc-RHEL-55471-9.patch Normal file
View File

@ -0,0 +1,35 @@
Downstream-only patch to restore the extern ABI for functions
like fprintf that use the FILE * type. Rebuilds of applications
receive ABI change reports because of this installed header change
(indirect subtype change in libabigail terms), and given that
this part of struct _IO_FILE is strictly internal, there is no
need to expose this change to installed headers.
diff --git a/libio/bits/types/struct_FILE.h b/libio/bits/types/struct_FILE.h
index 59f316f8a03d9498..ab64e4e43d663333 100644
--- a/libio/bits/types/struct_FILE.h
+++ b/libio/bits/types/struct_FILE.h
@@ -103,15 +103,19 @@ struct _IO_FILE_complete
void *_freeres_buf;
size_t __pad5;
int _mode;
-#if __WORDSIZE == 64
+#ifdef _LIBC
+# if __WORDSIZE == 64
int _unused3;
-#endif
+# endif
__uint64_t _total_written;
-#if __WORDSIZE == 32
+# if __WORDSIZE == 32
int _unused3;
-#endif
+# endif
/* Make sure we don't get into trouble again. */
char _unused2[12 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)];
+#else
+ char _unused2[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)];
+#endif
};
/* These macros are used by bits/stdio.h and internal headers. */

View File

@ -157,7 +157,7 @@ end \
Summary: The GNU libc libraries
Name: glibc
Version: %{glibcversion}
Release: 174%{?dist}
Release: 175%{?dist}
# In general, GPLv2+ is used by programs, LGPLv2+ is used for
# libraries.
@ -1130,6 +1130,16 @@ Patch822: glibc-RHEL-65280-4.patch
Patch823: glibc-RHEL-65280-5.patch
Patch824: glibc-RHEL-65280-6.patch
Patch825: glibc-RHEL-65280-7.patch
Patch826: glibc-RHEL-55471-1.patch
Patch827: glibc-RHEL-55471-2.patch
Patch828: glibc-RHEL-55471-3.patch
Patch829: glibc-RHEL-55471-4.patch
Patch830: glibc-RHEL-55471-5.patch
Patch831: glibc-RHEL-55471-6.patch
Patch832: glibc-RHEL-55471-7.patch
Patch833: glibc-RHEL-55471-8.patch
Patch834: glibc-RHEL-55471-9.patch
Patch835: glibc-RHEL-55471-10.patch
##############################################################################
# Continued list of core "glibc" package information:
@ -3123,6 +3133,9 @@ update_gconv_modules_cache ()
%endif
%changelog
* Mon Mar 10 2025 Tulio Magno Quites Machado Filho <tuliom@redhat.com> - 2.34-175
- Backport fwrite tests and a fix for BZ 29459 (RHEL-55471)
* Fri Mar 07 2025 Arjun Shankar <arjun@redhat.com> - 2.34-174
- nptl: Keep __rseq_size consistent (RHEL-65280)