[COMMITTED 2.42 2/2] stdio-common: Fix buffer overflow in scanf %mc [BZ #34008]
Aurelien Jarno
aurelien@aurel32.net
Sat May 9 11:54:37 GMT 2026
Hi Florian,
Thanks for looking into that. I have done the backport and checked that
the new test passes, so I though that the fix was fine for that branch.
On 2026-05-09 12:01, Florian Weimer wrote:
> * Aurelien Jarno:
>
> > From: Rocket Ma <marocketbd@gmail.com>
> >
> > * 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>
> > Reviewed-by: H.J. Lu <hjl.tools@gmail.com>
> > (cherry picked from commit 839898777226a3ed88c0859f25ffe712519b4ead)
>
> I believe scanf %mc remains non-functional on glibc 2.42 and earlier due
> to bug 12701:
>
> Bug 12701 - scanf accepts non-matching input
> <https://sourceware.org/bugzilla/show_bug.cgi?id=12701>
>
> The fix that changes %Nc not to accept partial matches is unfortunately
> not backportable because it breaks some applications.
>
> Without further changes, there is no way for the application to know how
> much memory glibc has allocated after a failed match.
Do you mean the following commits two commits that are not in 2.42?
b52ecff316: Reject significands w/o digits in scanf [BZ #12701]
2b16c76609: Reject insufficient character data in scanf [BZ #12701]
The others commit listed in that bug report are included in 2.42.
> Consider this example program:
>
> #include <stdio.h>
> #include <stdlib.h>
> #include <string.h>
>
> int
> main (void)
> {
> {
> char *volatile p = malloc (40);
> memset (p, 'X', 40);
> free (p);
> printf ("old p: %p\n", p);
> }
> char *p = NULL;
> printf ("sscanf: %d\n",
> sscanf ("YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY", "%40mc", &p));
> printf ("new p: %p\np: ", p);
> fwrite (p, 1, 40, stdout);
> putchar ('\n');
> }
>
>
> With valgrind, we can see that the allocation size is 32, not 40:
>
> ==504594==
> old p: 0x4a6c040
> sscanf: 1
> new p: 0x4a6c560
> ==504594== Invalid read of size 1
> ==504594== at 0x48E07AD: _IO_new_file_xsputn (fileops.c:1280)
> ==504594== by 0x48E07AD: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1258)
> ==504594== by 0x48D32CA: fwrite (iofwrite.c:44)
> ==504594== by 0x40113A: main (sscanf-mc.c:18)
> ==504594== Address 0x4a6c587 is 7 bytes after a block of size 32 alloc'd
> ==504594== at 0x48488E1: realloc (vg_replace_malloc.c:1741)
> ==504594== by 0x48BA102: __vfscanf_internal (vfscanf-internal.c:896)
> ==504594== by 0x48ABCD0: __isoc23_sscanf (isoc23_sscanf.c:31)
> ==504594== by 0x401100: main (sscanf-mc.c:15)
> ==504594==
> ==504594== Invalid read of size 1
> ==504594== at 0x485024E: mempcpy (vg_replace_strmem.c:1715)
> ==504594== by 0x48E069E: _IO_new_file_xsputn (fileops.c:1297)
> ==504594== by 0x48E069E: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1258)
> ==504594== by 0x48D32CA: fwrite (iofwrite.c:44)
> ==504594== by 0x40113A: main (sscanf-mc.c:18)
> ==504594== Address 0x4a6c580 is 0 bytes after a block of size 32 alloc'd
> ==504594== at 0x48488E1: realloc (vg_replace_malloc.c:1741)
> ==504594== by 0x48BA102: __vfscanf_internal (vfscanf-internal.c:896)
> ==504594== by 0x48ABCD0: __isoc23_sscanf (isoc23_sscanf.c:31)
> ==504594== by 0x401100: main (sscanf-mc.c:15)
> ==504594==
> ==504594== Invalid read of size 1
> ==504594== at 0x4850240: mempcpy (vg_replace_strmem.c:1715)
> ==504594== by 0x48E069E: _IO_new_file_xsputn (fileops.c:1297)
> ==504594== by 0x48E069E: _IO_file_xsputn@@GLIBC_2.2.5 (fileops.c:1258)
> ==504594== by 0x48D32CA: fwrite (iofwrite.c:44)
> ==504594== by 0x40113A: main (sscanf-mc.c:18)
> ==504594== Address 0x4a6c581 is 1 bytes after a block of size 32 alloc'd
> ==504594== at 0x48488E1: realloc (vg_replace_malloc.c:1741)
> ==504594== by 0x48BA102: __vfscanf_internal (vfscanf-internal.c:896)
> ==504594== by 0x48ABCD0: __isoc23_sscanf (isoc23_sscanf.c:31)
> ==504594== by 0x401100: main (sscanf-mc.c:15)
> ==504594==
> p: YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
>
> And without valgrind, it becomes clear that the application sees
> uninitialized memory:
>
> old p: 0x14b65310
> sscanf: 1
> new p: 0x14b65310
> p: YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYXXXXXXXX
Indeed, thanks for the example.
> I think we need always allocate the expected size, and clear the tail
> that was not matched.
Do you mean we need a different fix for 2.42 and older? In that case I
guess it's better I just revert the commit for now.
Regards
Aurelien
--
Aurelien Jarno GPG: 4096R/1DDD8C9B
aurelien@aurel32.net http://aurel32.net
More information about the Libc-stable
mailing list