Fix CVE-2026-58013: buffer over-read in GIOChannel with long terminators

Backport upstream fix for CVE-2026-58013, which addresses a
buffer over-read in GIOChannel when using multi-byte line
terminators. The memcmp() call could read past the end of
the internal GString buffer when the line extends to the
buffer boundary. The patch adds a length check before the
memcmp() and includes a new unit test. The test was adapted
for GLib 2.68 by replacing unavailable C99/GLib 2.70
constructs (uint8_t, g_steal_fd) with their GLib 2.68
equivalents.

CVE: CVE-2026-58013
Upstream patches:
 - 9f557746c5.patch
Resolves: RHEL-212237

This commit was backported by Ymir, a Red Hat Enterprise Linux software maintenance AI agent.

Assisted-by: Ymir
This commit is contained in:
RHEL Packaging Agent 2026-08-19 16:13:21 +00:00
parent 02c48c2acb
commit 3c65c9e35d
2 changed files with 178 additions and 1 deletions

170
CVE-2026-58013.patch Normal file
View File

@ -0,0 +1,170 @@
From 89f27b020f0e61a66c9308a0e67846e155c110ba Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@gnome.org>
Date: Tue, 28 Apr 2026 16:45:14 +0100
Subject: [PATCH 1/2] giochannel: Fix memcmp() off the end of the buffer with
long terminators
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
If the line terminator is longer than a single byte, and the current
line extends to the end of the buffer, and the buffer (which is a
`GString`) is near a power of two in length (as thats how `GString`s
are allocated) its possible for the `memcmp()` which checks the
terminator to read off the end of the string buffer.
Fix that by checking the terminator length against the last character
before calling `memcmp()`. Add a unit test.
Spotted by linhlhq as #YWH-PGM9867-199. The fix is theirs (validated by
me), and the unit test is adapted from their proof of concept.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
Fixes: #3925
---
glib/giochannel.c | 3 ++-
glib/tests/io-channel.c | 60 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 62 insertions(+), 1 deletion(-)
diff --git a/glib/giochannel.c b/glib/giochannel.c
index e93c4b458..66b68e645 100644
--- a/glib/giochannel.c
+++ b/glib/giochannel.c
@@ -1830,7 +1830,8 @@ read_again:
{
if (channel->line_term)
{
- if (memcmp (channel->line_term, nextchar, line_term_len) == 0)
+ if ((size_t) (lastchar - nextchar) >= line_term_len &&
+ memcmp (channel->line_term, nextchar, line_term_len) == 0)
{
line_length = nextchar - use_buf->str;
got_term_len = line_term_len;
diff --git a/glib/tests/io-channel.c b/glib/tests/io-channel.c
index 4a1b10876..c619fb00f 100644
--- a/glib/tests/io-channel.c
+++ b/glib/tests/io-channel.c
@@ -69,6 +69,65 @@ test_read_line_embedded_nuls (void)
g_free (filename);
}
+static void
+test_read_line_long_terminator (void)
+{
+ uint8_t *test_data = NULL;
+ size_t test_data_len = 0;
+ int fd;
+ char *filename = NULL;
+ GIOChannel *channel = NULL;
+ GError *local_error = NULL;
+ char *line = NULL;
+ size_t line_length, terminator_pos;
+ const char *line_term;
+ int line_term_length;
+ GIOStatus status;
+
+ g_test_summary ("Test that reading a line when using a long terminator doesnt over-read the buffer.");
+ g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/work_items/3925");
+
+ /* Write out a temporary file containing 2047 bytes. This is enough to make it
+ * near the length of the GString buffer when read back in. */
+ fd = g_file_open_tmp ("glib-test-io-channel-XXXXXX", &filename, &local_error);
+ g_assert_no_error (local_error);
+ g_close (g_steal_fd (&fd), NULL);
+
+ test_data_len = 2047;
+ test_data = g_malloc (test_data_len);
+ memset (test_data, 'M', test_data_len);
+ g_file_set_contents (filename, (const gchar *) test_data, test_data_len, &local_error);
+ g_assert_no_error (local_error);
+
+ /* Create the channel. */
+ channel = g_io_channel_new_file (filename, "r", &local_error);
+ g_assert_no_error (local_error);
+
+ /* Use a long line terminator so it could potentially over-read the end of the buffer. */
+ g_io_channel_set_line_term (channel, "DEADBEEF", 8);
+
+ line_term = g_io_channel_get_line_term (channel, &line_term_length);
+ g_assert_cmpstr (line_term, ==, "DEADBEEF");
+ g_assert_cmpint (line_term_length, ==, 8);
+
+ g_io_channel_set_encoding (channel, "UTF-8", &local_error);
+ g_assert_no_error (local_error);
+
+ status = g_io_channel_read_line (channel, &line, &line_length,
+ &terminator_pos, &local_error);
+ g_assert_no_error (local_error);
+ g_assert_cmpint (status, ==, G_IO_STATUS_NORMAL);
+ g_assert_cmpuint (line_length, ==, 2047);
+ g_assert_cmpuint (terminator_pos, ==, 2047);
+ g_assert_cmpmem (line, line_length, test_data, test_data_len);
+
+ g_free (line);
+ g_io_channel_unref (channel);
+ g_free (test_data);
+ g_unlink (filename);
+ g_free (filename);
+}
+
int
main (int argc,
char *argv[])
@@ -76,6 +135,7 @@ main (int argc,
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/io-channel/read-line/embedded-nuls", test_read_line_embedded_nuls);
+ g_test_add_func ("/io-channel/read-line/long-terminator", test_read_line_long_terminator);
return g_test_run ();
}
From 94b3e1b6d1380c8f78e1e869b5cb46b2fbfe6689 Mon Sep 17 00:00:00 2001
From: RHEL Packaging Agent <redhat-ymir-agent@redhat.com>
Date: Wed, 19 Aug 2026 15:07:16 +0000
Subject: [PATCH 2/2] Adapt test for GLib 2.68: replace uint8_t with guint8 and
g_steal_fd with inline equivalent
---
glib/tests/io-channel.c | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)
diff --git a/glib/tests/io-channel.c b/glib/tests/io-channel.c
index c619fb00f..afcea93f0 100644
--- a/glib/tests/io-channel.c
+++ b/glib/tests/io-channel.c
@@ -72,16 +72,16 @@ test_read_line_embedded_nuls (void)
static void
test_read_line_long_terminator (void)
{
- uint8_t *test_data = NULL;
- size_t test_data_len = 0;
- int fd;
- char *filename = NULL;
+ guint8 *test_data = NULL;
+ gsize test_data_len = 0;
+ gint fd;
+ gchar *filename = NULL;
GIOChannel *channel = NULL;
GError *local_error = NULL;
- char *line = NULL;
- size_t line_length, terminator_pos;
- const char *line_term;
- int line_term_length;
+ gchar *line = NULL;
+ gsize line_length, terminator_pos;
+ const gchar *line_term;
+ gint line_term_length;
GIOStatus status;
g_test_summary ("Test that reading a line when using a long terminator doesnt over-read the buffer.");
@@ -91,7 +91,8 @@ test_read_line_long_terminator (void)
* near the length of the GString buffer when read back in. */
fd = g_file_open_tmp ("glib-test-io-channel-XXXXXX", &filename, &local_error);
g_assert_no_error (local_error);
- g_close (g_steal_fd (&fd), NULL);
+ g_close (fd, NULL);
+ fd = -1;
test_data_len = 2047;
test_data = g_malloc (test_data_len);

View File

@ -1,6 +1,6 @@
Name: glib2
Version: 2.68.4
Release: 24%{?dist}
Release: 25%{?dist}
Summary: A library of handy utility functions
License: LGPLv2+
@ -104,6 +104,9 @@ Patch: CVE-2026-58012.patch
# https://gitlab.gnome.org/GNOME/glib/-/commit/aa1cb87d56111ef989811e824f0ac77484cc997f
Patch: CVE-2026-58010.patch
# https://gitlab.gnome.org/GNOME/glib/-/commit/9f557746c52ae2a62fd5929f532b77024a18abe2
Patch: CVE-2026-58013.patch
BuildRequires: chrpath
BuildRequires: gcc
BuildRequires: gcc-c++
@ -325,6 +328,10 @@ glib-compile-schemas %{_datadir}/glib-2.0/schemas &> /dev/null || :
%{_datadir}/installed-tests
%changelog
* Wed Aug 19 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2.68.4-25
- Fix CVE-2026-58013: buffer over-read in GIOChannel with long terminators
Resolves: RHEL-212237
* Wed Aug 19 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2.68.4-24
- Fix CVE-2026-58010: off-by-one error in GVariant tuple normal form checking
Resolves: RHEL-212171