* [PATCH] libio: Ignore setbuf for open_memstream [BZ #34019]
@ 2026-04-28 7:56 Gao Xiang
2026-04-28 8:14 ` [PATCH v2] " Gao Xiang
0 siblings, 1 reply; 22+ messages in thread
From: Gao Xiang @ 2026-04-28 7:56 UTC (permalink / raw)
To: libc-alpha; +Cc: Xiang Gao, Rocket Ma
From: Xiang Gao <gaoxiang@kylinos.cn>
open_memstream and open_wmemstream manage an internal growable buffer.
The default setbuf hook can reset that buffer, breaking the assumptions
used by the string stream overflow paths.
Install setbuf hooks that leave the internal buffer unchanged, and add
regression test cases for the narrow and wide cases, based on the
reproducer in BZ #34019.
Checked on x86_64 with no regression in the libio tests.
Reported-by: Rocket Ma <marocketbd@gmail.com>
Signed-off-by: Xiang Gao <gaoxiang@kylinos.cn>
---
libio/Makefile | 1 +
libio/libioP.h | 5 +++-
libio/memstream.c | 7 ++++++
libio/tst-memstream5.c | 54 ++++++++++++++++++++++++++++++++++++++++++
libio/vtables.c | 6 +++--
libio/wmemstream.c | 8 +++++++
6 files changed, 78 insertions(+), 3 deletions(-)
create mode 100644 libio/tst-memstream5.c
diff --git a/libio/Makefile b/libio/Makefile
index 93656466df..584fcdb14d 100644
--- a/libio/Makefile
+++ b/libio/Makefile
@@ -120,6 +120,7 @@ tests = \
tst-memstream2 \
tst-memstream3 \
tst-memstream4 \
+ tst-memstream5 \
tst-mmap-eofsync \
tst-mmap-fflushsync \
tst-mmap-offend \
diff --git a/libio/libioP.h b/libio/libioP.h
index 1485d22619..25a4988884 100644
--- a/libio/libioP.h
+++ b/libio/libioP.h
@@ -740,9 +740,12 @@ extern size_t __IO_obstack_xsputn (FILE *fp, const void *data, size_t n)
/* Jumptable functions for open_{w}memstream. */
extern int _IO_mem_sync (FILE* fp) __THROW attribute_hidden;
extern void _IO_mem_finish (FILE* fp, int) __THROW attribute_hidden;
+extern FILE *_IO_mem_setbuf (FILE* fp, char *buf, ssize_t size)
+ __THROW attribute_hidden;
extern int _IO_wmem_sync (FILE* fp) __THROW attribute_hidden;
extern void _IO_wmem_finish (FILE* fp, int) __THROW attribute_hidden;
-
+extern FILE *_IO_wmem_setbuf (FILE *fp, char *buf, ssize_t size)
+ __THROW attribute_hidden;
/* Other strfile functions */
struct _IO_strfile_;
extern ssize_t _IO_str_count (FILE *) __THROW;
diff --git a/libio/memstream.c b/libio/memstream.c
index 0456adb92f..eeef75a7b6 100644
--- a/libio/memstream.c
+++ b/libio/memstream.c
@@ -112,3 +112,10 @@ _IO_mem_finish (FILE *fp, int dummy)
_IO_str_finish (fp, 0);
}
+
+FILE *
+_IO_mem_setbuf (FILE *fp, char *p, ssize_t len)
+{
+ /* Memstream manage a growable buffer internally. */
+ return fp;
+}
diff --git a/libio/tst-memstream5.c b/libio/tst-memstream5.c
new file mode 100644
index 0000000000..c92ac95cc8
--- /dev/null
+++ b/libio/tst-memstream5.c
@@ -0,0 +1,54 @@
+/* Test for open_memstream BZ #34019.
+ Copyright (C) 2026 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 <wchar.h>
+#include <support/check.h>
+
+static int
+do_test (void)
+{
+ /* Narrow: setbuf must not replace the internal growable buffer */
+ char *buf = NULL;
+ size_t len = 0;
+ FILE *fp = open_memstream (&buf, &len);
+ setbuf (fp, NULL);
+ TEST_COMPARE (fputc ('A', fp), 'A');
+ TEST_COMPARE (fclose (fp), 0);
+ TEST_COMPARE (len, 1);
+ TEST_COMPARE_STRING (buf, "A");
+ free (buf);
+
+ /* Wide: same crash via _IO_wstr_overflow */
+ wchar_t *wbuf = NULL;
+ size_t wlen = 0;
+ FILE *wfp = open_wmemstream (&wbuf, &wlen);
+ TEST_VERIFY_EXIT (wfp != NULL);
+ setbuf (wfp, NULL);
+ TEST_COMPARE (fputwc (L'A', wfp), L'A');
+ TEST_COMPARE (fclose (wfp), 0);
+ TEST_COMPARE (wlen, 1);
+ TEST_VERIFY (wbuf != NULL);
+ TEST_VERIFY (wbuf[0] == L'A' && wbuf[1] == L'\0');
+ free (wbuf);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/libio/vtables.c b/libio/vtables.c
index 00d9d25b5e..ca9f1b2dc4 100644
--- a/libio/vtables.c
+++ b/libio/vtables.c
@@ -77,9 +77,11 @@
# pragma weak _IO_cookie_write
# pragma weak _IO_mem_finish
+# pragma weak _IO_mem_setbuf
# pragma weak _IO_mem_sync
# pragma weak _IO_wmem_finish
+# pragma weak _IO_wmem_setbuf
# pragma weak _IO_wmem_sync
# pragma weak __printf_buffer_as_file_overflow
@@ -334,7 +336,7 @@ const struct _IO_jump_t __io_vtables[] attribute_relro =
JUMP_INIT (xsgetn, _IO_default_xsgetn),
JUMP_INIT (seekoff, _IO_str_seekoff),
JUMP_INIT (seekpos, _IO_default_seekpos),
- JUMP_INIT (setbuf, _IO_default_setbuf),
+ JUMP_INIT (setbuf, _IO_mem_setbuf),
JUMP_INIT (sync, _IO_mem_sync),
JUMP_INIT (doallocate, _IO_default_doallocate),
JUMP_INIT (read, _IO_default_read),
@@ -357,7 +359,7 @@ const struct _IO_jump_t __io_vtables[] attribute_relro =
JUMP_INIT (xsgetn, _IO_wdefault_xsgetn),
JUMP_INIT (seekoff, _IO_wstr_seekoff),
JUMP_INIT (seekpos, _IO_default_seekpos),
- JUMP_INIT (setbuf, _IO_default_setbuf),
+ JUMP_INIT (setbuf, _IO_wmem_setbuf),
JUMP_INIT (sync, _IO_wmem_sync),
JUMP_INIT (doallocate, _IO_wdefault_doallocate),
JUMP_INIT (read, _IO_default_read),
diff --git a/libio/wmemstream.c b/libio/wmemstream.c
index d0c639be70..154993b76b 100644
--- a/libio/wmemstream.c
+++ b/libio/wmemstream.c
@@ -117,3 +117,11 @@ _IO_wmem_finish (FILE *fp, int dummy)
_IO_wstr_finish (fp, 0);
}
+
+
+FILE *
+_IO_wmem_setbuf (FILE *fp, char *p, ssize_t len)
+{
+ /* Wide memstreams manage a growable buffer internally */
+ return fp;
+}
--
2.53.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v2] libio: Ignore setbuf for open_memstream [BZ #34019]
2026-04-28 7:56 [PATCH] libio: Ignore setbuf for open_memstream [BZ #34019] Gao Xiang
@ 2026-04-28 8:14 ` Gao Xiang
2026-04-30 19:13 ` Adhemerval Zanella Netto
0 siblings, 1 reply; 22+ messages in thread
From: Gao Xiang @ 2026-04-28 8:14 UTC (permalink / raw)
To: libc-alpha; +Cc: Xiang Gao, Rocket Ma
From: Xiang Gao <gaoxiang@kylinos.cn>
open_memstream and open_wmemstream manage an internal growable buffer.
The default setbuf hook can reset that buffer, breaking the assumptions
used by the string stream overflow paths.
Install setbuf hooks that leave the internal buffer unchanged, and add
regression test cases for the narrow and wide cases, based on the
reproducer in BZ #34019.
Checked on x86_64 with no regression in the libio tests.
Reported-by: Rocket Ma <marocketbd@gmail.com>
Signed-off-by: Xiang Gao <gaoxiang@kylinos.cn>
---
Changes in v2: Update the prototypes in libioP.h to using FILE *fp
spacing consistently.
---
libio/Makefile | 1 +
libio/libioP.h | 12 ++++++----
libio/memstream.c | 9 +++++++
libio/tst-memstream5.c | 54 ++++++++++++++++++++++++++++++++++++++++++
libio/vtables.c | 6 +++--
libio/wmemstream.c | 10 ++++++++
6 files changed, 86 insertions(+), 6 deletions(-)
create mode 100644 libio/tst-memstream5.c
diff --git a/libio/Makefile b/libio/Makefile
index 93656466df..584fcdb14d 100644
--- a/libio/Makefile
+++ b/libio/Makefile
@@ -120,6 +120,7 @@ tests = \
tst-memstream2 \
tst-memstream3 \
tst-memstream4 \
+ tst-memstream5 \
tst-mmap-eofsync \
tst-mmap-fflushsync \
tst-mmap-offend \
diff --git a/libio/libioP.h b/libio/libioP.h
index 1485d22619..17c0b6e76d 100644
--- a/libio/libioP.h
+++ b/libio/libioP.h
@@ -738,10 +738,14 @@ extern size_t __IO_obstack_xsputn (FILE *fp, const void *data, size_t n)
attribute_hidden;
/* Jumptable functions for open_{w}memstream. */
-extern int _IO_mem_sync (FILE* fp) __THROW attribute_hidden;
-extern void _IO_mem_finish (FILE* fp, int) __THROW attribute_hidden;
-extern int _IO_wmem_sync (FILE* fp) __THROW attribute_hidden;
-extern void _IO_wmem_finish (FILE* fp, int) __THROW attribute_hidden;
+extern int _IO_mem_sync (FILE *fp) __THROW attribute_hidden;
+extern void _IO_mem_finish (FILE *fp, int) __THROW attribute_hidden;
+extern FILE *_IO_mem_setbuf (FILE *fp, char *buf, ssize_t size)
+ __THROW attribute_hidden;
+extern int _IO_wmem_sync (FILE *fp) __THROW attribute_hidden;
+extern void _IO_wmem_finish (FILE *fp, int) __THROW attribute_hidden;
+extern FILE *_IO_wmem_setbuf (FILE *fp, char *buf, ssize_t size)
+ __THROW attribute_hidden;
/* Other strfile functions */
struct _IO_strfile_;
diff --git a/libio/memstream.c b/libio/memstream.c
index 0456adb92f..a5f909cf64 100644
--- a/libio/memstream.c
+++ b/libio/memstream.c
@@ -112,3 +112,12 @@ _IO_mem_finish (FILE *fp, int dummy)
_IO_str_finish (fp, 0);
}
+
+FILE *
+_IO_mem_setbuf (FILE *fp, char *p, ssize_t len)
+{
+ /* memstream manage a growable buffer internally. */
+ (void) p;
+ (void) len;
+ return fp;
+}
diff --git a/libio/tst-memstream5.c b/libio/tst-memstream5.c
new file mode 100644
index 0000000000..e9d05bb1df
--- /dev/null
+++ b/libio/tst-memstream5.c
@@ -0,0 +1,54 @@
+/* Test for open_memstream BZ #34019.
+ Copyright (C) 2026 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 <wchar.h>
+#include <support/check.h>
+
+static int
+do_test (void)
+{
+ /* Narrow: setbuf must not replace the internal growable buffer. */
+ char *buf = NULL;
+ size_t len = 0;
+ FILE *fp = open_memstream (&buf, &len);
+ setbuf (fp, NULL);
+ TEST_COMPARE (fputc ('A', fp), 'A');
+ TEST_COMPARE (fclose (fp), 0);
+ TEST_COMPARE (len, 1);
+ TEST_COMPARE_STRING (buf, "A");
+ free (buf);
+
+ /* Wide: same crash via _IO_wstr_overflow. */
+ wchar_t *wbuf = NULL;
+ size_t wlen = 0;
+ FILE *wfp = open_wmemstream (&wbuf, &wlen);
+ TEST_VERIFY_EXIT (wfp != NULL);
+ setbuf (wfp, NULL);
+ TEST_COMPARE (fputwc (L'A', wfp), L'A');
+ TEST_COMPARE (fclose (wfp), 0);
+ TEST_COMPARE (wlen, 1);
+ TEST_VERIFY (wbuf != NULL);
+ TEST_VERIFY (wbuf[0] == L'A' && wbuf[1] == L'\0');
+ free (wbuf);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/libio/vtables.c b/libio/vtables.c
index 00d9d25b5e..ca9f1b2dc4 100644
--- a/libio/vtables.c
+++ b/libio/vtables.c
@@ -77,9 +77,11 @@
# pragma weak _IO_cookie_write
# pragma weak _IO_mem_finish
+# pragma weak _IO_mem_setbuf
# pragma weak _IO_mem_sync
# pragma weak _IO_wmem_finish
+# pragma weak _IO_wmem_setbuf
# pragma weak _IO_wmem_sync
# pragma weak __printf_buffer_as_file_overflow
@@ -334,7 +336,7 @@ const struct _IO_jump_t __io_vtables[] attribute_relro =
JUMP_INIT (xsgetn, _IO_default_xsgetn),
JUMP_INIT (seekoff, _IO_str_seekoff),
JUMP_INIT (seekpos, _IO_default_seekpos),
- JUMP_INIT (setbuf, _IO_default_setbuf),
+ JUMP_INIT (setbuf, _IO_mem_setbuf),
JUMP_INIT (sync, _IO_mem_sync),
JUMP_INIT (doallocate, _IO_default_doallocate),
JUMP_INIT (read, _IO_default_read),
@@ -357,7 +359,7 @@ const struct _IO_jump_t __io_vtables[] attribute_relro =
JUMP_INIT (xsgetn, _IO_wdefault_xsgetn),
JUMP_INIT (seekoff, _IO_wstr_seekoff),
JUMP_INIT (seekpos, _IO_default_seekpos),
- JUMP_INIT (setbuf, _IO_default_setbuf),
+ JUMP_INIT (setbuf, _IO_wmem_setbuf),
JUMP_INIT (sync, _IO_wmem_sync),
JUMP_INIT (doallocate, _IO_wdefault_doallocate),
JUMP_INIT (read, _IO_default_read),
diff --git a/libio/wmemstream.c b/libio/wmemstream.c
index d0c639be70..8f37a6efb8 100644
--- a/libio/wmemstream.c
+++ b/libio/wmemstream.c
@@ -117,3 +117,13 @@ _IO_wmem_finish (FILE *fp, int dummy)
_IO_wstr_finish (fp, 0);
}
+
+
+FILE *
+_IO_wmem_setbuf (FILE *fp, char *p, ssize_t len)
+{
+ /* wmemstreams manage a growable buffer internally. */
+ (void) p;
+ (void) len;
+ return fp;
+}
--
2.53.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2] libio: Ignore setbuf for open_memstream [BZ #34019]
2026-04-28 8:14 ` [PATCH v2] " Gao Xiang
@ 2026-04-30 19:13 ` Adhemerval Zanella Netto
2026-05-01 16:24 ` Rocket Ma
0 siblings, 1 reply; 22+ messages in thread
From: Adhemerval Zanella Netto @ 2026-04-30 19:13 UTC (permalink / raw)
To: Gao Xiang, libc-alpha; +Cc: Rocket Ma
On 28/04/26 05:14, Gao Xiang wrote:
> From: Xiang Gao <gaoxiang@kylinos.cn>
>
> open_memstream and open_wmemstream manage an internal growable buffer.
> The default setbuf hook can reset that buffer, breaking the assumptions
> used by the string stream overflow paths.
>
> Install setbuf hooks that leave the internal buffer unchanged, and add
> regression test cases for the narrow and wide cases, based on the
> reproducer in BZ #34019.
>
> Checked on x86_64 with no regression in the libio tests.
>
> Reported-by: Rocket Ma <marocketbd@gmail.com>
> Signed-off-by: Xiang Gao <gaoxiang@kylinos.cn>
Wouldn't this change the behavior or setbuf (NULL, ..) and setbuf (..., 0)
to not be _IO_UNBUFFERED?
I think it does not seems to matter though, since the resulting buffer operated
by open_memstream can only be accessible after a fflush/fclose. The
setbuf/setvbuf would only be a way to optimize the stdio buffer flush to
final one, which I am also not fully sure it would matter here.
>
> ---
>
> Changes in v2: Update the prototypes in libioP.h to using FILE *fp
> spacing consistently.
> ---
> libio/Makefile | 1 +
> libio/libioP.h | 12 ++++++----
> libio/memstream.c | 9 +++++++
> libio/tst-memstream5.c | 54 ++++++++++++++++++++++++++++++++++++++++++
> libio/vtables.c | 6 +++--
> libio/wmemstream.c | 10 ++++++++
> 6 files changed, 86 insertions(+), 6 deletions(-)
> create mode 100644 libio/tst-memstream5.c
>
> diff --git a/libio/Makefile b/libio/Makefile
> index 93656466df..584fcdb14d 100644
> --- a/libio/Makefile
> +++ b/libio/Makefile
> @@ -120,6 +120,7 @@ tests = \
> tst-memstream2 \
> tst-memstream3 \
> tst-memstream4 \
> + tst-memstream5 \
> tst-mmap-eofsync \
> tst-mmap-fflushsync \
> tst-mmap-offend \
> diff --git a/libio/libioP.h b/libio/libioP.h
> index 1485d22619..17c0b6e76d 100644
> --- a/libio/libioP.h
> +++ b/libio/libioP.h
> @@ -738,10 +738,14 @@ extern size_t __IO_obstack_xsputn (FILE *fp, const void *data, size_t n)
> attribute_hidden;
>
> /* Jumptable functions for open_{w}memstream. */
> -extern int _IO_mem_sync (FILE* fp) __THROW attribute_hidden;
> -extern void _IO_mem_finish (FILE* fp, int) __THROW attribute_hidden;
> -extern int _IO_wmem_sync (FILE* fp) __THROW attribute_hidden;
> -extern void _IO_wmem_finish (FILE* fp, int) __THROW attribute_hidden;
> +extern int _IO_mem_sync (FILE *fp) __THROW attribute_hidden;
> +extern void _IO_mem_finish (FILE *fp, int) __THROW attribute_hidden;
> +extern FILE *_IO_mem_setbuf (FILE *fp, char *buf, ssize_t size)
> + __THROW attribute_hidden;
> +extern int _IO_wmem_sync (FILE *fp) __THROW attribute_hidden;
> +extern void _IO_wmem_finish (FILE *fp, int) __THROW attribute_hidden;
> +extern FILE *_IO_wmem_setbuf (FILE *fp, char *buf, ssize_t size)
> + __THROW attribute_hidden;
These changes does not seem required, I am failing to see what it is
doing here.
>
> /* Other strfile functions */
> struct _IO_strfile_;
> diff --git a/libio/memstream.c b/libio/memstream.c
> index 0456adb92f..a5f909cf64 100644
> --- a/libio/memstream.c
> +++ b/libio/memstream.c
> @@ -112,3 +112,12 @@ _IO_mem_finish (FILE *fp, int dummy)
>
> _IO_str_finish (fp, 0);
> }
> +
> +FILE *
> +_IO_mem_setbuf (FILE *fp, char *p, ssize_t len)
> +{
> + /* memstream manage a growable buffer internally. */
> + (void) p;
> + (void) len;
> + return fp;
No need to use these cast to avoid compiler warnings here.
> +}
> diff --git a/libio/tst-memstream5.c b/libio/tst-memstream5.c
> new file mode 100644
> index 0000000000..e9d05bb1df
> --- /dev/null
> +++ b/libio/tst-memstream5.c
> @@ -0,0 +1,54 @@
> +/* Test for open_memstream BZ #34019.
> + Copyright (C) 2026 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 <wchar.h>
> +#include <support/check.h>
> +
> +static int
> +do_test (void)
> +{
> + /* Narrow: setbuf must not replace the internal growable buffer. */
> + char *buf = NULL;
> + size_t len = 0;
> + FILE *fp = open_memstream (&buf, &len);
> + setbuf (fp, NULL);
> + TEST_COMPARE (fputc ('A', fp), 'A');
> + TEST_COMPARE (fclose (fp), 0);
> + TEST_COMPARE (len, 1);
> + TEST_COMPARE_STRING (buf, "A");
> + free (buf);
> +
> + /* Wide: same crash via _IO_wstr_overflow. */
> + wchar_t *wbuf = NULL;
> + size_t wlen = 0;
> + FILE *wfp = open_wmemstream (&wbuf, &wlen);
> + TEST_VERIFY_EXIT (wfp != NULL);
> + setbuf (wfp, NULL);
> + TEST_COMPARE (fputwc (L'A', wfp), L'A');
> + TEST_COMPARE (fclose (wfp), 0);
> + TEST_COMPARE (wlen, 1);
> + TEST_VERIFY (wbuf != NULL);
> + TEST_VERIFY (wbuf[0] == L'A' && wbuf[1] == L'\0');
> + free (wbuf);
Could you add setvbuf tests along with different modes (_IONBF, etc.)?
> +
> + return 0;
> +}
> +
> +#include <support/test-driver.c>
> diff --git a/libio/vtables.c b/libio/vtables.c
> index 00d9d25b5e..ca9f1b2dc4 100644
> --- a/libio/vtables.c
> +++ b/libio/vtables.c
> @@ -77,9 +77,11 @@
> # pragma weak _IO_cookie_write
>
> # pragma weak _IO_mem_finish
> +# pragma weak _IO_mem_setbuf
> # pragma weak _IO_mem_sync
>
> # pragma weak _IO_wmem_finish
> +# pragma weak _IO_wmem_setbuf
> # pragma weak _IO_wmem_sync
>
> # pragma weak __printf_buffer_as_file_overflow
> @@ -334,7 +336,7 @@ const struct _IO_jump_t __io_vtables[] attribute_relro =
> JUMP_INIT (xsgetn, _IO_default_xsgetn),
> JUMP_INIT (seekoff, _IO_str_seekoff),
> JUMP_INIT (seekpos, _IO_default_seekpos),
> - JUMP_INIT (setbuf, _IO_default_setbuf),
> + JUMP_INIT (setbuf, _IO_mem_setbuf),
> JUMP_INIT (sync, _IO_mem_sync),
> JUMP_INIT (doallocate, _IO_default_doallocate),
> JUMP_INIT (read, _IO_default_read),
> @@ -357,7 +359,7 @@ const struct _IO_jump_t __io_vtables[] attribute_relro =
> JUMP_INIT (xsgetn, _IO_wdefault_xsgetn),
> JUMP_INIT (seekoff, _IO_wstr_seekoff),
> JUMP_INIT (seekpos, _IO_default_seekpos),
> - JUMP_INIT (setbuf, _IO_default_setbuf),
> + JUMP_INIT (setbuf, _IO_wmem_setbuf),
> JUMP_INIT (sync, _IO_wmem_sync),
> JUMP_INIT (doallocate, _IO_wdefault_doallocate),
> JUMP_INIT (read, _IO_default_read),
> diff --git a/libio/wmemstream.c b/libio/wmemstream.c
> index d0c639be70..8f37a6efb8 100644
> --- a/libio/wmemstream.c
> +++ b/libio/wmemstream.c
> @@ -117,3 +117,13 @@ _IO_wmem_finish (FILE *fp, int dummy)
>
> _IO_wstr_finish (fp, 0);
> }
> +
> +
> +FILE *
> +_IO_wmem_setbuf (FILE *fp, char *p, ssize_t len)
> +{
> + /* wmemstreams manage a growable buffer internally. */
> + (void) p;
> + (void) len;
> + return fp;
> +}
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2] libio: Ignore setbuf for open_memstream [BZ #34019]
2026-04-30 19:13 ` Adhemerval Zanella Netto
@ 2026-05-01 16:24 ` Rocket Ma
2026-05-04 12:04 ` Adhemerval Zanella Netto
0 siblings, 1 reply; 22+ messages in thread
From: Rocket Ma @ 2026-05-01 16:24 UTC (permalink / raw)
To: Adhemerval Zanella Netto; +Cc: libc-alpha, Gao Xiang
> Wouldn't this change the behavior or setbuf (NULL, ..) and setbuf (..., 0)
> to not be _IO_UNBUFFERED?
>
> I think it does not seems to matter though, since the resulting buffer operated
> by open_memstream can only be accessible after a fflush/fclose. The
> setbuf/setvbuf would only be a way to optimize the stdio buffer flush to
> final one, which I am also not fully sure it would matter here.
>
Referencing POSIX standard[1], fflush and fclose are explicitly
mentioned when describing `bufp` and `sizep`, which may indicate that
user shall always flush the stream. But the standard does not mention
the behavior of setbuf. I'm also not sure if we should allow user to
setbuf.
[1]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/open_memstream.html
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2] libio: Ignore setbuf for open_memstream [BZ #34019]
2026-05-01 16:24 ` Rocket Ma
@ 2026-05-04 12:04 ` Adhemerval Zanella Netto
2026-05-04 16:54 ` Alex Gao
0 siblings, 1 reply; 22+ messages in thread
From: Adhemerval Zanella Netto @ 2026-05-04 12:04 UTC (permalink / raw)
To: Rocket Ma; +Cc: libc-alpha, Gao Xiang
On 01/05/26 13:24, Rocket Ma wrote:
>> Wouldn't this change the behavior or setbuf (NULL, ..) and setbuf (..., 0)
>> to not be _IO_UNBUFFERED?
>>
>> I think it does not seems to matter though, since the resulting buffer operated
>> by open_memstream can only be accessible after a fflush/fclose. The
>> setbuf/setvbuf would only be a way to optimize the stdio buffer flush to
>> final one, which I am also not fully sure it would matter here.
>>
>
> Referencing POSIX standard[1], fflush and fclose are explicitly
> mentioned when describing `bufp` and `sizep`, which may indicate that
> user shall always flush the stream. But the standard does not mention
> the behavior of setbuf. I'm also not sure if we should allow user to
> setbuf.
>
> [1]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/open_memstream.html
I think ignoring set{v}buf makes sense here, we can make it orthogonal to other
stream by the current POSIX description makes invisible to user anyway.
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2] libio: Ignore setbuf for open_memstream [BZ #34019]
2026-05-04 12:04 ` Adhemerval Zanella Netto
@ 2026-05-04 16:54 ` Alex Gao
2026-05-04 17:58 ` Adhemerval Zanella Netto
0 siblings, 1 reply; 22+ messages in thread
From: Alex Gao @ 2026-05-04 16:54 UTC (permalink / raw)
To: Adhemerval Zanella Netto, Rocket Ma; +Cc: libc-alpha
On 2026/5/4 20:04, Adhemerval Zanella Netto wrote:
>
>
> On 01/05/26 13:24, Rocket Ma wrote:
>>> Wouldn't this change the behavior or setbuf (NULL, ..) and setbuf (..., 0)
>>> to not be _IO_UNBUFFERED?
>>>
>>> I think it does not seems to matter though, since the resulting buffer operated
>>> by open_memstream can only be accessible after a fflush/fclose. The
>>> setbuf/setvbuf would only be a way to optimize the stdio buffer flush to
>>> final one, which I am also not fully sure it would matter here.
>>>
>>
>> Referencing POSIX standard[1], fflush and fclose are explicitly
>> mentioned when describing `bufp` and `sizep`, which may indicate that
>> user shall always flush the stream. But the standard does not mention
>> the behavior of setbuf. I'm also not sure if we should allow user to
>> setbuf.
>>
>> [1]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/open_memstream.html
>
> I think ignoring set{v}buf makes sense here, we can make it orthogonal to other
> stream by the current POSIX description makes invisible to user anyway.
Thanks, that makes sense.
I will treat setbuf/setvbuf as orthogonal to the memory-stream result
buffer.The backing buffer of open_memstream/open_wmemstream is the
growable result object itself, not discardable. Therefore the generic
buffering operation is not appropriate here: it must not reset the
get/put areas.
While adding setvbuf coverage, I confirmed that the current hook change
covers the setvbuf paths that reach _IO_SETBUF, but I also found one
related case:
setvbuf(fp, NULL, _IOFBF, 0)
This can bypass the setbuf hook and go through doallocate instead. In
that path default wide buffering code may still disturb the wide result
buffer. I will send a separate patch(v3) for wmemstream doallocate path.
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v2] libio: Ignore setbuf for open_memstream [BZ #34019]
2026-05-04 16:54 ` Alex Gao
@ 2026-05-04 17:58 ` Adhemerval Zanella Netto
2026-05-06 9:34 ` [PATCH v3 1/2] libio: Ignore setbuf for open_memstream and open_wmemstream " Gao Xiang
2026-05-06 10:23 ` Gao Xiang
0 siblings, 2 replies; 22+ messages in thread
From: Adhemerval Zanella Netto @ 2026-05-04 17:58 UTC (permalink / raw)
To: Alex Gao, Rocket Ma; +Cc: libc-alpha
On 04/05/26 13:54, Alex Gao wrote:
> On 2026/5/4 20:04, Adhemerval Zanella Netto wrote:
>>
>>
>> On 01/05/26 13:24, Rocket Ma wrote:
>>>> Wouldn't this change the behavior or setbuf (NULL, ..) and setbuf (..., 0)
>>>> to not be _IO_UNBUFFERED?
>>>>
>>>> I think it does not seems to matter though, since the resulting buffer operated
>>>> by open_memstream can only be accessible after a fflush/fclose. The
>>>> setbuf/setvbuf would only be a way to optimize the stdio buffer flush to
>>>> final one, which I am also not fully sure it would matter here.
>>>>
>>>
>>> Referencing POSIX standard[1], fflush and fclose are explicitly
>>> mentioned when describing `bufp` and `sizep`, which may indicate that
>>> user shall always flush the stream. But the standard does not mention
>>> the behavior of setbuf. I'm also not sure if we should allow user to
>>> setbuf.
>>>
>>> [1]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/open_memstream.html
>>
>> I think ignoring set{v}buf makes sense here, we can make it orthogonal to other
>> stream by the current POSIX description makes invisible to user anyway.
>
> Thanks, that makes sense.
>
> I will treat setbuf/setvbuf as orthogonal to the memory-stream result buffer.The backing buffer of open_memstream/open_wmemstream is the growable result object itself, not discardable. Therefore the generic buffering operation is not appropriate here: it must not reset the get/put areas.
>
> While adding setvbuf coverage, I confirmed that the current hook change covers the setvbuf paths that reach _IO_SETBUF, but I also found one related case:
>
> setvbuf(fp, NULL, _IOFBF, 0)
>
> This can bypass the setbuf hook and go through doallocate instead. In that path default wide buffering code may still disturb the wide result buffer. I will send a separate patch(v3) for wmemstream doallocate path.
Alright, sounds reasonable. Thanks for working on this.
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v3 1/2] libio: Ignore setbuf for open_memstream and open_wmemstream [BZ #34019]
2026-05-04 17:58 ` Adhemerval Zanella Netto
@ 2026-05-06 9:34 ` Gao Xiang
2026-05-06 10:23 ` Gao Xiang
1 sibling, 0 replies; 22+ messages in thread
From: Gao Xiang @ 2026-05-06 9:34 UTC (permalink / raw)
To: libc-alpha; +Cc: Adhemerval Zanella Netto, Rocket Ma, Xiang Gao
From: Xiang Gao <gaoxiang@kylinos.cn>
open_memstream and open_wmemstream manage an internal growable buffer.
The default setbuf hook can reset that buffer, breaking the assumptions
used by the string stream overflow paths.
Install setbuf hooks that leave the internal buffer unchanged, and add
regression test cases for the narrow and wide cases, based on the
reproducer in BZ #34019.
Checked on x86_64 with no regression in the libio tests.
Reported-by: Rocket Ma <marocketbd@gmail.com>
Signed-off-by: Xiang Gao <gaoxiang@kylinos.cn>
---
libio/Makefile | 1 +
libio/libioP.h | 12 ++++++----
libio/memstream.c | 9 +++++++
libio/tst-memstream5.c | 54 ++++++++++++++++++++++++++++++++++++++++++
libio/vtables.c | 6 +++--
libio/wmemstream.c | 10 ++++++++
6 files changed, 86 insertions(+), 6 deletions(-)
create mode 100644 libio/tst-memstream5.c
diff --git a/libio/Makefile b/libio/Makefile
index 93656466df..584fcdb14d 100644
--- a/libio/Makefile
+++ b/libio/Makefile
@@ -120,6 +120,7 @@ tests = \
tst-memstream2 \
tst-memstream3 \
tst-memstream4 \
+ tst-memstream5 \
tst-mmap-eofsync \
tst-mmap-fflushsync \
tst-mmap-offend \
diff --git a/libio/libioP.h b/libio/libioP.h
index 1485d22619..17c0b6e76d 100644
--- a/libio/libioP.h
+++ b/libio/libioP.h
@@ -738,10 +738,14 @@ extern size_t __IO_obstack_xsputn (FILE *fp, const void *data, size_t n)
attribute_hidden;
/* Jumptable functions for open_{w}memstream. */
-extern int _IO_mem_sync (FILE* fp) __THROW attribute_hidden;
-extern void _IO_mem_finish (FILE* fp, int) __THROW attribute_hidden;
-extern int _IO_wmem_sync (FILE* fp) __THROW attribute_hidden;
-extern void _IO_wmem_finish (FILE* fp, int) __THROW attribute_hidden;
+extern int _IO_mem_sync (FILE *fp) __THROW attribute_hidden;
+extern void _IO_mem_finish (FILE *fp, int) __THROW attribute_hidden;
+extern FILE *_IO_mem_setbuf (FILE *fp, char *buf, ssize_t size)
+ __THROW attribute_hidden;
+extern int _IO_wmem_sync (FILE *fp) __THROW attribute_hidden;
+extern void _IO_wmem_finish (FILE *fp, int) __THROW attribute_hidden;
+extern FILE *_IO_wmem_setbuf (FILE *fp, char *buf, ssize_t size)
+ __THROW attribute_hidden;
/* Other strfile functions */
struct _IO_strfile_;
diff --git a/libio/memstream.c b/libio/memstream.c
index 0456adb92f..a5f909cf64 100644
--- a/libio/memstream.c
+++ b/libio/memstream.c
@@ -112,3 +112,12 @@ _IO_mem_finish (FILE *fp, int dummy)
_IO_str_finish (fp, 0);
}
+
+FILE *
+_IO_mem_setbuf (FILE *fp, char *p, ssize_t len)
+{
+ /* memstream manage a growable buffer internally. */
+ (void) p;
+ (void) len;
+ return fp;
+}
diff --git a/libio/tst-memstream5.c b/libio/tst-memstream5.c
new file mode 100644
index 0000000000..e9d05bb1df
--- /dev/null
+++ b/libio/tst-memstream5.c
@@ -0,0 +1,54 @@
+/* Test for open_memstream BZ #34019.
+ Copyright (C) 2026 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 <wchar.h>
+#include <support/check.h>
+
+static int
+do_test (void)
+{
+ /* Narrow: setbuf must not replace the internal growable buffer. */
+ char *buf = NULL;
+ size_t len = 0;
+ FILE *fp = open_memstream (&buf, &len);
+ setbuf (fp, NULL);
+ TEST_COMPARE (fputc ('A', fp), 'A');
+ TEST_COMPARE (fclose (fp), 0);
+ TEST_COMPARE (len, 1);
+ TEST_COMPARE_STRING (buf, "A");
+ free (buf);
+
+ /* Wide: same crash via _IO_wstr_overflow. */
+ wchar_t *wbuf = NULL;
+ size_t wlen = 0;
+ FILE *wfp = open_wmemstream (&wbuf, &wlen);
+ TEST_VERIFY_EXIT (wfp != NULL);
+ setbuf (wfp, NULL);
+ TEST_COMPARE (fputwc (L'A', wfp), L'A');
+ TEST_COMPARE (fclose (wfp), 0);
+ TEST_COMPARE (wlen, 1);
+ TEST_VERIFY (wbuf != NULL);
+ TEST_VERIFY (wbuf[0] == L'A' && wbuf[1] == L'\0');
+ free (wbuf);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/libio/vtables.c b/libio/vtables.c
index 00d9d25b5e..ca9f1b2dc4 100644
--- a/libio/vtables.c
+++ b/libio/vtables.c
@@ -77,9 +77,11 @@
# pragma weak _IO_cookie_write
# pragma weak _IO_mem_finish
+# pragma weak _IO_mem_setbuf
# pragma weak _IO_mem_sync
# pragma weak _IO_wmem_finish
+# pragma weak _IO_wmem_setbuf
# pragma weak _IO_wmem_sync
# pragma weak __printf_buffer_as_file_overflow
@@ -334,7 +336,7 @@ const struct _IO_jump_t __io_vtables[] attribute_relro =
JUMP_INIT (xsgetn, _IO_default_xsgetn),
JUMP_INIT (seekoff, _IO_str_seekoff),
JUMP_INIT (seekpos, _IO_default_seekpos),
- JUMP_INIT (setbuf, _IO_default_setbuf),
+ JUMP_INIT (setbuf, _IO_mem_setbuf),
JUMP_INIT (sync, _IO_mem_sync),
JUMP_INIT (doallocate, _IO_default_doallocate),
JUMP_INIT (read, _IO_default_read),
@@ -357,7 +359,7 @@ const struct _IO_jump_t __io_vtables[] attribute_relro =
JUMP_INIT (xsgetn, _IO_wdefault_xsgetn),
JUMP_INIT (seekoff, _IO_wstr_seekoff),
JUMP_INIT (seekpos, _IO_default_seekpos),
- JUMP_INIT (setbuf, _IO_default_setbuf),
+ JUMP_INIT (setbuf, _IO_wmem_setbuf),
JUMP_INIT (sync, _IO_wmem_sync),
JUMP_INIT (doallocate, _IO_wdefault_doallocate),
JUMP_INIT (read, _IO_default_read),
diff --git a/libio/wmemstream.c b/libio/wmemstream.c
index d0c639be70..8f37a6efb8 100644
--- a/libio/wmemstream.c
+++ b/libio/wmemstream.c
@@ -117,3 +117,13 @@ _IO_wmem_finish (FILE *fp, int dummy)
_IO_wstr_finish (fp, 0);
}
+
+
+FILE *
+_IO_wmem_setbuf (FILE *fp, char *p, ssize_t len)
+{
+ /* wmemstreams manage a growable buffer internally. */
+ (void) p;
+ (void) len;
+ return fp;
+}
--
2.53.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v3 1/2] libio: Ignore setbuf for open_memstream and open_wmemstream [BZ #34019]
2026-05-04 17:58 ` Adhemerval Zanella Netto
2026-05-06 9:34 ` [PATCH v3 1/2] libio: Ignore setbuf for open_memstream and open_wmemstream " Gao Xiang
@ 2026-05-06 10:23 ` Gao Xiang
2026-05-06 10:23 ` [PATCH v3 2/2] libio: Ignore doallocate " Gao Xiang
1 sibling, 1 reply; 22+ messages in thread
From: Gao Xiang @ 2026-05-06 10:23 UTC (permalink / raw)
To: libc-alpha; +Cc: Adhemerval Zanella Netto, Rocket Ma, Xiang Gao
From: Xiang Gao <gaoxiang@kylinos.cn>
open_memstream and open_wmemstream manage an internal growable buffer.
The default setbuf hook can reset that buffer, breaking the assumptions
used by the string stream overflow paths.
Install setbuf hooks that leave the internal buffer unchanged, and add
regression test cases for the narrow and wide cases, based on the
reproducer in BZ #34019.
Checked on x86_64 with no regression in the libio tests.
Reported-by: Rocket Ma <marocketbd@gmail.com>
Signed-off-by: Xiang Gao <gaoxiang@kylinos.cn>
---
libio/Makefile | 1 +
libio/libioP.h | 12 ++++++----
libio/memstream.c | 9 +++++++
libio/tst-memstream5.c | 54 ++++++++++++++++++++++++++++++++++++++++++
libio/vtables.c | 6 +++--
libio/wmemstream.c | 10 ++++++++
6 files changed, 86 insertions(+), 6 deletions(-)
create mode 100644 libio/tst-memstream5.c
diff --git a/libio/Makefile b/libio/Makefile
index 93656466df..584fcdb14d 100644
--- a/libio/Makefile
+++ b/libio/Makefile
@@ -120,6 +120,7 @@ tests = \
tst-memstream2 \
tst-memstream3 \
tst-memstream4 \
+ tst-memstream5 \
tst-mmap-eofsync \
tst-mmap-fflushsync \
tst-mmap-offend \
diff --git a/libio/libioP.h b/libio/libioP.h
index 1485d22619..17c0b6e76d 100644
--- a/libio/libioP.h
+++ b/libio/libioP.h
@@ -738,10 +738,14 @@ extern size_t __IO_obstack_xsputn (FILE *fp, const void *data, size_t n)
attribute_hidden;
/* Jumptable functions for open_{w}memstream. */
-extern int _IO_mem_sync (FILE* fp) __THROW attribute_hidden;
-extern void _IO_mem_finish (FILE* fp, int) __THROW attribute_hidden;
-extern int _IO_wmem_sync (FILE* fp) __THROW attribute_hidden;
-extern void _IO_wmem_finish (FILE* fp, int) __THROW attribute_hidden;
+extern int _IO_mem_sync (FILE *fp) __THROW attribute_hidden;
+extern void _IO_mem_finish (FILE *fp, int) __THROW attribute_hidden;
+extern FILE *_IO_mem_setbuf (FILE *fp, char *buf, ssize_t size)
+ __THROW attribute_hidden;
+extern int _IO_wmem_sync (FILE *fp) __THROW attribute_hidden;
+extern void _IO_wmem_finish (FILE *fp, int) __THROW attribute_hidden;
+extern FILE *_IO_wmem_setbuf (FILE *fp, char *buf, ssize_t size)
+ __THROW attribute_hidden;
/* Other strfile functions */
struct _IO_strfile_;
diff --git a/libio/memstream.c b/libio/memstream.c
index 0456adb92f..a5f909cf64 100644
--- a/libio/memstream.c
+++ b/libio/memstream.c
@@ -112,3 +112,12 @@ _IO_mem_finish (FILE *fp, int dummy)
_IO_str_finish (fp, 0);
}
+
+FILE *
+_IO_mem_setbuf (FILE *fp, char *p, ssize_t len)
+{
+ /* memstream manage a growable buffer internally. */
+ (void) p;
+ (void) len;
+ return fp;
+}
diff --git a/libio/tst-memstream5.c b/libio/tst-memstream5.c
new file mode 100644
index 0000000000..e9d05bb1df
--- /dev/null
+++ b/libio/tst-memstream5.c
@@ -0,0 +1,54 @@
+/* Test for open_memstream BZ #34019.
+ Copyright (C) 2026 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 <wchar.h>
+#include <support/check.h>
+
+static int
+do_test (void)
+{
+ /* Narrow: setbuf must not replace the internal growable buffer. */
+ char *buf = NULL;
+ size_t len = 0;
+ FILE *fp = open_memstream (&buf, &len);
+ setbuf (fp, NULL);
+ TEST_COMPARE (fputc ('A', fp), 'A');
+ TEST_COMPARE (fclose (fp), 0);
+ TEST_COMPARE (len, 1);
+ TEST_COMPARE_STRING (buf, "A");
+ free (buf);
+
+ /* Wide: same crash via _IO_wstr_overflow. */
+ wchar_t *wbuf = NULL;
+ size_t wlen = 0;
+ FILE *wfp = open_wmemstream (&wbuf, &wlen);
+ TEST_VERIFY_EXIT (wfp != NULL);
+ setbuf (wfp, NULL);
+ TEST_COMPARE (fputwc (L'A', wfp), L'A');
+ TEST_COMPARE (fclose (wfp), 0);
+ TEST_COMPARE (wlen, 1);
+ TEST_VERIFY (wbuf != NULL);
+ TEST_VERIFY (wbuf[0] == L'A' && wbuf[1] == L'\0');
+ free (wbuf);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/libio/vtables.c b/libio/vtables.c
index 00d9d25b5e..ca9f1b2dc4 100644
--- a/libio/vtables.c
+++ b/libio/vtables.c
@@ -77,9 +77,11 @@
# pragma weak _IO_cookie_write
# pragma weak _IO_mem_finish
+# pragma weak _IO_mem_setbuf
# pragma weak _IO_mem_sync
# pragma weak _IO_wmem_finish
+# pragma weak _IO_wmem_setbuf
# pragma weak _IO_wmem_sync
# pragma weak __printf_buffer_as_file_overflow
@@ -334,7 +336,7 @@ const struct _IO_jump_t __io_vtables[] attribute_relro =
JUMP_INIT (xsgetn, _IO_default_xsgetn),
JUMP_INIT (seekoff, _IO_str_seekoff),
JUMP_INIT (seekpos, _IO_default_seekpos),
- JUMP_INIT (setbuf, _IO_default_setbuf),
+ JUMP_INIT (setbuf, _IO_mem_setbuf),
JUMP_INIT (sync, _IO_mem_sync),
JUMP_INIT (doallocate, _IO_default_doallocate),
JUMP_INIT (read, _IO_default_read),
@@ -357,7 +359,7 @@ const struct _IO_jump_t __io_vtables[] attribute_relro =
JUMP_INIT (xsgetn, _IO_wdefault_xsgetn),
JUMP_INIT (seekoff, _IO_wstr_seekoff),
JUMP_INIT (seekpos, _IO_default_seekpos),
- JUMP_INIT (setbuf, _IO_default_setbuf),
+ JUMP_INIT (setbuf, _IO_wmem_setbuf),
JUMP_INIT (sync, _IO_wmem_sync),
JUMP_INIT (doallocate, _IO_wdefault_doallocate),
JUMP_INIT (read, _IO_default_read),
diff --git a/libio/wmemstream.c b/libio/wmemstream.c
index d0c639be70..8f37a6efb8 100644
--- a/libio/wmemstream.c
+++ b/libio/wmemstream.c
@@ -117,3 +117,13 @@ _IO_wmem_finish (FILE *fp, int dummy)
_IO_wstr_finish (fp, 0);
}
+
+
+FILE *
+_IO_wmem_setbuf (FILE *fp, char *p, ssize_t len)
+{
+ /* wmemstreams manage a growable buffer internally. */
+ (void) p;
+ (void) len;
+ return fp;
+}
--
2.53.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v3 2/2] libio: Ignore doallocate for open_memstream and open_wmemstream [BZ #34019]
2026-05-06 10:23 ` Gao Xiang
@ 2026-05-06 10:23 ` Gao Xiang
2026-05-06 10:38 ` Andreas Schwab
0 siblings, 1 reply; 22+ messages in thread
From: Gao Xiang @ 2026-05-06 10:23 UTC (permalink / raw)
To: libc-alpha; +Cc: Adhemerval Zanella Netto, Rocket Ma, Xiang Gao
From: Xiang Gao <gaoxiang@kylinos.cn>
setvbuf (stream, NULL, _IOFBF, 0) takes a special path in
_IO_setvbuf: if the byte-oriented buffer base is NULL, it calls
_IO_DOALLOCATE and returns without invoking the stream setbuf hook.
For open_wmemstream, the byte-oriented buffer base is NULL although
the wide result buffer has already been initialized in _wide_data.
As a result, this path calls _IO_wdefault_doallocate, which may
replace the wide buffer managed by open_wmemstream.
Install an open_wmemstream-specific doallocate hook that leaves
the growable result buffer unchanged. Add a regression test for this
path.
Install a narrow memstream doallocate hook as well. It keeps both
memstream vtables consistent (generic stdio allocation must not
replace the growable result buffer).
Signed-off-by: Xiang Gao <gaoxiang@kylinos.cn>
---
libio/Makefile | 1 +
libio/libioP.h | 3 ++
libio/memstream.c | 9 ++++
libio/tst-wmemstream-setvbuf-doallocate.c | 64 +++++++++++++++++++++++
libio/vtables.c | 6 ++-
libio/wmemstream.c | 9 ++++
6 files changed, 90 insertions(+), 2 deletions(-)
create mode 100644 libio/tst-wmemstream-setvbuf-doallocate.c
diff --git a/libio/Makefile b/libio/Makefile
index 584fcdb14d..b3c2d818b3 100644
--- a/libio/Makefile
+++ b/libio/Makefile
@@ -140,6 +140,7 @@ tests = \
tst-wfile-sync \
tst-wfiledoallocate-static \
tst-widetext \
+ tst-wmemstream-setvbuf-doallocate \
tst-wmemstream1 \
tst-wmemstream2 \
tst-wmemstream3 \
diff --git a/libio/libioP.h b/libio/libioP.h
index 17c0b6e76d..fa547a4a29 100644
--- a/libio/libioP.h
+++ b/libio/libioP.h
@@ -742,10 +742,13 @@ extern int _IO_mem_sync (FILE *fp) __THROW attribute_hidden;
extern void _IO_mem_finish (FILE *fp, int) __THROW attribute_hidden;
extern FILE *_IO_mem_setbuf (FILE *fp, char *buf, ssize_t size)
__THROW attribute_hidden;
+extern int _IO_mem_doallocate (FILE *fp) __THROW attribute_hidden;
+
extern int _IO_wmem_sync (FILE *fp) __THROW attribute_hidden;
extern void _IO_wmem_finish (FILE *fp, int) __THROW attribute_hidden;
extern FILE *_IO_wmem_setbuf (FILE *fp, char *buf, ssize_t size)
__THROW attribute_hidden;
+extern int _IO_wmem_doallocate (FILE *fp) __THROW attribute_hidden;
/* Other strfile functions */
struct _IO_strfile_;
diff --git a/libio/memstream.c b/libio/memstream.c
index a5f909cf64..918a2d6015 100644
--- a/libio/memstream.c
+++ b/libio/memstream.c
@@ -121,3 +121,12 @@ _IO_mem_setbuf (FILE *fp, char *p, ssize_t len)
(void) len;
return fp;
}
+
+int
+_IO_mem_doallocate (FILE *fp)
+{
+ /* memstream manage a growable buffer internally. The doallocate
+ hook must not replace it with a generic stdio buffer. */
+ (void) fp;
+ return 1;
+}
diff --git a/libio/tst-wmemstream-setvbuf-doallocate.c b/libio/tst-wmemstream-setvbuf-doallocate.c
new file mode 100644
index 0000000000..595a716db5
--- /dev/null
+++ b/libio/tst-wmemstream-setvbuf-doallocate.c
@@ -0,0 +1,64 @@
+/* Test setvbuf on open_wmenstream, BZ #34019.
+ Copyright (C) 2026 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 <wchar.h>
+#include <support/check.h>
+
+static int
+do_test (void)
+{
+ /* Regression test for setvbuf doallocate on open_wmemstream.
+ This test cover the _IO_setvbuf path for:
+
+ setvbuf (stream, NULL, _IOFBF, 0)
+
+ This path may call _IO_DOALLOCATE and return without invoking
+ the stream setbuf hook. For open_wmemstream, the generic wide
+ doallocate hook must not replace the growable result buffer. */
+
+ wchar_t *wbuf = NULL;
+ size_t wlen = 0;
+ FILE *fp = open_wmemstream (&wbuf, &wlen);
+
+ TEST_VERIFY_EXIT (fp != NULL);
+
+ TEST_COMPARE (setvbuf (fp, NULL, _IOFBF, 0), 0);
+
+ TEST_COMPARE (fputwc (L'A', fp), L'A');
+ TEST_COMPARE (fflush (fp), 0);
+ TEST_COMPARE (wlen, 1);
+ TEST_VERIFY (wbuf != NULL);
+ TEST_VERIFY (wbuf[0] == L'A');
+ TEST_VERIFY (wbuf[1] == L'\0');
+
+ TEST_COMPARE (fputwc (L'B', fp), L'B');
+ TEST_COMPARE (fclose (fp), 0);
+ TEST_COMPARE (wlen, 2);
+ TEST_VERIFY (wbuf != NULL);
+ TEST_VERIFY (wbuf[0] == L'A');
+ TEST_VERIFY (wbuf[1] == L'B');
+ TEST_VERIFY (wbuf[2] == L'\0');
+
+ free (wbuf);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/libio/vtables.c b/libio/vtables.c
index ca9f1b2dc4..ba3f9566f8 100644
--- a/libio/vtables.c
+++ b/libio/vtables.c
@@ -79,10 +79,12 @@
# pragma weak _IO_mem_finish
# pragma weak _IO_mem_setbuf
# pragma weak _IO_mem_sync
+# pragma weak _IO_mem_doallocate
# pragma weak _IO_wmem_finish
# pragma weak _IO_wmem_setbuf
# pragma weak _IO_wmem_sync
+# pragma weak _IO_wmem_doallocate
# pragma weak __printf_buffer_as_file_overflow
# pragma weak __printf_buffer_as_file_xsputn
@@ -338,7 +340,7 @@ const struct _IO_jump_t __io_vtables[] attribute_relro =
JUMP_INIT (seekpos, _IO_default_seekpos),
JUMP_INIT (setbuf, _IO_mem_setbuf),
JUMP_INIT (sync, _IO_mem_sync),
- JUMP_INIT (doallocate, _IO_default_doallocate),
+ JUMP_INIT (doallocate, _IO_mem_doallocate),
JUMP_INIT (read, _IO_default_read),
JUMP_INIT (write, _IO_default_write),
JUMP_INIT (seek, _IO_default_seek),
@@ -361,7 +363,7 @@ const struct _IO_jump_t __io_vtables[] attribute_relro =
JUMP_INIT (seekpos, _IO_default_seekpos),
JUMP_INIT (setbuf, _IO_wmem_setbuf),
JUMP_INIT (sync, _IO_wmem_sync),
- JUMP_INIT (doallocate, _IO_wdefault_doallocate),
+ JUMP_INIT (doallocate, _IO_wmem_doallocate),
JUMP_INIT (read, _IO_default_read),
JUMP_INIT (write, _IO_default_write),
JUMP_INIT (seek, _IO_default_seek),
diff --git a/libio/wmemstream.c b/libio/wmemstream.c
index 8f37a6efb8..498f5420e1 100644
--- a/libio/wmemstream.c
+++ b/libio/wmemstream.c
@@ -127,3 +127,12 @@ _IO_wmem_setbuf (FILE *fp, char *p, ssize_t len)
(void) len;
return fp;
}
+
+int
+_IO_wmem_doallocate (FILE *fp)
+{
+ /* wmemstreams manage a growable buffer internally. The doallocate
+ hook must not replace it with a generic stdio buffer. */
+ (void) fp;
+ return 1;
+}
--
2.53.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v3 2/2] libio: Ignore doallocate for open_memstream and open_wmemstream [BZ #34019]
2026-05-06 10:23 ` [PATCH v3 2/2] libio: Ignore doallocate " Gao Xiang
@ 2026-05-06 10:38 ` Andreas Schwab
2026-05-07 9:39 ` [PATCH v4 0/2] libio: Keep memstream result buffers independent of setvbuf Gao Xiang
0 siblings, 1 reply; 22+ messages in thread
From: Andreas Schwab @ 2026-05-06 10:38 UTC (permalink / raw)
To: Gao Xiang; +Cc: libc-alpha, Adhemerval Zanella Netto, Rocket Ma
On Mai 06 2026, Gao Xiang wrote:
> diff --git a/libio/tst-wmemstream-setvbuf-doallocate.c b/libio/tst-wmemstream-setvbuf-doallocate.c
> new file mode 100644
> index 0000000000..595a716db5
> --- /dev/null
> +++ b/libio/tst-wmemstream-setvbuf-doallocate.c
> @@ -0,0 +1,64 @@
> +/* Test setvbuf on open_wmenstream, BZ #34019.
> + Copyright (C) 2026 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 <wchar.h>
> +#include <support/check.h>
> +
> +static int
> +do_test (void)
> +{
> + /* Regression test for setvbuf doallocate on open_wmemstream.
> + This test cover the _IO_setvbuf path for:
> +
> + setvbuf (stream, NULL, _IOFBF, 0)
> +
> + This path may call _IO_DOALLOCATE and return without invoking
> + the stream setbuf hook. For open_wmemstream, the generic wide
> + doallocate hook must not replace the growable result buffer. */
> +
> + wchar_t *wbuf = NULL;
> + size_t wlen = 0;
> + FILE *fp = open_wmemstream (&wbuf, &wlen);
If you base the test on libio/tst-memstream.h you can test both narrow
and wide streams with the same test.
--
Andreas Schwab, SUSE Labs, schwab@suse.de
GPG Key fingerprint = 0196 BAD8 1CE9 1970 F4BE 1748 E4D4 88E3 0EEA B9D7
"And now for something completely different."
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v4 0/2] libio: Keep memstream result buffers independent of setvbuf
2026-05-06 10:38 ` Andreas Schwab
@ 2026-05-07 9:39 ` Gao Xiang
2026-05-07 9:39 ` [PATCH v4 1/2] libio: Ignore setbuf for open_memstream and open_wmemstream [BZ #34019] Gao Xiang
` (2 more replies)
0 siblings, 3 replies; 22+ messages in thread
From: Gao Xiang @ 2026-05-07 9:39 UTC (permalink / raw)
To: libc-alpha; +Cc: Andreas Schwab, Adhemerval Zanella Netto, Rocket Ma, Xiang Gao
From: Xiang Gao <gaoxiang@kylinos.cn>
This series fixes two paths where setbuf/setvbuf can interfere with
the growable result buffers managed by open_memstream and
open_wmemstream.
Patch 1 keeps the v2 setbuf fix: memstream-specific setbuf hooks keep
the internal growable result buffer unchanged.
Patch 2 handles the setvbuf (stream, NULL, _IOFBF, 0) path, which can
call _IO_DOALLOCATE and return without invoking the stream setbuf hook.
Changes in v4:
* Refactor the test cases in both patches by using libio/tst-memstream.h,
so that the regression tests cover both open_memstream and
open_wmemstream, as suggested by Andreas Schwab.
* Use TEST_VERIFY_EXIT for pointer guard checks.
* Fix typos.
Tested on x86_64-linux-gnu with no regression:
make -j$(nproc) subdirs=libio check
Xiang Gao (2):
libio: Ignore setbuf for open_memstream and open_wmemstream [BZ
#34019]
libio: Ignore doallocate for open_memstream and open_wmemstream [BZ
#34019]
libio/Makefile | 4 ++
libio/libioP.h | 15 ++++--
libio/memstream.c | 20 ++++++++
libio/tst-memstream-setbuf.c | 41 +++++++++++++++
libio/tst-memstream-setvbuf-doallocate.c | 61 +++++++++++++++++++++++
libio/tst-wmemstream-setbuf.c | 20 ++++++++
libio/tst-wmemstream-setvbuf-doallocate.c | 20 ++++++++
libio/vtables.c | 12 +++--
libio/wmemstream.c | 20 ++++++++
9 files changed, 205 insertions(+), 8 deletions(-)
create mode 100644 libio/tst-memstream-setbuf.c
create mode 100644 libio/tst-memstream-setvbuf-doallocate.c
create mode 100644 libio/tst-wmemstream-setbuf.c
create mode 100644 libio/tst-wmemstream-setvbuf-doallocate.c
--
2.53.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v4 1/2] libio: Ignore setbuf for open_memstream and open_wmemstream [BZ #34019]
2026-05-07 9:39 ` [PATCH v4 0/2] libio: Keep memstream result buffers independent of setvbuf Gao Xiang
@ 2026-05-07 9:39 ` Gao Xiang
2026-05-07 13:47 ` Adhemerval Zanella Netto
2026-05-07 9:40 ` [PATCH v4 2/2] libio: Ignore doallocate " Gao Xiang
2026-05-08 6:04 ` [PATCH v5 0/2] libio: Keep memstream result buffers independent of setvbuf Gao Xiang
2 siblings, 1 reply; 22+ messages in thread
From: Gao Xiang @ 2026-05-07 9:39 UTC (permalink / raw)
To: libc-alpha; +Cc: Andreas Schwab, Adhemerval Zanella Netto, Rocket Ma, Xiang Gao
From: Xiang Gao <gaoxiang@kylinos.cn>
open_memstream and open_wmemstream manage an internal growable buffer.
The default setbuf hook can reset that buffer, breaking the assumptions
used by the string stream overflow paths.
Install setbuf hooks that leave the internal buffer unchanged, and add
regression test cases for the narrow and wide cases, based on the
reproducer in BZ #34019.
Checked on x86_64 with no regression in the libio tests.
Reported-by: Rocket Ma <marocketbd@gmail.com>
Signed-off-by: Xiang Gao <gaoxiang@kylinos.cn>
---
libio/Makefile | 2 ++
libio/libioP.h | 12 ++++++----
libio/memstream.c | 10 +++++++++
libio/tst-memstream-setbuf.c | 41 +++++++++++++++++++++++++++++++++++
libio/tst-wmemstream-setbuf.c | 20 +++++++++++++++++
libio/vtables.c | 6 +++--
libio/wmemstream.c | 10 +++++++++
7 files changed, 95 insertions(+), 6 deletions(-)
create mode 100644 libio/tst-memstream-setbuf.c
create mode 100644 libio/tst-wmemstream-setbuf.c
diff --git a/libio/Makefile b/libio/Makefile
index 93656466df..6b56567d44 100644
--- a/libio/Makefile
+++ b/libio/Makefile
@@ -116,6 +116,7 @@ tests = \
tst-ftell-partial-wide \
tst-fwrite-error \
tst-getdelim \
+ tst-memstream-setbuf \
tst-memstream1 \
tst-memstream2 \
tst-memstream3 \
@@ -139,6 +140,7 @@ tests = \
tst-wfile-sync \
tst-wfiledoallocate-static \
tst-widetext \
+ tst-wmemstream-setbuf \
tst-wmemstream1 \
tst-wmemstream2 \
tst-wmemstream3 \
diff --git a/libio/libioP.h b/libio/libioP.h
index 1485d22619..17c0b6e76d 100644
--- a/libio/libioP.h
+++ b/libio/libioP.h
@@ -738,10 +738,14 @@ extern size_t __IO_obstack_xsputn (FILE *fp, const void *data, size_t n)
attribute_hidden;
/* Jumptable functions for open_{w}memstream. */
-extern int _IO_mem_sync (FILE* fp) __THROW attribute_hidden;
-extern void _IO_mem_finish (FILE* fp, int) __THROW attribute_hidden;
-extern int _IO_wmem_sync (FILE* fp) __THROW attribute_hidden;
-extern void _IO_wmem_finish (FILE* fp, int) __THROW attribute_hidden;
+extern int _IO_mem_sync (FILE *fp) __THROW attribute_hidden;
+extern void _IO_mem_finish (FILE *fp, int) __THROW attribute_hidden;
+extern FILE *_IO_mem_setbuf (FILE *fp, char *buf, ssize_t size)
+ __THROW attribute_hidden;
+extern int _IO_wmem_sync (FILE *fp) __THROW attribute_hidden;
+extern void _IO_wmem_finish (FILE *fp, int) __THROW attribute_hidden;
+extern FILE *_IO_wmem_setbuf (FILE *fp, char *buf, ssize_t size)
+ __THROW attribute_hidden;
/* Other strfile functions */
struct _IO_strfile_;
diff --git a/libio/memstream.c b/libio/memstream.c
index 0456adb92f..739080e96a 100644
--- a/libio/memstream.c
+++ b/libio/memstream.c
@@ -112,3 +112,13 @@ _IO_mem_finish (FILE *fp, int dummy)
_IO_str_finish (fp, 0);
}
+
+
+FILE *
+_IO_mem_setbuf (FILE *fp, char *p, ssize_t len)
+{
+ /* Memstream manage a growable buffer internally. */
+ (void) p;
+ (void) len;
+ return fp;
+}
diff --git a/libio/tst-memstream-setbuf.c b/libio/tst-memstream-setbuf.c
new file mode 100644
index 0000000000..eee176ede5
--- /dev/null
+++ b/libio/tst-memstream-setbuf.c
@@ -0,0 +1,41 @@
+/* Test for open_memstream BZ #34019.
+ Copyright (C) 2026 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 <libio/tst-memstream.h>
+
+static int
+do_test (void)
+{
+ /* setbuf must not replace the internal growable buffer. */
+ CHAR_T *buf = NULL;
+ size_t len = 0;
+ FILE *fp = OPEN_MEMSTREAM (&buf, &len);
+ setbuf (fp, NULL);
+ TEST_COMPARE (FPUTC (W('A'), fp), W('A'));
+ TEST_COMPARE (fclose (fp), 0);
+ TEST_COMPARE (len, 1);
+ TEST_VERIFY_EXIT (buf != NULL);
+ TEST_VERIFY (buf[0] == W('A'));
+ TEST_VERIFY (buf[1] == W('\0'));
+
+ free (buf);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/libio/tst-wmemstream-setbuf.c b/libio/tst-wmemstream-setbuf.c
new file mode 100644
index 0000000000..6ce751708a
--- /dev/null
+++ b/libio/tst-wmemstream-setbuf.c
@@ -0,0 +1,20 @@
+/* Test for open_wmemstream BZ #34019.
+ Copyright (C) 2026 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/>. */
+
+#define TEST_WCHAR
+#include <libio/tst-memstream-setbuf.c>
diff --git a/libio/vtables.c b/libio/vtables.c
index 00d9d25b5e..ca9f1b2dc4 100644
--- a/libio/vtables.c
+++ b/libio/vtables.c
@@ -77,9 +77,11 @@
# pragma weak _IO_cookie_write
# pragma weak _IO_mem_finish
+# pragma weak _IO_mem_setbuf
# pragma weak _IO_mem_sync
# pragma weak _IO_wmem_finish
+# pragma weak _IO_wmem_setbuf
# pragma weak _IO_wmem_sync
# pragma weak __printf_buffer_as_file_overflow
@@ -334,7 +336,7 @@ const struct _IO_jump_t __io_vtables[] attribute_relro =
JUMP_INIT (xsgetn, _IO_default_xsgetn),
JUMP_INIT (seekoff, _IO_str_seekoff),
JUMP_INIT (seekpos, _IO_default_seekpos),
- JUMP_INIT (setbuf, _IO_default_setbuf),
+ JUMP_INIT (setbuf, _IO_mem_setbuf),
JUMP_INIT (sync, _IO_mem_sync),
JUMP_INIT (doallocate, _IO_default_doallocate),
JUMP_INIT (read, _IO_default_read),
@@ -357,7 +359,7 @@ const struct _IO_jump_t __io_vtables[] attribute_relro =
JUMP_INIT (xsgetn, _IO_wdefault_xsgetn),
JUMP_INIT (seekoff, _IO_wstr_seekoff),
JUMP_INIT (seekpos, _IO_default_seekpos),
- JUMP_INIT (setbuf, _IO_default_setbuf),
+ JUMP_INIT (setbuf, _IO_wmem_setbuf),
JUMP_INIT (sync, _IO_wmem_sync),
JUMP_INIT (doallocate, _IO_wdefault_doallocate),
JUMP_INIT (read, _IO_default_read),
diff --git a/libio/wmemstream.c b/libio/wmemstream.c
index d0c639be70..168232314a 100644
--- a/libio/wmemstream.c
+++ b/libio/wmemstream.c
@@ -117,3 +117,13 @@ _IO_wmem_finish (FILE *fp, int dummy)
_IO_wstr_finish (fp, 0);
}
+
+
+FILE *
+_IO_wmem_setbuf (FILE *fp, char *p, ssize_t len)
+{
+ /* Wmemstreams manage a growable buffer internally. */
+ (void) p;
+ (void) len;
+ return fp;
+}
--
2.53.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v4 2/2] libio: Ignore doallocate for open_memstream and open_wmemstream [BZ #34019]
2026-05-07 9:39 ` [PATCH v4 0/2] libio: Keep memstream result buffers independent of setvbuf Gao Xiang
2026-05-07 9:39 ` [PATCH v4 1/2] libio: Ignore setbuf for open_memstream and open_wmemstream [BZ #34019] Gao Xiang
@ 2026-05-07 9:40 ` Gao Xiang
2026-05-07 13:49 ` Adhemerval Zanella Netto
2026-05-08 6:04 ` [PATCH v5 0/2] libio: Keep memstream result buffers independent of setvbuf Gao Xiang
2 siblings, 1 reply; 22+ messages in thread
From: Gao Xiang @ 2026-05-07 9:40 UTC (permalink / raw)
To: libc-alpha; +Cc: Andreas Schwab, Adhemerval Zanella Netto, Rocket Ma, Xiang Gao
From: Xiang Gao <gaoxiang@kylinos.cn>
setvbuf (stream, NULL, _IOFBF, 0) takes a special path in
_IO_setvbuf: if the byte-oriented buffer base is NULL, it calls
_IO_DOALLOCATE and returns without invoking the stream setbuf hook.
For open_wmemstream, the byte-oriented buffer base is NULL although
the wide result buffer has already been initialized in _wide_data.
As a result, this path calls _IO_wdefault_doallocate, which may
replace the wide buffer managed by open_wmemstream.
Install an open_wmemstream-specific doallocate hook that leaves
the growable result buffer unchanged. Add a regression test for this
path.
Install a narrow memstream doallocate hook as well. It keeps both
memstream vtables consistent (generic stdio allocation must not
replace the growable result buffer).
Signed-off-by: Xiang Gao <gaoxiang@kylinos.cn>
---
libio/Makefile | 2 +
libio/libioP.h | 3 ++
libio/memstream.c | 10 ++++
libio/tst-memstream-setvbuf-doallocate.c | 61 +++++++++++++++++++++++
libio/tst-wmemstream-setvbuf-doallocate.c | 20 ++++++++
libio/vtables.c | 6 ++-
libio/wmemstream.c | 10 ++++
7 files changed, 110 insertions(+), 2 deletions(-)
create mode 100644 libio/tst-memstream-setvbuf-doallocate.c
create mode 100644 libio/tst-wmemstream-setvbuf-doallocate.c
diff --git a/libio/Makefile b/libio/Makefile
index 6b56567d44..e165a8b4cb 100644
--- a/libio/Makefile
+++ b/libio/Makefile
@@ -117,6 +117,7 @@ tests = \
tst-fwrite-error \
tst-getdelim \
tst-memstream-setbuf \
+ tst-memstream-setvbuf-doallocate \
tst-memstream1 \
tst-memstream2 \
tst-memstream3 \
@@ -141,6 +142,7 @@ tests = \
tst-wfiledoallocate-static \
tst-widetext \
tst-wmemstream-setbuf \
+ tst-wmemstream-setvbuf-doallocate \
tst-wmemstream1 \
tst-wmemstream2 \
tst-wmemstream3 \
diff --git a/libio/libioP.h b/libio/libioP.h
index 17c0b6e76d..fa547a4a29 100644
--- a/libio/libioP.h
+++ b/libio/libioP.h
@@ -742,10 +742,13 @@ extern int _IO_mem_sync (FILE *fp) __THROW attribute_hidden;
extern void _IO_mem_finish (FILE *fp, int) __THROW attribute_hidden;
extern FILE *_IO_mem_setbuf (FILE *fp, char *buf, ssize_t size)
__THROW attribute_hidden;
+extern int _IO_mem_doallocate (FILE *fp) __THROW attribute_hidden;
+
extern int _IO_wmem_sync (FILE *fp) __THROW attribute_hidden;
extern void _IO_wmem_finish (FILE *fp, int) __THROW attribute_hidden;
extern FILE *_IO_wmem_setbuf (FILE *fp, char *buf, ssize_t size)
__THROW attribute_hidden;
+extern int _IO_wmem_doallocate (FILE *fp) __THROW attribute_hidden;
/* Other strfile functions */
struct _IO_strfile_;
diff --git a/libio/memstream.c b/libio/memstream.c
index 739080e96a..3ba7bcb28b 100644
--- a/libio/memstream.c
+++ b/libio/memstream.c
@@ -122,3 +122,13 @@ _IO_mem_setbuf (FILE *fp, char *p, ssize_t len)
(void) len;
return fp;
}
+
+
+int
+_IO_mem_doallocate (FILE *fp)
+{
+ /* Memstream manage a growable buffer internally. The doallocate
+ hook must not replace it with a generic stdio buffer. */
+ (void) fp;
+ return 1;
+}
diff --git a/libio/tst-memstream-setvbuf-doallocate.c b/libio/tst-memstream-setvbuf-doallocate.c
new file mode 100644
index 0000000000..4910f02d5c
--- /dev/null
+++ b/libio/tst-memstream-setvbuf-doallocate.c
@@ -0,0 +1,61 @@
+/* Test setvbuf on open_memstream, BZ #34019.
+ Copyright (C) 2026 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 <libio/tst-memstream.h>
+
+static int
+do_test (void)
+{
+ /* Regression test for setvbuf doallocate on open_memstream and
+ open_wmemstream. This test covers the _IO_setvbuf path for:
+
+ setvbuf (stream, NULL, _IOFBF, 0)
+
+ This path may call _IO_DOALLOCATE and return without invoking
+ the stream setbuf hook. The generic doallocate hook must not
+ replace the growable result buffer. */
+
+ CHAR_T *buf = NULL;
+ size_t len = 0;
+ FILE *fp = OPEN_MEMSTREAM (&buf, &len);
+
+ TEST_VERIFY_EXIT (fp != NULL);
+
+ TEST_COMPARE (setvbuf (fp, NULL, _IOFBF, 0), 0);
+
+ TEST_COMPARE (FPUTC (W('A'), fp), W('A'));
+ TEST_COMPARE (fflush (fp), 0);
+ TEST_COMPARE (len, 1);
+ TEST_VERIFY_EXIT (buf != NULL);
+ TEST_VERIFY (buf[0] == W('A'));
+ TEST_VERIFY (buf[1] == W('\0'));
+
+ TEST_COMPARE (FPUTC (W('B'), fp), W('B'));
+ TEST_COMPARE (fclose (fp), 0);
+ TEST_COMPARE (len, 2);
+ TEST_VERIFY_EXIT (buf != NULL);
+ TEST_VERIFY (buf[0] == W('A'));
+ TEST_VERIFY (buf[1] == W('B'));
+ TEST_VERIFY (buf[2] == W('\0'));
+
+ free (buf);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/libio/tst-wmemstream-setvbuf-doallocate.c b/libio/tst-wmemstream-setvbuf-doallocate.c
new file mode 100644
index 0000000000..857239aa85
--- /dev/null
+++ b/libio/tst-wmemstream-setvbuf-doallocate.c
@@ -0,0 +1,20 @@
+/* Test setvbuf on open_wmemstream, BZ #34019.
+ Copyright (C) 2026 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/>. */
+
+#define TEST_WCHAR
+#include <libio/tst-memstream-setvbuf-doallocate.c>
diff --git a/libio/vtables.c b/libio/vtables.c
index ca9f1b2dc4..ba3f9566f8 100644
--- a/libio/vtables.c
+++ b/libio/vtables.c
@@ -79,10 +79,12 @@
# pragma weak _IO_mem_finish
# pragma weak _IO_mem_setbuf
# pragma weak _IO_mem_sync
+# pragma weak _IO_mem_doallocate
# pragma weak _IO_wmem_finish
# pragma weak _IO_wmem_setbuf
# pragma weak _IO_wmem_sync
+# pragma weak _IO_wmem_doallocate
# pragma weak __printf_buffer_as_file_overflow
# pragma weak __printf_buffer_as_file_xsputn
@@ -338,7 +340,7 @@ const struct _IO_jump_t __io_vtables[] attribute_relro =
JUMP_INIT (seekpos, _IO_default_seekpos),
JUMP_INIT (setbuf, _IO_mem_setbuf),
JUMP_INIT (sync, _IO_mem_sync),
- JUMP_INIT (doallocate, _IO_default_doallocate),
+ JUMP_INIT (doallocate, _IO_mem_doallocate),
JUMP_INIT (read, _IO_default_read),
JUMP_INIT (write, _IO_default_write),
JUMP_INIT (seek, _IO_default_seek),
@@ -361,7 +363,7 @@ const struct _IO_jump_t __io_vtables[] attribute_relro =
JUMP_INIT (seekpos, _IO_default_seekpos),
JUMP_INIT (setbuf, _IO_wmem_setbuf),
JUMP_INIT (sync, _IO_wmem_sync),
- JUMP_INIT (doallocate, _IO_wdefault_doallocate),
+ JUMP_INIT (doallocate, _IO_wmem_doallocate),
JUMP_INIT (read, _IO_default_read),
JUMP_INIT (write, _IO_default_write),
JUMP_INIT (seek, _IO_default_seek),
diff --git a/libio/wmemstream.c b/libio/wmemstream.c
index 168232314a..c449e482c8 100644
--- a/libio/wmemstream.c
+++ b/libio/wmemstream.c
@@ -127,3 +127,13 @@ _IO_wmem_setbuf (FILE *fp, char *p, ssize_t len)
(void) len;
return fp;
}
+
+
+int
+_IO_wmem_doallocate (FILE *fp)
+{
+ /* Wmemstreams manage a growable buffer internally. The doallocate
+ hook must not replace it with a generic stdio buffer. */
+ (void) fp;
+ return 1;
+}
--
2.53.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v4 1/2] libio: Ignore setbuf for open_memstream and open_wmemstream [BZ #34019]
2026-05-07 9:39 ` [PATCH v4 1/2] libio: Ignore setbuf for open_memstream and open_wmemstream [BZ #34019] Gao Xiang
@ 2026-05-07 13:47 ` Adhemerval Zanella Netto
0 siblings, 0 replies; 22+ messages in thread
From: Adhemerval Zanella Netto @ 2026-05-07 13:47 UTC (permalink / raw)
To: Gao Xiang, libc-alpha; +Cc: Andreas Schwab, Rocket Ma
On 07/05/26 06:39, Gao Xiang wrote:
> From: Xiang Gao <gaoxiang@kylinos.cn>
>
> open_memstream and open_wmemstream manage an internal growable buffer.
> The default setbuf hook can reset that buffer, breaking the assumptions
> used by the string stream overflow paths.
>
> Install setbuf hooks that leave the internal buffer unchanged, and add
> regression test cases for the narrow and wide cases, based on the
> reproducer in BZ #34019.
>
> Checked on x86_64 with no regression in the libio tests.
>
> Reported-by: Rocket Ma <marocketbd@gmail.com>
> Signed-off-by: Xiang Gao <gaoxiang@kylinos.cn>
The patch looks good, some minor comments below.
> ---
> libio/Makefile | 2 ++
> libio/libioP.h | 12 ++++++----
> libio/memstream.c | 10 +++++++++
> libio/tst-memstream-setbuf.c | 41 +++++++++++++++++++++++++++++++++++
> libio/tst-wmemstream-setbuf.c | 20 +++++++++++++++++
> libio/vtables.c | 6 +++--
> libio/wmemstream.c | 10 +++++++++
> 7 files changed, 95 insertions(+), 6 deletions(-)
> create mode 100644 libio/tst-memstream-setbuf.c
> create mode 100644 libio/tst-wmemstream-setbuf.c
>
> diff --git a/libio/Makefile b/libio/Makefile
> index 93656466df..6b56567d44 100644
> --- a/libio/Makefile
> +++ b/libio/Makefile
> @@ -116,6 +116,7 @@ tests = \
> tst-ftell-partial-wide \
> tst-fwrite-error \
> tst-getdelim \
> + tst-memstream-setbuf \
> tst-memstream1 \
> tst-memstream2 \
> tst-memstream3 \
> @@ -139,6 +140,7 @@ tests = \
> tst-wfile-sync \
> tst-wfiledoallocate-static \
> tst-widetext \
> + tst-wmemstream-setbuf \
> tst-wmemstream1 \
> tst-wmemstream2 \
> tst-wmemstream3 \
> diff --git a/libio/libioP.h b/libio/libioP.h
> index 1485d22619..17c0b6e76d 100644
> --- a/libio/libioP.h
> +++ b/libio/libioP.h
> @@ -738,10 +738,14 @@ extern size_t __IO_obstack_xsputn (FILE *fp, const void *data, size_t n)
> attribute_hidden;
>
> /* Jumptable functions for open_{w}memstream. */
> -extern int _IO_mem_sync (FILE* fp) __THROW attribute_hidden;
> -extern void _IO_mem_finish (FILE* fp, int) __THROW attribute_hidden;
> -extern int _IO_wmem_sync (FILE* fp) __THROW attribute_hidden;
> -extern void _IO_wmem_finish (FILE* fp, int) __THROW attribute_hidden;
> +extern int _IO_mem_sync (FILE *fp) __THROW attribute_hidden;
> +extern void _IO_mem_finish (FILE *fp, int) __THROW attribute_hidden;
> +extern FILE *_IO_mem_setbuf (FILE *fp, char *buf, ssize_t size)
> + __THROW attribute_hidden;
> +extern int _IO_wmem_sync (FILE *fp) __THROW attribute_hidden;
> +extern void _IO_wmem_finish (FILE *fp, int) __THROW attribute_hidden;
> +extern FILE *_IO_wmem_setbuf (FILE *fp, char *buf, ssize_t size)
> + __THROW attribute_hidden;
>
Please drop the style changes for _IO_mem_sync, _IO_mem_finish, _IO_wmem_sync,
and _IO_wmem_finish.
> /* Other strfile functions */
> struct _IO_strfile_;
> diff --git a/libio/memstream.c b/libio/memstream.c
> index 0456adb92f..739080e96a 100644
> --- a/libio/memstream.c
> +++ b/libio/memstream.c
> @@ -112,3 +112,13 @@ _IO_mem_finish (FILE *fp, int dummy)
>
> _IO_str_finish (fp, 0);
> }
> +
> +
> +FILE *
> +_IO_mem_setbuf (FILE *fp, char *p, ssize_t len)
> +{
> + /* Memstream manage a growable buffer internally. */
> + (void) p;
> + (void) len;
There is no need to add the cast to avoid ununsed variable warnings.
> + return fp;
> +}
> diff --git a/libio/tst-memstream-setbuf.c b/libio/tst-memstream-setbuf.c
> new file mode 100644
> index 0000000000..eee176ede5
> --- /dev/null
> +++ b/libio/tst-memstream-setbuf.c
> @@ -0,0 +1,41 @@
> +/* Test for open_memstream BZ #34019.
> + Copyright (C) 2026 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 <libio/tst-memstream.h>
> +
> +static int
> +do_test (void)
> +{
> + /* setbuf must not replace the internal growable buffer. */
> + CHAR_T *buf = NULL;
> + size_t len = 0;
> + FILE *fp = OPEN_MEMSTREAM (&buf, &len);
> + setbuf (fp, NULL);
> + TEST_COMPARE (FPUTC (W('A'), fp), W('A'));
> + TEST_COMPARE (fclose (fp), 0);
> + TEST_COMPARE (len, 1);
> + TEST_VERIFY_EXIT (buf != NULL);
> + TEST_VERIFY (buf[0] == W('A'));
> + TEST_VERIFY (buf[1] == W('\0'));
> +
> + free (buf);
> +
> + return 0;
> +}
> +
> +#include <support/test-driver.c>
> diff --git a/libio/tst-wmemstream-setbuf.c b/libio/tst-wmemstream-setbuf.c
> new file mode 100644
> index 0000000000..6ce751708a
> --- /dev/null
> +++ b/libio/tst-wmemstream-setbuf.c
> @@ -0,0 +1,20 @@
> +/* Test for open_wmemstream BZ #34019.
> + Copyright (C) 2026 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/>. */
> +
> +#define TEST_WCHAR
> +#include <libio/tst-memstream-setbuf.c>
> diff --git a/libio/vtables.c b/libio/vtables.c
> index 00d9d25b5e..ca9f1b2dc4 100644
> --- a/libio/vtables.c
> +++ b/libio/vtables.c
> @@ -77,9 +77,11 @@
> # pragma weak _IO_cookie_write
>
> # pragma weak _IO_mem_finish
> +# pragma weak _IO_mem_setbuf
> # pragma weak _IO_mem_sync
>
> # pragma weak _IO_wmem_finish
> +# pragma weak _IO_wmem_setbuf
> # pragma weak _IO_wmem_sync
>
> # pragma weak __printf_buffer_as_file_overflow
> @@ -334,7 +336,7 @@ const struct _IO_jump_t __io_vtables[] attribute_relro =
> JUMP_INIT (xsgetn, _IO_default_xsgetn),
> JUMP_INIT (seekoff, _IO_str_seekoff),
> JUMP_INIT (seekpos, _IO_default_seekpos),
> - JUMP_INIT (setbuf, _IO_default_setbuf),
> + JUMP_INIT (setbuf, _IO_mem_setbuf),
> JUMP_INIT (sync, _IO_mem_sync),
> JUMP_INIT (doallocate, _IO_default_doallocate),
> JUMP_INIT (read, _IO_default_read),
> @@ -357,7 +359,7 @@ const struct _IO_jump_t __io_vtables[] attribute_relro =
> JUMP_INIT (xsgetn, _IO_wdefault_xsgetn),
> JUMP_INIT (seekoff, _IO_wstr_seekoff),
> JUMP_INIT (seekpos, _IO_default_seekpos),
> - JUMP_INIT (setbuf, _IO_default_setbuf),
> + JUMP_INIT (setbuf, _IO_wmem_setbuf),
> JUMP_INIT (sync, _IO_wmem_sync),
> JUMP_INIT (doallocate, _IO_wdefault_doallocate),
> JUMP_INIT (read, _IO_default_read),
> diff --git a/libio/wmemstream.c b/libio/wmemstream.c
> index d0c639be70..168232314a 100644
> --- a/libio/wmemstream.c
> +++ b/libio/wmemstream.c
> @@ -117,3 +117,13 @@ _IO_wmem_finish (FILE *fp, int dummy)
>
> _IO_wstr_finish (fp, 0);
> }
> +
> +
> +FILE *
> +_IO_wmem_setbuf (FILE *fp, char *p, ssize_t len)
> +{
> + /* Wmemstreams manage a growable buffer internally. */
> + (void) p;
> + (void) len;
Same as before.
> + return fp;
> +}
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v4 2/2] libio: Ignore doallocate for open_memstream and open_wmemstream [BZ #34019]
2026-05-07 9:40 ` [PATCH v4 2/2] libio: Ignore doallocate " Gao Xiang
@ 2026-05-07 13:49 ` Adhemerval Zanella Netto
0 siblings, 0 replies; 22+ messages in thread
From: Adhemerval Zanella Netto @ 2026-05-07 13:49 UTC (permalink / raw)
To: Gao Xiang, libc-alpha; +Cc: Andreas Schwab, Rocket Ma
On 07/05/26 06:40, Gao Xiang wrote:
> From: Xiang Gao <gaoxiang@kylinos.cn>
>
> setvbuf (stream, NULL, _IOFBF, 0) takes a special path in
> _IO_setvbuf: if the byte-oriented buffer base is NULL, it calls
> _IO_DOALLOCATE and returns without invoking the stream setbuf hook.
>
> For open_wmemstream, the byte-oriented buffer base is NULL although
> the wide result buffer has already been initialized in _wide_data.
> As a result, this path calls _IO_wdefault_doallocate, which may
> replace the wide buffer managed by open_wmemstream.
>
> Install an open_wmemstream-specific doallocate hook that leaves
> the growable result buffer unchanged. Add a regression test for this
> path.
>
> Install a narrow memstream doallocate hook as well. It keeps both
> memstream vtables consistent (generic stdio allocation must not
> replace the growable result buffer).
>
> Signed-off-by: Xiang Gao <gaoxiang@kylinos.cn>
The patch looks good, some minor comments below.
> ---
> libio/Makefile | 2 +
> libio/libioP.h | 3 ++
> libio/memstream.c | 10 ++++
> libio/tst-memstream-setvbuf-doallocate.c | 61 +++++++++++++++++++++++
> libio/tst-wmemstream-setvbuf-doallocate.c | 20 ++++++++
> libio/vtables.c | 6 ++-
> libio/wmemstream.c | 10 ++++
> 7 files changed, 110 insertions(+), 2 deletions(-)
> create mode 100644 libio/tst-memstream-setvbuf-doallocate.c
> create mode 100644 libio/tst-wmemstream-setvbuf-doallocate.c
>
> diff --git a/libio/Makefile b/libio/Makefile
> index 6b56567d44..e165a8b4cb 100644
> --- a/libio/Makefile
> +++ b/libio/Makefile
> @@ -117,6 +117,7 @@ tests = \
> tst-fwrite-error \
> tst-getdelim \
> tst-memstream-setbuf \
> + tst-memstream-setvbuf-doallocate \
> tst-memstream1 \
> tst-memstream2 \
> tst-memstream3 \
> @@ -141,6 +142,7 @@ tests = \
> tst-wfiledoallocate-static \
> tst-widetext \
> tst-wmemstream-setbuf \
> + tst-wmemstream-setvbuf-doallocate \
> tst-wmemstream1 \
> tst-wmemstream2 \
> tst-wmemstream3 \
> diff --git a/libio/libioP.h b/libio/libioP.h
> index 17c0b6e76d..fa547a4a29 100644
> --- a/libio/libioP.h
> +++ b/libio/libioP.h
> @@ -742,10 +742,13 @@ extern int _IO_mem_sync (FILE *fp) __THROW attribute_hidden;
> extern void _IO_mem_finish (FILE *fp, int) __THROW attribute_hidden;
> extern FILE *_IO_mem_setbuf (FILE *fp, char *buf, ssize_t size)
> __THROW attribute_hidden;
> +extern int _IO_mem_doallocate (FILE *fp) __THROW attribute_hidden;
> +
> extern int _IO_wmem_sync (FILE *fp) __THROW attribute_hidden;
> extern void _IO_wmem_finish (FILE *fp, int) __THROW attribute_hidden;
> extern FILE *_IO_wmem_setbuf (FILE *fp, char *buf, ssize_t size)
> __THROW attribute_hidden;
> +extern int _IO_wmem_doallocate (FILE *fp) __THROW attribute_hidden;
>
> /* Other strfile functions */
> struct _IO_strfile_;
> diff --git a/libio/memstream.c b/libio/memstream.c
> index 739080e96a..3ba7bcb28b 100644
> --- a/libio/memstream.c
> +++ b/libio/memstream.c
> @@ -122,3 +122,13 @@ _IO_mem_setbuf (FILE *fp, char *p, ssize_t len)
> (void) len;
> return fp;
> }
> +
> +
> +int
> +_IO_mem_doallocate (FILE *fp)
> +{
> + /* Memstream manage a growable buffer internally. The doallocate
> + hook must not replace it with a generic stdio buffer. */
> + (void) fp;
No need the cast here.
> + return 1;
> +}
> diff --git a/libio/tst-memstream-setvbuf-doallocate.c b/libio/tst-memstream-setvbuf-doallocate.c
> new file mode 100644
> index 0000000000..4910f02d5c
> --- /dev/null
> +++ b/libio/tst-memstream-setvbuf-doallocate.c
> @@ -0,0 +1,61 @@
> +/* Test setvbuf on open_memstream, BZ #34019.
> + Copyright (C) 2026 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 <libio/tst-memstream.h>
> +
> +static int
> +do_test (void)
> +{
> + /* Regression test for setvbuf doallocate on open_memstream and
> + open_wmemstream. This test covers the _IO_setvbuf path for:
Minor style nit: double space after period.
> +
> + setvbuf (stream, NULL, _IOFBF, 0)
> +
> + This path may call _IO_DOALLOCATE and return without invoking
> + the stream setbuf hook. The generic doallocate hook must not
> + replace the growable result buffer. */
> +
> + CHAR_T *buf = NULL;
> + size_t len = 0;
> + FILE *fp = OPEN_MEMSTREAM (&buf, &len);
> +
> + TEST_VERIFY_EXIT (fp != NULL);
> +
> + TEST_COMPARE (setvbuf (fp, NULL, _IOFBF, 0), 0);
> +
> + TEST_COMPARE (FPUTC (W('A'), fp), W('A'));
> + TEST_COMPARE (fflush (fp), 0);
> + TEST_COMPARE (len, 1);
> + TEST_VERIFY_EXIT (buf != NULL);
> + TEST_VERIFY (buf[0] == W('A'));
> + TEST_VERIFY (buf[1] == W('\0'));
> +
> + TEST_COMPARE (FPUTC (W('B'), fp), W('B'));
> + TEST_COMPARE (fclose (fp), 0);
> + TEST_COMPARE (len, 2);
> + TEST_VERIFY_EXIT (buf != NULL);
> + TEST_VERIFY (buf[0] == W('A'));
> + TEST_VERIFY (buf[1] == W('B'));
> + TEST_VERIFY (buf[2] == W('\0'));
> +
> + free (buf);
> +
> + return 0;
> +}
> +
> +#include <support/test-driver.c>
> diff --git a/libio/tst-wmemstream-setvbuf-doallocate.c b/libio/tst-wmemstream-setvbuf-doallocate.c
> new file mode 100644
> index 0000000000..857239aa85
> --- /dev/null
> +++ b/libio/tst-wmemstream-setvbuf-doallocate.c
> @@ -0,0 +1,20 @@
> +/* Test setvbuf on open_wmemstream, BZ #34019.
> + Copyright (C) 2026 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/>. */
> +
> +#define TEST_WCHAR
> +#include <libio/tst-memstream-setvbuf-doallocate.c>
> diff --git a/libio/vtables.c b/libio/vtables.c
> index ca9f1b2dc4..ba3f9566f8 100644
> --- a/libio/vtables.c
> +++ b/libio/vtables.c
> @@ -79,10 +79,12 @@
> # pragma weak _IO_mem_finish
> # pragma weak _IO_mem_setbuf
> # pragma weak _IO_mem_sync
> +# pragma weak _IO_mem_doallocate
>
> # pragma weak _IO_wmem_finish
> # pragma weak _IO_wmem_setbuf
> # pragma weak _IO_wmem_sync
> +# pragma weak _IO_wmem_doallocate
>
> # pragma weak __printf_buffer_as_file_overflow
> # pragma weak __printf_buffer_as_file_xsputn
> @@ -338,7 +340,7 @@ const struct _IO_jump_t __io_vtables[] attribute_relro =
> JUMP_INIT (seekpos, _IO_default_seekpos),
> JUMP_INIT (setbuf, _IO_mem_setbuf),
> JUMP_INIT (sync, _IO_mem_sync),
> - JUMP_INIT (doallocate, _IO_default_doallocate),
> + JUMP_INIT (doallocate, _IO_mem_doallocate),
> JUMP_INIT (read, _IO_default_read),
> JUMP_INIT (write, _IO_default_write),
> JUMP_INIT (seek, _IO_default_seek),
> @@ -361,7 +363,7 @@ const struct _IO_jump_t __io_vtables[] attribute_relro =
> JUMP_INIT (seekpos, _IO_default_seekpos),
> JUMP_INIT (setbuf, _IO_wmem_setbuf),
> JUMP_INIT (sync, _IO_wmem_sync),
> - JUMP_INIT (doallocate, _IO_wdefault_doallocate),
> + JUMP_INIT (doallocate, _IO_wmem_doallocate),
> JUMP_INIT (read, _IO_default_read),
> JUMP_INIT (write, _IO_default_write),
> JUMP_INIT (seek, _IO_default_seek),
> diff --git a/libio/wmemstream.c b/libio/wmemstream.c
> index 168232314a..c449e482c8 100644
> --- a/libio/wmemstream.c
> +++ b/libio/wmemstream.c
> @@ -127,3 +127,13 @@ _IO_wmem_setbuf (FILE *fp, char *p, ssize_t len)
> (void) len;
> return fp;
> }
> +
> +
> +int
> +_IO_wmem_doallocate (FILE *fp)
> +{
> + /* Wmemstreams manage a growable buffer internally. The doallocate
> + hook must not replace it with a generic stdio buffer. */
> + (void) fp;
Ditto.
> + return 1;
> +}
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v5 0/2] libio: Keep memstream result buffers independent of setvbuf
2026-05-07 9:39 ` [PATCH v4 0/2] libio: Keep memstream result buffers independent of setvbuf Gao Xiang
2026-05-07 9:39 ` [PATCH v4 1/2] libio: Ignore setbuf for open_memstream and open_wmemstream [BZ #34019] Gao Xiang
2026-05-07 9:40 ` [PATCH v4 2/2] libio: Ignore doallocate " Gao Xiang
@ 2026-05-08 6:04 ` Gao Xiang
2026-05-08 6:04 ` [PATCH v5 1/2] libio: Ignore setbuf for open_memstream and open_wmemstream [BZ #34019] Gao Xiang
` (2 more replies)
2 siblings, 3 replies; 22+ messages in thread
From: Gao Xiang @ 2026-05-08 6:04 UTC (permalink / raw)
To: libc-alpha; +Cc: Adhemerval Zanella Netto, Andreas Schwab, Rocket Ma, Xiang Gao
From: Xiang Gao <gaoxiang@kylinos.cn>
This series fixes two paths where setbuf/setvbuf can interfere with
the growable result buffers managed by open_memstream and
open_wmemstream.
Patch 1 keeps the v2 setbuf fix: memstream-specific setbuf hooks keep
the internal growable result buffer unchanged.
Patch 2 handles the setvbuf (stream, NULL, _IOFBF, 0) path, which can
call _IO_DOALLOCATE and return without invoking the stream setbuf hook.
Changes in v5:
* Address coding-style comments following the GNU Coding Standards:
- Drop unrelated style changes in existing libioP.h declarations.
- Remove unnecessary casts for unused hook parameters.
- Fix GNU comment spacing.
Changes in v4:
* Refactor the test cases in both patches by using libio/tst-memstream.h,
so that the regression tests cover both open_memstream and
open_wmemstream, as suggested by Andreas Schwab.
* Use TEST_VERIFY_EXIT for pointer guard checks.
* Fix typos.
Tested on x86_64-linux-gnu with no regression:
make -j$(nproc) subdirs=libio check
Xiang Gao (2):
libio: Ignore setbuf for open_memstream and open_wmemstream [BZ
#34019]
libio: Ignore doallocate for open_memstream and open_wmemstream [BZ
#34019]
libio/Makefile | 4 ++
libio/libioP.h | 6 +++
libio/memstream.c | 17 +++++++
libio/tst-memstream-setbuf.c | 41 +++++++++++++++
libio/tst-memstream-setvbuf-doallocate.c | 61 +++++++++++++++++++++++
libio/tst-wmemstream-setbuf.c | 20 ++++++++
libio/tst-wmemstream-setvbuf-doallocate.c | 20 ++++++++
libio/vtables.c | 12 +++--
libio/wmemstream.c | 17 +++++++
9 files changed, 194 insertions(+), 4 deletions(-)
create mode 100644 libio/tst-memstream-setbuf.c
create mode 100644 libio/tst-memstream-setvbuf-doallocate.c
create mode 100644 libio/tst-wmemstream-setbuf.c
create mode 100644 libio/tst-wmemstream-setvbuf-doallocate.c
--
2.53.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v5 1/2] libio: Ignore setbuf for open_memstream and open_wmemstream [BZ #34019]
2026-05-08 6:04 ` [PATCH v5 0/2] libio: Keep memstream result buffers independent of setvbuf Gao Xiang
@ 2026-05-08 6:04 ` Gao Xiang
2026-05-20 18:47 ` Adhemerval Zanella Netto
2026-05-08 6:04 ` [PATCH v5 2/2] libio: Ignore doallocate " Gao Xiang
2026-05-18 7:57 ` [PATCH v5 0/2] libio: Keep memstream result buffers independent of setvbuf Alex Gao
2 siblings, 1 reply; 22+ messages in thread
From: Gao Xiang @ 2026-05-08 6:04 UTC (permalink / raw)
To: libc-alpha; +Cc: Adhemerval Zanella Netto, Andreas Schwab, Rocket Ma, Xiang Gao
From: Xiang Gao <gaoxiang@kylinos.cn>
open_memstream and open_wmemstream manage an internal growable buffer.
The default setbuf hook can reset that buffer, breaking the assumptions
used by the string stream overflow paths.
Install setbuf hooks that leave the internal buffer unchanged, and add
regression test cases for the narrow and wide cases, based on the
reproducer in BZ #34019.
Checked on x86_64 with no regression in the libio tests.
Reported-by: Rocket Ma <marocketbd@gmail.com>
Signed-off-by: Xiang Gao <gaoxiang@kylinos.cn>
---
libio/Makefile | 2 ++
libio/libioP.h | 4 ++++
libio/memstream.c | 8 +++++++
libio/tst-memstream-setbuf.c | 41 +++++++++++++++++++++++++++++++++++
libio/tst-wmemstream-setbuf.c | 20 +++++++++++++++++
libio/vtables.c | 6 +++--
libio/wmemstream.c | 8 +++++++
7 files changed, 87 insertions(+), 2 deletions(-)
create mode 100644 libio/tst-memstream-setbuf.c
create mode 100644 libio/tst-wmemstream-setbuf.c
diff --git a/libio/Makefile b/libio/Makefile
index 93656466df..6b56567d44 100644
--- a/libio/Makefile
+++ b/libio/Makefile
@@ -116,6 +116,7 @@ tests = \
tst-ftell-partial-wide \
tst-fwrite-error \
tst-getdelim \
+ tst-memstream-setbuf \
tst-memstream1 \
tst-memstream2 \
tst-memstream3 \
@@ -139,6 +140,7 @@ tests = \
tst-wfile-sync \
tst-wfiledoallocate-static \
tst-widetext \
+ tst-wmemstream-setbuf \
tst-wmemstream1 \
tst-wmemstream2 \
tst-wmemstream3 \
diff --git a/libio/libioP.h b/libio/libioP.h
index 1485d22619..fa44ad0e57 100644
--- a/libio/libioP.h
+++ b/libio/libioP.h
@@ -740,8 +740,12 @@ extern size_t __IO_obstack_xsputn (FILE *fp, const void *data, size_t n)
/* Jumptable functions for open_{w}memstream. */
extern int _IO_mem_sync (FILE* fp) __THROW attribute_hidden;
extern void _IO_mem_finish (FILE* fp, int) __THROW attribute_hidden;
+extern FILE *_IO_mem_setbuf (FILE *fp, char *buf, ssize_t size)
+ __THROW attribute_hidden;
extern int _IO_wmem_sync (FILE* fp) __THROW attribute_hidden;
extern void _IO_wmem_finish (FILE* fp, int) __THROW attribute_hidden;
+extern FILE *_IO_wmem_setbuf (FILE *fp, char *buf, ssize_t size)
+ __THROW attribute_hidden;
/* Other strfile functions */
struct _IO_strfile_;
diff --git a/libio/memstream.c b/libio/memstream.c
index 0456adb92f..69b400d928 100644
--- a/libio/memstream.c
+++ b/libio/memstream.c
@@ -112,3 +112,11 @@ _IO_mem_finish (FILE *fp, int dummy)
_IO_str_finish (fp, 0);
}
+
+
+FILE *
+_IO_mem_setbuf (FILE *fp, char *p, ssize_t len)
+{
+ /* Memstream manage a growable buffer internally. */
+ return fp;
+}
diff --git a/libio/tst-memstream-setbuf.c b/libio/tst-memstream-setbuf.c
new file mode 100644
index 0000000000..eee176ede5
--- /dev/null
+++ b/libio/tst-memstream-setbuf.c
@@ -0,0 +1,41 @@
+/* Test for open_memstream BZ #34019.
+ Copyright (C) 2026 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 <libio/tst-memstream.h>
+
+static int
+do_test (void)
+{
+ /* setbuf must not replace the internal growable buffer. */
+ CHAR_T *buf = NULL;
+ size_t len = 0;
+ FILE *fp = OPEN_MEMSTREAM (&buf, &len);
+ setbuf (fp, NULL);
+ TEST_COMPARE (FPUTC (W('A'), fp), W('A'));
+ TEST_COMPARE (fclose (fp), 0);
+ TEST_COMPARE (len, 1);
+ TEST_VERIFY_EXIT (buf != NULL);
+ TEST_VERIFY (buf[0] == W('A'));
+ TEST_VERIFY (buf[1] == W('\0'));
+
+ free (buf);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/libio/tst-wmemstream-setbuf.c b/libio/tst-wmemstream-setbuf.c
new file mode 100644
index 0000000000..6ce751708a
--- /dev/null
+++ b/libio/tst-wmemstream-setbuf.c
@@ -0,0 +1,20 @@
+/* Test for open_wmemstream BZ #34019.
+ Copyright (C) 2026 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/>. */
+
+#define TEST_WCHAR
+#include <libio/tst-memstream-setbuf.c>
diff --git a/libio/vtables.c b/libio/vtables.c
index 00d9d25b5e..ca9f1b2dc4 100644
--- a/libio/vtables.c
+++ b/libio/vtables.c
@@ -77,9 +77,11 @@
# pragma weak _IO_cookie_write
# pragma weak _IO_mem_finish
+# pragma weak _IO_mem_setbuf
# pragma weak _IO_mem_sync
# pragma weak _IO_wmem_finish
+# pragma weak _IO_wmem_setbuf
# pragma weak _IO_wmem_sync
# pragma weak __printf_buffer_as_file_overflow
@@ -334,7 +336,7 @@ const struct _IO_jump_t __io_vtables[] attribute_relro =
JUMP_INIT (xsgetn, _IO_default_xsgetn),
JUMP_INIT (seekoff, _IO_str_seekoff),
JUMP_INIT (seekpos, _IO_default_seekpos),
- JUMP_INIT (setbuf, _IO_default_setbuf),
+ JUMP_INIT (setbuf, _IO_mem_setbuf),
JUMP_INIT (sync, _IO_mem_sync),
JUMP_INIT (doallocate, _IO_default_doallocate),
JUMP_INIT (read, _IO_default_read),
@@ -357,7 +359,7 @@ const struct _IO_jump_t __io_vtables[] attribute_relro =
JUMP_INIT (xsgetn, _IO_wdefault_xsgetn),
JUMP_INIT (seekoff, _IO_wstr_seekoff),
JUMP_INIT (seekpos, _IO_default_seekpos),
- JUMP_INIT (setbuf, _IO_default_setbuf),
+ JUMP_INIT (setbuf, _IO_wmem_setbuf),
JUMP_INIT (sync, _IO_wmem_sync),
JUMP_INIT (doallocate, _IO_wdefault_doallocate),
JUMP_INIT (read, _IO_default_read),
diff --git a/libio/wmemstream.c b/libio/wmemstream.c
index d0c639be70..cdee2a4151 100644
--- a/libio/wmemstream.c
+++ b/libio/wmemstream.c
@@ -117,3 +117,11 @@ _IO_wmem_finish (FILE *fp, int dummy)
_IO_wstr_finish (fp, 0);
}
+
+
+FILE *
+_IO_wmem_setbuf (FILE *fp, char *p, ssize_t len)
+{
+ /* Wmemstreams manage a growable buffer internally. */
+ return fp;
+}
--
2.53.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v5 2/2] libio: Ignore doallocate for open_memstream and open_wmemstream [BZ #34019]
2026-05-08 6:04 ` [PATCH v5 0/2] libio: Keep memstream result buffers independent of setvbuf Gao Xiang
2026-05-08 6:04 ` [PATCH v5 1/2] libio: Ignore setbuf for open_memstream and open_wmemstream [BZ #34019] Gao Xiang
@ 2026-05-08 6:04 ` Gao Xiang
2026-05-20 18:49 ` Adhemerval Zanella Netto
2026-05-18 7:57 ` [PATCH v5 0/2] libio: Keep memstream result buffers independent of setvbuf Alex Gao
2 siblings, 1 reply; 22+ messages in thread
From: Gao Xiang @ 2026-05-08 6:04 UTC (permalink / raw)
To: libc-alpha; +Cc: Adhemerval Zanella Netto, Andreas Schwab, Rocket Ma, Xiang Gao
From: Xiang Gao <gaoxiang@kylinos.cn>
setvbuf (stream, NULL, _IOFBF, 0) takes a special path in
_IO_setvbuf: if the byte-oriented buffer base is NULL, it calls
_IO_DOALLOCATE and returns without invoking the stream setbuf hook.
For open_wmemstream, the byte-oriented buffer base is NULL although
the wide result buffer has already been initialized in _wide_data.
As a result, this path calls _IO_wdefault_doallocate, which may
replace the wide buffer managed by open_wmemstream.
Install an open_wmemstream-specific doallocate hook that leaves
the growable result buffer unchanged. Add a regression test for this
path.
Install a narrow memstream doallocate hook as well. It keeps both
memstream vtables consistent (generic stdio allocation must not
replace the growable result buffer).
Signed-off-by: Xiang Gao <gaoxiang@kylinos.cn>
---
libio/Makefile | 2 +
libio/libioP.h | 2 +
libio/memstream.c | 9 ++++
libio/tst-memstream-setvbuf-doallocate.c | 61 +++++++++++++++++++++++
libio/tst-wmemstream-setvbuf-doallocate.c | 20 ++++++++
libio/vtables.c | 6 ++-
libio/wmemstream.c | 9 ++++
7 files changed, 107 insertions(+), 2 deletions(-)
create mode 100644 libio/tst-memstream-setvbuf-doallocate.c
create mode 100644 libio/tst-wmemstream-setvbuf-doallocate.c
diff --git a/libio/Makefile b/libio/Makefile
index 6b56567d44..e165a8b4cb 100644
--- a/libio/Makefile
+++ b/libio/Makefile
@@ -117,6 +117,7 @@ tests = \
tst-fwrite-error \
tst-getdelim \
tst-memstream-setbuf \
+ tst-memstream-setvbuf-doallocate \
tst-memstream1 \
tst-memstream2 \
tst-memstream3 \
@@ -141,6 +142,7 @@ tests = \
tst-wfiledoallocate-static \
tst-widetext \
tst-wmemstream-setbuf \
+ tst-wmemstream-setvbuf-doallocate \
tst-wmemstream1 \
tst-wmemstream2 \
tst-wmemstream3 \
diff --git a/libio/libioP.h b/libio/libioP.h
index fa44ad0e57..f1a4a896a3 100644
--- a/libio/libioP.h
+++ b/libio/libioP.h
@@ -742,10 +742,12 @@ extern int _IO_mem_sync (FILE* fp) __THROW attribute_hidden;
extern void _IO_mem_finish (FILE* fp, int) __THROW attribute_hidden;
extern FILE *_IO_mem_setbuf (FILE *fp, char *buf, ssize_t size)
__THROW attribute_hidden;
+extern int _IO_mem_doallocate (FILE *fp) __THROW attribute_hidden;
extern int _IO_wmem_sync (FILE* fp) __THROW attribute_hidden;
extern void _IO_wmem_finish (FILE* fp, int) __THROW attribute_hidden;
extern FILE *_IO_wmem_setbuf (FILE *fp, char *buf, ssize_t size)
__THROW attribute_hidden;
+extern int _IO_wmem_doallocate (FILE *fp) __THROW attribute_hidden;
/* Other strfile functions */
struct _IO_strfile_;
diff --git a/libio/memstream.c b/libio/memstream.c
index 69b400d928..1af7224ab5 100644
--- a/libio/memstream.c
+++ b/libio/memstream.c
@@ -120,3 +120,12 @@ _IO_mem_setbuf (FILE *fp, char *p, ssize_t len)
/* Memstream manage a growable buffer internally. */
return fp;
}
+
+
+int
+_IO_mem_doallocate (FILE *fp)
+{
+ /* Memstream manage a growable buffer internally. The doallocate
+ hook must not replace it with a generic stdio buffer. */
+ return 1;
+}
diff --git a/libio/tst-memstream-setvbuf-doallocate.c b/libio/tst-memstream-setvbuf-doallocate.c
new file mode 100644
index 0000000000..aa6f86f02d
--- /dev/null
+++ b/libio/tst-memstream-setvbuf-doallocate.c
@@ -0,0 +1,61 @@
+/* Test setvbuf on open_memstream, BZ #34019.
+ Copyright (C) 2026 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 <libio/tst-memstream.h>
+
+static int
+do_test (void)
+{
+ /* Regression test for setvbuf doallocate on open_memstream and
+ open_wmemstream. This test covers the _IO_setvbuf path for:
+
+ setvbuf (stream, NULL, _IOFBF, 0)
+
+ This path may call _IO_DOALLOCATE and return without invoking
+ the stream setbuf hook. The generic doallocate hook must not
+ replace the growable result buffer. */
+
+ CHAR_T *buf = NULL;
+ size_t len = 0;
+ FILE *fp = OPEN_MEMSTREAM (&buf, &len);
+
+ TEST_VERIFY_EXIT (fp != NULL);
+
+ TEST_COMPARE (setvbuf (fp, NULL, _IOFBF, 0), 0);
+
+ TEST_COMPARE (FPUTC (W('A'), fp), W('A'));
+ TEST_COMPARE (fflush (fp), 0);
+ TEST_COMPARE (len, 1);
+ TEST_VERIFY_EXIT (buf != NULL);
+ TEST_VERIFY (buf[0] == W('A'));
+ TEST_VERIFY (buf[1] == W('\0'));
+
+ TEST_COMPARE (FPUTC (W('B'), fp), W('B'));
+ TEST_COMPARE (fclose (fp), 0);
+ TEST_COMPARE (len, 2);
+ TEST_VERIFY_EXIT (buf != NULL);
+ TEST_VERIFY (buf[0] == W('A'));
+ TEST_VERIFY (buf[1] == W('B'));
+ TEST_VERIFY (buf[2] == W('\0'));
+
+ free (buf);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/libio/tst-wmemstream-setvbuf-doallocate.c b/libio/tst-wmemstream-setvbuf-doallocate.c
new file mode 100644
index 0000000000..857239aa85
--- /dev/null
+++ b/libio/tst-wmemstream-setvbuf-doallocate.c
@@ -0,0 +1,20 @@
+/* Test setvbuf on open_wmemstream, BZ #34019.
+ Copyright (C) 2026 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/>. */
+
+#define TEST_WCHAR
+#include <libio/tst-memstream-setvbuf-doallocate.c>
diff --git a/libio/vtables.c b/libio/vtables.c
index ca9f1b2dc4..ba3f9566f8 100644
--- a/libio/vtables.c
+++ b/libio/vtables.c
@@ -79,10 +79,12 @@
# pragma weak _IO_mem_finish
# pragma weak _IO_mem_setbuf
# pragma weak _IO_mem_sync
+# pragma weak _IO_mem_doallocate
# pragma weak _IO_wmem_finish
# pragma weak _IO_wmem_setbuf
# pragma weak _IO_wmem_sync
+# pragma weak _IO_wmem_doallocate
# pragma weak __printf_buffer_as_file_overflow
# pragma weak __printf_buffer_as_file_xsputn
@@ -338,7 +340,7 @@ const struct _IO_jump_t __io_vtables[] attribute_relro =
JUMP_INIT (seekpos, _IO_default_seekpos),
JUMP_INIT (setbuf, _IO_mem_setbuf),
JUMP_INIT (sync, _IO_mem_sync),
- JUMP_INIT (doallocate, _IO_default_doallocate),
+ JUMP_INIT (doallocate, _IO_mem_doallocate),
JUMP_INIT (read, _IO_default_read),
JUMP_INIT (write, _IO_default_write),
JUMP_INIT (seek, _IO_default_seek),
@@ -361,7 +363,7 @@ const struct _IO_jump_t __io_vtables[] attribute_relro =
JUMP_INIT (seekpos, _IO_default_seekpos),
JUMP_INIT (setbuf, _IO_wmem_setbuf),
JUMP_INIT (sync, _IO_wmem_sync),
- JUMP_INIT (doallocate, _IO_wdefault_doallocate),
+ JUMP_INIT (doallocate, _IO_wmem_doallocate),
JUMP_INIT (read, _IO_default_read),
JUMP_INIT (write, _IO_default_write),
JUMP_INIT (seek, _IO_default_seek),
diff --git a/libio/wmemstream.c b/libio/wmemstream.c
index cdee2a4151..dffbb4a808 100644
--- a/libio/wmemstream.c
+++ b/libio/wmemstream.c
@@ -125,3 +125,12 @@ _IO_wmem_setbuf (FILE *fp, char *p, ssize_t len)
/* Wmemstreams manage a growable buffer internally. */
return fp;
}
+
+
+int
+_IO_wmem_doallocate (FILE *fp)
+{
+ /* Wmemstreams manage a growable buffer internally. The doallocate
+ hook must not replace it with a generic stdio buffer. */
+ return 1;
+}
--
2.53.0
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v5 0/2] libio: Keep memstream result buffers independent of setvbuf
2026-05-08 6:04 ` [PATCH v5 0/2] libio: Keep memstream result buffers independent of setvbuf Gao Xiang
2026-05-08 6:04 ` [PATCH v5 1/2] libio: Ignore setbuf for open_memstream and open_wmemstream [BZ #34019] Gao Xiang
2026-05-08 6:04 ` [PATCH v5 2/2] libio: Ignore doallocate " Gao Xiang
@ 2026-05-18 7:57 ` Alex Gao
2 siblings, 0 replies; 22+ messages in thread
From: Alex Gao @ 2026-05-18 7:57 UTC (permalink / raw)
To: libc-alpha; +Cc: Adhemerval Zanella Netto, Andreas Schwab, Rocket Ma
在 2026/5/8 14:04, Gao Xiang 写道:
> From: Xiang Gao <gaoxiang@kylinos.cn>
>
> This series fixes two paths where setbuf/setvbuf can interfere with
> the growable result buffers managed by open_memstream and
> open_wmemstream.
>
> Patch 1 keeps the v2 setbuf fix: memstream-specific setbuf hooks keep
> the internal growable result buffer unchanged.
>
> Patch 2 handles the setvbuf (stream, NULL, _IOFBF, 0) path, which can
> call _IO_DOALLOCATE and return without invoking the stream setbuf hook.
>
> Changes in v5:
> * Address coding-style comments following the GNU Coding Standards:
> - Drop unrelated style changes in existing libioP.h declarations.
> - Remove unnecessary casts for unused hook parameters.
> - Fix GNU comment spacing.
>
> Changes in v4:
> * Refactor the test cases in both patches by using libio/tst-memstream.h,
> so that the regression tests cover both open_memstream and
> open_wmemstream, as suggested by Andreas Schwab.
> * Use TEST_VERIFY_EXIT for pointer guard checks.
> * Fix typos.
>
> Tested on x86_64-linux-gnu with no regression:
> make -j$(nproc) subdirs=libio check
>
>
> Xiang Gao (2):
> libio: Ignore setbuf for open_memstream and open_wmemstream [BZ
> #34019]
> libio: Ignore doallocate for open_memstream and open_wmemstream [BZ
> #34019]
>
> libio/Makefile | 4 ++
> libio/libioP.h | 6 +++
> libio/memstream.c | 17 +++++++
> libio/tst-memstream-setbuf.c | 41 +++++++++++++++
> libio/tst-memstream-setvbuf-doallocate.c | 61 +++++++++++++++++++++++
> libio/tst-wmemstream-setbuf.c | 20 ++++++++
> libio/tst-wmemstream-setvbuf-doallocate.c | 20 ++++++++
> libio/vtables.c | 12 +++--
> libio/wmemstream.c | 17 +++++++
> 9 files changed, 194 insertions(+), 4 deletions(-)
> create mode 100644 libio/tst-memstream-setbuf.c
> create mode 100644 libio/tst-memstream-setvbuf-doallocate.c
> create mode 100644 libio/tst-wmemstream-setbuf.c
> create mode 100644 libio/tst-wmemstream-setvbuf-doallocate.c
>
Hi,
Ping for this series.
v5 addresses the review comments from v4, is there anything else I
should adjust, or is this version OK for commit?
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v5 1/2] libio: Ignore setbuf for open_memstream and open_wmemstream [BZ #34019]
2026-05-08 6:04 ` [PATCH v5 1/2] libio: Ignore setbuf for open_memstream and open_wmemstream [BZ #34019] Gao Xiang
@ 2026-05-20 18:47 ` Adhemerval Zanella Netto
0 siblings, 0 replies; 22+ messages in thread
From: Adhemerval Zanella Netto @ 2026-05-20 18:47 UTC (permalink / raw)
To: Gao Xiang, libc-alpha; +Cc: Andreas Schwab, Rocket Ma
On 08/05/26 03:04, Gao Xiang wrote:
> From: Xiang Gao <gaoxiang@kylinos.cn>
>
> open_memstream and open_wmemstream manage an internal growable buffer.
> The default setbuf hook can reset that buffer, breaking the assumptions
> used by the string stream overflow paths.
>
> Install setbuf hooks that leave the internal buffer unchanged, and add
> regression test cases for the narrow and wide cases, based on the
> reproducer in BZ #34019.
>
> Checked on x86_64 with no regression in the libio tests.
>
> Reported-by: Rocket Ma <marocketbd@gmail.com>
> Signed-off-by: Xiang Gao <gaoxiang@kylinos.cn>
LGTM, thanks.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
> ---
> libio/Makefile | 2 ++
> libio/libioP.h | 4 ++++
> libio/memstream.c | 8 +++++++
> libio/tst-memstream-setbuf.c | 41 +++++++++++++++++++++++++++++++++++
> libio/tst-wmemstream-setbuf.c | 20 +++++++++++++++++
> libio/vtables.c | 6 +++--
> libio/wmemstream.c | 8 +++++++
> 7 files changed, 87 insertions(+), 2 deletions(-)
> create mode 100644 libio/tst-memstream-setbuf.c
> create mode 100644 libio/tst-wmemstream-setbuf.c
>
> diff --git a/libio/Makefile b/libio/Makefile
> index 93656466df..6b56567d44 100644
> --- a/libio/Makefile
> +++ b/libio/Makefile
> @@ -116,6 +116,7 @@ tests = \
> tst-ftell-partial-wide \
> tst-fwrite-error \
> tst-getdelim \
> + tst-memstream-setbuf \
> tst-memstream1 \
> tst-memstream2 \
> tst-memstream3 \
> @@ -139,6 +140,7 @@ tests = \
> tst-wfile-sync \
> tst-wfiledoallocate-static \
> tst-widetext \
> + tst-wmemstream-setbuf \
> tst-wmemstream1 \
> tst-wmemstream2 \
> tst-wmemstream3 \
> diff --git a/libio/libioP.h b/libio/libioP.h
> index 1485d22619..fa44ad0e57 100644
> --- a/libio/libioP.h
> +++ b/libio/libioP.h
> @@ -740,8 +740,12 @@ extern size_t __IO_obstack_xsputn (FILE *fp, const void *data, size_t n)
> /* Jumptable functions for open_{w}memstream. */
> extern int _IO_mem_sync (FILE* fp) __THROW attribute_hidden;
> extern void _IO_mem_finish (FILE* fp, int) __THROW attribute_hidden;
> +extern FILE *_IO_mem_setbuf (FILE *fp, char *buf, ssize_t size)
> + __THROW attribute_hidden;
> extern int _IO_wmem_sync (FILE* fp) __THROW attribute_hidden;
> extern void _IO_wmem_finish (FILE* fp, int) __THROW attribute_hidden;
> +extern FILE *_IO_wmem_setbuf (FILE *fp, char *buf, ssize_t size)
> + __THROW attribute_hidden;
>
> /* Other strfile functions */
> struct _IO_strfile_;
> diff --git a/libio/memstream.c b/libio/memstream.c
> index 0456adb92f..69b400d928 100644
> --- a/libio/memstream.c
> +++ b/libio/memstream.c
> @@ -112,3 +112,11 @@ _IO_mem_finish (FILE *fp, int dummy)
>
> _IO_str_finish (fp, 0);
> }
> +
> +
> +FILE *
> +_IO_mem_setbuf (FILE *fp, char *p, ssize_t len)
> +{
> + /* Memstream manage a growable buffer internally. */
> + return fp;
> +}
> diff --git a/libio/tst-memstream-setbuf.c b/libio/tst-memstream-setbuf.c
> new file mode 100644
> index 0000000000..eee176ede5
> --- /dev/null
> +++ b/libio/tst-memstream-setbuf.c
> @@ -0,0 +1,41 @@
> +/* Test for open_memstream BZ #34019.
> + Copyright (C) 2026 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 <libio/tst-memstream.h>
> +
> +static int
> +do_test (void)
> +{
> + /* setbuf must not replace the internal growable buffer. */
> + CHAR_T *buf = NULL;
> + size_t len = 0;
> + FILE *fp = OPEN_MEMSTREAM (&buf, &len);
> + setbuf (fp, NULL);
> + TEST_COMPARE (FPUTC (W('A'), fp), W('A'));
> + TEST_COMPARE (fclose (fp), 0);
> + TEST_COMPARE (len, 1);
> + TEST_VERIFY_EXIT (buf != NULL);
> + TEST_VERIFY (buf[0] == W('A'));
> + TEST_VERIFY (buf[1] == W('\0'));
> +
> + free (buf);
> +
> + return 0;
> +}
> +
> +#include <support/test-driver.c>
> diff --git a/libio/tst-wmemstream-setbuf.c b/libio/tst-wmemstream-setbuf.c
> new file mode 100644
> index 0000000000..6ce751708a
> --- /dev/null
> +++ b/libio/tst-wmemstream-setbuf.c
> @@ -0,0 +1,20 @@
> +/* Test for open_wmemstream BZ #34019.
> + Copyright (C) 2026 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/>. */
> +
> +#define TEST_WCHAR
> +#include <libio/tst-memstream-setbuf.c>
> diff --git a/libio/vtables.c b/libio/vtables.c
> index 00d9d25b5e..ca9f1b2dc4 100644
> --- a/libio/vtables.c
> +++ b/libio/vtables.c
> @@ -77,9 +77,11 @@
> # pragma weak _IO_cookie_write
>
> # pragma weak _IO_mem_finish
> +# pragma weak _IO_mem_setbuf
> # pragma weak _IO_mem_sync
>
> # pragma weak _IO_wmem_finish
> +# pragma weak _IO_wmem_setbuf
> # pragma weak _IO_wmem_sync
>
> # pragma weak __printf_buffer_as_file_overflow
> @@ -334,7 +336,7 @@ const struct _IO_jump_t __io_vtables[] attribute_relro =
> JUMP_INIT (xsgetn, _IO_default_xsgetn),
> JUMP_INIT (seekoff, _IO_str_seekoff),
> JUMP_INIT (seekpos, _IO_default_seekpos),
> - JUMP_INIT (setbuf, _IO_default_setbuf),
> + JUMP_INIT (setbuf, _IO_mem_setbuf),
> JUMP_INIT (sync, _IO_mem_sync),
> JUMP_INIT (doallocate, _IO_default_doallocate),
> JUMP_INIT (read, _IO_default_read),
> @@ -357,7 +359,7 @@ const struct _IO_jump_t __io_vtables[] attribute_relro =
> JUMP_INIT (xsgetn, _IO_wdefault_xsgetn),
> JUMP_INIT (seekoff, _IO_wstr_seekoff),
> JUMP_INIT (seekpos, _IO_default_seekpos),
> - JUMP_INIT (setbuf, _IO_default_setbuf),
> + JUMP_INIT (setbuf, _IO_wmem_setbuf),
> JUMP_INIT (sync, _IO_wmem_sync),
> JUMP_INIT (doallocate, _IO_wdefault_doallocate),
> JUMP_INIT (read, _IO_default_read),
> diff --git a/libio/wmemstream.c b/libio/wmemstream.c
> index d0c639be70..cdee2a4151 100644
> --- a/libio/wmemstream.c
> +++ b/libio/wmemstream.c
> @@ -117,3 +117,11 @@ _IO_wmem_finish (FILE *fp, int dummy)
>
> _IO_wstr_finish (fp, 0);
> }
> +
> +
> +FILE *
> +_IO_wmem_setbuf (FILE *fp, char *p, ssize_t len)
> +{
> + /* Wmemstreams manage a growable buffer internally. */
> + return fp;
> +}
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v5 2/2] libio: Ignore doallocate for open_memstream and open_wmemstream [BZ #34019]
2026-05-08 6:04 ` [PATCH v5 2/2] libio: Ignore doallocate " Gao Xiang
@ 2026-05-20 18:49 ` Adhemerval Zanella Netto
0 siblings, 0 replies; 22+ messages in thread
From: Adhemerval Zanella Netto @ 2026-05-20 18:49 UTC (permalink / raw)
To: Gao Xiang, libc-alpha; +Cc: Andreas Schwab, Rocket Ma
On 08/05/26 03:04, Gao Xiang wrote:
> From: Xiang Gao <gaoxiang@kylinos.cn>
>
> setvbuf (stream, NULL, _IOFBF, 0) takes a special path in
> _IO_setvbuf: if the byte-oriented buffer base is NULL, it calls
> _IO_DOALLOCATE and returns without invoking the stream setbuf hook.
>
> For open_wmemstream, the byte-oriented buffer base is NULL although
> the wide result buffer has already been initialized in _wide_data.
> As a result, this path calls _IO_wdefault_doallocate, which may
> replace the wide buffer managed by open_wmemstream.
>
> Install an open_wmemstream-specific doallocate hook that leaves
> the growable result buffer unchanged. Add a regression test for this
> path.
>
> Install a narrow memstream doallocate hook as well. It keeps both
> memstream vtables consistent (generic stdio allocation must not
> replace the growable result buffer).
>
> Signed-off-by: Xiang Gao <gaoxiang@kylinos.cn>
LGTM, thanks.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
> ---
> libio/Makefile | 2 +
> libio/libioP.h | 2 +
> libio/memstream.c | 9 ++++
> libio/tst-memstream-setvbuf-doallocate.c | 61 +++++++++++++++++++++++
> libio/tst-wmemstream-setvbuf-doallocate.c | 20 ++++++++
> libio/vtables.c | 6 ++-
> libio/wmemstream.c | 9 ++++
> 7 files changed, 107 insertions(+), 2 deletions(-)
> create mode 100644 libio/tst-memstream-setvbuf-doallocate.c
> create mode 100644 libio/tst-wmemstream-setvbuf-doallocate.c
>
> diff --git a/libio/Makefile b/libio/Makefile
> index 6b56567d44..e165a8b4cb 100644
> --- a/libio/Makefile
> +++ b/libio/Makefile
> @@ -117,6 +117,7 @@ tests = \
> tst-fwrite-error \
> tst-getdelim \
> tst-memstream-setbuf \
> + tst-memstream-setvbuf-doallocate \
> tst-memstream1 \
> tst-memstream2 \
> tst-memstream3 \
> @@ -141,6 +142,7 @@ tests = \
> tst-wfiledoallocate-static \
> tst-widetext \
> tst-wmemstream-setbuf \
> + tst-wmemstream-setvbuf-doallocate \
> tst-wmemstream1 \
> tst-wmemstream2 \
> tst-wmemstream3 \
> diff --git a/libio/libioP.h b/libio/libioP.h
> index fa44ad0e57..f1a4a896a3 100644
> --- a/libio/libioP.h
> +++ b/libio/libioP.h
> @@ -742,10 +742,12 @@ extern int _IO_mem_sync (FILE* fp) __THROW attribute_hidden;
> extern void _IO_mem_finish (FILE* fp, int) __THROW attribute_hidden;
> extern FILE *_IO_mem_setbuf (FILE *fp, char *buf, ssize_t size)
> __THROW attribute_hidden;
> +extern int _IO_mem_doallocate (FILE *fp) __THROW attribute_hidden;
> extern int _IO_wmem_sync (FILE* fp) __THROW attribute_hidden;
> extern void _IO_wmem_finish (FILE* fp, int) __THROW attribute_hidden;
> extern FILE *_IO_wmem_setbuf (FILE *fp, char *buf, ssize_t size)
> __THROW attribute_hidden;
> +extern int _IO_wmem_doallocate (FILE *fp) __THROW attribute_hidden;
>
> /* Other strfile functions */
> struct _IO_strfile_;
> diff --git a/libio/memstream.c b/libio/memstream.c
> index 69b400d928..1af7224ab5 100644
> --- a/libio/memstream.c
> +++ b/libio/memstream.c
> @@ -120,3 +120,12 @@ _IO_mem_setbuf (FILE *fp, char *p, ssize_t len)
> /* Memstream manage a growable buffer internally. */
> return fp;
> }
> +
> +
> +int
> +_IO_mem_doallocate (FILE *fp)
> +{
> + /* Memstream manage a growable buffer internally. The doallocate
> + hook must not replace it with a generic stdio buffer. */
> + return 1;
> +}
> diff --git a/libio/tst-memstream-setvbuf-doallocate.c b/libio/tst-memstream-setvbuf-doallocate.c
> new file mode 100644
> index 0000000000..aa6f86f02d
> --- /dev/null
> +++ b/libio/tst-memstream-setvbuf-doallocate.c
> @@ -0,0 +1,61 @@
> +/* Test setvbuf on open_memstream, BZ #34019.
> + Copyright (C) 2026 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 <libio/tst-memstream.h>
> +
> +static int
> +do_test (void)
> +{
> + /* Regression test for setvbuf doallocate on open_memstream and
> + open_wmemstream. This test covers the _IO_setvbuf path for:
> +
> + setvbuf (stream, NULL, _IOFBF, 0)
> +
> + This path may call _IO_DOALLOCATE and return without invoking
> + the stream setbuf hook. The generic doallocate hook must not
> + replace the growable result buffer. */
> +
> + CHAR_T *buf = NULL;
> + size_t len = 0;
> + FILE *fp = OPEN_MEMSTREAM (&buf, &len);
> +
> + TEST_VERIFY_EXIT (fp != NULL);
> +
> + TEST_COMPARE (setvbuf (fp, NULL, _IOFBF, 0), 0);
> +
> + TEST_COMPARE (FPUTC (W('A'), fp), W('A'));
> + TEST_COMPARE (fflush (fp), 0);
> + TEST_COMPARE (len, 1);
> + TEST_VERIFY_EXIT (buf != NULL);
> + TEST_VERIFY (buf[0] == W('A'));
> + TEST_VERIFY (buf[1] == W('\0'));
> +
> + TEST_COMPARE (FPUTC (W('B'), fp), W('B'));
> + TEST_COMPARE (fclose (fp), 0);
> + TEST_COMPARE (len, 2);
> + TEST_VERIFY_EXIT (buf != NULL);
> + TEST_VERIFY (buf[0] == W('A'));
> + TEST_VERIFY (buf[1] == W('B'));
> + TEST_VERIFY (buf[2] == W('\0'));
> +
> + free (buf);
> +
> + return 0;
> +}
> +
> +#include <support/test-driver.c>
> diff --git a/libio/tst-wmemstream-setvbuf-doallocate.c b/libio/tst-wmemstream-setvbuf-doallocate.c
> new file mode 100644
> index 0000000000..857239aa85
> --- /dev/null
> +++ b/libio/tst-wmemstream-setvbuf-doallocate.c
> @@ -0,0 +1,20 @@
> +/* Test setvbuf on open_wmemstream, BZ #34019.
> + Copyright (C) 2026 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/>. */
> +
> +#define TEST_WCHAR
> +#include <libio/tst-memstream-setvbuf-doallocate.c>
> diff --git a/libio/vtables.c b/libio/vtables.c
> index ca9f1b2dc4..ba3f9566f8 100644
> --- a/libio/vtables.c
> +++ b/libio/vtables.c
> @@ -79,10 +79,12 @@
> # pragma weak _IO_mem_finish
> # pragma weak _IO_mem_setbuf
> # pragma weak _IO_mem_sync
> +# pragma weak _IO_mem_doallocate
>
> # pragma weak _IO_wmem_finish
> # pragma weak _IO_wmem_setbuf
> # pragma weak _IO_wmem_sync
> +# pragma weak _IO_wmem_doallocate
>
> # pragma weak __printf_buffer_as_file_overflow
> # pragma weak __printf_buffer_as_file_xsputn
> @@ -338,7 +340,7 @@ const struct _IO_jump_t __io_vtables[] attribute_relro =
> JUMP_INIT (seekpos, _IO_default_seekpos),
> JUMP_INIT (setbuf, _IO_mem_setbuf),
> JUMP_INIT (sync, _IO_mem_sync),
> - JUMP_INIT (doallocate, _IO_default_doallocate),
> + JUMP_INIT (doallocate, _IO_mem_doallocate),
> JUMP_INIT (read, _IO_default_read),
> JUMP_INIT (write, _IO_default_write),
> JUMP_INIT (seek, _IO_default_seek),
> @@ -361,7 +363,7 @@ const struct _IO_jump_t __io_vtables[] attribute_relro =
> JUMP_INIT (seekpos, _IO_default_seekpos),
> JUMP_INIT (setbuf, _IO_wmem_setbuf),
> JUMP_INIT (sync, _IO_wmem_sync),
> - JUMP_INIT (doallocate, _IO_wdefault_doallocate),
> + JUMP_INIT (doallocate, _IO_wmem_doallocate),
> JUMP_INIT (read, _IO_default_read),
> JUMP_INIT (write, _IO_default_write),
> JUMP_INIT (seek, _IO_default_seek),
> diff --git a/libio/wmemstream.c b/libio/wmemstream.c
> index cdee2a4151..dffbb4a808 100644
> --- a/libio/wmemstream.c
> +++ b/libio/wmemstream.c
> @@ -125,3 +125,12 @@ _IO_wmem_setbuf (FILE *fp, char *p, ssize_t len)
> /* Wmemstreams manage a growable buffer internally. */
> return fp;
> }
> +
> +
> +int
> +_IO_wmem_doallocate (FILE *fp)
> +{
> + /* Wmemstreams manage a growable buffer internally. The doallocate
> + hook must not replace it with a generic stdio buffer. */
> + return 1;
> +}
^ permalink raw reply [flat|nested] 22+ messages in thread
end of thread, other threads:[~2026-05-20 18:49 UTC | newest]
Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-04-28 7:56 [PATCH] libio: Ignore setbuf for open_memstream [BZ #34019] Gao Xiang
2026-04-28 8:14 ` [PATCH v2] " Gao Xiang
2026-04-30 19:13 ` Adhemerval Zanella Netto
2026-05-01 16:24 ` Rocket Ma
2026-05-04 12:04 ` Adhemerval Zanella Netto
2026-05-04 16:54 ` Alex Gao
2026-05-04 17:58 ` Adhemerval Zanella Netto
2026-05-06 9:34 ` [PATCH v3 1/2] libio: Ignore setbuf for open_memstream and open_wmemstream " Gao Xiang
2026-05-06 10:23 ` Gao Xiang
2026-05-06 10:23 ` [PATCH v3 2/2] libio: Ignore doallocate " Gao Xiang
2026-05-06 10:38 ` Andreas Schwab
2026-05-07 9:39 ` [PATCH v4 0/2] libio: Keep memstream result buffers independent of setvbuf Gao Xiang
2026-05-07 9:39 ` [PATCH v4 1/2] libio: Ignore setbuf for open_memstream and open_wmemstream [BZ #34019] Gao Xiang
2026-05-07 13:47 ` Adhemerval Zanella Netto
2026-05-07 9:40 ` [PATCH v4 2/2] libio: Ignore doallocate " Gao Xiang
2026-05-07 13:49 ` Adhemerval Zanella Netto
2026-05-08 6:04 ` [PATCH v5 0/2] libio: Keep memstream result buffers independent of setvbuf Gao Xiang
2026-05-08 6:04 ` [PATCH v5 1/2] libio: Ignore setbuf for open_memstream and open_wmemstream [BZ #34019] Gao Xiang
2026-05-20 18:47 ` Adhemerval Zanella Netto
2026-05-08 6:04 ` [PATCH v5 2/2] libio: Ignore doallocate " Gao Xiang
2026-05-20 18:49 ` Adhemerval Zanella Netto
2026-05-18 7:57 ` [PATCH v5 0/2] libio: Keep memstream result buffers independent of setvbuf Alex Gao
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).