From 7df6850e22b48f68008bf4d8329b3a2db9f182ed Mon Sep 17 00:00:00 2001
From: Rocket Ma <marocketbd@gmail.com>
Date: Fri, 1 May 2026 20:39:07 -0700
Subject: [PATCH] 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 <marocketbd@gmail.com>
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
---
 libio/Makefile              |  2 +-
 libio/bug-wgenops-bz33998.c | 54 +++++++++++++++++++++++++++++++++++++
 libio/wgenops.c             |  4 +--
 3 files changed, 57 insertions(+), 3 deletions(-)
 create mode 100644 libio/bug-wgenops-bz33998.c

diff --git a/libio/Makefile b/libio/Makefile
index 8f9f302807..56710fcd99 100644
--- a/libio/Makefile
+++ b/libio/Makefile
@@ -83,7 +83,7 @@ tests = tst_swprintf tst_wprintf tst_swscanf tst_wscanf tst_getwc tst_putwc   \
 	tst-fwrite-error tst-ftell-partial-wide tst-ftell-active-handler \
 	tst-ftell-append tst-fputws tst-bz22415 tst-fgetc-after-eof \
 	tst-sprintf-ub tst-sprintf-chk-ub tst-bz24051 tst-bz24153 \
-	tst-wfile-sync tst-bz28828 tst-getdelim
+	tst-wfile-sync tst-bz28828 tst-getdelim bug-wgenops-bz33998
 
 tests-internal = tst-vtables tst-vtables-interposed
 
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 4a278802f9..c593debe58 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!*/
-- 
2.54.0

