diff --git a/libio/Makefile b/libio/Makefile
index cd5a3afeeb..4370152964 100644
--- a/libio/Makefile
+++ b/libio/Makefile
@@ -86,6 +86,8 @@ tests = \
bug-wmemstream1 \
bug-wsetpos \
test-fmemopen \
+ test-fputs-unbuffered-full \
+ test-fputws-unbuffered-full \
tst-atime \
tst-bz22415 \
tst-bz24051 \
diff --git a/libio/test-fputs-unbuffered-full.c b/libio/test-fputs-unbuffered-full.c
new file mode 100644
index 0000000000..145b4b6a57
--- /dev/null
+++ b/libio/test-fputs-unbuffered-full.c
@@ -0,0 +1,78 @@
+/* Regression test for 20632.
+ Copyright (C) 2024 Free Software Foundation, Inc.
+ Copyright The GNU Toolchain Authors.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ . */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#ifndef WIDE
+# define TEST_NAME "fputs-unbuffered-full"
+# define CHAR char
+# define FPUTS fputs
+# define TEXT "0123456789ABCDEF"
+#else
+# include
+# define TEST_NAME "fputws-unbuffered-full"
+# define CHAR wchar_t
+# define FPUTS fputws
+# define TEXT L"0123456789ABCDEF"
+#endif /* WIDE */
+
+
+static int
+do_test (void)
+{
+ /* Open an unbuffered stream to /dev/full. */
+ FILE *fp = fopen ("/dev/full", "w");
+ TEST_VERIFY_EXIT (fp != NULL);
+ int ret = setvbuf (fp, NULL, _IONBF, 0);
+ TEST_VERIFY_EXIT (ret == 0);
+
+ /* Output a long string. */
+ const int sz = 4096;
+ CHAR *buff = calloc (sz+1, sizeof *buff);
+ for (int i=0; i < sz; i++)
+ buff[i] = (CHAR) 'x';
+ buff[sz] = (CHAR) '\0';
+ errno = 0;
+ ret = FPUTS (buff, fp);
+ TEST_VERIFY (ret == EOF);
+ TEST_VERIFY (errno == ENOSPC);
+ free (buff);
+
+ /* Output shorter strings. */
+ for (int i=0; i < 1024; i++)
+ {
+ errno = 0;
+ ret = FPUTS (TEXT, fp);
+ TEST_VERIFY (ret == EOF);
+ TEST_VERIFY (errno == ENOSPC);
+
+ /* Call malloc, triggering a crash if its
+ function pointers have been overwritten. */
+ void *volatile ptr = malloc (1);
+ free (ptr);
+ }
+ return 0;
+}
+
+#include
diff --git a/libio/test-fputws-unbuffered-full.c b/libio/test-fputws-unbuffered-full.c
new file mode 100644
index 0000000000..f3d79326e4
--- /dev/null
+++ b/libio/test-fputws-unbuffered-full.c
@@ -0,0 +1,21 @@
+/* Regression test for 20632.
+ Copyright (C) 2024 Free Software Foundation, Inc.
+ Copyright The GNU Toolchain Authors.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ . */
+
+#define WIDE 1
+#include "./test-fputs-unbuffered-full.c"
diff --git a/libio/wfileops.c b/libio/wfileops.c
index 6de5968358..fdbe8692e8 100644
--- a/libio/wfileops.c
+++ b/libio/wfileops.c
@@ -420,14 +420,14 @@ _IO_wfile_overflow (FILE *f, wint_t wch)
{
_IO_wdoallocbuf (f);
_IO_free_wbackup_area (f);
- _IO_wsetg (f, f->_wide_data->_IO_buf_base,
- f->_wide_data->_IO_buf_base, f->_wide_data->_IO_buf_base);
if (f->_IO_write_base == NULL)
{
_IO_doallocbuf (f);
_IO_setg (f, f->_IO_buf_base, f->_IO_buf_base, f->_IO_buf_base);
}
+ _IO_wsetg (f, f->_wide_data->_IO_buf_base,
+ f->_wide_data->_IO_buf_base, f->_wide_data->_IO_buf_base);
}
else
{
@@ -958,7 +958,7 @@ _IO_wfile_xsputn (FILE *f, const void *data, size_t n)
const wchar_t *s = (const wchar_t *) data;
size_t to_do = n;
int must_flush = 0;
- size_t count;
+ size_t count = 0;
if (n <= 0)
return 0;
@@ -967,7 +967,6 @@ _IO_wfile_xsputn (FILE *f, const void *data, size_t n)
(or the filebuf is unbuffered), use sys_write directly. */
/* First figure out how much space is available in the buffer. */
- count = f->_wide_data->_IO_write_end - f->_wide_data->_IO_write_ptr;
if ((f->_flags & _IO_LINE_BUF) && (f->_flags & _IO_CURRENTLY_PUTTING))
{
count = f->_wide_data->_IO_buf_end - f->_wide_data->_IO_write_ptr;
@@ -985,6 +984,10 @@ _IO_wfile_xsputn (FILE *f, const void *data, size_t n)
}
}
}
+ else if (f->_wide_data->_IO_write_end > f->_wide_data->_IO_write_ptr)
+ count = f->_wide_data->_IO_write_end
+ - f->_wide_data->_IO_write_ptr; /* Space available. */
+
/* Then fill the buffer. */
if (count > 0)
{