[glibc/release/2.41/master] libio: Fix ungetwc operating on byte stream [BZ #33998]

Aurelien Jarno via Glibc-cvs <[email protected]> Tue, 23 Jun 2026 04:34:11 +0000 (GMT)
Newsgroups gmane.comp.lib.glibc.cvs
Message-ID <[email protected]>
https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=8941538b92a574a24e9308e164869239d2b0d845

commit 8941538b92a574a24e9308e164869239d2b0d845
Author: Rocket Ma <[email protected]>
Date:   Fri May 1 20:39:07 2026 -0700

    libio: Fix ungetwc operating on byte stream [BZ #33998]
    
    * libio/wgenops.c: When _IO_wdefault_pbackfail attempts to push back one
    character, it accidently compare the wchar to push back with the last
    char from byte stream, instead of wide stream. Under specific coding,
    attacker may exploit this to leak information. This commit fix bug
    33998, or CVE-2026-5928.
    
    Signed-off-by: Rocket Ma <[email protected]>
    Reviewed-by: Carlos O'Donell <[email protected]>
    (cherry picked from commit ef3bfb5f910011f3780cb06aa47e730035f53285)

Diff:
---
 libio/Makefile              |  1 +
 libio/bug-wgenops-bz33998.c | 54 +++++++++++++++++++++++++++++++++++++++++++++
 libio/wgenops.c             |  4 ++--
 3 files changed, 57 insertions(+), 2 deletions(-)

diff --git a/libio/Makefile b/libio/Makefile
index e143ccdb2c..43ee8db06d 100644
--- a/libio/Makefile
+++ b/libio/Makefile
@@ -83,6 +83,7 @@ tests = \
   bug-ungetwc1 \
   bug-ungetwc2 \
   bug-wfflush \
+  bug-wgenops-bz33998 \
   bug-wmemstream1 \
   bug-wsetpos \
   test-fmemopen \
diff --git a/libio/bug-wgenops-bz33998.c b/libio/bug-wgenops-bz33998.c
new file mode 100644
index 0000000000..cc4067da99
--- /dev/null
+++ b/libio/bug-wgenops-bz33998.c
@@ -0,0 +1,54 @@
+/* Regression test for ungetwc operating on byte stream (BZ #33998)
+   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 "support/temp_file.h"
+#include "support/xstdio.h"
+#include "support/xunistd.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <stdio.h>
+#include <wchar.h>
+#include <support/check.h>
+
+static int
+do_test (void)
+{
+  char *filename;
+  int fd = create_temp_file ("tst-bz33998-", &filename);
+  TEST_VERIFY (fd != -1);
+  xwrite (fd, "A", sizeof ("A")); // write "A\0" by design
+  xclose (fd);
+
+  FILE *fp = xfopen (filename, "r+");
+  TEST_COMPARE (getwc (fp), L'A');
+  /* If the bug is fixed, then ungetwc should not touch byte stream.
+     If the bug is not fixed, ungetwc firstly match last read char, L'A',
+     failed, then the pbackfail branch, matching last read char in byte
+     stream, that is, '\0' (initialized when setup wide stream). */
+  char *old_read_ptr = fp->_IO_read_ptr;
+  TEST_COMPARE (ungetwc (L'\0', fp), L'\0');
+  TEST_VERIFY (fp->_IO_read_ptr == old_read_ptr);
+
+  xfclose (fp);
+  free (filename);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/libio/wgenops.c b/libio/wgenops.c
index 0a11d1b1de..9e0b2c00ea 100644
--- a/libio/wgenops.c
+++ b/libio/wgenops.c
@@ -108,8 +108,8 @@ _IO_wdefault_pbackfail (FILE *fp, wint_t c)
 {
   if (fp->_wide_data->_IO_read_ptr > fp->_wide_data->_IO_read_base
       && !_IO_in_backup (fp)
-      && (wint_t) fp->_IO_read_ptr[-1] == c)
-    --fp->_IO_read_ptr;
+      && (wint_t) fp->_wide_data->_IO_read_ptr[-1] == c)
+    --fp->_wide_data->_IO_read_ptr;
   else
     {
       /* Need to handle a filebuf in write mode (switch to read mode). FIXME!*/