143 lines
4.9 KiB
Diff
143 lines
4.9 KiB
Diff
From 632da30304f724b806e224531775d9054b1d829a 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 that’s how `GString`s
|
||
are allocated) it’s 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 b44fff35b..4e682e2a9 100644
|
||
--- a/glib/giochannel.c
|
||
+++ b/glib/giochannel.c
|
||
@@ -1822,7 +1822,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 c5dd01d04..cf81a9f6b 100644
|
||
--- a/glib/tests/io-channel.c
|
||
+++ b/glib/tests/io-channel.c
|
||
@@ -216,6 +216,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 doesn’t 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[])
|
||
@@ -224,6 +283,7 @@ main (int argc,
|
||
|
||
g_test_add_func ("/io-channel/read-write", test_read_write);
|
||
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 71eb9479c3521ce6b0bf007f51271aeb9354f797 Mon Sep 17 00:00:00 2001
|
||
From: RHEL Packaging Agent <redhat-ymir-agent@redhat.com>
|
||
Date: Mon, 20 Jul 2026 06:25:23 +0000
|
||
Subject: [PATCH 2/2] tests: Include stdint.h for uint8_t in io-channel test
|
||
|
||
---
|
||
glib/tests/io-channel.c | 1 +
|
||
1 file changed, 1 insertion(+)
|
||
|
||
diff --git a/glib/tests/io-channel.c b/glib/tests/io-channel.c
|
||
index cf81a9f6b..4b4189b68 100644
|
||
--- a/glib/tests/io-channel.c
|
||
+++ b/glib/tests/io-channel.c
|
||
@@ -30,6 +30,7 @@
|
||
|
||
#include <glib.h>
|
||
#include <glib/gstdio.h>
|
||
+#include <stdint.h>
|
||
|
||
static void
|
||
test_small_writes (void)
|