glib2/CVE-2026-58013.patch
RHEL Packaging Agent b3d5305454 Fix CVE-2026-58013: buffer over-read in GIOChannel with long terminators
Backport fix for CVE-2026-58013 to glib2-2.56.4. The patch
fixes a buffer over-read in giochannel.c where memcmp()
could read past the end of the buffer when a multi-byte
line terminator is used and the line extends to the end of
the buffer. A bounds check is added before the memcmp()
call. A minimal test file (io-channel.c) is included,
adapted for 2.56.4 API compatibility.

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

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

Assisted-by: Ymir
2026-07-30 12:42:21 +00:00

128 lines
4.3 KiB
Diff
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From bb5c37897584ca09c583161f908046edc2ec9d85 Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@gnome.org>
Date: Tue, 28 Apr 2026 16:45:14 +0100
Subject: [PATCH] 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 | 78 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 80 insertions(+), 1 deletion(-)
create mode 100644 glib/tests/io-channel.c
diff --git a/glib/giochannel.c b/glib/giochannel.c
index f01817a83..32a1550b7 100644
--- a/glib/giochannel.c
+++ b/glib/giochannel.c
@@ -1807,7 +1807,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
new file mode 100644
index 000000000..447675ee8
--- /dev/null
+++ b/glib/tests/io-channel.c
@@ -0,0 +1,78 @@
+/* Test for CVE-2026-58013: memcmp() off the end of the buffer with long terminators
+ *
+ * Copyright © 2026 Philip Withnall
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ */
+
+#include <glib.h>
+#include <glib/gstdio.h>
+#include <string.h>
+
+static void
+test_read_line_long_terminator (void)
+{
+ guint8 *test_data = NULL;
+ gsize test_data_len = 0;
+ gint fd;
+ gchar *filename = NULL;
+ GIOChannel *channel = NULL;
+ GError *local_error = NULL;
+ gchar *line = NULL;
+ gsize line_length, terminator_pos;
+ const gchar *line_term;
+ gint line_term_length;
+ GIOStatus status;
+
+ /* 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 (fd, NULL);
+ fd = -1;
+
+ 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[])
+{
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/io-channel/read-line/long-terminator", test_read_line_long_terminator);
+
+ return g_test_run ();
+}