* [PATCH v4 0/2] stdio-common: Fix heap overflow in scanf %mc pattern [BZ #34008]
@ 2026-04-05 18:18 Rocket Ma
2026-04-05 18:18 ` [PATCH v4 1/2] stdio-common: Add regression test " Rocket Ma
2026-04-05 18:18 ` [PATCH v4 2/2] stdio-common: Fix buffer overflow in scanf %mc " Rocket Ma
0 siblings, 2 replies; 18+ messages in thread
From: Rocket Ma @ 2026-04-05 18:18 UTC (permalink / raw)
To: libc-alpha; +Cc: Carlos O'Donell, Adhemerval Zanella Netto
This series includes a patch with requested fix for CVE-2026-5450,
unified optimized function to calculate %m pattern array expansion, as
well as a neat regression test with mcheck.
Rocket Ma (2):
stdio-common: Add regression test [BZ #34008]
stdio-common: Fix buffer overflow in scanf %mc [BZ #34008]
stdio-common/Makefile | 4 ++
stdio-common/tst-vfscanf-bz34008.c | 48 +++++++++++++++++++
stdio-common/vfscanf-internal.c | 74 +++++++++++++++++++-----------
3 files changed, 98 insertions(+), 28 deletions(-)
create mode 100644 stdio-common/tst-vfscanf-bz34008.c
--
2.53.0
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v4 1/2] stdio-common: Add regression test [BZ #34008]
2026-04-05 18:18 [PATCH v4 0/2] stdio-common: Fix heap overflow in scanf %mc pattern [BZ #34008] Rocket Ma
@ 2026-04-05 18:18 ` Rocket Ma
2026-04-05 18:18 ` [PATCH v4 2/2] stdio-common: Fix buffer overflow in scanf %mc " Rocket Ma
1 sibling, 0 replies; 18+ messages in thread
From: Rocket Ma @ 2026-04-05 18:18 UTC (permalink / raw)
To: libc-alpha
* stdio-common/tst-vfscanf-bz34008.c: This separate regression test is
added to test if BZ #34008 is fixed.
Signed-off-by: Rocket Ma <marocketbd@gmail.com>
---
stdio-common/Makefile | 4 +++
stdio-common/tst-vfscanf-bz34008.c | 48 ++++++++++++++++++++++++++++++
2 files changed, 52 insertions(+)
create mode 100644 stdio-common/tst-vfscanf-bz34008.c
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index 210944837e..0c0085e607 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -349,6 +349,7 @@ tests := \
tst-vfprintf-user-type \
tst-vfprintf-width-i18n \
tst-vfprintf-width-prec-alloc \
+ tst-vfscanf-bz34008 \
tst-wc-printf \
tstdiomisc \
tstgetln \
@@ -564,6 +565,9 @@ tst-printf-bz18872-ENV = MALLOC_TRACE=$(objpfx)tst-printf-bz18872.mtrace \
tst-vfprintf-width-prec-ENV = \
MALLOC_TRACE=$(objpfx)tst-vfprintf-width-prec.mtrace \
LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so
+tst-vfscanf-bz34008-ENV = \
+ MALLOC_CHECK_=3 \
+ LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so
tst-printf-bz25691-ENV = \
MALLOC_TRACE=$(objpfx)tst-printf-bz25691.mtrace \
LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so
diff --git a/stdio-common/tst-vfscanf-bz34008.c b/stdio-common/tst-vfscanf-bz34008.c
new file mode 100644
index 0000000000..af746821fb
--- /dev/null
+++ b/stdio-common/tst-vfscanf-bz34008.c
@@ -0,0 +1,48 @@
+/* Regression test for vfscanf %Nmc out-of-bound write (BZ #34008)
+ Copyright (C) 2012-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 "malloc/mcheck.h"
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <wchar.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <support/check.h>
+
+#define WIDTH 0x410
+#define SCANFSTR "%1040mc"
+static int
+do_test (void)
+{
+ mcheck_pedantic (NULL);
+ char *input = malloc (WIDTH + 1);
+ TEST_VERIFY (input != NULL);
+ memset (input, 'A', WIDTH);
+ input[WIDTH] = '\0';
+
+ char *buf = NULL;
+ TEST_VERIFY (sscanf (input, SCANFSTR, &buf) != -1);
+ TEST_VERIFY (buf != NULL);
+
+ free (buf);
+ free (input);
+ return 0;
+}
+
+#include <support/test-driver.c>
--
2.53.0
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v4 2/2] stdio-common: Fix buffer overflow in scanf %mc [BZ #34008]
2026-04-05 18:18 [PATCH v4 0/2] stdio-common: Fix heap overflow in scanf %mc pattern [BZ #34008] Rocket Ma
2026-04-05 18:18 ` [PATCH v4 1/2] stdio-common: Add regression test " Rocket Ma
@ 2026-04-05 18:18 ` Rocket Ma
2026-04-06 9:00 ` Florian Weimer
1 sibling, 1 reply; 18+ messages in thread
From: Rocket Ma @ 2026-04-05 18:18 UTC (permalink / raw)
To: libc-alpha
* stdio-common/vfscanf-internal.c: When enlarging allocated buffer with
format %mc or %mC, glibc allocates one byte less, leading to
user-controlled one byte overflow. This commit fixes BZ #34008, or
CVE-2026-5450. Unify newsize calculation of allocated buffer.
Signed-off-by: Rocket Ma <marocketbd@gmail.com>
---
stdio-common/vfscanf-internal.c | 74 ++++++++++++++++++++-------------
1 file changed, 46 insertions(+), 28 deletions(-)
diff --git a/stdio-common/vfscanf-internal.c b/stdio-common/vfscanf-internal.c
index 59fc8208aa..6bf2a55876 100644
--- a/stdio-common/vfscanf-internal.c
+++ b/stdio-common/vfscanf-internal.c
@@ -265,6 +265,19 @@ char_buffer_add (struct char_buffer *buffer, CHAR_T ch)
*buffer->current++ = ch;
}
+/* Calculate the result size of expanded char array in %ms, %mS,
+ %m[, %lm[, %mc or %mC. */
+static __always_inline size_t
+grow_to_fit (size_t oldsize, int need, int extra)
+{
+ /* extra = 0 if %m[cC], %m[cC] always have positive width */
+ if ((extra && need < 0) || oldsize < need)
+ return oldsize * 2;
+ /* oldsize >= need:
+ grow requested capacity and `extra' byte for `\0' */
+ return oldsize + need + extra;
+}
+
/* Read formatted input from S according to the format string
FORMAT, using the argument list in ARG.
Return the number of assignments made, or -1 for an input error. */
@@ -804,7 +817,8 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
&& *strptr + strsize - str <= MB_LEN_MAX)
{
/* We have to enlarge the buffer if the `m' flag
- was given. */
+ was given. And we may not expand str by width
+ as the wcrtomb may return various bytes */
size_t strleng = str - *strptr;
char *newstr;
@@ -854,9 +868,7 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
&& (char *) str == *strptr + strsize)
{
/* Enlarge the buffer. */
- size_t newsize
- = strsize
- + (strsize >= width ? width - 1 : strsize);
+ size_t newsize = grow_to_fit (strsize, width, 0);
str = (char *) realloc (*strptr, newsize);
if (str == NULL)
@@ -928,8 +940,7 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
if ((flags & MALLOC)
&& wstr == (wchar_t *) *strptr + strsize)
{
- size_t newsize
- = strsize + (strsize > width ? width - 1 : strsize);
+ size_t newsize = grow_to_fit (strsize, width, 0);
/* Enlarge the buffer. */
wstr = (wchar_t *) realloc (*strptr,
newsize * sizeof (wchar_t));
@@ -983,8 +994,7 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
if (!(flags & SUPPRESS) && (flags & MALLOC)
&& wstr == (wchar_t *) *strptr + strsize)
{
- size_t newsize
- = strsize + (strsize > width ? width - 1 : strsize);
+ size_t newsize = grow_to_fit (strsize, width, 0);
/* Enlarge the buffer. */
wstr = (wchar_t *) realloc (*strptr,
newsize * sizeof (wchar_t));
@@ -1099,7 +1109,8 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
&& *strptr + strsize - str <= MB_LEN_MAX)
{
/* We have to enlarge the buffer if the `a' or `m'
- flag was given. */
+ flag was given. And we may not expand str by
+ width as the wcrtomb may return various bytes */
size_t strleng = str - *strptr;
char *newstr;
@@ -1157,7 +1168,8 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
&& (char *) str == *strptr + strsize)
{
/* Enlarge the buffer. */
- str = (char *) realloc (*strptr, 2 * strsize);
+ size_t newsize = grow_to_fit (strsize, width, 1);
+ str = (char *) realloc (*strptr, newsize);
if (str == NULL)
{
/* Can't allocate that much. Last-ditch
@@ -1189,7 +1201,7 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
{
*strptr = (char *) str;
str += strsize;
- strsize *= 2;
+ strsize = newsize;
}
}
}
@@ -1287,9 +1299,10 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
&& wstr == (wchar_t *) *strptr + strsize)
{
/* Enlarge the buffer. */
- wstr = (wchar_t *) realloc (*strptr,
- (2 * strsize)
- * sizeof (wchar_t));
+ size_t newsize = grow_to_fit (strsize, width, 1);
+
+ wstr = (wchar_t *) realloc (
+ *strptr, newsize * sizeof (wchar_t));
if (wstr == NULL)
{
/* Can't allocate that much. Last-ditch
@@ -1323,7 +1336,7 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
{
*strptr = (char *) wstr;
wstr += strsize;
- strsize *= 2;
+ strsize = newsize;
}
}
}
@@ -1363,9 +1376,10 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
&& wstr == (wchar_t *) *strptr + strsize)
{
/* Enlarge the buffer. */
+ size_t newsize = grow_to_fit (strsize, width, 1);
+
wstr = (wchar_t *) realloc (*strptr,
- (2 * strsize
- * sizeof (wchar_t)));
+ newsize * sizeof (wchar_t));
if (wstr == NULL)
{
/* Can't allocate that much. Last-ditch effort. */
@@ -1398,7 +1412,7 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
{
*strptr = (char *) wstr;
wstr += strsize;
- strsize *= 2;
+ strsize = newsize;
}
}
}
@@ -2755,9 +2769,10 @@ digits_extended_fail:
&& wstr == (wchar_t *) *strptr + strsize)
{
/* Enlarge the buffer. */
- wstr = (wchar_t *) realloc (*strptr,
- (2 * strsize)
- * sizeof (wchar_t));
+ size_t newsize = grow_to_fit (strsize, width, 1);
+
+ wstr = (wchar_t *) realloc (
+ *strptr, newsize * sizeof (wchar_t));
if (wstr == NULL)
{
/* Can't allocate that much. Last-ditch
@@ -2791,7 +2806,7 @@ digits_extended_fail:
{
*strptr = (char *) wstr;
wstr += strsize;
- strsize *= 2;
+ strsize = newsize;
}
}
}
@@ -2840,9 +2855,10 @@ digits_extended_fail:
&& wstr == (wchar_t *) *strptr + strsize)
{
/* Enlarge the buffer. */
- wstr = (wchar_t *) realloc (*strptr,
- (2 * strsize
- * sizeof (wchar_t)));
+ size_t newsize = grow_to_fit (strsize, width, 1);
+
+ wstr = (wchar_t *) realloc (
+ *strptr, newsize * sizeof (wchar_t));
if (wstr == NULL)
{
/* Can't allocate that much. Last-ditch
@@ -2876,7 +2892,7 @@ digits_extended_fail:
{
*strptr = (char *) wstr;
wstr += strsize;
- strsize *= 2;
+ strsize = newsize;
}
}
}
@@ -2984,7 +3000,9 @@ digits_extended_fail:
if ((flags & MALLOC)
&& *strptr + strsize - str <= MB_LEN_MAX)
{
- /* Enlarge the buffer. */
+ /* Enlarge the buffer. And we may not
+ expand str by width as the wcrtomb may
+ return various bytes */
size_t strleng = str - *strptr;
char *newstr;
@@ -3052,7 +3070,7 @@ digits_extended_fail:
&& (char *) str == *strptr + strsize)
{
/* Enlarge the buffer. */
- size_t newsize = 2 * strsize;
+ size_t newsize = grow_to_fit (strsize, width, 1);
allocagain:
str = (char *) realloc (*strptr, newsize);
--
2.53.0
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v4 2/2] stdio-common: Fix buffer overflow in scanf %mc [BZ #34008]
2026-04-05 18:18 ` [PATCH v4 2/2] stdio-common: Fix buffer overflow in scanf %mc " Rocket Ma
@ 2026-04-06 9:00 ` Florian Weimer
2026-04-06 16:17 ` Rocket Ma
0 siblings, 1 reply; 18+ messages in thread
From: Florian Weimer @ 2026-04-06 9:00 UTC (permalink / raw)
To: Rocket Ma; +Cc: libc-alpha
* Rocket Ma:
> * stdio-common/vfscanf-internal.c: When enlarging allocated buffer with
> format %mc or %mC, glibc allocates one byte less, leading to
> user-controlled one byte overflow. This commit fixes BZ #34008, or
> CVE-2026-5450. Unify newsize calculation of allocated buffer.
>
> Signed-off-by: Rocket Ma <marocketbd@gmail.com>
> ---
> stdio-common/vfscanf-internal.c | 74 ++++++++++++++++++++-------------
> 1 file changed, 46 insertions(+), 28 deletions(-)
>
> diff --git a/stdio-common/vfscanf-internal.c b/stdio-common/vfscanf-internal.c
> index 59fc8208aa..6bf2a55876 100644
> --- a/stdio-common/vfscanf-internal.c
> +++ b/stdio-common/vfscanf-internal.c
> @@ -265,6 +265,19 @@ char_buffer_add (struct char_buffer *buffer, CHAR_T ch)
> *buffer->current++ = ch;
> }
>
> +/* Calculate the result size of expanded char array in %ms, %mS,
> + %m[, %lm[, %mc or %mC. */
> +static __always_inline size_t
> +grow_to_fit (size_t oldsize, int need, int extra)
> +{
> + /* extra = 0 if %m[cC], %m[cC] always have positive width */
> + if ((extra && need < 0) || oldsize < need)
> + return oldsize * 2;
> + /* oldsize >= need:
> + grow requested capacity and `extra' byte for `\0' */
> + return oldsize + need + extra;
> +}
Thanks for working on this.
The last (extra) argument is constant. I'd suggest two functions with
descriptive names instead (maybe grow_to_fit_for_fixed for the %c
family, and grow_to_fit_with_null for the %s/%[] family that perform
null termination). The new functions probably shouldn't be inline, so
that the compiler can apply its heuristics.
As written, the interaction with the extra argument and a negative
need argument is not quite obvious from the function alone (code and
comments). The function seems correct because need (called width in
the caller) can only be -1 for the %s/%[] case, where extra is 1.
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v4 2/2] stdio-common: Fix buffer overflow in scanf %mc [BZ #34008]
2026-04-06 9:00 ` Florian Weimer
@ 2026-04-06 16:17 ` Rocket Ma
2026-04-08 16:13 ` Rocket Ma
0 siblings, 1 reply; 18+ messages in thread
From: Rocket Ma @ 2026-04-06 16:17 UTC (permalink / raw)
To: Florian Weimer; +Cc: libc-alpha
Florian Weimer <fw@deneb.enyo.de> 于2026年4月6日周一 17:00写道:
> The last (extra) argument is constant. I'd suggest two functions with
> descriptive names instead (maybe grow_to_fit_for_fixed for the %c
> family, and grow_to_fit_with_null for the %s/%[] family that perform
> null termination). The new functions probably shouldn't be inline, so
> that the compiler can apply its heuristics.
If the function need to be separated, then the old behavior, "size_t
newsize = strsize + (strsize >= width ? width : strsize)", is not
worth a new function. And the function only expands to several
instructions, less than 10, observed via Compiler Explorer. I think
it's OK to inline the function.
> As written, the interaction with the extra argument and a negative
> need argument is not quite obvious from the function alone (code and
> comments). The function seems correct because need (called width in
> the caller) can only be -1 for the %s/%[] case, where extra is 1.
Since the behavior mentioned has become some sort of convention, the
function should be OK? Readers has the constant to distinguish between
%ms and %mc, and they can understand the code via enough comments.
Personally I think it's worth to put these two actions together to do
one thing: calculate the size of expanded array.
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v4 2/2] stdio-common: Fix buffer overflow in scanf %mc [BZ #34008]
2026-04-06 16:17 ` Rocket Ma
@ 2026-04-08 16:13 ` Rocket Ma
2026-04-13 13:46 ` Carlos O'Donell
0 siblings, 1 reply; 18+ messages in thread
From: Rocket Ma @ 2026-04-08 16:13 UTC (permalink / raw)
To: Florian Weimer; +Cc: libc-alpha
> If the function need to be separated, then the old behavior, "size_t
> newsize = strsize + (strsize >= width ? width : strsize)", is not
> worth a new function. And the function only expands to several
> instructions, less than 10, observed via Compiler Explorer. I think
> it's OK to inline the function.
>
> Since the behavior mentioned has become some sort of convention, the
> function should be OK? Readers has the constant to distinguish between
> %ms and %mc, and they can understand the code via enough comments.
> Personally I think it's worth to put these two actions together to do
> one thing: calculate the size of expanded array.
Any idea?
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v4 2/2] stdio-common: Fix buffer overflow in scanf %mc [BZ #34008]
2026-04-08 16:13 ` Rocket Ma
@ 2026-04-13 13:46 ` Carlos O'Donell
2026-04-14 2:17 ` [PATCH v5 0/3] " Rocket Ma
0 siblings, 1 reply; 18+ messages in thread
From: Carlos O'Donell @ 2026-04-13 13:46 UTC (permalink / raw)
To: Rocket Ma, Florian Weimer; +Cc: libc-alpha
On 4/8/26 12:13 PM, Rocket Ma wrote:
>> If the function need to be separated, then the old behavior, "size_t
>> newsize = strsize + (strsize >= width ? width : strsize)", is not
>> worth a new function. And the function only expands to several
>> instructions, less than 10, observed via Compiler Explorer. I think
>> it's OK to inline the function.
>>
>> Since the behavior mentioned has become some sort of convention, the
>> function should be OK? Readers has the constant to distinguish between
>> %ms and %mc, and they can understand the code via enough comments.
>> Personally I think it's worth to put these two actions together to do
>> one thing: calculate the size of expanded array.
>
> Any idea?
>
The best way forward is to simplify and just fix the %mc case for bug
34008 and the CVE.
Then we can have a follow-on changes that refactors?
--
Cheers,
Carlos.
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v5 0/3] Re: [PATCH v4 2/2] stdio-common: Fix buffer overflow in scanf %mc [BZ #34008]
2026-04-13 13:46 ` Carlos O'Donell
@ 2026-04-14 2:17 ` Rocket Ma
2026-04-14 2:17 ` [PATCH v5 1/3] stdio-common: Add regression test " Rocket Ma
` (3 more replies)
0 siblings, 4 replies; 18+ messages in thread
From: Rocket Ma @ 2026-04-14 2:17 UTC (permalink / raw)
To: Carlos O'Donell; +Cc: libc-alpha, Florian Weimer
One commit for regression, one commit to directly fix the overflow, and
one commit to refactor array expansion.
Rocket Ma (3):
stdio-common: Add regression test [BZ #34008]
stdio-common: Fix buffer overflow in scanf %mc [BZ #34008]
stdio-common: Optimize %ms expansion for best fit
stdio-common/Makefile | 4 ++
stdio-common/tst-vfscanf-bz34008.c | 48 ++++++++++++++++++++
stdio-common/vfscanf-internal.c | 70 +++++++++++++++++++-----------
3 files changed, 97 insertions(+), 25 deletions(-)
create mode 100644 stdio-common/tst-vfscanf-bz34008.c
--
2.53.0
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v5 1/3] stdio-common: Add regression test [BZ #34008]
2026-04-14 2:17 ` [PATCH v5 0/3] " Rocket Ma
@ 2026-04-14 2:17 ` Rocket Ma
2026-04-17 21:59 ` Carlos O'Donell
2026-04-14 2:17 ` [PATCH v5 2/3] stdio-common: Fix buffer overflow in scanf %mc " Rocket Ma
` (2 subsequent siblings)
3 siblings, 1 reply; 18+ messages in thread
From: Rocket Ma @ 2026-04-14 2:17 UTC (permalink / raw)
To: Carlos O'Donell; +Cc: libc-alpha, Florian Weimer
* stdio-common/tst-vfscanf-bz34008.c: This separate regression test is
added to test if BZ #34008 is fixed.
Signed-off-by: Rocket Ma <marocketbd@gmail.com>
---
stdio-common/Makefile | 4 +++
stdio-common/tst-vfscanf-bz34008.c | 48 ++++++++++++++++++++++++++++++
2 files changed, 52 insertions(+)
create mode 100644 stdio-common/tst-vfscanf-bz34008.c
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index 210944837e..0c0085e607 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -349,6 +349,7 @@ tests := \
tst-vfprintf-user-type \
tst-vfprintf-width-i18n \
tst-vfprintf-width-prec-alloc \
+ tst-vfscanf-bz34008 \
tst-wc-printf \
tstdiomisc \
tstgetln \
@@ -564,6 +565,9 @@ tst-printf-bz18872-ENV = MALLOC_TRACE=$(objpfx)tst-printf-bz18872.mtrace \
tst-vfprintf-width-prec-ENV = \
MALLOC_TRACE=$(objpfx)tst-vfprintf-width-prec.mtrace \
LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so
+tst-vfscanf-bz34008-ENV = \
+ MALLOC_CHECK_=3 \
+ LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so
tst-printf-bz25691-ENV = \
MALLOC_TRACE=$(objpfx)tst-printf-bz25691.mtrace \
LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so
diff --git a/stdio-common/tst-vfscanf-bz34008.c b/stdio-common/tst-vfscanf-bz34008.c
new file mode 100644
index 0000000000..af746821fb
--- /dev/null
+++ b/stdio-common/tst-vfscanf-bz34008.c
@@ -0,0 +1,48 @@
+/* Regression test for vfscanf %Nmc out-of-bound write (BZ #34008)
+ Copyright (C) 2012-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 "malloc/mcheck.h"
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <wchar.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <support/check.h>
+
+#define WIDTH 0x410
+#define SCANFSTR "%1040mc"
+static int
+do_test (void)
+{
+ mcheck_pedantic (NULL);
+ char *input = malloc (WIDTH + 1);
+ TEST_VERIFY (input != NULL);
+ memset (input, 'A', WIDTH);
+ input[WIDTH] = '\0';
+
+ char *buf = NULL;
+ TEST_VERIFY (sscanf (input, SCANFSTR, &buf) != -1);
+ TEST_VERIFY (buf != NULL);
+
+ free (buf);
+ free (input);
+ return 0;
+}
+
+#include <support/test-driver.c>
--
2.53.0
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v5 2/3] stdio-common: Fix buffer overflow in scanf %mc [BZ #34008]
2026-04-14 2:17 ` [PATCH v5 0/3] " Rocket Ma
2026-04-14 2:17 ` [PATCH v5 1/3] stdio-common: Add regression test " Rocket Ma
@ 2026-04-14 2:17 ` Rocket Ma
2026-04-17 21:55 ` Carlos O'Donell
2026-04-14 2:17 ` [PATCH v5 3/3] stdio-common: Optimize %ms expansion for best fit Rocket Ma
2026-04-17 21:54 ` [PATCH v5 0/3] Re: [PATCH v4 2/2] stdio-common: Fix buffer overflow in scanf %mc [BZ #34008] Carlos O'Donell
3 siblings, 1 reply; 18+ messages in thread
From: Rocket Ma @ 2026-04-14 2:17 UTC (permalink / raw)
To: Carlos O'Donell; +Cc: libc-alpha, Florian Weimer
* stdio-common/vfscanf-internal.c: When enlarging allocated buffer with
format %mc or %mC, glibc allocates one byte less, leading to
user-controlled one byte overflow. This commit fixes BZ #34008, or
CVE-2026-5450.
Signed-off-by: Rocket Ma <marocketbd@gmail.com>
---
stdio-common/vfscanf-internal.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)
diff --git a/stdio-common/vfscanf-internal.c b/stdio-common/vfscanf-internal.c
index 59fc8208aa..3d11ac261e 100644
--- a/stdio-common/vfscanf-internal.c
+++ b/stdio-common/vfscanf-internal.c
@@ -855,8 +855,7 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
{
/* Enlarge the buffer. */
size_t newsize
- = strsize
- + (strsize >= width ? width - 1 : strsize);
+ = strsize + (strsize >= width ? width : strsize);
str = (char *) realloc (*strptr, newsize);
if (str == NULL)
@@ -929,7 +928,7 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
&& wstr == (wchar_t *) *strptr + strsize)
{
size_t newsize
- = strsize + (strsize > width ? width - 1 : strsize);
+ = strsize + (strsize >= width ? width : strsize);
/* Enlarge the buffer. */
wstr = (wchar_t *) realloc (*strptr,
newsize * sizeof (wchar_t));
@@ -984,7 +983,7 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
&& wstr == (wchar_t *) *strptr + strsize)
{
size_t newsize
- = strsize + (strsize > width ? width - 1 : strsize);
+ = strsize + (strsize >= width ? width : strsize);
/* Enlarge the buffer. */
wstr = (wchar_t *) realloc (*strptr,
newsize * sizeof (wchar_t));
--
2.53.0
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v5 3/3] stdio-common: Optimize %ms expansion for best fit
2026-04-14 2:17 ` [PATCH v5 0/3] " Rocket Ma
2026-04-14 2:17 ` [PATCH v5 1/3] stdio-common: Add regression test " Rocket Ma
2026-04-14 2:17 ` [PATCH v5 2/3] stdio-common: Fix buffer overflow in scanf %mc " Rocket Ma
@ 2026-04-14 2:17 ` Rocket Ma
2026-04-17 21:54 ` [PATCH v5 0/3] Re: [PATCH v4 2/2] stdio-common: Fix buffer overflow in scanf %mc [BZ #34008] Carlos O'Donell
3 siblings, 0 replies; 18+ messages in thread
From: Rocket Ma @ 2026-04-14 2:17 UTC (permalink / raw)
To: Carlos O'Donell; +Cc: libc-alpha, Florian Weimer
* stdio-common/vfscanf-internal.c: Add grow_to_fit to calculate the size
of expanded string during %ms/%m[ scan for best fit.
Signed-off-by: Rocket Ma <marocketbd@gmail.com>
---
stdio-common/vfscanf-internal.c | 63 ++++++++++++++++++++++-----------
1 file changed, 42 insertions(+), 21 deletions(-)
diff --git a/stdio-common/vfscanf-internal.c b/stdio-common/vfscanf-internal.c
index 3d11ac261e..8eec294a98 100644
--- a/stdio-common/vfscanf-internal.c
+++ b/stdio-common/vfscanf-internal.c
@@ -265,6 +265,17 @@ char_buffer_add (struct char_buffer *buffer, CHAR_T ch)
*buffer->current++ = ch;
}
+/* Calculate the result size of expanded char array in %ms, %mS,
+ %m[ or %lm[. */
+static __always_inline size_t
+grow_to_fit (size_t oldsize, int need)
+{
+ if (need < 0 || oldsize < need)
+ return oldsize * 2;
+ /* oldsize >= need: grow requested capacity and 1 byte for `\0' */
+ return oldsize + need + 1;
+}
+
/* Read formatted input from S according to the format string
FORMAT, using the argument list in ARG.
Return the number of assignments made, or -1 for an input error. */
@@ -804,7 +815,8 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
&& *strptr + strsize - str <= MB_LEN_MAX)
{
/* We have to enlarge the buffer if the `m' flag
- was given. */
+ was given. And we may not expand str by width
+ as the wcrtomb may return various bytes */
size_t strleng = str - *strptr;
char *newstr;
@@ -1098,7 +1110,8 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
&& *strptr + strsize - str <= MB_LEN_MAX)
{
/* We have to enlarge the buffer if the `a' or `m'
- flag was given. */
+ flag was given. And we may not expand str by
+ width as the wcrtomb may return various bytes */
size_t strleng = str - *strptr;
char *newstr;
@@ -1156,7 +1169,9 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
&& (char *) str == *strptr + strsize)
{
/* Enlarge the buffer. */
- str = (char *) realloc (*strptr, 2 * strsize);
+ size_t newsize = grow_to_fit (strsize, width);
+
+ str = (char *) realloc (*strptr, newsize);
if (str == NULL)
{
/* Can't allocate that much. Last-ditch
@@ -1188,7 +1203,7 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
{
*strptr = (char *) str;
str += strsize;
- strsize *= 2;
+ strsize = newsize;
}
}
}
@@ -1286,9 +1301,10 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
&& wstr == (wchar_t *) *strptr + strsize)
{
/* Enlarge the buffer. */
- wstr = (wchar_t *) realloc (*strptr,
- (2 * strsize)
- * sizeof (wchar_t));
+ size_t newsize = grow_to_fit (strsize, width);
+
+ wstr = (wchar_t *) realloc (
+ *strptr, newsize * sizeof (wchar_t));
if (wstr == NULL)
{
/* Can't allocate that much. Last-ditch
@@ -1322,7 +1338,7 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
{
*strptr = (char *) wstr;
wstr += strsize;
- strsize *= 2;
+ strsize = newsize;
}
}
}
@@ -1362,9 +1378,10 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
&& wstr == (wchar_t *) *strptr + strsize)
{
/* Enlarge the buffer. */
+ size_t newsize = grow_to_fit (strsize, width);
+
wstr = (wchar_t *) realloc (*strptr,
- (2 * strsize
- * sizeof (wchar_t)));
+ newsize * sizeof (wchar_t));
if (wstr == NULL)
{
/* Can't allocate that much. Last-ditch effort. */
@@ -1397,7 +1414,7 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
{
*strptr = (char *) wstr;
wstr += strsize;
- strsize *= 2;
+ strsize = newsize;
}
}
}
@@ -2754,9 +2771,10 @@ digits_extended_fail:
&& wstr == (wchar_t *) *strptr + strsize)
{
/* Enlarge the buffer. */
- wstr = (wchar_t *) realloc (*strptr,
- (2 * strsize)
- * sizeof (wchar_t));
+ size_t newsize = grow_to_fit (strsize, width);
+
+ wstr = (wchar_t *) realloc (
+ *strptr, newsize * sizeof (wchar_t));
if (wstr == NULL)
{
/* Can't allocate that much. Last-ditch
@@ -2790,7 +2808,7 @@ digits_extended_fail:
{
*strptr = (char *) wstr;
wstr += strsize;
- strsize *= 2;
+ strsize = newsize;
}
}
}
@@ -2839,9 +2857,10 @@ digits_extended_fail:
&& wstr == (wchar_t *) *strptr + strsize)
{
/* Enlarge the buffer. */
- wstr = (wchar_t *) realloc (*strptr,
- (2 * strsize
- * sizeof (wchar_t)));
+ size_t newsize = grow_to_fit (strsize, width);
+
+ wstr = (wchar_t *) realloc (
+ *strptr, newsize * sizeof (wchar_t));
if (wstr == NULL)
{
/* Can't allocate that much. Last-ditch
@@ -2875,7 +2894,7 @@ digits_extended_fail:
{
*strptr = (char *) wstr;
wstr += strsize;
- strsize *= 2;
+ strsize = newsize;
}
}
}
@@ -2983,7 +3002,9 @@ digits_extended_fail:
if ((flags & MALLOC)
&& *strptr + strsize - str <= MB_LEN_MAX)
{
- /* Enlarge the buffer. */
+ /* Enlarge the buffer. And we may not
+ expand str by width as the wcrtomb may
+ return various bytes */
size_t strleng = str - *strptr;
char *newstr;
@@ -3051,7 +3072,7 @@ digits_extended_fail:
&& (char *) str == *strptr + strsize)
{
/* Enlarge the buffer. */
- size_t newsize = 2 * strsize;
+ size_t newsize = grow_to_fit (strsize, width);
allocagain:
str = (char *) realloc (*strptr, newsize);
--
2.53.0
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v5 0/3] Re: [PATCH v4 2/2] stdio-common: Fix buffer overflow in scanf %mc [BZ #34008]
2026-04-14 2:17 ` [PATCH v5 0/3] " Rocket Ma
` (2 preceding siblings ...)
2026-04-14 2:17 ` [PATCH v5 3/3] stdio-common: Optimize %ms expansion for best fit Rocket Ma
@ 2026-04-17 21:54 ` Carlos O'Donell
2026-04-18 6:48 ` [PATCH v6] " Rocket Ma
3 siblings, 1 reply; 18+ messages in thread
From: Carlos O'Donell @ 2026-04-17 21:54 UTC (permalink / raw)
To: Rocket Ma; +Cc: libc-alpha, Florian Weimer
On 4/13/26 10:17 PM, Rocket Ma wrote:
> One commit for regression, one commit to directly fix the overflow, and
> one commit to refactor array expansion.
>
> Rocket Ma (3):
> stdio-common: Add regression test [BZ #34008]
> stdio-common: Fix buffer overflow in scanf %mc [BZ #34008]
May you please fold these two into one commit and post that as v6 resolving #34008?
If you split the regression test into one commit it breaks bisecting.
With the test first someone who is bisecting will see the failure immediately show up.
With the test and fix together every commit in the tree keeps the testsuite clean.
> stdio-common: Optimize %ms expansion for best fit
This we can handle as a distinct followup patch.
Please post this again after we've merged the fix for 34008?
Thank you!
> stdio-common/Makefile | 4 ++
> stdio-common/tst-vfscanf-bz34008.c | 48 ++++++++++++++++++++
> stdio-common/vfscanf-internal.c | 70 +++++++++++++++++++-----------
> 3 files changed, 97 insertions(+), 25 deletions(-)
> create mode 100644 stdio-common/tst-vfscanf-bz34008.c
>
--
Cheers,
Carlos.
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v5 2/3] stdio-common: Fix buffer overflow in scanf %mc [BZ #34008]
2026-04-14 2:17 ` [PATCH v5 2/3] stdio-common: Fix buffer overflow in scanf %mc " Rocket Ma
@ 2026-04-17 21:55 ` Carlos O'Donell
2026-04-28 14:24 ` Rocket Ma
2026-05-07 16:37 ` Rocket Ma
0 siblings, 2 replies; 18+ messages in thread
From: Carlos O'Donell @ 2026-04-17 21:55 UTC (permalink / raw)
To: Rocket Ma; +Cc: libc-alpha, Florian Weimer
On 4/13/26 10:17 PM, Rocket Ma wrote:
> * stdio-common/vfscanf-internal.c: When enlarging allocated buffer with
> format %mc or %mC, glibc allocates one byte less, leading to
> user-controlled one byte overflow. This commit fixes BZ #34008, or
> CVE-2026-5450.
>
> Signed-off-by: Rocket Ma <marocketbd@gmail.com>
This looks correct to me.
You can keep my Reviewed-by in your v6 post if you don't change the fix.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
> ---
> stdio-common/vfscanf-internal.c | 7 +++----
> 1 file changed, 3 insertions(+), 4 deletions(-)
>
> diff --git a/stdio-common/vfscanf-internal.c b/stdio-common/vfscanf-internal.c
> index 59fc8208aa..3d11ac261e 100644
> --- a/stdio-common/vfscanf-internal.c
> +++ b/stdio-common/vfscanf-internal.c
> @@ -855,8 +855,7 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
> {
> /* Enlarge the buffer. */
> size_t newsize
> - = strsize
> - + (strsize >= width ? width - 1 : strsize);
> + = strsize + (strsize >= width ? width : strsize);
>
> str = (char *) realloc (*strptr, newsize);
> if (str == NULL)
> @@ -929,7 +928,7 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
> && wstr == (wchar_t *) *strptr + strsize)
> {
> size_t newsize
> - = strsize + (strsize > width ? width - 1 : strsize);
> + = strsize + (strsize >= width ? width : strsize);
> /* Enlarge the buffer. */
> wstr = (wchar_t *) realloc (*strptr,
> newsize * sizeof (wchar_t));
> @@ -984,7 +983,7 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
> && wstr == (wchar_t *) *strptr + strsize)
> {
> size_t newsize
> - = strsize + (strsize > width ? width - 1 : strsize);
> + = strsize + (strsize >= width ? width : strsize);
> /* Enlarge the buffer. */
> wstr = (wchar_t *) realloc (*strptr,
> newsize * sizeof (wchar_t));
--
Cheers,
Carlos.
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v5 1/3] stdio-common: Add regression test [BZ #34008]
2026-04-14 2:17 ` [PATCH v5 1/3] stdio-common: Add regression test " Rocket Ma
@ 2026-04-17 21:59 ` Carlos O'Donell
0 siblings, 0 replies; 18+ messages in thread
From: Carlos O'Donell @ 2026-04-17 21:59 UTC (permalink / raw)
To: Rocket Ma; +Cc: libc-alpha, Florian Weimer
On 4/13/26 10:17 PM, Rocket Ma wrote:
> * stdio-common/tst-vfscanf-bz34008.c: This separate regression test is
> added to test if BZ #34008 is fixed.
>
> Signed-off-by: Rocket Ma <marocketbd@gmail.com>
Thank you.
Confirmed it fails before the fix and passes after.
> ---
> stdio-common/Makefile | 4 +++
> stdio-common/tst-vfscanf-bz34008.c | 48 ++++++++++++++++++++++++++++++
> 2 files changed, 52 insertions(+)
> create mode 100644 stdio-common/tst-vfscanf-bz34008.c
>
> diff --git a/stdio-common/Makefile b/stdio-common/Makefile
> index 210944837e..0c0085e607 100644
> --- a/stdio-common/Makefile
> +++ b/stdio-common/Makefile
> @@ -349,6 +349,7 @@ tests := \
> tst-vfprintf-user-type \
> tst-vfprintf-width-i18n \
> tst-vfprintf-width-prec-alloc \
> + tst-vfscanf-bz34008 \
OK.
> tst-wc-printf \
> tstdiomisc \
> tstgetln \
> @@ -564,6 +565,9 @@ tst-printf-bz18872-ENV = MALLOC_TRACE=$(objpfx)tst-printf-bz18872.mtrace \
> tst-vfprintf-width-prec-ENV = \
> MALLOC_TRACE=$(objpfx)tst-vfprintf-width-prec.mtrace \
> LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so
> +tst-vfscanf-bz34008-ENV = \
> + MALLOC_CHECK_=3 \
> + LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so
OK.
> tst-printf-bz25691-ENV = \
> MALLOC_TRACE=$(objpfx)tst-printf-bz25691.mtrace \
> LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so
> diff --git a/stdio-common/tst-vfscanf-bz34008.c b/stdio-common/tst-vfscanf-bz34008.c
> new file mode 100644
> index 0000000000..af746821fb
> --- /dev/null
> +++ b/stdio-common/tst-vfscanf-bz34008.c
> @@ -0,0 +1,48 @@
> +/* Regression test for vfscanf %Nmc out-of-bound write (BZ #34008)
OK. One line description.
> + Copyright (C) 2012-2026 Free Software Foundation, Inc.
This should say:
"Copyright The GNU Toolchain Authors"
Since you are contributing under DCO and wrote the test from scratch.
> + 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 "malloc/mcheck.h"
> +#include <stddef.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <wchar.h>
> +#include <stdlib.h>
> +#include <malloc.h>
> +#include <support/check.h>
> +
> +#define WIDTH 0x410
> +#define SCANFSTR "%1040mc"
> +static int
> +do_test (void)
> +{
> + mcheck_pedantic (NULL);
> + char *input = malloc (WIDTH + 1);
> + TEST_VERIFY (input != NULL);
> + memset (input, 'A', WIDTH);
> + input[WIDTH] = '\0';
> +
> + char *buf = NULL;
> + TEST_VERIFY (sscanf (input, SCANFSTR, &buf) != -1);
> + TEST_VERIFY (buf != NULL);
> +
> + free (buf);
> + free (input);
OK. Confirmed mcheck catches the overflow.
> + return 0;
> +}
> +
> +#include <support/test-driver.c>
--
Cheers,
Carlos.
^ permalink raw reply [flat|nested] 18+ messages in thread
* [PATCH v6] stdio-common: Fix buffer overflow in scanf %mc [BZ #34008]
2026-04-17 21:54 ` [PATCH v5 0/3] Re: [PATCH v4 2/2] stdio-common: Fix buffer overflow in scanf %mc [BZ #34008] Carlos O'Donell
@ 2026-04-18 6:48 ` Rocket Ma
2026-05-07 22:29 ` H.J. Lu
0 siblings, 1 reply; 18+ messages in thread
From: Rocket Ma @ 2026-04-18 6:48 UTC (permalink / raw)
To: Carlos O'Donell; +Cc: libc-alpha, Florian Weimer
* stdio-common/vfscanf-internal.c: When enlarging allocated buffer with
format %mc or %mC, glibc allocates one byte less, leading to
user-controlled one byte overflow. This commit fixes BZ #34008, or
CVE-2026-5450.
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Signed-off-by: Rocket Ma <marocketbd@gmail.com>
---
stdio-common/Makefile | 4 +++
stdio-common/tst-vfscanf-bz34008.c | 48 ++++++++++++++++++++++++++++++
stdio-common/vfscanf-internal.c | 7 ++---
3 files changed, 55 insertions(+), 4 deletions(-)
create mode 100644 stdio-common/tst-vfscanf-bz34008.c
diff --git a/stdio-common/Makefile b/stdio-common/Makefile
index 210944837e..0c0085e607 100644
--- a/stdio-common/Makefile
+++ b/stdio-common/Makefile
@@ -349,6 +349,7 @@ tests := \
tst-vfprintf-user-type \
tst-vfprintf-width-i18n \
tst-vfprintf-width-prec-alloc \
+ tst-vfscanf-bz34008 \
tst-wc-printf \
tstdiomisc \
tstgetln \
@@ -564,6 +565,9 @@ tst-printf-bz18872-ENV = MALLOC_TRACE=$(objpfx)tst-printf-bz18872.mtrace \
tst-vfprintf-width-prec-ENV = \
MALLOC_TRACE=$(objpfx)tst-vfprintf-width-prec.mtrace \
LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so
+tst-vfscanf-bz34008-ENV = \
+ MALLOC_CHECK_=3 \
+ LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so
tst-printf-bz25691-ENV = \
MALLOC_TRACE=$(objpfx)tst-printf-bz25691.mtrace \
LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so
diff --git a/stdio-common/tst-vfscanf-bz34008.c b/stdio-common/tst-vfscanf-bz34008.c
new file mode 100644
index 0000000000..af746821fb
--- /dev/null
+++ b/stdio-common/tst-vfscanf-bz34008.c
@@ -0,0 +1,48 @@
+/* Regression test for vfscanf %Nmc out-of-bound write (BZ #34008)
+ Copyright (C) 2026 The GNU Toolchain Authors.
+ This file is part of the GNU C Library.
+
+ The GNU C Library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 of the License, or (at your option) any later version.
+
+ The GNU C Library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <https://www.gnu.org/licenses/>. */
+
+#include "malloc/mcheck.h"
+#include <stddef.h>
+#include <stdio.h>
+#include <string.h>
+#include <wchar.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <support/check.h>
+
+#define WIDTH 0x410
+#define SCANFSTR "%1040mc"
+static int
+do_test (void)
+{
+ mcheck_pedantic (NULL);
+ char *input = malloc (WIDTH + 1);
+ TEST_VERIFY (input != NULL);
+ memset (input, 'A', WIDTH);
+ input[WIDTH] = '\0';
+
+ char *buf = NULL;
+ TEST_VERIFY (sscanf (input, SCANFSTR, &buf) != -1);
+ TEST_VERIFY (buf != NULL);
+
+ free (buf);
+ free (input);
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/stdio-common/vfscanf-internal.c b/stdio-common/vfscanf-internal.c
index 59fc8208aa..3d11ac261e 100644
--- a/stdio-common/vfscanf-internal.c
+++ b/stdio-common/vfscanf-internal.c
@@ -855,8 +855,7 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
{
/* Enlarge the buffer. */
size_t newsize
- = strsize
- + (strsize >= width ? width - 1 : strsize);
+ = strsize + (strsize >= width ? width : strsize);
str = (char *) realloc (*strptr, newsize);
if (str == NULL)
@@ -929,7 +928,7 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
&& wstr == (wchar_t *) *strptr + strsize)
{
size_t newsize
- = strsize + (strsize > width ? width - 1 : strsize);
+ = strsize + (strsize >= width ? width : strsize);
/* Enlarge the buffer. */
wstr = (wchar_t *) realloc (*strptr,
newsize * sizeof (wchar_t));
@@ -984,7 +983,7 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
&& wstr == (wchar_t *) *strptr + strsize)
{
size_t newsize
- = strsize + (strsize > width ? width - 1 : strsize);
+ = strsize + (strsize >= width ? width : strsize);
/* Enlarge the buffer. */
wstr = (wchar_t *) realloc (*strptr,
newsize * sizeof (wchar_t));
--
2.53.0
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v5 2/3] stdio-common: Fix buffer overflow in scanf %mc [BZ #34008]
2026-04-17 21:55 ` Carlos O'Donell
@ 2026-04-28 14:24 ` Rocket Ma
2026-05-07 16:37 ` Rocket Ma
1 sibling, 0 replies; 18+ messages in thread
From: Rocket Ma @ 2026-04-28 14:24 UTC (permalink / raw)
To: Carlos O'Donell; +Cc: libc-alpha
Any progress? I've uploaded a patch with only the fix and regression
test days ago.
Rocket
Carlos O'Donell <carlos@redhat.com> 于2026年4月18日周六 05:55写道:
>
> This looks correct to me.
>
> You can keep my Reviewed-by in your v6 post if you don't change the fix.
>
> Reviewed-by: Carlos O'Donell <carlos@redhat.com>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v5 2/3] stdio-common: Fix buffer overflow in scanf %mc [BZ #34008]
2026-04-17 21:55 ` Carlos O'Donell
2026-04-28 14:24 ` Rocket Ma
@ 2026-05-07 16:37 ` Rocket Ma
1 sibling, 0 replies; 18+ messages in thread
From: Rocket Ma @ 2026-05-07 16:37 UTC (permalink / raw)
To: Carlos O'Donell; +Cc: libc-alpha
Ping for my v6 patch
Rocket
> This looks correct to me.
>
> You can keep my Reviewed-by in your v6 post if you don't change the fix.
>
> Reviewed-by: Carlos O'Donell <carlos@redhat.com>
^ permalink raw reply [flat|nested] 18+ messages in thread
* Re: [PATCH v6] stdio-common: Fix buffer overflow in scanf %mc [BZ #34008]
2026-04-18 6:48 ` [PATCH v6] " Rocket Ma
@ 2026-05-07 22:29 ` H.J. Lu
0 siblings, 0 replies; 18+ messages in thread
From: H.J. Lu @ 2026-05-07 22:29 UTC (permalink / raw)
To: Rocket Ma; +Cc: Carlos O'Donell, GNU C Library, Florian Weimer
On Sat, Apr 18, 2026 at 2:49 PM Rocket Ma <marocketbd@gmail.com> wrote:
>
> * stdio-common/vfscanf-internal.c: When enlarging allocated buffer with
> format %mc or %mC, glibc allocates one byte less, leading to
> user-controlled one byte overflow. This commit fixes BZ #34008, or
> CVE-2026-5450.
>
> Reviewed-by: Carlos O'Donell <carlos@redhat.com>
> Signed-off-by: Rocket Ma <marocketbd@gmail.com>
> ---
> stdio-common/Makefile | 4 +++
> stdio-common/tst-vfscanf-bz34008.c | 48 ++++++++++++++++++++++++++++++
> stdio-common/vfscanf-internal.c | 7 ++---
> 3 files changed, 55 insertions(+), 4 deletions(-)
> create mode 100644 stdio-common/tst-vfscanf-bz34008.c
>
> diff --git a/stdio-common/Makefile b/stdio-common/Makefile
> index 210944837e..0c0085e607 100644
> --- a/stdio-common/Makefile
> +++ b/stdio-common/Makefile
> @@ -349,6 +349,7 @@ tests := \
> tst-vfprintf-user-type \
> tst-vfprintf-width-i18n \
> tst-vfprintf-width-prec-alloc \
> + tst-vfscanf-bz34008 \
> tst-wc-printf \
> tstdiomisc \
> tstgetln \
> @@ -564,6 +565,9 @@ tst-printf-bz18872-ENV = MALLOC_TRACE=$(objpfx)tst-printf-bz18872.mtrace \
> tst-vfprintf-width-prec-ENV = \
> MALLOC_TRACE=$(objpfx)tst-vfprintf-width-prec.mtrace \
> LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so
> +tst-vfscanf-bz34008-ENV = \
> + MALLOC_CHECK_=3 \
> + LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so
> tst-printf-bz25691-ENV = \
> MALLOC_TRACE=$(objpfx)tst-printf-bz25691.mtrace \
> LD_PRELOAD=$(common-objpfx)/malloc/libc_malloc_debug.so
> diff --git a/stdio-common/tst-vfscanf-bz34008.c b/stdio-common/tst-vfscanf-bz34008.c
> new file mode 100644
> index 0000000000..af746821fb
> --- /dev/null
> +++ b/stdio-common/tst-vfscanf-bz34008.c
> @@ -0,0 +1,48 @@
> +/* Regression test for vfscanf %Nmc out-of-bound write (BZ #34008)
> + Copyright (C) 2026 The GNU Toolchain Authors.
> + This file is part of the GNU C Library.
> +
> + The GNU C Library is free software; you can redistribute it and/or
> + modify it under the terms of the GNU Lesser General Public
> + License as published by the Free Software Foundation; either
> + version 2.1 of the License, or (at your option) any later version.
> +
> + The GNU C Library is distributed in the hope that it will be useful,
> + but WITHOUT ANY WARRANTY; without even the implied warranty of
> + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
> + Lesser General Public License for more details.
> +
> + You should have received a copy of the GNU Lesser General Public
> + License along with the GNU C Library; if not, see
> + <https://www.gnu.org/licenses/>. */
> +
> +#include "malloc/mcheck.h"
> +#include <stddef.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <wchar.h>
> +#include <stdlib.h>
> +#include <malloc.h>
> +#include <support/check.h>
> +
> +#define WIDTH 0x410
> +#define SCANFSTR "%1040mc"
> +static int
> +do_test (void)
> +{
> + mcheck_pedantic (NULL);
> + char *input = malloc (WIDTH + 1);
> + TEST_VERIFY (input != NULL);
> + memset (input, 'A', WIDTH);
> + input[WIDTH] = '\0';
> +
> + char *buf = NULL;
> + TEST_VERIFY (sscanf (input, SCANFSTR, &buf) != -1);
> + TEST_VERIFY (buf != NULL);
> +
> + free (buf);
> + free (input);
> + return 0;
> +}
> +
> +#include <support/test-driver.c>
> diff --git a/stdio-common/vfscanf-internal.c b/stdio-common/vfscanf-internal.c
> index 59fc8208aa..3d11ac261e 100644
> --- a/stdio-common/vfscanf-internal.c
> +++ b/stdio-common/vfscanf-internal.c
> @@ -855,8 +855,7 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
> {
> /* Enlarge the buffer. */
> size_t newsize
> - = strsize
> - + (strsize >= width ? width - 1 : strsize);
> + = strsize + (strsize >= width ? width : strsize);
>
> str = (char *) realloc (*strptr, newsize);
> if (str == NULL)
> @@ -929,7 +928,7 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
> && wstr == (wchar_t *) *strptr + strsize)
> {
> size_t newsize
> - = strsize + (strsize > width ? width - 1 : strsize);
> + = strsize + (strsize >= width ? width : strsize);
> /* Enlarge the buffer. */
> wstr = (wchar_t *) realloc (*strptr,
> newsize * sizeof (wchar_t));
> @@ -984,7 +983,7 @@ __vfscanf_internal (FILE *s, const char *format, va_list argptr,
> && wstr == (wchar_t *) *strptr + strsize)
> {
> size_t newsize
> - = strsize + (strsize > width ? width - 1 : strsize);
> + = strsize + (strsize >= width ? width : strsize);
> /* Enlarge the buffer. */
> wstr = (wchar_t *) realloc (*strptr,
> newsize * sizeof (wchar_t));
> --
> 2.53.0
LGTM.
Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
Thanks.
--
H.J.
^ permalink raw reply [flat|nested] 18+ messages in thread
end of thread, other threads:[~2026-05-07 22:30 UTC | newest]
Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-04-05 18:18 [PATCH v4 0/2] stdio-common: Fix heap overflow in scanf %mc pattern [BZ #34008] Rocket Ma
2026-04-05 18:18 ` [PATCH v4 1/2] stdio-common: Add regression test " Rocket Ma
2026-04-05 18:18 ` [PATCH v4 2/2] stdio-common: Fix buffer overflow in scanf %mc " Rocket Ma
2026-04-06 9:00 ` Florian Weimer
2026-04-06 16:17 ` Rocket Ma
2026-04-08 16:13 ` Rocket Ma
2026-04-13 13:46 ` Carlos O'Donell
2026-04-14 2:17 ` [PATCH v5 0/3] " Rocket Ma
2026-04-14 2:17 ` [PATCH v5 1/3] stdio-common: Add regression test " Rocket Ma
2026-04-17 21:59 ` Carlos O'Donell
2026-04-14 2:17 ` [PATCH v5 2/3] stdio-common: Fix buffer overflow in scanf %mc " Rocket Ma
2026-04-17 21:55 ` Carlos O'Donell
2026-04-28 14:24 ` Rocket Ma
2026-05-07 16:37 ` Rocket Ma
2026-04-14 2:17 ` [PATCH v5 3/3] stdio-common: Optimize %ms expansion for best fit Rocket Ma
2026-04-17 21:54 ` [PATCH v5 0/3] Re: [PATCH v4 2/2] stdio-common: Fix buffer overflow in scanf %mc [BZ #34008] Carlos O'Donell
2026-04-18 6:48 ` [PATCH v6] " Rocket Ma
2026-05-07 22:29 ` H.J. Lu
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).