Compare commits

..

No commits in common. "c8s" and "c8-beta" have entirely different histories.
c8s ... c8-beta

15 changed files with 2 additions and 1474 deletions

1
.gitignore vendored
View File

@ -1,2 +1 @@
SOURCES/glib-2.70.1.tar.xz SOURCES/glib-2.70.1.tar.xz
/glib-2.70.1.tar.xz

1
.mingw-glib2.metadata Normal file
View File

@ -0,0 +1 @@
4704b98b3635950e69b86f8dedc01b0e2a7232b9 SOURCES/glib-2.70.1.tar.xz

View File

@ -5,7 +5,7 @@
Name: mingw-glib2 Name: mingw-glib2
Version: 2.70.1 Version: 2.70.1
Release: 9%{?dist} Release: 1%{?dist}
Summary: MinGW Windows GLib2 library Summary: MinGW Windows GLib2 library
License: LGPLv2+ License: LGPLv2+
@ -55,31 +55,6 @@ Patch1: 0001-Use-CreateFile-on-Win32-to-make-sure-g_unlink-always.patch
# https://bugzilla.gnome.org/show_bug.cgi?id=698118 # https://bugzilla.gnome.org/show_bug.cgi?id=698118
Patch2: glib-prefer-constructors-over-DllMain.patch Patch2: glib-prefer-constructors-over-DllMain.patch
# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/5172
Patch3: mingw-glib2-2.70.1-CVE-2026-58015.patch
# CVE-2026-58014: one-byte heap under-read with g_key_file_get_locale_string_list()
# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/5171
Patch4: mingw-glib2-2.70.1-CVE-2026-58014.patch
# https://github.com/GNOME/glib/commit/c9da977c178f
Patch5: mingw-glib2-2.70.1-CVE-2026-58016.patch
# https://github.com/GNOME/glib/commit/31f82e22e21bae520b7228f7f57d357fb20df8a4
Patch6: mingw-glib2-2.70.1-CVE-2025-14087.patch
# https://gitlab.gnome.org/GNOME/glib/-/issues/3918
Patch7: mingw-glib2-2.70.1-CVE-2026-58012.patch
# https://gitlab.gnome.org/GNOME/glib/-/work_items/3925
Patch8: mingw-glib2-2.70.1-CVE-2026-58013.patch
# https://gitlab.gnome.org/GNOME/glib/-/issues/3917
Patch9: mingw-glib2-2.70.1-CVE-2026-58011.patch
# https://gitlab.gnome.org/GNOME/glib/-/issues/3915
Patch10: mingw-glib2-2.70.1-CVE-2026-58010.patch
%description %description
MinGW Windows Glib2 library. MinGW Windows Glib2 library.
@ -127,14 +102,6 @@ Static version of the MinGW Windows GLib2 library.
%setup -q -n glib-%{version} %setup -q -n glib-%{version}
%patch1 -p1 %patch1 -p1
%patch2 -p1 %patch2 -p1
%patch3 -p1
%patch4 -p1
%patch5 -p1
%patch6 -p1
%patch7 -p1
%patch8 -p1
%patch9 -p1
%patch10 -p1
%build %build
%mingw_meson --default-library=both \ %mingw_meson --default-library=both \
@ -312,38 +279,6 @@ find $RPM_BUILD_ROOT -name "*.la" -delete
%changelog %changelog
* Wed Jul 22 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2.70.1-9
- Fix CVE-2026-58010: off-by-one error in gvs_tuple_is_normal()
Resolves: RHEL-212164
* Wed Jul 22 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2.70.1-8
- Fix CVE-2026-58011: g_date_time_add_full() range validation
Resolves: RHEL-212184
* Wed Jul 22 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2.70.1-7
- Fix CVE-2026-58013: memcmp buffer over-read in giochannel
Resolves: RHEL-212234
* Wed Jul 22 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2.70.1-6
- Fix CVE-2026-58012: buffer overflow in gregex substitutions
Resolves: RHEL-212200
* Wed Jul 22 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2.70.1-5
- Fix CVE-2025-14087: integer overflow in GVariant parser
Resolves: RHEL-154707
* Wed Jul 22 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2.70.1-4
- Fix CVE-2026-58016: D-Bus introspection XML node nesting check
Resolves: RHEL-190617
* Wed Jul 22 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2.70.1-3
- Fix CVE-2026-58014: one-byte heap under-read in mingw-glib2
Resolves: RHEL-190609
* Mon Jul 20 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2.70.1-2
- Fix CVE-2026-58015: D-Bus cookie context path traversal
Resolves: RHEL-212246
* Thu Dec 23 2021 Yan Vugenfirer <yvugenfi@redhat.com> 2.70.1-1 * Thu Dec 23 2021 Yan Vugenfirer <yvugenfi@redhat.com> 2.70.1-1
- Update to 2.70.1 - Update to 2.70.1
- Resolves: rhbz#2034959 - Resolves: rhbz#2034959

View File

@ -1,6 +0,0 @@
--- !Policy
product_versions:
- rhel-8
decision_context: osci_compose_gate
rules:
- !PassingTestCaseRule {test_case_name: desktop-qe.desktop-ci.tier1-gating.functional}

View File

@ -1,65 +0,0 @@
From 074d82962028125c7886e5edcff1a58a508e0e61 Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@gnome.org>
Date: Tue, 25 Nov 2025 19:02:56 +0000
Subject: [PATCH] gvariant-parser: Fix potential integer overflow parsing
(byte)strings
The termination condition for parsing string and bytestring literals in
GVariant text format input was subject to an integer overflow for input
string (or bytestring) literals longer than `INT_MAX`.
Fix that by counting as a `size_t` rather than as an `int`. The counter
can never correctly be negative.
Spotted by treeplus. Thanks to the Sovereign Tech Resilience programme
from the Sovereign Tech Agency. ID: #YWH-PGM9867-145
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
Fixes: #3834
---
glib/gvariant-parser.c | 10 +++++-----
1 file changed, 5 insertions(+), 5 deletions(-)
diff --git a/glib/gvariant-parser.c b/glib/gvariant-parser.c
index bb5238bea..af6527d40 100644
--- a/glib/gvariant-parser.c
+++ b/glib/gvariant-parser.c
@@ -594,7 +594,7 @@ ast_resolve (AST *ast,
{
GVariant *value;
gchar *pattern;
- gint i, j = 0;
+ size_t i, j = 0;
pattern = ast_get_pattern (ast, error);
@@ -1555,9 +1555,9 @@ string_free (AST *ast)
* No leading/trailing space allowed. */
static gboolean
unicode_unescape (const gchar *src,
- gint *src_ofs,
+ size_t *src_ofs,
gchar *dest,
- gint *dest_ofs,
+ size_t *dest_ofs,
gsize length,
SourceRef *ref,
GError **error)
@@ -1618,7 +1618,7 @@ string_parse (TokenStream *stream,
gsize length;
gchar quote;
gchar *str;
- gint i, j;
+ size_t i, j;
token_stream_start_ref (stream, &ref);
token = token_stream_get (stream);
@@ -1748,7 +1748,7 @@ bytestring_parse (TokenStream *stream,
gsize length;
gchar quote;
gchar *str;
- gint i, j;
+ size_t i, j;
token_stream_start_ref (stream, &ref);
token = token_stream_get (stream);

View File

@ -1,106 +0,0 @@
From eb63e89e237d35fb1d3cb757ea97b4c8806269a0 Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@gnome.org>
Date: Sun, 29 Mar 2026 19:10:41 +0100
Subject: [PATCH] gvariant: Fix an off-by-one error in an offset comparison
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
This allows a single byte out-of-bounds read off the end of the
(potentially untrusted) byte array backing a `GVariant` when its
being checked for normal form.
I cant see how this could practically be exploited, but its certainly
a security bug as the `GVariant` normal form checking code is supposed
to be robust to malicious inputs.
Spotted by linhlhq as #YWH-PGM9867-190, and fix and reproducer provided
by them too, thanks. Confirmed and turned into a unit test by me.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
Fixes: #3915
---
glib/gvariant-serialiser.c | 2 +-
glib/tests/gvariant.c | 48 ++++++++++++++++++++++++++++++++++++++
2 files changed, 49 insertions(+), 1 deletion(-)
diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
index 832a8fdc2..1bf3e4688 100644
--- a/glib/gvariant-serialiser.c
+++ b/glib/gvariant-serialiser.c
@@ -1079,7 +1079,7 @@ gvs_tuple_is_normal (GVariantSerialised value)
while (offset & alignment)
{
- if (offset > value.size || value.data[offset] != '\0')
+ if (offset >= value.size || value.data[offset] != '\0')
return FALSE;
offset++;
}
diff --git a/glib/tests/gvariant.c b/glib/tests/gvariant.c
index 0110f2664..dd3af6941 100644
--- a/glib/tests/gvariant.c
+++ b/glib/tests/gvariant.c
@@ -5047,6 +5047,52 @@ test_normal_checking_tuple_offsets (void)
g_variant_unref (variant);
}
+/* This is a regression test that looping over the padding bytes in a short
+ * (non-normal) tuple doesnt overflow the input data.
+ *
+ * See https://gitlab.gnome.org/GNOME/glib/-/issues/3915 */
+static void
+test_normal_checking_tuple_offsets6 (void)
+{
+ /*
+ * Type: (ynqiuxthdsog) — 12 members, first member 'y' (byte) has
+ * alignment 0, second 'n' (int16) has alignment 1.
+ * With 1 byte of data (0x28), after reading the first byte member,
+ * offset=1, alignment check for 'n' requires offset to be even,
+ * so the while loop checks value.data[1] — but size is only 1.
+ *
+ * Use heap allocation via GBytes so ASan reports heap-buffer-overflow.
+ */
+ uint8_t *heap_data = NULL;
+ GBytes *bytes = NULL;
+ const GVariantType *data_type = G_VARIANT_TYPE ("(ynqiuxthdsog)");
+ GVariant *variant = NULL;
+ GVariant *normal_variant = NULL;
+ GVariant *expected = NULL;
+
+ g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/3915");
+
+ heap_data = g_malloc (1);
+ heap_data[0] = 0x28;
+ bytes = g_bytes_new_take (heap_data, 1);
+
+ variant = g_variant_new_from_bytes (data_type, bytes, FALSE);
+ g_assert_nonnull (variant);
+
+ g_assert_false (g_variant_is_normal_form (variant));
+
+ normal_variant = g_variant_get_normal_form (variant);
+ g_assert_nonnull (normal_variant);
+
+ expected = g_variant_new_parsed ("(byte 0x28, int16 0, uint16 0, 0, uint32 0, int64 0, uint64 0, handle 0, 0.0, '', objectpath '/', signature '')");
+ g_assert_cmpvariant (expected, variant);
+ g_assert_cmpvariant (expected, normal_variant);
+
+ g_variant_unref (expected);
+ g_variant_unref (normal_variant);
+ g_variant_unref (variant);
+}
+
/* Test that an empty object path is normalised successfully to the base object
* path, /. */
static void
@@ -5191,6 +5237,8 @@ main (int argc, char **argv)
test_normal_checking_array_offsets);
g_test_add_func ("/gvariant/normal-checking/tuple-offsets",
test_normal_checking_tuple_offsets);
+ g_test_add_func ("/gvariant/normal-checking/tuple-offsets6",
+ test_normal_checking_tuple_offsets6);
g_test_add_func ("/gvariant/normal-checking/empty-object-path",
test_normal_checking_empty_object_path);

View File

@ -1,122 +0,0 @@
From fbef21c7da7757239d6df411a24c7dbb345ab966 Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@gnome.org>
Date: Sun, 29 Mar 2026 23:19:47 +0100
Subject: [PATCH 1/2] gdatetime: Factor out a couple of magic constants
This introduces no functional changes, it just makes the code a little
clearer.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
---
glib/gdatetime.c | 9 ++++++---
1 file changed, 6 insertions(+), 3 deletions(-)
diff --git a/glib/gdatetime.c b/glib/gdatetime.c
index a31afe713..029636c08 100644
--- a/glib/gdatetime.c
+++ b/glib/gdatetime.c
@@ -130,7 +130,7 @@ struct _GDateTime
gint interval;
/* 1 is 0001-01-01 in Proleptic Gregorian */
- gint32 days;
+ gint32 days; /* in range [MIN_DAYS, MAX_DAYS] */
gint ref_count; /* (atomic) */
};
@@ -172,6 +172,9 @@ struct _GDateTime
#define JULIAN_YEAR(d) ((d)->julian / 365.25)
#define DAYS_PER_PERIOD (G_GINT64_CONSTANT (2914695))
+#define MIN_DAYS 1 /* the days count for 0001-01-01 in Proleptic Gregorian */
+#define MAX_DAYS 3652059 /* the days count for 9999-12-31 in Proleptic Gregorian */
+
static const guint16 days_in_months[2][13] =
{
{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
@@ -781,7 +784,7 @@ g_date_time_from_instant (GTimeZone *tz,
datetime->days = instant / USEC_PER_DAY;
datetime->usec = instant % USEC_PER_DAY;
- if (datetime->days < 1 || 3652059 < datetime->days)
+ if (datetime->days < MIN_DAYS || datetime->days > MAX_DAYS)
{
g_date_time_unref (datetime);
datetime = NULL;
@@ -817,7 +820,7 @@ g_date_time_deal_with_date_change (GDateTime *datetime)
gint64 full_time;
gint64 usec;
- if (datetime->days < 1 || datetime->days > 3652059)
+ if (datetime->days < MIN_DAYS || datetime->days > MAX_DAYS)
return FALSE;
was_dst = g_time_zone_is_dst (datetime->tz, datetime->interval);
From 5f0c67435364fe682fe9dcc1d23645134a445a17 Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@gnome.org>
Date: Sun, 29 Mar 2026 23:46:17 +0100
Subject: [PATCH 2/2] gdatetime: Add missing range validation to
g_date_time_add_full()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Otherwise its possible to create a non-`NULL` but invalid `GDateTime`,
which breaks all kinds of internal assumptions.
Spotted by linhlhq as #YWH-PGM9867-191. Thanks to them for providing a
suggested fix and a test case, which I have adapted and validated.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
Fixes: #3917
---
glib/gdatetime.c | 4 +++-
glib/tests/gdatetime.c | 18 ++++++++++++++++++
2 files changed, 21 insertions(+), 1 deletion(-)
diff --git a/glib/gdatetime.c b/glib/gdatetime.c
index 029636c08..883322891 100644
--- a/glib/gdatetime.c
+++ b/glib/gdatetime.c
@@ -2022,7 +2022,9 @@ g_date_time_add_full (GDateTime *datetime,
new->days = full_time / USEC_PER_DAY;
new->usec = full_time % USEC_PER_DAY;
- /* XXX validate */
+ /* Validate its still in the range 0001-01-01 to 9999-12-31 */
+ if (new->days < MIN_DAYS || new->days > MAX_DAYS)
+ g_clear_pointer (&new, g_date_time_unref);
return new;
}
diff --git a/glib/tests/gdatetime.c b/glib/tests/gdatetime.c
index 12f332b44..88041a684 100644
--- a/glib/tests/gdatetime.c
+++ b/glib/tests/gdatetime.c
@@ -1098,6 +1098,24 @@ test_GDateTime_add_full (void)
TEST_ADD_FULL (2010, 8, 25, 22, 45, 0,
0, 1, 6, 1, 25, 0,
2010, 10, 2, 0, 10, 0);
+
+#define TEST_ADD_FULL_ERROR(y,m,d,h,mi,s,ay,am,ad,ah,ami,as) G_STMT_START { \
+ GDateTime *dt; \
+ dt = g_date_time_new_utc (y, m, d, h, mi, s); \
+ g_assert_null (g_date_time_add_full (dt, ay, am, ad, ah, ami, as)); \
+ g_date_time_unref (dt); \
+} G_STMT_END
+
+ TEST_ADD_FULL_ERROR ( 1, 12, 1, 0, 0, 0,
+ -1, 0, 0, 0, 0, 0);
+ TEST_ADD_FULL_ERROR ( 1, 12, 1, 0, 0, 0,
+ 10000, 0, 0, 0, 0, 0);
+ TEST_ADD_FULL_ERROR ( 9999, 12, 1, 0, 0, 0,
+ -10000, 0, 0, 0, 0, 0);
+ TEST_ADD_FULL_ERROR ( 1, 12, 1, 0, 0, 0,
+ 0, 0, 3660001, 0, 0, 0);
+ TEST_ADD_FULL_ERROR ( 9999, 12, 1, 0, 0, 0,
+ 0, 0, -3660001, 0, 0, 0);
}
static void

View File

@ -1,221 +0,0 @@
From a9a4c378a05723eb164f193d585b6611e4646b65 Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@gnome.org>
Date: Tue, 31 Mar 2026 16:13:57 +0100
Subject: [PATCH] gregex: Fix case changing substitutions with G_REGEX_RAW
In `G_REGEX_RAW` mode, the input string is treated as a byte array
(basically ASCII) rather than a unichar array. Accordingly, the case
changing code for substitutions needs to operate on bytes with
`G_REGEX_RAW`, rather than operating on unichars.
This fixes a potential buffer overflow when trying to do a case change
on a match of a set of bytes which are a truncated multi-byte UTF-8
encoding at the end of the input buffer.
Spotted by linhlhq as #YWH-PGM9867-193. I adapted their reproducer as
the unit test, but implemented the fix in `gregex.c` independently.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
Fixes: #3918
---
glib/gregex.c | 59 ++++++++++++++++++++++++++++++++++------------
glib/tests/regex.c | 53 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 97 insertions(+), 15 deletions(-)
diff --git a/glib/gregex.c b/glib/gregex.c
index a8a35a424..948df3f95 100644
--- a/glib/gregex.c
+++ b/glib/gregex.c
@@ -2655,19 +2655,25 @@ split_replacement (const gchar *replacement,
return g_list_reverse (list);
}
-/* Change the case of c based on change_case. */
-#define CHANGE_CASE(c, change_case) \
+/* Change the case of c based on change_case.
+ * g_ascii_to*() will happily pass through non-ASCII bytes unchanged. */
+#define UTF8_CHANGE_CASE(c, change_case) \
(((change_case) & CHANGE_CASE_LOWER_MASK) ? \
g_unichar_tolower (c) : \
g_unichar_toupper (c))
+#define RAW_CHANGE_CASE(c, change_case) \
+ (((change_case) & CHANGE_CASE_LOWER_MASK) ? \
+ g_ascii_tolower (c) : \
+ g_ascii_toupper (c))
+/* If @text_is_raw is set, @text might not be valid UTF-8 (but will be
+ * nul-terminated). */
static void
string_append (GString *string,
const gchar *text,
+ gboolean text_is_raw,
ChangeCase *change_case)
{
- gunichar c;
-
if (text[0] == '\0')
return;
@@ -2677,22 +2683,44 @@ string_append (GString *string,
}
else if (*change_case & CHANGE_CASE_SINGLE_MASK)
{
- c = g_utf8_get_char (text);
- g_string_append_unichar (string, CHANGE_CASE (c, *change_case));
- g_string_append (string, g_utf8_next_char (text));
+ if (!text_is_raw)
+ {
+ gunichar c = g_utf8_get_char (text);
+ g_string_append_unichar (string, UTF8_CHANGE_CASE (c, *change_case));
+ g_string_append (string, g_utf8_next_char (text));
+ }
+ else
+ {
+ g_string_append_c (string, RAW_CHANGE_CASE (text[0], *change_case));
+ g_string_append (string, text + 1);
+ }
+
*change_case = CHANGE_CASE_NONE;
}
else
{
- while (*text != '\0')
+ if (!text_is_raw)
{
- c = g_utf8_get_char (text);
- g_string_append_unichar (string, CHANGE_CASE (c, *change_case));
- text = g_utf8_next_char (text);
+ while (*text != '\0')
+ {
+ gunichar c = g_utf8_get_char (text);
+ g_string_append_unichar (string, UTF8_CHANGE_CASE (c, *change_case));
+ text = g_utf8_next_char (text);
+ }
+ }
+ else
+ {
+ while (*text != '\0')
+ {
+ char c = *text;
+ g_string_append_c (string, RAW_CHANGE_CASE (c, *change_case));
+ text++;
+ }
}
}
}
+/* @match_info is (nullable) */
static gboolean
interpolate_replacement (const GMatchInfo *match_info,
GString *result,
@@ -2702,6 +2730,7 @@ interpolate_replacement (const GMatchInfo *match_info,
InterpolationData *idata;
gchar *match;
ChangeCase change_case = CHANGE_CASE_NONE;
+ gboolean is_raw = (match_info != NULL && (match_info->regex->compile_opts & G_REGEX_RAW));
for (list = data; list; list = list->next)
{
@@ -2709,10 +2738,10 @@ interpolate_replacement (const GMatchInfo *match_info,
switch (idata->type)
{
case REPL_TYPE_STRING:
- string_append (result, idata->text, &change_case);
+ string_append (result, idata->text, is_raw, &change_case);
break;
case REPL_TYPE_CHARACTER:
- g_string_append_c (result, CHANGE_CASE (idata->c, change_case));
+ g_string_append_c (result, UTF8_CHANGE_CASE (idata->c, change_case));
if (change_case & CHANGE_CASE_SINGLE_MASK)
change_case = CHANGE_CASE_NONE;
break;
@@ -2720,7 +2749,7 @@ interpolate_replacement (const GMatchInfo *match_info,
match = g_match_info_fetch (match_info, idata->num);
if (match)
{
- string_append (result, match, &change_case);
+ string_append (result, match, is_raw, &change_case);
g_free (match);
}
break;
@@ -2728,7 +2757,7 @@ interpolate_replacement (const GMatchInfo *match_info,
match = g_match_info_fetch_named (match_info, idata->text);
if (match)
{
- string_append (result, match, &change_case);
+ string_append (result, match, is_raw, &change_case);
g_free (match);
}
break;
diff --git a/glib/tests/regex.c b/glib/tests/regex.c
index 88d12edf6..818d6d0a0 100644
--- a/glib/tests/regex.c
+++ b/glib/tests/regex.c
@@ -2183,6 +2183,58 @@ pcre_ge (guint64 major, guint64 minor)
return (pcre_major > major) || (pcre_major == major && pcre_minor >= minor);
}
+static void
+test_replace_raw_change_case (void)
+{
+ GError *local_error = NULL;
+ GRegex *regex = NULL;
+
+ g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/3918");
+ g_test_summary ("Test that case changes as part of a replacement are handled correctly in G_REGEX_RAW mode");
+
+ /*
+ * Match a multi-byte sequence in RAW mode. The pattern matches
+ * exactly 2 bytes. The subject contains a 4-byte UTF-8 lead (0xF4)
+ * followed by only one continuation byte, then NUL.
+ *
+ * The matched substring will be "\xf4\x80" (2 bytes, heap-allocated
+ * as 3-byte buffer with NUL). If the code regresses and tries to handle
+ * the replacement as UTF-8 then g_utf8_get_char() would see 0xF4 and try
+ * to read 4 bytes, going 1 byte past the NUL into OOB territory.
+ */
+ regex = g_regex_new ("..", G_REGEX_RAW, 0, &local_error);
+ g_assert_no_error (local_error);
+
+ /*
+ * Build a subject string with truncated UTF-8.
+ * \xF4 = 4-byte UTF-8 lead byte
+ * \x80 = continuation byte
+ * No 3rd/4th continuation bytes — the match is only 2 bytes.
+ *
+ * \U\0 = uppercase the entire match → triggers string_append()
+ * with case change on the 2-byte non-UTF-8 match.
+ */
+ char subject[] = "\xf4\x80";
+ char *result = g_regex_replace (regex, subject, -1, 0, "\\U\\0", 0, &local_error);
+ g_assert_no_error (local_error);
+
+ g_clear_pointer (&result, g_free);
+ g_clear_pointer (&regex, g_regex_unref);
+
+ /*
+ * Second variant: single-char case change \u with \0 backreference.
+ */
+ regex = g_regex_new (".", G_REGEX_RAW, 0, &local_error);
+ g_assert_no_error (local_error);
+
+ char subject2[] = "\xe6\xb0"; /* 3-byte UTF-8 lead, only 2 bytes */
+ result = g_regex_replace (regex, subject2, -1, 0, "\\u\\0", 0, &local_error);
+ g_assert_no_error (local_error);
+
+ g_clear_pointer (&result, g_free);
+ g_clear_pointer (&regex, g_regex_unref);
+}
+
int
main (int argc, char *argv[])
{
@@ -2200,6 +2252,7 @@ main (int argc, char *argv[])
g_test_add_func ("/regex/multiline", test_multiline);
g_test_add_func ("/regex/explicit-crlf", test_explicit_crlf);
g_test_add_func ("/regex/max-lookbehind", test_max_lookbehind);
+ g_test_add_func ("/regex/replace-raw-change-case", test_replace_raw_change_case);
/* TEST_NEW(pattern, compile_opts, match_opts) */
TEST_NEW("[A-Z]+", G_REGEX_CASELESS | G_REGEX_EXTENDED | G_REGEX_OPTIMIZE, G_REGEX_MATCH_NOTBOL | G_REGEX_MATCH_PARTIAL);

View File

@ -1,120 +0,0 @@
From 5e8a44975bddd21a762e58c6717f841e90594f03 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 | 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 ();
}

View File

@ -1,101 +0,0 @@
From 8e49ae633b6eaceff9881ea15c8bb48cf11c8a53 Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@gnome.org>
Date: Sat, 11 Apr 2026 14:42:57 +0100
Subject: [PATCH] gkeyfile: Fix a one-byte heap under-read with
g_key_file_get_locale_string_list()
If this method was called on a key file key which has an empty value,
`len == 0` and this leads to a one-byte under-read off the start of the
key file buffer.
Spotted by linhlhq as #YWH-PGM9867-200. The suggested fix is theirs, and
the unit test is adapted from their report. I added the fuzzing test.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
Fixes: #3930
---
fuzzing/fuzz_key.c | 9 +++++++++
glib/gkeyfile.c | 2 +-
glib/tests/keyfile.c | 23 +++++++++++++++++++++++
3 files changed, 33 insertions(+), 1 deletion(-)
diff --git a/fuzzing/fuzz_key.c b/fuzzing/fuzz_key.c
index 9f1f9187e..285bad26f 100644
--- a/fuzzing/fuzz_key.c
+++ b/fuzzing/fuzz_key.c
@@ -6,11 +6,20 @@ test_parse (const gchar *data,
GKeyFileFlags flags)
{
GKeyFile *key = NULL;
+ char *comment = NULL;
+ char **list = NULL;
key = g_key_file_new ();
g_key_file_load_from_data (key, (const gchar*) data, size, G_KEY_FILE_NONE,
NULL);
+ /* Also try some additional parsing and see if it crashes */
+ comment = g_key_file_get_comment (key, "group", "key", NULL);
+ g_free (comment);
+
+ list = g_key_file_get_locale_string_list (key, "group", "key", "de", NULL, NULL);
+ g_strfreev (list);
+
g_key_file_free (key);
}
diff --git a/glib/gkeyfile.c b/glib/gkeyfile.c
index 17cf85660..76459daab 100644
--- a/glib/gkeyfile.c
+++ b/glib/gkeyfile.c
@@ -2407,7 +2407,7 @@ g_key_file_get_locale_string_list (GKeyFile *key_file,
}
len = strlen (value);
- if (value[len - 1] == key_file->list_separator)
+ if (len > 0 && value[len - 1] == key_file->list_separator)
value[len - 1] = '\0';
list_separator[0] = key_file->list_separator;
diff --git a/glib/tests/keyfile.c b/glib/tests/keyfile.c
index 1f5be8b38..17724bc15 100644
--- a/glib/tests/keyfile.c
+++ b/glib/tests/keyfile.c
@@ -800,6 +800,28 @@ test_locale_string_multiple_loads (void)
g_free (old_locale);
}
+static void
+test_locale_string_empty (void)
+{
+ GKeyFile *keyfile = NULL;
+ GError *local_error = NULL;
+ const char *data =
+ "[valid]\n"
+ "key1=\n";
+
+ g_test_summary ("Check that loading an empty translatable string works");
+ g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/3930");
+
+ keyfile = g_key_file_new ();
+
+ g_key_file_load_from_data (keyfile, data, -1, G_KEY_FILE_NONE, &local_error);
+ g_assert_no_error (local_error);
+
+ check_locale_string_list_value (keyfile, "valid", "key1", NULL, NULL);
+
+ g_key_file_free (keyfile);
+}
+
static void
test_lists (void)
{
@@ -1832,6 +1854,7 @@ main (int argc, char *argv[])
g_test_add_func ("/keyfile/number", test_number);
g_test_add_func ("/keyfile/locale-string", test_locale_string);
g_test_add_func ("/keyfile/locale-string/multiple-loads", test_locale_string_multiple_loads);
+ g_test_add_func ("/keyfile/locale-string/empty", test_locale_string_empty);
g_test_add_func ("/keyfile/lists", test_lists);
g_test_add_func ("/keyfile/lists-set-get", test_lists_set_get);
g_test_add_func ("/keyfile/group-remove", test_group_remove);

View File

@ -1,578 +0,0 @@
From a37a31d4088cf9563756dbbf0c3a7d78e43cb9ee Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@gnome.org>
Date: Tue, 28 Apr 2026 15:47:30 +0100
Subject: [PATCH 1/5] gdbusauthmechanismsha1: Validate cookie context
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Without validation, the server could send a malicious context which
contains path traversal characters, allowing it to exfiltrate a SHA-1
hashed copy of arbitrary data from the clients file system.
To exploit this successfully would require the client to choose to
connect peer-to-peer to a malicious D-Bus server and to choose the SHA-1
authentication mechanism in preference to all the other mechanisms. This
is vanishingly unlikely.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
Fixes: #3931
---
gio/gdbusauthmechanismsha1.c | 36 ++++++++++++++++++++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/gio/gdbusauthmechanismsha1.c b/gio/gdbusauthmechanismsha1.c
index 94fe0bce8..d2a045f4c 100644
--- a/gio/gdbusauthmechanismsha1.c
+++ b/gio/gdbusauthmechanismsha1.c
@@ -1160,6 +1160,34 @@ mechanism_client_initiate (GDBusAuthMechanism *mechanism,
return initial_response;
}
+/* Context names must be valid ASCII, nonzero length, and may not contain the
+ * characters slash ("/"), backslash ("\"), space (" "), newline ("\n"),
+ * carriage return ("\r"), tab ("\t"), or period (".").
+ *
+ * See https://dbus.freedesktop.org/doc/dbus-specification.html#auth-mechanisms-sha */
+static gboolean
+validate_cookie_context (const char *cookie_context)
+{
+ size_t i = 0;
+
+ g_return_val_if_fail (cookie_context != NULL, FALSE);
+
+ for (i = 0; cookie_context[i] != '\0'; i++)
+ {
+ if ((uint8_t) cookie_context[i] >= 128 ||
+ cookie_context[i] == '/' ||
+ cookie_context[i] == '\\' ||
+ cookie_context[i] == ' ' ||
+ cookie_context[i] == '\n' ||
+ cookie_context[i] == '\r' ||
+ cookie_context[i] == '\t' ||
+ cookie_context[i] == '.')
+ return FALSE;
+ }
+
+ return (i > 0);
+}
+
static void
mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
const gchar *data,
@@ -1194,6 +1222,14 @@ mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
}
cookie_context = tokens[0];
+ if (!validate_cookie_context (tokens[0]))
+ {
+ g_free (m->priv->reject_reason);
+ m->priv->reject_reason = g_strdup_printf ("Malformed cookie_context '%s'", tokens[0]);
+ m->priv->state = G_DBUS_AUTH_MECHANISM_STATE_REJECTED;
+ goto out;
+ }
+
cookie_id = g_ascii_strtoll (tokens[1], &endp, 10);
if (*endp != '\0')
{
From 5bf53b50de87f0ad037508e4bd0099c4d0519b4b Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@gnome.org>
Date: Tue, 28 Apr 2026 15:49:54 +0100
Subject: [PATCH 2/5] gdbusauthmechanismsha1: Improve validation of cookie ID
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The D-Bus specification says the cookie ID has to be non-negative, but
we werent checking that (or checking that it was non-empty).
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
---
gio/gdbusauthmechanismsha1.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/gio/gdbusauthmechanismsha1.c b/gio/gdbusauthmechanismsha1.c
index d2a045f4c..9f2d3ded6 100644
--- a/gio/gdbusauthmechanismsha1.c
+++ b/gio/gdbusauthmechanismsha1.c
@@ -1196,7 +1196,7 @@ mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
gchar **tokens;
const gchar *cookie_context;
- guint cookie_id;
+ int64_t cookie_id;
const gchar *server_challenge;
gchar *client_challenge;
gchar *endp;
@@ -1231,7 +1231,7 @@ mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
}
cookie_id = g_ascii_strtoll (tokens[1], &endp, 10);
- if (*endp != '\0')
+ if (*endp != '\0' || endp == tokens[1] || cookie_id < 0 || cookie_id > UINT32_MAX)
{
g_free (m->priv->reject_reason);
m->priv->reject_reason = g_strdup_printf ("Malformed cookie_id '%s'", tokens[1]);
@@ -1241,7 +1241,7 @@ mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
server_challenge = tokens[2];
error = NULL;
- cookie = keyring_lookup_entry (cookie_context, cookie_id, &error);
+ cookie = keyring_lookup_entry (cookie_context, (unsigned int) cookie_id, &error);
if (cookie == NULL)
{
g_free (m->priv->reject_reason);
From 2b185a1b6ff0fca50e3bf0032e81a09a4dbfd550 Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@gnome.org>
Date: Tue, 28 Apr 2026 15:51:00 +0100
Subject: [PATCH 3/5] gdbusauthmechanism: Expose client reject reason as a new
vfunc
We can do this because `gdbusauthmechanism.h` is a private header.
Hook it up to the existing `reject_reason` code in each
`GDBusAuthMechanism` implementation, as all three implementations
currently intermingle reject reasons from the server and client code, so
there would currently be no benefit to having a separate server and
client implementation of `*_get_reject_reason()`.
This new private API will be used in a new unit test in the following
commit.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
---
gio/gdbusauthmechanism.c | 7 +++++++
gio/gdbusauthmechanism.h | 2 ++
gio/gdbusauthmechanismanon.c | 8 ++++----
gio/gdbusauthmechanismexternal.c | 8 ++++----
gio/gdbusauthmechanismsha1.c | 8 ++++----
5 files changed, 21 insertions(+), 12 deletions(-)
diff --git a/gio/gdbusauthmechanism.c b/gio/gdbusauthmechanism.c
index 897d41496..6ed7fb546 100644
--- a/gio/gdbusauthmechanism.c
+++ b/gio/gdbusauthmechanism.c
@@ -324,6 +324,13 @@ _g_dbus_auth_mechanism_client_data_send (GDBusAuthMechanism *mechanism,
return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_data_send (mechanism, out_data_len);
}
+gchar *
+_g_dbus_auth_mechanism_client_get_reject_reason (GDBusAuthMechanism *mechanism)
+{
+ g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM (mechanism), NULL);
+ return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_get_reject_reason (mechanism);
+}
+
void
_g_dbus_auth_mechanism_client_shutdown (GDBusAuthMechanism *mechanism)
{
diff --git a/gio/gdbusauthmechanism.h b/gio/gdbusauthmechanism.h
index cee87b0a2..5ab3e34c9 100644
--- a/gio/gdbusauthmechanism.h
+++ b/gio/gdbusauthmechanism.h
@@ -97,6 +97,7 @@ struct _GDBusAuthMechanismClass
gsize data_len);
gchar *(*client_data_send) (GDBusAuthMechanism *mechanism,
gsize *out_data_len);
+ gchar *(*client_get_reject_reason) (GDBusAuthMechanism *mechanism);
void (*client_shutdown) (GDBusAuthMechanism *mechanism);
};
@@ -144,6 +145,7 @@ void _g_dbus_auth_mechanism_client_data_receive (GDBus
gsize data_len);
gchar *_g_dbus_auth_mechanism_client_data_send (GDBusAuthMechanism *mechanism,
gsize *out_data_len);
+gchar *_g_dbus_auth_mechanism_client_get_reject_reason (GDBusAuthMechanism *mechanism);
void _g_dbus_auth_mechanism_client_shutdown (GDBusAuthMechanism *mechanism);
diff --git a/gio/gdbusauthmechanismanon.c b/gio/gdbusauthmechanismanon.c
index dd57826ff..ab947bba6 100644
--- a/gio/gdbusauthmechanismanon.c
+++ b/gio/gdbusauthmechanismanon.c
@@ -54,7 +54,7 @@ static void mechanism_server_data_receive (GDBusAuthMe
gsize data_len);
static gchar *mechanism_server_data_send (GDBusAuthMechanism *mechanism,
gsize *out_data_len);
-static gchar *mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism);
+static gchar *mechanism_server_or_client_get_reject_reason (GDBusAuthMechanism *mechanism);
static void mechanism_server_shutdown (GDBusAuthMechanism *mechanism);
static GDBusAuthMechanismState mechanism_client_get_state (GDBusAuthMechanism *mechanism);
static gchar *mechanism_client_initiate (GDBusAuthMechanism *mechanism,
@@ -100,12 +100,13 @@ _g_dbus_auth_mechanism_anon_class_init (GDBusAuthMechanismAnonClass *klass)
mechanism_class->server_initiate = mechanism_server_initiate;
mechanism_class->server_data_receive = mechanism_server_data_receive;
mechanism_class->server_data_send = mechanism_server_data_send;
- mechanism_class->server_get_reject_reason = mechanism_server_get_reject_reason;
+ mechanism_class->server_get_reject_reason = mechanism_server_or_client_get_reject_reason;
mechanism_class->server_shutdown = mechanism_server_shutdown;
mechanism_class->client_get_state = mechanism_client_get_state;
mechanism_class->client_initiate = mechanism_client_initiate;
mechanism_class->client_data_receive = mechanism_client_data_receive;
mechanism_class->client_data_send = mechanism_client_data_send;
+ mechanism_class->client_get_reject_reason = mechanism_server_or_client_get_reject_reason;
mechanism_class->client_shutdown = mechanism_client_shutdown;
}
@@ -219,12 +220,11 @@ mechanism_server_data_send (GDBusAuthMechanism *mechanism,
}
static gchar *
-mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism)
+mechanism_server_or_client_get_reject_reason (GDBusAuthMechanism *mechanism)
{
GDBusAuthMechanismAnon *m = G_DBUS_AUTH_MECHANISM_ANON (mechanism);
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_ANON (mechanism), NULL);
- g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_REJECTED, NULL);
/* can never end up here because we are never in the REJECTED state */
diff --git a/gio/gdbusauthmechanismexternal.c b/gio/gdbusauthmechanismexternal.c
index 182c57278..30adfc415 100644
--- a/gio/gdbusauthmechanismexternal.c
+++ b/gio/gdbusauthmechanismexternal.c
@@ -57,7 +57,7 @@ static void mechanism_server_data_receive (GDBusAuthMe
gsize data_len);
static gchar *mechanism_server_data_send (GDBusAuthMechanism *mechanism,
gsize *out_data_len);
-static gchar *mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism);
+static gchar *mechanism_server_or_client_get_reject_reason (GDBusAuthMechanism *mechanism);
static void mechanism_server_shutdown (GDBusAuthMechanism *mechanism);
static GDBusAuthMechanismState mechanism_client_get_state (GDBusAuthMechanism *mechanism);
static gchar *mechanism_client_initiate (GDBusAuthMechanism *mechanism,
@@ -103,12 +103,13 @@ _g_dbus_auth_mechanism_external_class_init (GDBusAuthMechanismExternalClass *kla
mechanism_class->server_initiate = mechanism_server_initiate;
mechanism_class->server_data_receive = mechanism_server_data_receive;
mechanism_class->server_data_send = mechanism_server_data_send;
- mechanism_class->server_get_reject_reason = mechanism_server_get_reject_reason;
+ mechanism_class->server_get_reject_reason = mechanism_server_or_client_get_reject_reason;
mechanism_class->server_shutdown = mechanism_server_shutdown;
mechanism_class->client_get_state = mechanism_client_get_state;
mechanism_class->client_initiate = mechanism_client_initiate;
mechanism_class->client_data_receive = mechanism_client_data_receive;
mechanism_class->client_data_send = mechanism_client_data_send;
+ mechanism_class->client_get_reject_reason = mechanism_server_or_client_get_reject_reason;
mechanism_class->client_shutdown = mechanism_client_shutdown;
}
@@ -285,12 +286,11 @@ mechanism_server_data_send (GDBusAuthMechanism *mechanism,
}
static gchar *
-mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism)
+mechanism_server_or_client_get_reject_reason (GDBusAuthMechanism *mechanism)
{
GDBusAuthMechanismExternal *m = G_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism);
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_EXTERNAL (mechanism), NULL);
- g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_REJECTED, NULL);
/* can never end up here because we are never in the REJECTED state */
diff --git a/gio/gdbusauthmechanismsha1.c b/gio/gdbusauthmechanismsha1.c
index 9f2d3ded6..d74f70387 100644
--- a/gio/gdbusauthmechanismsha1.c
+++ b/gio/gdbusauthmechanismsha1.c
@@ -111,7 +111,7 @@ static void mechanism_server_data_receive (GDBusAuthMe
gsize data_len);
static gchar *mechanism_server_data_send (GDBusAuthMechanism *mechanism,
gsize *out_data_len);
-static gchar *mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism);
+static gchar *mechanism_server_or_client_get_reject_reason (GDBusAuthMechanism *mechanism);
static void mechanism_server_shutdown (GDBusAuthMechanism *mechanism);
static GDBusAuthMechanismState mechanism_client_get_state (GDBusAuthMechanism *mechanism);
static gchar *mechanism_client_initiate (GDBusAuthMechanism *mechanism,
@@ -163,12 +163,13 @@ _g_dbus_auth_mechanism_sha1_class_init (GDBusAuthMechanismSha1Class *klass)
mechanism_class->server_initiate = mechanism_server_initiate;
mechanism_class->server_data_receive = mechanism_server_data_receive;
mechanism_class->server_data_send = mechanism_server_data_send;
- mechanism_class->server_get_reject_reason = mechanism_server_get_reject_reason;
+ mechanism_class->server_get_reject_reason = mechanism_server_or_client_get_reject_reason;
mechanism_class->server_shutdown = mechanism_server_shutdown;
mechanism_class->client_get_state = mechanism_client_get_state;
mechanism_class->client_initiate = mechanism_client_initiate;
mechanism_class->client_data_receive = mechanism_client_data_receive;
mechanism_class->client_data_send = mechanism_client_data_send;
+ mechanism_class->client_get_reject_reason = mechanism_server_or_client_get_reject_reason;
mechanism_class->client_shutdown = mechanism_client_shutdown;
}
@@ -1096,12 +1097,11 @@ mechanism_server_data_send (GDBusAuthMechanism *mechanism,
}
static gchar *
-mechanism_server_get_reject_reason (GDBusAuthMechanism *mechanism)
+mechanism_server_or_client_get_reject_reason (GDBusAuthMechanism *mechanism)
{
GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
g_return_val_if_fail (G_IS_DBUS_AUTH_MECHANISM_SHA1 (mechanism), NULL);
- g_return_val_if_fail (m->priv->is_server && !m->priv->is_client, NULL);
g_return_val_if_fail (m->priv->state == G_DBUS_AUTH_MECHANISM_STATE_REJECTED, NULL);
return g_strdup (m->priv->reject_reason);
From 0c526648c0346fd7287941817641d235ddf4998a Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@gnome.org>
Date: Tue, 28 Apr 2026 15:52:53 +0100
Subject: [PATCH 4/5] tests: Add a unit test for GDBusAuthMechanismSha1 cookie
context parsing
This checks for regressions in the fixes from the previous few commits.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
Helps: #3931
---
gio/gdbusauthmechanismsha1.c | 2 +-
gio/tests/gdbus-auth-mechanism-sha1.c | 177 ++++++++++++++++++++++++++
gio/tests/meson.build | 1 +
3 files changed, 179 insertions(+), 1 deletion(-)
create mode 100644 gio/tests/gdbus-auth-mechanism-sha1.c
diff --git a/gio/gdbusauthmechanismsha1.c b/gio/gdbusauthmechanismsha1.c
index d74f70387..cb1a4f828 100644
--- a/gio/gdbusauthmechanismsha1.c
+++ b/gio/gdbusauthmechanismsha1.c
@@ -1174,7 +1174,7 @@ validate_cookie_context (const char *cookie_context)
for (i = 0; cookie_context[i] != '\0'; i++)
{
- if ((uint8_t) cookie_context[i] >= 128 ||
+ if ((guint8) cookie_context[i] >= 128 ||
cookie_context[i] == '/' ||
cookie_context[i] == '\\' ||
cookie_context[i] == ' ' ||
diff --git a/gio/tests/gdbus-auth-mechanism-sha1.c b/gio/tests/gdbus-auth-mechanism-sha1.c
new file mode 100644
index 000000000..abcdb4e3e
--- /dev/null
+++ b/gio/tests/gdbus-auth-mechanism-sha1.c
@@ -0,0 +1,177 @@
+/* GLib testing framework examples and tests
+ *
+ * Copyright (C) 2026 Philip Withnall
+ *
+ * SPDX-License-Identifier: LGPL-2.1-or-later
+ *
+ * This 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.
+ *
+ * This 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 this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Philip Withnall <pwithnall@gnome.org>
+ */
+
+#include <locale.h>
+#include <gio/gio.h>
+
+#include <string.h>
+#include <unistd.h>
+
+#include "gdbus-tests.h"
+
+#ifdef G_OS_UNIX
+#include <gio/gunixconnection.h>
+#include <gio/gnetworkingprivate.h>
+#include <gio/gunixsocketaddress.h>
+#include <gio/gunixfdlist.h>
+#endif
+
+#define GIO_COMPILATION 1
+#include "gdbusauthmechanism.h"
+#include "gdbusauthmechanismsha1.h"
+
+/* Vfunc wrappers copied from gdbusauthmechanism.c as they are not public. */
+static gboolean
+dbus_auth_mechanism_is_supported (GDBusAuthMechanism *mechanism)
+{
+ return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->is_supported (mechanism);
+}
+
+static GDBusAuthMechanismState
+dbus_auth_mechanism_client_get_state (GDBusAuthMechanism *mechanism)
+{
+ return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_get_state (mechanism);
+}
+
+static gchar *
+dbus_auth_mechanism_client_initiate (GDBusAuthMechanism *mechanism,
+ GDBusConnectionFlags conn_flags,
+ size_t *out_initial_response_len)
+{
+ return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_initiate (mechanism,
+ conn_flags,
+ out_initial_response_len);
+}
+
+static void
+dbus_auth_mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
+ const char *data,
+ size_t data_len)
+{
+ G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_data_receive (mechanism, data, data_len);
+}
+
+static char *
+dbus_auth_mechanism_client_get_reject_reason (GDBusAuthMechanism *mechanism)
+{
+ return G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_get_reject_reason (mechanism);
+}
+
+static void
+dbus_auth_mechanism_client_shutdown (GDBusAuthMechanism *mechanism)
+{
+ G_DBUS_AUTH_MECHANISM_GET_CLASS (mechanism)->client_shutdown (mechanism);
+}
+
+static void
+test_server_challenge_validation (void)
+{
+ const struct
+ {
+ const char *server_challenge;
+ const char *expected_reject_reason_prefix;
+ }
+ vectors[] = {
+ { "valid_context 123 456", "Problems looking up entry in keyring" },
+ { "invalid/context 123 456", "Malformed cookie_context" },
+ { "invalid.context 123 456", "Malformed cookie_context" },
+ { " 123 456", "Malformed cookie_context" },
+ { "😀 123 456", "Malformed cookie_context" },
+ { "invalid\ncontext 123 456", "Malformed cookie_context" },
+ { "invalid\rcontext 123 456", "Malformed cookie_context" },
+ { "invalid\tcontext 123 456", "Malformed cookie_context" },
+ { "invalid\\context 123 456", "Malformed cookie_context" },
+ { "valid_context 456", "Malformed cookie_id" },
+ { "valid_context 123notanumber 456", "Malformed cookie_id" },
+ { "valid_context -1 456", "Malformed cookie_id" },
+ { "valid_context 4294967296 456", "Malformed cookie_id" },
+ { "valid_context 123 ", "Malformed data" },
+ { "valid_context ", "Malformed data" },
+ };
+ GType mechanism_type;
+ GDBusConnection *connection = NULL;
+
+ g_test_summary ("Test that GDBusAuthMechanismSha1 rejects various malformed server data lines");
+
+ /* Briefly connect to the actual bus to ensure the GDBusAuth mechanisms are
+ * all registered. */
+ session_bus_up ();
+
+ connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL);
+ g_assert_nonnull (connection);
+ g_clear_object (&connection);
+
+ session_bus_down ();
+
+ /* Check that we now have the type ID for GDBusAuthMechanismSha1 */
+ mechanism_type = g_type_from_name ("GDBusAuthMechanismSha1");
+ g_assert_cmpint (mechanism_type, !=, 0);
+
+ for (size_t i = 0; i < G_N_ELEMENTS (vectors); i++)
+ {
+ GDBusAuthMechanism *mechanism = NULL;
+ char *data = NULL;
+ size_t data_len = 0;
+ char *reject_reason = NULL;
+
+ mechanism = g_object_new (mechanism_type, NULL);
+
+ if (!dbus_auth_mechanism_is_supported (mechanism))
+ {
+ g_test_skip ("Mechanism not supported");
+ g_clear_object (&mechanism);
+ return;
+ }
+
+ data = dbus_auth_mechanism_client_initiate (mechanism,
+ G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT,
+ &data_len);
+ g_free (data);
+
+ dbus_auth_mechanism_client_data_receive (mechanism, vectors[i].server_challenge, strlen (vectors[i].server_challenge));
+
+ g_assert_cmpint (dbus_auth_mechanism_client_get_state (mechanism), ==, G_DBUS_AUTH_MECHANISM_STATE_REJECTED);
+
+ reject_reason = dbus_auth_mechanism_client_get_reject_reason (mechanism);
+ g_assert_true (g_str_has_prefix (reject_reason, vectors[i].expected_reject_reason_prefix));
+ g_free (reject_reason);
+
+ dbus_auth_mechanism_client_shutdown (mechanism);
+
+ g_clear_object (&mechanism);
+ }
+}
+
+int
+main (int argc,
+ char *argv[])
+{
+ setlocale (LC_ALL, "C");
+
+ g_test_init (&argc, &argv, G_TEST_OPTION_ISOLATE_DIRS, NULL);
+
+ g_test_dbus_unset ();
+
+ g_test_add_func ("/gdbus/auth-mechanism-sha1/server-challenge-validation", test_server_challenge_validation);
+
+ return g_test_run ();
+}
diff --git a/gio/tests/meson.build b/gio/tests/meson.build
index 5dbfb8e60..c5380c16a 100644
--- a/gio/tests/meson.build
+++ b/gio/tests/meson.build
@@ -294,6 +294,7 @@ if host_machine.system() != 'windows'
'suite' : ['slow'],
},
'gdbus-auth' : {'extra_sources' : extra_sources},
+ 'gdbus-auth-mechanism-sha1': {'extra_sources' : extra_sources},
'gdbus-bz627724' : {'extra_sources' : extra_sources},
'gdbus-close-pending' : {'extra_sources' : extra_sources},
'gdbus-connection' : {'extra_sources' : extra_sources},
From 8c11c462af6ca9338e95ed3e4ce78d59df1c7474 Mon Sep 17 00:00:00 2001
From: RHEL Packaging Agent <redhat-ymir-agent@redhat.com>
Date: Mon, 20 Jul 2026 07:10:32 +0000
Subject: [PATCH 5/5] Fix: use GLib types instead of stdint.h types for
portability
Replace int64_t with gint64 and UINT32_MAX with G_MAXUINT32 to avoid
dependency on <stdint.h> which may not be included in older GLib builds.
---
gio/gdbusauthmechanismsha1.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/gio/gdbusauthmechanismsha1.c b/gio/gdbusauthmechanismsha1.c
index cb1a4f828..007c6b901 100644
--- a/gio/gdbusauthmechanismsha1.c
+++ b/gio/gdbusauthmechanismsha1.c
@@ -1196,7 +1196,7 @@ mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
GDBusAuthMechanismSha1 *m = G_DBUS_AUTH_MECHANISM_SHA1 (mechanism);
gchar **tokens;
const gchar *cookie_context;
- int64_t cookie_id;
+ gint64 cookie_id;
const gchar *server_challenge;
gchar *client_challenge;
gchar *endp;
@@ -1231,7 +1231,7 @@ mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
}
cookie_id = g_ascii_strtoll (tokens[1], &endp, 10);
- if (*endp != '\0' || endp == tokens[1] || cookie_id < 0 || cookie_id > UINT32_MAX)
+ if (*endp != '\0' || endp == tokens[1] || cookie_id < 0 || cookie_id > G_MAXUINT32)
{
g_free (m->priv->reject_reason);
m->priv->reject_reason = g_strdup_printf ("Malformed cookie_id '%s'", tokens[1]);

View File

@ -1,87 +0,0 @@
From 0cf6b5ef06f44a4b259a014870cdc02780cc8ba4 Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@gnome.org>
Date: Thu, 16 Apr 2026 15:27:37 +0100
Subject: [PATCH] gdbusintrospection: Fix XML parser state handling for <node>
element nesting
The check for whether a `<node>` element in D-Bus introspection XML was
nested correctly was broken. `<node>` elements can only be at the top
level, or nested immediately within another `<node>` element.
Fix the check and add some unit tests for it.
Spotted by linhlhq as #YWH-PGM9867-204. The fix is mine, and the unit test
uses example XML strings adapted from their report.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
Fixes: #3932
---
gio/gdbusintrospection.c | 2 +-
gio/tests/gdbus-introspection.c | 33 +++++++++++++++++++++++++++++++++
2 files changed, 34 insertions(+), 1 deletion(-)
diff --git a/gio/gdbusintrospection.c b/gio/gdbusintrospection.c
index d6aa445d5..1e0cb81de 100644
--- a/gio/gdbusintrospection.c
+++ b/gio/gdbusintrospection.c
@@ -1270,7 +1270,7 @@ parser_start_element (GMarkupParseContext *context,
/* ---------------------------------------------------------------------------------------------------- */
if (strcmp (element_name, "node") == 0)
{
- if (!(g_slist_length (stack) >= 1 || strcmp (stack->next->data, "node") != 0))
+ if (stack->next != NULL && strcmp (stack->next->data, "node") != 0)
{
g_set_error_literal (error,
G_MARKUP_ERROR,
diff --git a/gio/tests/gdbus-introspection.c b/gio/tests/gdbus-introspection.c
index 50c0cc721..e2cdf0a25 100644
--- a/gio/tests/gdbus-introspection.c
+++ b/gio/tests/gdbus-introspection.c
@@ -297,6 +297,38 @@ test_extra_data (void)
g_dbus_node_info_unref (info);
}
+static void
+test_invalid (void)
+{
+ const struct
+ {
+ const char *xml;
+ GMarkupError expected_error_code;
+ }
+ vectors[] =
+ {
+ { "", G_MARKUP_ERROR_EMPTY },
+ { "<node><interface name=\"I\"><method name=\"M\"><node><interface name=\"I2\"></interface></node></method>", G_MARKUP_ERROR_INVALID_CONTENT },
+ { "<node><interface name=\"I\"><signal name=\"S\"><node><interface name=\"I2\"><signal name=\"S2\"></signal></interface></node></signal>", G_MARKUP_ERROR_INVALID_CONTENT },
+ { "<node><interface name=\"I\"><property name=\"P\" type=\"s\" access=\"read\"><node><interface name=\"I2\"></interface></node></property>", G_MARKUP_ERROR_INVALID_CONTENT },
+ { "<node><interface name=\"I\"><method name=\"M\"><arg type=\"\"><node><interface name=\"I2\"><method name=\"M2\"></method></interface></node></arg>", G_MARKUP_ERROR_INVALID_CONTENT },
+ };
+
+ for (size_t i = 0; i < G_N_ELEMENTS (vectors); i++)
+ {
+ GDBusNodeInfo *node;
+ GError *local_error = NULL;
+
+ g_test_message ("Testing parsing of %s gives an error", vectors[i].xml);
+
+ node = g_dbus_node_info_new_for_xml (vectors[i].xml, &local_error);
+ g_assert_error (local_error, G_MARKUP_ERROR, (int) vectors[i].expected_error_code);
+ g_assert_null (node);
+
+ g_clear_error (&local_error);
+ }
+}
+
/* ---------------------------------------------------------------------------------------------------- */
int
@@ -314,6 +346,7 @@ main (int argc,
g_test_add_func ("/gdbus/introspection-generate", test_generate);
g_test_add_func ("/gdbus/introspection-default-direction", test_default_direction);
g_test_add_func ("/gdbus/introspection-extra-data", test_extra_data);
+ g_test_add_func ("/gdbus/introspection-invalid", test_invalid);
ret = session_bus_run ();

View File

@ -1 +0,0 @@
SHA512 (glib-2.70.1.tar.xz) = 639317c98ab72ad853608ab4d395484daff135c0222556c51ca93fd8533c5759db14478beda964e4feb02bb2737a46a4eda25063f98a9c6ba6ae4bc5d74bf5e1