Compare commits

...

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

21 changed files with 1 additions and 7636 deletions

View File

@ -1,83 +0,0 @@
From d9fec76b594fccc6eda3ce04a74beae1c8b8c1d2 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Fri, 12 Jul 2024 11:14:10 +0200
Subject: [PATCH] gfile: Add support for x-gvfs-trash mount option
Currently, the trash functionality is disabled for system internal mounts.
That might be a problem in some cases. The `x-gvfs-notrash` mount option
allows disabling the trash functionality for certain mounts. Let's add
support for the `x-gvfs-trash` mount option to allow the opposite.
See: https://issues.redhat.com/browse/RHEL-46828
---
gio/gfile.c | 7 +++++--
gio/glocalfile.c | 22 +++++++++++++---------
2 files changed, 18 insertions(+), 11 deletions(-)
diff --git a/gio/gfile.c b/gio/gfile.c
index 4f9b9c6750..5ac73c03e8 100644
--- a/gio/gfile.c
+++ b/gio/gfile.c
@@ -4744,10 +4744,13 @@ g_file_delete_finish (GFile *file,
*
* Sends @file to the "Trashcan", if possible. This is similar to
* deleting it, but the user can recover it before emptying the trashcan.
- * Not all file systems support trashing, so this call can return the
+ * Trashing is disabled for system mounts by default (see
+ * g_unix_mount_is_system_internal()), so this call can return the
* %G_IO_ERROR_NOT_SUPPORTED error. Since GLib 2.66, the `x-gvfs-notrash` unix
- * mount option can be used to disable g_file_trash() support for certain
+ * mount option can be used to disable g_file_trash() support for particular
* mounts, the %G_IO_ERROR_NOT_SUPPORTED error will be returned in that case.
+ * Since 2.82, the `x-gvfs-trash` unix mount option can be used to enable
+ * g_file_trash() support for particular system mounts.
*
* If @cancellable is not %NULL, then the operation can be cancelled by
* triggering the cancellable object from another thread. If the operation
diff --git a/gio/glocalfile.c b/gio/glocalfile.c
index 7b70c614c6..ac918d25e3 100644
--- a/gio/glocalfile.c
+++ b/gio/glocalfile.c
@@ -1807,10 +1807,6 @@ ignore_trash_mount (GUnixMountEntry *mount)
{
GUnixMountPoint *mount_point = NULL;
const gchar *mount_options;
- gboolean retval = TRUE;
-
- if (g_unix_mount_is_system_internal (mount))
- return TRUE;
mount_options = g_unix_mount_get_options (mount);
if (mount_options == NULL)
@@ -1819,15 +1815,23 @@ ignore_trash_mount (GUnixMountEntry *mount)
NULL);
if (mount_point != NULL)
mount_options = g_unix_mount_point_get_options (mount_point);
+
+ g_clear_pointer (&mount_point, g_unix_mount_point_free);
}
- if (mount_options == NULL ||
- strstr (mount_options, "x-gvfs-notrash") == NULL)
- retval = FALSE;
+ if (mount_options != NULL)
+ {
+ if (strstr (mount_options, "x-gvfs-trash") != NULL)
+ return FALSE;
+
+ if (strstr (mount_options, "x-gvfs-notrash") != NULL)
+ return TRUE;
+ }
- g_clear_pointer (&mount_point, g_unix_mount_point_free);
+ if (g_unix_mount_is_system_internal (mount))
+ return TRUE;
- return retval;
+ return FALSE;
}
static gboolean
--
GitLab

File diff suppressed because it is too large Load Diff

View File

@ -1,45 +0,0 @@
From 25833cefda24c60af913d6f2d532b5afd608b821 Mon Sep 17 00:00:00 2001
From: Michael Catanzaro <mcatanzaro@redhat.com>
Date: Thu, 19 Sep 2024 18:35:53 +0100
Subject: [PATCH] gsocks4aproxy: Fix a single byte buffer overflow in connect
messages
`SOCKS4_CONN_MSG_LEN` failed to account for the length of the final nul
byte in the connect message, which is an addition in SOCKSv4a vs
SOCKSv4.
This means that the buffer for building and transmitting the connect
message could be overflowed if the username and hostname are both
`SOCKS4_MAX_LEN` (255) bytes long.
Proxy configurations are normally statically configured, so the username
is very unlikely to be near its maximum length, and hence this overflow
is unlikely to be triggered in practice.
(Commit message by Philip Withnall, diagnosis and fix by Michael
Catanzaro.)
Fixes: #3461
---
gio/gsocks4aproxy.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/gio/gsocks4aproxy.c b/gio/gsocks4aproxy.c
index 3dad118eb7..b3146d08fd 100644
--- a/gio/gsocks4aproxy.c
+++ b/gio/gsocks4aproxy.c
@@ -79,9 +79,9 @@ g_socks4a_proxy_init (GSocks4aProxy *proxy)
* +----+----+----+----+----+----+----+----+----+----+....+----+------+....+------+
* | VN | CD | DSTPORT | DSTIP | USERID |NULL| HOST | | NULL |
* +----+----+----+----+----+----+----+----+----+----+....+----+------+....+------+
- * 1 1 2 4 variable 1 variable
+ * 1 1 2 4 variable 1 variable 1
*/
-#define SOCKS4_CONN_MSG_LEN (9 + SOCKS4_MAX_LEN * 2)
+#define SOCKS4_CONN_MSG_LEN (10 + SOCKS4_MAX_LEN * 2)
static gint
set_connect_msg (guint8 *msg,
const gchar *hostname,
--
GitLab

View File

@ -1,124 +0,0 @@
From 115b2b74d93ba1c61d842baba3612938a5bfb759 Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@gnome.org>
Date: Thu, 13 Nov 2025 18:27:22 +0000
Subject: [PATCH] gconvert: Error out if g_escape_uri_string() would overflow
If the string to escape contains a very large number of unacceptable
characters (which would need escaping), the calculation of the length of
the escaped string could overflow, leading to a potential write off the
end of the newly allocated string.
In addition to that, the number of unacceptable characters was counted
in a signed integer, which would overflow to become negative, making it
easier for an attacker to craft an input string which would cause an
out-of-bounds write.
Fix that by validating the allocation length, and using an unsigned
integer to count the number of unacceptable characters.
Spotted by treeplus. Thanks to the Sovereign Tech Resilience programme
from the Sovereign Tech Agency. ID: #YWH-PGM9867-134
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
Fixes: #3827
---
glib/gconvert.c | 36 +++++++++++++++++++++++++-----------
1 file changed, 25 insertions(+), 11 deletions(-)
diff --git a/glib/gconvert.c b/glib/gconvert.c
index 50286478c..066c391d6 100644
--- a/glib/gconvert.c
+++ b/glib/gconvert.c
@@ -1386,8 +1386,9 @@ static const gchar hex[16] = "0123456789ABCDEF";
/* Note: This escape function works on file: URIs, but if you want to
* escape something else, please read RFC-2396 */
static gchar *
-g_escape_uri_string (const gchar *string,
- UnsafeCharacterSet mask)
+g_escape_uri_string (const gchar *string,
+ UnsafeCharacterSet mask,
+ GError **error)
{
#define ACCEPTABLE(a) ((a)>=32 && (a)<128 && (acceptable[(a)-32] & use_mask))
@@ -1395,7 +1396,7 @@ g_escape_uri_string (const gchar *string,
gchar *q;
gchar *result;
int c;
- gint unacceptable;
+ size_t unacceptable;
UnsafeCharacterSet use_mask;
g_return_val_if_fail (mask == UNSAFE_ALL
@@ -1412,7 +1413,14 @@ g_escape_uri_string (const gchar *string,
if (!ACCEPTABLE (c))
unacceptable++;
}
-
+
+ if (unacceptable >= (G_MAXSIZE - (p - string)) / 2)
+ {
+ g_set_error_literal (error, G_CONVERT_ERROR, G_CONVERT_ERROR_BAD_URI,
+ _("The URI is too long"));
+ return NULL;
+ }
+
result = g_malloc (p - string + unacceptable * 2 + 1);
use_mask = mask;
@@ -1437,12 +1445,13 @@ g_escape_uri_string (const gchar *string,
static gchar *
-g_escape_file_uri (const gchar *hostname,
- const gchar *pathname)
+g_escape_file_uri (const gchar *hostname,
+ const gchar *pathname,
+ GError **error)
{
char *escaped_hostname = NULL;
- char *escaped_path;
- char *res;
+ char *escaped_path = NULL;
+ char *res = NULL;
#ifdef G_OS_WIN32
char *p, *backslash;
@@ -1463,10 +1472,14 @@ g_escape_file_uri (const gchar *hostname,
if (hostname && *hostname != '\0')
{
- escaped_hostname = g_escape_uri_string (hostname, UNSAFE_HOST);
+ escaped_hostname = g_escape_uri_string (hostname, UNSAFE_HOST, error);
+ if (escaped_hostname == NULL)
+ goto out;
}
- escaped_path = g_escape_uri_string (pathname, UNSAFE_PATH);
+ escaped_path = g_escape_uri_string (pathname, UNSAFE_PATH, error);
+ if (escaped_path == NULL)
+ goto out;
res = g_strconcat ("file://",
(escaped_hostname) ? escaped_hostname : "",
@@ -1474,6 +1487,7 @@ g_escape_file_uri (const gchar *hostname,
escaped_path,
NULL);
+out:
#ifdef G_OS_WIN32
g_free ((char *) pathname);
#endif
@@ -1793,7 +1807,7 @@ g_filename_to_uri (const gchar *filename,
hostname = NULL;
#endif
- escaped_uri = g_escape_file_uri (hostname, filename);
+ escaped_uri = g_escape_file_uri (hostname, filename, error);
return escaped_uri;
}
--
2.52.0

View File

@ -1,70 +0,0 @@
From 3caf00b0a137dabd47571b57384aeda96c9ecee7 Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@gnome.org>
Date: Thu, 4 Dec 2025 16:37:19 +0000
Subject: [PATCH] gfileattribute: Fix integer overflow calculating escaping for
byte strings
The number of invalid characters in the byte string (characters which
would have to be percent-encoded) was only stored in an `int`, which
gave the possibility of a long string largely full of invalid
characters overflowing this and allowing an attacker-controlled buffer
size to be allocated.
This could be triggered by an attacker controlled file attribute (of
type `G_FILE_ATTRIBUTE_TYPE_BYTE_STRING`), such as
`G_FILE_ATTRIBUTE_THUMBNAIL_PATH` or `G_FILE_ATTRIBUTE_STANDARD_NAME`,
being read by user code.
Spotted by Codean Labs.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
Fixes: #3845
---
gio/gfileattribute.c | 13 ++++++++++---
1 file changed, 10 insertions(+), 3 deletions(-)
diff --git a/gio/gfileattribute.c b/gio/gfileattribute.c
index 9b16a765f..164d27f9a 100644
--- a/gio/gfileattribute.c
+++ b/gio/gfileattribute.c
@@ -20,6 +20,7 @@
#include "config.h"
+#include <stdint.h>
#include <string.h>
#include "gfileattribute.h"
@@ -271,11 +272,12 @@ valid_char (char c)
return c >= 32 && c <= 126 && c != '\\';
}
+/* Returns NULL on error */
static char *
escape_byte_string (const char *str)
{
- size_t len;
- int num_invalid, i;
+ size_t i, len;
+ size_t num_invalid;
char *escaped_val, *p;
unsigned char c;
const char hex_digits[] = "0123456789abcdef";
@@ -293,7 +295,12 @@ escape_byte_string (const char *str)
return g_strdup (str);
else
{
- escaped_val = g_malloc (len + num_invalid*3 + 1);
+ /* Check for overflow. We want to check the inequality:
+ * !(len + num_invalid * 3 + 1 > SIZE_MAX) */
+ if (num_invalid >= (SIZE_MAX - len) / 3)
+ return NULL;
+
+ escaped_val = g_malloc (len + num_invalid * 3 + 1);
p = escaped_val;
for (i = 0; i < len; i++)
--
2.53.0

View File

@ -1,578 +0,0 @@
From 446d8077dad5ae0faad7b9cff9c61529e34d21bb Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@gnome.org>
Date: Tue, 25 Nov 2025 19:27:51 +0000
Subject: [PATCH 1/4] gutf8: Add tests and clarify documentation for
g_unichar_to_utf8()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
While using `g_unichar_to_utf8()`, I noticed the return type is `int`,
which suggests that it could return negative values. Wanting to make
sure I properly handled this in my code, I checked the implementation of
`g_unichar_to_utf8()` and found that it can actually only return [1, 6].
It looks like the `int` return type is a historical C-ism, and it should
really return an unsigned integer type.
Improve the docs and add some unit tests while Im there.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
---
glib/gutf8.c | 8 ++++----
glib/tests/unicode.c | 36 ++++++++++++++++++++++++++++++++++++
2 files changed, 40 insertions(+), 4 deletions(-)
diff --git a/glib/gutf8.c b/glib/gutf8.c
index 06cb9e779..5deee968e 100644
--- a/glib/gutf8.c
+++ b/glib/gutf8.c
@@ -454,16 +454,16 @@ g_utf8_strncpy (gchar *dest,
*
* Converts a single character to UTF-8.
*
- * Returns: number of bytes written
+ * Returns: number of bytes written, guaranteed to be in the range [1, 6]
*/
int
g_unichar_to_utf8 (gunichar c,
gchar *outbuf)
{
/* If this gets modified, also update the copy in g_string_insert_unichar() */
- guint len = 0;
- int first;
- int i;
+ size_t len = 0;
+ char first;
+ size_t i;
if (c < 0x80)
{
diff --git a/glib/tests/unicode.c b/glib/tests/unicode.c
index c81c9b8e8..8aacb8167 100644
--- a/glib/tests/unicode.c
+++ b/glib/tests/unicode.c
@@ -525,6 +525,41 @@ test_wide (void)
}
};
+static void
+test_unichar_to_utf8 (void)
+{
+ const struct
+ {
+ gunichar unichar;
+ int expected_length;
+ const char *expected_output; /* must be of length `expected_length` */
+ }
+ vectors[] =
+ {
+ { 0x21, 1, "!" },
+ { 0x00A1, 2, "\xc2\xa1" },
+ { 0x0800, 3, "\xe0\xa0\x80" },
+ { 0x10000, 4, "\xf0\x90\x80\x80" },
+ { 0x200000, 5, "\xf8\x88\x80\x80\x80" },
+ { 0x4000000, 6, "\xfc\x84\x80\x80\x80\x80" },
+ };
+
+ for (size_t i = 0; i < G_N_ELEMENTS (vectors); i++)
+ {
+ int length;
+ char output[6];
+
+ length = g_unichar_to_utf8 (vectors[i].unichar, NULL);
+ g_assert_cmpint (length, ==, vectors[i].expected_length);
+
+ length = g_unichar_to_utf8 (vectors[i].unichar, output);
+ g_assert_cmpint (length, ==, vectors[i].expected_length);
+ g_assert_cmpmem (output, length, vectors[i].expected_output, vectors[i].expected_length);
+ }
+}
+
+/* Test that g_unichar_compose() returns the correct value for various
+ * ASCII and Unicode alphabetic, numeric, and other, codepoints. */
static void
test_compose (void)
{
@@ -946,6 +981,7 @@ main (int argc,
g_test_add_func ("/unicode/fully-decompose-len", test_fully_decompose_len);
g_test_add_func ("/unicode/iso15924", test_iso15924);
g_test_add_func ("/unicode/cases", test_cases);
+ g_test_add_func ("/unicode/unichar-to-utf8", test_unichar_to_utf8);
return g_test_run();
}
--
2.53.0
From 207d406271e3b5f89337ebdcefc0362c5b724d12 Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@gnome.org>
Date: Tue, 25 Nov 2025 19:02:56 +0000
Subject: [PATCH 2/4] 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 | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/glib/gvariant-parser.c b/glib/gvariant-parser.c
index 3261bc1af..4c1d2626b 100644
--- a/glib/gvariant-parser.c
+++ b/glib/gvariant-parser.c
@@ -585,7 +585,7 @@ ast_resolve (AST *ast,
{
GVariant *value;
gchar *pattern;
- gint i, j = 0;
+ size_t i, j = 0;
pattern = ast_get_pattern (ast, error);
@@ -1525,10 +1525,10 @@ string_free (AST *ast)
static gboolean
unicode_unescape (const gchar *src,
- gint *src_ofs,
+ size_t *src_ofs,
gchar *dest,
- gint *dest_ofs,
- gint length,
+ size_t *dest_ofs,
+ gsize length,
SourceRef *ref,
GError **error)
{
@@ -1548,7 +1548,7 @@ unicode_unescape (const gchar *src,
{
parser_set_error (error, ref, NULL,
G_VARIANT_PARSE_ERROR_INVALID_CHARACTER,
- "invalid %d-character unicode escape", length);
+ "invalid %zu-character unicode escape", length);
return FALSE;
}
@@ -1576,7 +1576,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);
@@ -1704,7 +1704,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);
--
2.53.0
From abadf4e327d2936012089a7368b7e0ee7dcfabd3 Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@gnome.org>
Date: Tue, 25 Nov 2025 19:19:16 +0000
Subject: [PATCH 3/4] gvariant-parser: Use size_t to count numbers of child
elements
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Rather than using `gint`, which could overflow for arrays (or dicts, or
tuples) longer than `INT_MAX`. There may be other limits which prevent
parsed containers becoming that long, but we might as well make the type
system reflect the programmers intention as best it can anyway.
For arrays and tuples this is straightforward. For dictionaries, its
slightly complicated by the fact that the code used
`dict->n_children == -1` to indicate that the `Dictionary` struct in
question actually represented a single freestanding dict entry. In
GVariant text format, that would be `{1, "one"}`.
The implementation previously didnt define the semantics of
`dict->n_children < -1`.
Now, instead, change `Dictionary.n_children` to `size_t`, and define a
magic value `DICTIONARY_N_CHILDREN_FREESTANDING_ENTRY` to indicate that
the `Dictionary` represents a single freestanding dict entry.
This magic value is `SIZE_MAX`, and given that a dictionary entry takes
more than one byte to represent in GVariant text format, that means its
not possible to have that many entries in a parsed dictionary, so this
magic value wont be hit by a normal dictionary. An assertion checks
this anyway.
Spotted while working on #3834.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
---
glib/gvariant-parser.c | 58 ++++++++++++++++++++++++------------------
1 file changed, 33 insertions(+), 25 deletions(-)
diff --git a/glib/gvariant-parser.c b/glib/gvariant-parser.c
index 4c1d2626b..adc559408 100644
--- a/glib/gvariant-parser.c
+++ b/glib/gvariant-parser.c
@@ -637,9 +637,9 @@ static AST *parse (TokenStream *stream,
GError **error);
static void
-ast_array_append (AST ***array,
- gint *n_items,
- AST *ast)
+ast_array_append (AST ***array,
+ size_t *n_items,
+ AST *ast)
{
if ((*n_items & (*n_items - 1)) == 0)
*array = g_renew (AST *, *array, *n_items ? 2 ** n_items : 1);
@@ -648,10 +648,10 @@ ast_array_append (AST ***array,
}
static void
-ast_array_free (AST **array,
- gint n_items)
+ast_array_free (AST **array,
+ size_t n_items)
{
- gint i;
+ size_t i;
for (i = 0; i < n_items; i++)
ast_free (array[i]);
@@ -660,11 +660,11 @@ ast_array_free (AST **array,
static gchar *
ast_array_get_pattern (AST **array,
- gint n_items,
+ size_t n_items,
GError **error)
{
gchar *pattern;
- gint i;
+ size_t i;
pattern = ast_get_pattern (array[0], error);
@@ -693,7 +693,7 @@ ast_array_get_pattern (AST **array,
* pair of values.
*/
{
- int j = 0;
+ size_t j = 0;
while (TRUE)
{
@@ -867,7 +867,7 @@ typedef struct
AST ast;
AST **children;
- gint n_children;
+ size_t n_children;
} Array;
static gchar *
@@ -900,7 +900,7 @@ array_get_value (AST *ast,
Array *array = (Array *) ast;
const GVariantType *childtype;
GVariantBuilder builder;
- gint i;
+ size_t i;
if (!g_variant_type_is_array (type))
return ast_type_error (ast, type, error);
@@ -985,7 +985,7 @@ typedef struct
AST ast;
AST **children;
- gint n_children;
+ size_t n_children;
} Tuple;
static gchar *
@@ -995,7 +995,7 @@ tuple_get_pattern (AST *ast,
Tuple *tuple = (Tuple *) ast;
gchar *result = NULL;
gchar **parts;
- gint i;
+ size_t i;
parts = g_new (gchar *, tuple->n_children + 4);
parts[tuple->n_children + 1] = (gchar *) ")";
@@ -1025,7 +1025,7 @@ tuple_get_value (AST *ast,
Tuple *tuple = (Tuple *) ast;
const GVariantType *childtype;
GVariantBuilder builder;
- gint i;
+ size_t i;
if (!g_variant_type_is_tuple (type))
return ast_type_error (ast, type, error);
@@ -1215,9 +1215,16 @@ typedef struct
AST **keys;
AST **values;
- gint n_children;
+
+ /* Iff this is DICTIONARY_N_CHILDREN_FREESTANDING_ENTRY then this struct
+ * represents a single freestanding dict entry (`{1, "one"}`) rather than a
+ * full dict. In the freestanding case, @keys and @values have exactly one
+ * member each. */
+ size_t n_children;
} Dictionary;
+#define DICTIONARY_N_CHILDREN_FREESTANDING_ENTRY ((size_t) -1)
+
static gchar *
dictionary_get_pattern (AST *ast,
GError **error)
@@ -1232,7 +1239,7 @@ dictionary_get_pattern (AST *ast,
return g_strdup ("Ma{**}");
key_pattern = ast_array_get_pattern (dict->keys,
- abs (dict->n_children),
+ (dict->n_children == DICTIONARY_N_CHILDREN_FREESTANDING_ENTRY) ? 1 : dict->n_children,
error);
if (key_pattern == NULL)
@@ -1263,7 +1270,7 @@ dictionary_get_pattern (AST *ast,
return NULL;
result = g_strdup_printf ("M%s{%c%s}",
- dict->n_children > 0 ? "a" : "",
+ (dict->n_children > 0 && dict->n_children != DICTIONARY_N_CHILDREN_FREESTANDING_ENTRY) ? "a" : "",
key_char, value_pattern);
g_free (value_pattern);
@@ -1277,7 +1284,7 @@ dictionary_get_value (AST *ast,
{
Dictionary *dict = (Dictionary *) ast;
- if (dict->n_children == -1)
+ if (dict->n_children == DICTIONARY_N_CHILDREN_FREESTANDING_ENTRY)
{
const GVariantType *subtype;
GVariantBuilder builder;
@@ -1310,7 +1317,7 @@ dictionary_get_value (AST *ast,
{
const GVariantType *entry, *key, *val;
GVariantBuilder builder;
- gint i;
+ size_t i;
if (!g_variant_type_is_subtype_of (type, G_VARIANT_TYPE_DICTIONARY))
return ast_type_error (ast, type, error);
@@ -1351,12 +1358,12 @@ static void
dictionary_free (AST *ast)
{
Dictionary *dict = (Dictionary *) ast;
- gint n_children;
+ size_t n_children;
- if (dict->n_children > -1)
- n_children = dict->n_children;
- else
+ if (dict->n_children == DICTIONARY_N_CHILDREN_FREESTANDING_ENTRY)
n_children = 1;
+ else
+ n_children = dict->n_children;
ast_array_free (dict->keys, n_children);
ast_array_free (dict->values, n_children);
@@ -1373,7 +1380,7 @@ dictionary_parse (TokenStream *stream,
maybe_wrapper, dictionary_get_value,
dictionary_free
};
- gint n_keys, n_values;
+ size_t n_keys, n_values;
gboolean only_one;
Dictionary *dict;
AST *first;
@@ -1416,7 +1423,7 @@ dictionary_parse (TokenStream *stream,
goto error;
g_assert (n_keys == 1 && n_values == 1);
- dict->n_children = -1;
+ dict->n_children = DICTIONARY_N_CHILDREN_FREESTANDING_ENTRY;
return (AST *) dict;
}
@@ -1449,6 +1456,7 @@ dictionary_parse (TokenStream *stream,
}
g_assert (n_keys == n_values);
+ g_assert (n_keys != DICTIONARY_N_CHILDREN_FREESTANDING_ENTRY);
dict->n_children = n_keys;
return (AST *) dict;
--
2.53.0
From 4de574f856d5ed40fc6981f313a35454d7678109 Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@gnome.org>
Date: Tue, 25 Nov 2025 19:25:58 +0000
Subject: [PATCH 4/4] gvariant-parser: Convert error handling code to use
size_t
The error handling code allows for printing out the range of input bytes
related to a parsing error. This was previously done using `gint`, but
the input could be longer than `INT_MAX`, so it should really be done
using `size_t`.
Spotted while working on #3834.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
---
glib/gvariant-parser.c | 36 +++++++++++++++++++++++-------------
1 file changed, 23 insertions(+), 13 deletions(-)
diff --git a/glib/gvariant-parser.c b/glib/gvariant-parser.c
index adc559408..199bd1f2c 100644
--- a/glib/gvariant-parser.c
+++ b/glib/gvariant-parser.c
@@ -86,7 +86,9 @@ g_variant_parser_get_error_quark (void)
typedef struct
{
- gint start, end;
+ /* Offsets from the start of the input, in bytes. Can be equal when referring
+ * to a point rather than a range. The invariant `end >= start` always holds. */
+ size_t start, end;
} SourceRef;
G_GNUC_PRINTF(5, 0)
@@ -101,14 +103,16 @@ parser_set_error_va (GError **error,
GString *msg = g_string_new (NULL);
if (location->start == location->end)
- g_string_append_printf (msg, "%d", location->start);
+ g_string_append_printf (msg, "%" G_GSIZE_FORMAT, location->start);
else
- g_string_append_printf (msg, "%d-%d", location->start, location->end);
+ g_string_append_printf (msg, "%" G_GSIZE_FORMAT "-%" G_GSIZE_FORMAT,
+ location->start, location->end);
if (other != NULL)
{
g_assert (other->start != other->end);
- g_string_append_printf (msg, ",%d-%d", other->start, other->end);
+ g_string_append_printf (msg, ",%" G_GSIZE_FORMAT "-%" G_GSIZE_FORMAT,
+ other->start, other->end);
}
g_string_append_c (msg, ':');
@@ -135,11 +139,15 @@ parser_set_error (GError **error,
typedef struct
{
+ /* We should always have the following ordering constraint:
+ * start <= this <= stream <= end
+ * Additionally, unless in an error or EOF state, `this < stream`.
+ */
const gchar *start;
const gchar *stream;
const gchar *end;
- const gchar *this;
+ const gchar *this; /* (nullable) */
} TokenStream;
@@ -170,7 +178,7 @@ token_stream_set_error (TokenStream *stream,
static gboolean
token_stream_prepare (TokenStream *stream)
{
- gint brackets = 0;
+ gssize brackets = 0;
const gchar *end;
if (stream->this != NULL)
@@ -395,7 +403,7 @@ static void
pattern_copy (gchar **out,
const gchar **in)
{
- gint brackets = 0;
+ gssize brackets = 0;
while (**in == 'a' || **in == 'm' || **in == 'M')
*(*out)++ = *(*in)++;
@@ -2592,7 +2600,7 @@ g_variant_builder_add_parsed (GVariantBuilder *builder,
static gboolean
parse_num (const gchar *num,
const gchar *limit,
- gint *result)
+ size_t *result)
{
gchar *endptr;
gint64 bignum;
@@ -2602,10 +2610,12 @@ parse_num (const gchar *num,
if (endptr != limit)
return FALSE;
+ /* The upper bound here is more restrictive than it technically needs to be,
+ * but should be enough for any practical situation: */
if (bignum < 0 || bignum > G_MAXINT)
return FALSE;
- *result = bignum;
+ *result = (size_t) bignum;
return TRUE;
}
@@ -2616,7 +2626,7 @@ add_last_line (GString *err,
{
const gchar *last_nl;
gchar *chomped;
- gint i;
+ size_t i;
/* This is an error at the end of input. If we have a file
* with newlines, that's probably the empty string after the
@@ -2761,7 +2771,7 @@ g_variant_parse_error_print_context (GError *error,
if (dash == NULL || colon < dash)
{
- gint point;
+ size_t point;
/* we have a single point */
if (!parse_num (error->message, colon, &point))
@@ -2779,7 +2789,7 @@ g_variant_parse_error_print_context (GError *error,
/* We have one or two ranges... */
if (comma && comma < colon)
{
- gint start1, end1, start2, end2;
+ size_t start1, end1, start2, end2;
const gchar *dash2;
/* Two ranges */
@@ -2795,7 +2805,7 @@ g_variant_parse_error_print_context (GError *error,
}
else
{
- gint start, end;
+ size_t start, end;
/* One range */
if (!parse_num (error->message, dash, &start) || !parse_num (dash + 1, colon, &end))
--
2.53.0

View File

@ -1,433 +0,0 @@
From 6c2178a3bc216a6cc765fc6ba3b0e6d22ce5af7e Mon Sep 17 00:00:00 2001
From: Emmanuel Fleury <emmanuel.fleury@u-bordeaux.fr>
Date: Mon, 4 Feb 2019 13:31:28 +0100
Subject: [PATCH 1/3] Fixing various warnings in glib/gstring.c
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
In file included from glib/glibconfig.h:9,
from glib/gtypes.h:32,
from glib/gstring.h:32,
from glib/gstring.c:37:
glib/gstring.c: In function g_string_insert_len:
glib/gstring.c:441:31: error: comparison of integer expressions of different signedness: gssize {aka long int} and gsize {aka long unsigned int} [-Werror=sign-compare]
g_return_val_if_fail (pos <= string->len, string);
^~
glib/gmacros.h:455:25: note: in definition of macro G_LIKELY
#define G_LIKELY(expr) (expr)
^~~~
glib/gstring.c:441:5: note: in expansion of macro g_return_val_if_fail
g_return_val_if_fail (pos <= string->len, string);
^~~~~~~~~~~~~~~~~~~~
glib/gstring.c:458:15: error: comparison of integer expressions of different signedness: gssize {aka long int} and gsize {aka long unsigned int} [-Werror=sign-compare]
if (pos < string->len)
^
glib/gstring.c:462:18: error: comparison of integer expressions of different signedness: gsize {aka long unsigned int} and gssize {aka long int} [-Werror=sign-compare]
if (offset < pos)
^
In file included from glib/glibconfig.h:9,
from glib/gtypes.h:32,
from glib/gstring.h:32,
from glib/gstring.c:37:
glib/gmacros.h:351:26: error: comparison of integer expressions of different signedness: gssize {aka long int} and long unsigned int [-Werror=sign-compare]
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
^
glib/gstring.c:464:22: note: in expansion of macro MIN
precount = MIN (len, pos - offset);
^~~
glib/gmacros.h:351:35: error: operand of ?: changes signedness from gssize {aka long int} to long unsigned int due to unsignedness of other operand [-Werror=sign-compare]
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
^~~
glib/gstring.c:464:22: note: in expansion of macro MIN
precount = MIN (len, pos - offset);
^~~
glib/gstring.c:469:15: error: comparison of integer expressions of different signedness: gssize {aka long int} and gsize {aka long unsigned int} [-Werror=sign-compare]
if (len > precount)
^
glib/gstring.c:481:15: error: comparison of integer expressions of different signedness: gssize {aka long int} and gsize {aka long unsigned int} [-Werror=sign-compare]
if (pos < string->len)
^
In file included from glib/glibconfig.h:9,
from glib/gtypes.h:32,
from glib/gstring.h:32,
from glib/gstring.c:37:
glib/gstring.c: In function g_string_insert_c:
glib/gstring.c:782:31: error: comparison of integer expressions of different signedness: gssize {aka long int} and gsize {aka long unsigned int} [-Werror=sign-compare]
g_return_val_if_fail (pos <= string->len, string);
^~
glib/gmacros.h:455:25: note: in definition of macro G_LIKELY
#define G_LIKELY(expr) (expr)
^~~~
glib/gstring.c:782:5: note: in expansion of macro g_return_val_if_fail
g_return_val_if_fail (pos <= string->len, string);
^~~~~~~~~~~~~~~~~~~~
glib/gstring.c:785:11: error: comparison of integer expressions of different signedness: gssize {aka long int} and gsize {aka long unsigned int} [-Werror=sign-compare]
if (pos < string->len)
^
In file included from glib/glibconfig.h:9,
from glib/gtypes.h:32,
from glib/gstring.h:32,
from glib/gstring.c:37:
glib/gstring.c: In function g_string_insert_unichar:
glib/gstring.c:857:31: error: comparison of integer expressions of different signedness: gssize {aka long int} and gsize {aka long unsigned int} [-Werror=sign-compare]
g_return_val_if_fail (pos <= string->len, string);
^~
glib/gmacros.h:455:25: note: in definition of macro G_LIKELY
#define G_LIKELY(expr) (expr)
^~~~
glib/gstring.c:857:5: note: in expansion of macro g_return_val_if_fail
g_return_val_if_fail (pos <= string->len, string);
^~~~~~~~~~~~~~~~~~~~
glib/gstring.c:860:11: error: comparison of integer expressions of different signedness: gssize {aka long int} and gsize {aka long unsigned int} [-Werror=sign-compare]
if (pos < string->len)
^
In file included from glib/glibconfig.h:9,
from glib/gtypes.h:32,
from glib/gstring.h:32,
from glib/gstring.c:37:
glib/gstring.c: In function g_string_erase:
glib/gstring.c:969:29: error: comparison of integer expressions of different signedness: gssize {aka long int} and gsize {aka long unsigned int} [-Werror=sign-compare]
g_return_val_if_fail (pos <= string->len, string);
^~
glib/gmacros.h:455:25: note: in definition of macro G_LIKELY
#define G_LIKELY(expr) (expr)
^~~~
glib/gstring.c:969:3: note: in expansion of macro g_return_val_if_fail
g_return_val_if_fail (pos <= string->len, string);
^~~~~~~~~~~~~~~~~~~~
glib/gstring.c:975:39: error: comparison of integer expressions of different signedness: gssize {aka long int} and gsize {aka long unsigned int} [-Werror=sign-compare]
g_return_val_if_fail (pos + len <= string->len, string);
^~
glib/gmacros.h:455:25: note: in definition of macro G_LIKELY
#define G_LIKELY(expr) (expr)
^~~~
glib/gstring.c:975:7: note: in expansion of macro g_return_val_if_fail
g_return_val_if_fail (pos + len <= string->len, string);
^~~~~~~~~~~~~~~~~~~~
glib/gstring.c:977:21: error: comparison of integer expressions of different signedness: gssize {aka long int} and gsize {aka long unsigned int} [-Werror=sign-compare]
if (pos + len < string->len)
^
---
glib/gstring.c | 82 +++++++++++++++++++++++++++++++-------------------
1 file changed, 51 insertions(+), 31 deletions(-)
diff --git a/glib/gstring.c b/glib/gstring.c
index 966502019..f5bfeb0ed 100644
--- a/glib/gstring.c
+++ b/glib/gstring.c
@@ -426,6 +426,8 @@ g_string_insert_len (GString *string,
const gchar *val,
gssize len)
{
+ gsize len_unsigned, pos_unsigned;
+
g_return_val_if_fail (string != NULL, NULL);
g_return_val_if_fail (len == 0 || val != NULL, string);
@@ -434,11 +436,15 @@ g_string_insert_len (GString *string,
if (len < 0)
len = strlen (val);
+ len_unsigned = len;
if (pos < 0)
- pos = string->len;
+ pos_unsigned = string->len;
else
- g_return_val_if_fail (pos <= string->len, string);
+ {
+ pos_unsigned = pos;
+ g_return_val_if_fail (pos_unsigned <= string->len, string);
+ }
/* Check whether val represents a substring of string.
* This test probably violates chapter and verse of the C standards,
@@ -450,45 +456,48 @@ g_string_insert_len (GString *string,
gsize offset = val - string->str;
gsize precount = 0;
- g_string_maybe_expand (string, len);
+ g_string_maybe_expand (string, len_unsigned);
val = string->str + offset;
/* At this point, val is valid again. */
/* Open up space where we are going to insert. */
- if (pos < string->len)
- memmove (string->str + pos + len, string->str + pos, string->len - pos);
+ if (pos_unsigned < string->len)
+ memmove (string->str + pos_unsigned + len_unsigned,
+ string->str + pos_unsigned, string->len - pos_unsigned);
/* Move the source part before the gap, if any. */
- if (offset < pos)
+ if (offset < pos_unsigned)
{
- precount = MIN (len, pos - offset);
- memcpy (string->str + pos, val, precount);
+ precount = MIN (len_unsigned, pos_unsigned - offset);
+ memcpy (string->str + pos_unsigned, val, precount);
}
/* Move the source part after the gap, if any. */
- if (len > precount)
- memcpy (string->str + pos + precount,
- val + /* Already moved: */ precount + /* Space opened up: */ len,
- len - precount);
+ if (len_unsigned > precount)
+ memcpy (string->str + pos_unsigned + precount,
+ val + /* Already moved: */ precount +
+ /* Space opened up: */ len_unsigned,
+ len_unsigned - precount);
}
else
{
- g_string_maybe_expand (string, len);
+ g_string_maybe_expand (string, len_unsigned);
/* If we aren't appending at the end, move a hunk
* of the old string to the end, opening up space
*/
- if (pos < string->len)
- memmove (string->str + pos + len, string->str + pos, string->len - pos);
+ if (pos_unsigned < string->len)
+ memmove (string->str + pos_unsigned + len_unsigned,
+ string->str + pos_unsigned, string->len - pos_unsigned);
/* insert the new string */
- if (len == 1)
- string->str[pos] = *val;
+ if (len_unsigned == 1)
+ string->str[pos_unsigned] = *val;
else
- memcpy (string->str + pos, val, len);
+ memcpy (string->str + pos_unsigned, val, len_unsigned);
}
- string->len += len;
+ string->len += len_unsigned;
string->str[string->len] = 0;
@@ -772,6 +781,8 @@ g_string_insert_c (GString *string,
gssize pos,
gchar c)
{
+ gsize pos_unsigned;
+
g_return_val_if_fail (string != NULL, NULL);
g_string_maybe_expand (string, 1);
@@ -779,13 +790,15 @@ g_string_insert_c (GString *string,
if (pos < 0)
pos = string->len;
else
- g_return_val_if_fail (pos <= string->len, string);
+ g_return_val_if_fail ((gsize) pos <= string->len, string);
+ pos_unsigned = pos;
/* If not just an append, move the old stuff */
- if (pos < string->len)
- memmove (string->str + pos + 1, string->str + pos, string->len - pos);
+ if (pos_unsigned < string->len)
+ memmove (string->str + pos_unsigned + 1,
+ string->str + pos_unsigned, string->len - pos_unsigned);
- string->str[pos] = c;
+ string->str[pos_unsigned] = c;
string->len += 1;
@@ -854,10 +867,10 @@ g_string_insert_unichar (GString *string,
if (pos < 0)
pos = string->len;
else
- g_return_val_if_fail (pos <= string->len, string);
+ g_return_val_if_fail ((gsize) pos <= string->len, string);
/* If not just an append, move the old stuff */
- if (pos < string->len)
+ if ((gsize) pos < string->len)
memmove (string->str + pos + charlen, string->str + pos, string->len - pos);
dest = string->str + pos;
@@ -964,21 +977,28 @@ g_string_erase (GString *string,
gssize pos,
gssize len)
{
+ gsize len_unsigned, pos_unsigned;
+
g_return_val_if_fail (string != NULL, NULL);
g_return_val_if_fail (pos >= 0, string);
- g_return_val_if_fail (pos <= string->len, string);
+ pos_unsigned = pos;
+
+ g_return_val_if_fail (pos_unsigned <= string->len, string);
if (len < 0)
- len = string->len - pos;
+ len_unsigned = string->len - pos_unsigned;
else
{
- g_return_val_if_fail (pos + len <= string->len, string);
+ len_unsigned = len;
+ g_return_val_if_fail (pos_unsigned + len_unsigned <= string->len, string);
- if (pos + len < string->len)
- memmove (string->str + pos, string->str + pos + len, string->len - (pos + len));
+ if (pos_unsigned + len_unsigned < string->len)
+ memmove (string->str + pos_unsigned,
+ string->str + pos_unsigned + len_unsigned,
+ string->len - (pos_unsigned + len_unsigned));
}
- string->len -= len;
+ string->len -= len_unsigned;
string->str[string->len] = 0;
--
2.50.0
From 101afb01778659cb7051b3cb33d55dc965c8dc7e Mon Sep 17 00:00:00 2001
From: Michael Catanzaro <mcatanzaro@redhat.com>
Date: Thu, 10 Apr 2025 10:57:20 -0500
Subject: [PATCH 2/3] gstring: carefully handle gssize parameters
Wherever we use gssize to allow passing -1, we need to ensure we don't
overflow the value by assigning a gsize to it without checking if the
size exceeds the maximum gssize. The safest way to do this is to just
use normal gsize everywhere instead and use gssize only for the
parameter.
Our computers don't have enough RAM to write tests for this. I tried
forcing string->len to high values for test purposes, but this isn't
valid and will just cause out of bounds reads/writes due to
string->allocated_len being unexpectedly small, so I don't think we can
test this easily.
---
glib/gstring.c | 36 +++++++++++++++++++++++-------------
1 file changed, 23 insertions(+), 13 deletions(-)
diff --git a/glib/gstring.c b/glib/gstring.c
index f5bfeb0ed..84a98da25 100644
--- a/glib/gstring.c
+++ b/glib/gstring.c
@@ -435,8 +435,9 @@ g_string_insert_len (GString *string,
return string;
if (len < 0)
- len = strlen (val);
- len_unsigned = len;
+ len_unsigned = strlen (val);
+ else
+ len_unsigned = len;
if (pos < 0)
pos_unsigned = string->len;
@@ -788,10 +789,12 @@ g_string_insert_c (GString *string,
g_string_maybe_expand (string, 1);
if (pos < 0)
- pos = string->len;
+ pos_unsigned = string->len;
else
- g_return_val_if_fail ((gsize) pos <= string->len, string);
- pos_unsigned = pos;
+ {
+ pos_unsigned = pos;
+ g_return_val_if_fail (pos_unsigned <= string->len, string);
+ }
/* If not just an append, move the old stuff */
if (pos_unsigned < string->len)
@@ -824,6 +827,7 @@ g_string_insert_unichar (GString *string,
gssize pos,
gunichar wc)
{
+ gsize pos_unsigned;
gint charlen, first, i;
gchar *dest;
@@ -865,15 +869,18 @@ g_string_insert_unichar (GString *string,
g_string_maybe_expand (string, charlen);
if (pos < 0)
- pos = string->len;
+ pos_unsigned = string->len;
else
- g_return_val_if_fail ((gsize) pos <= string->len, string);
+ {
+ pos_unsigned = pos;
+ g_return_val_if_fail (pos_unsigned <= string->len, string);
+ }
/* If not just an append, move the old stuff */
- if ((gsize) pos < string->len)
- memmove (string->str + pos + charlen, string->str + pos, string->len - pos);
+ if (pos_unsigned < string->len)
+ memmove (string->str + pos_unsigned + charlen, string->str + pos_unsigned, string->len - pos_unsigned);
- dest = string->str + pos;
+ dest = string->str + pos_unsigned;
/* Code copied from g_unichar_to_utf() */
for (i = charlen - 1; i > 0; --i)
{
@@ -931,6 +938,7 @@ g_string_overwrite_len (GString *string,
const gchar *val,
gssize len)
{
+ gssize len_unsigned;
gsize end;
g_return_val_if_fail (string != NULL, NULL);
@@ -942,14 +950,16 @@ g_string_overwrite_len (GString *string,
g_return_val_if_fail (pos <= string->len, string);
if (len < 0)
- len = strlen (val);
+ len_unsigned = strlen (val);
+ else
+ len_unsigned = len;
- end = pos + len;
+ end = pos + len_unsigned;
if (end > string->len)
g_string_maybe_expand (string, end - string->len);
- memcpy (string->str + pos, val, len);
+ memcpy (string->str + pos, val, len_unsigned);
if (end > string->len)
{
--
2.50.0
From 789c240db9738cae37ee77f7e135e5dbc39ab3ca Mon Sep 17 00:00:00 2001
From: Peter Bloomfield <peterbloomfield@bellsouth.net>
Date: Fri, 11 Apr 2025 05:52:33 +0000
Subject: [PATCH 3/3] gstring: Make len_unsigned unsigned
---
glib/gstring.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/glib/gstring.c b/glib/gstring.c
index 84a98da25..7379517f5 100644
--- a/glib/gstring.c
+++ b/glib/gstring.c
@@ -938,7 +938,7 @@ g_string_overwrite_len (GString *string,
const gchar *val,
gssize len)
{
- gssize len_unsigned;
+ gsize len_unsigned;
gsize end;
g_return_val_if_fail (string != NULL, NULL);
--
2.50.0

View File

@ -1,312 +0,0 @@
From fd2ce100affce9e822c81f51d66b2b4b93a76889 Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@gnome.org>
Date: Sat, 4 Jul 2026 18:13:08 +0100
Subject: [PATCH 1/2] gdbusauth: Limit length of lines read from client
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
The client isnt trusted at this point, and there was previously nothing
limiting how long a line `GDBusAuth` would read. So an untrusted client
could exhaust the servers memory by sending anything except `\r\n`.
Fix that by applying a reasonably length limit when reading a line, and
add a unit test.
Spotted by Gitee Codepecker Lab.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
Fixes: #3985
---
gio/gdbusauth.c | 55 ++++++++++++++++++-
gio/tests/gdbus-auth.c | 122 +++++++++++++++++++++++++++++++++++++++++
2 files changed, 175 insertions(+), 2 deletions(-)
diff --git a/gio/gdbusauth.c b/gio/gdbusauth.c
index e9a953a2c..a94a3e359 100644
--- a/gio/gdbusauth.c
+++ b/gio/gdbusauth.c
@@ -262,6 +262,22 @@ find_mech_by_name (GDBusAuth *auth,
return ret;
}
+static size_t
+get_longest_mechanism_name_length (GDBusAuth *auth)
+{
+ size_t len = 0;
+ GList *l;
+
+ for (l = auth->priv->available_mechanisms; l != NULL; l = l->next)
+ {
+ Mechanism *m = l->data;
+
+ len = MAX (len, strlen (m->name));
+ }
+
+ return len;
+}
+
GDBusAuth *
_g_dbus_auth_new (GIOStream *stream)
{
@@ -270,6 +286,20 @@ _g_dbus_auth_new (GIOStream *stream)
NULL);
}
+/* Arbitrarily chosen limit on the length of a DATA command payload, to prevent
+ * unbounded reads from malicious clients.
+ *
+ * - The ANONYMOUS mechanism doesnt use DATA.
+ * - The EXTERNAL mechanism just uses it to transfer a decimal-encoded UID.
+ * - The DBUS_COOKIE_SHA1 mechanism transfers a challenge and a SHA1 hash. The
+ * hash is bounded in length, but the challenge is not, so could potentially
+ * hit this limit. It doesnt seem unreasonable to bound the challenge to
+ * ~4KB though. GDBus itself generates a 16 byte challenge.
+ *
+ * See https://dbus.freedesktop.org/doc/dbus-specification.html#auth-command-data
+ */
+#define MAX_DATA_PAYLOAD_LENGTH_BYTES 4096
+
/* ---------------------------------------------------------------------------------------------------- */
/* like g_data_input_stream_read_line() but sets error if there's no content to read */
static gchar *
@@ -307,6 +337,7 @@ _my_g_data_input_stream_read_line (GDataInputStream *dis,
*/
static gchar *
_my_g_input_stream_read_line_safe (GInputStream *i,
+ size_t max_line_length,
gsize *out_line_length,
GCancellable *cancellable,
GError **error)
@@ -316,11 +347,22 @@ _my_g_input_stream_read_line_safe (GInputStream *i,
gssize num_read;
gboolean last_was_cr;
+ g_assert (max_line_length <= SIZE_MAX - 2);
+
str = g_string_new (NULL);
last_was_cr = FALSE;
while (TRUE)
{
+ if (str->len >= max_line_length + 2 /* allow for \r\n */)
+ {
+ g_set_error_literal (error,
+ G_IO_ERROR,
+ G_IO_ERROR_FAILED,
+ _("Malformed D-Bus authentication line"));
+ goto fail;
+ }
+
num_read = g_input_stream_read (i,
&c,
1,
@@ -1073,7 +1115,11 @@ _g_dbus_auth_run_server (GDBusAuth *auth,
{
case SERVER_STATE_WAITING_FOR_AUTH:
debug_print ("SERVER: WaitingForAuth");
- line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error);
+ line = _my_g_input_stream_read_line_safe (g_io_stream_get_input_stream (auth->priv->stream),
+ strlen ("AUTH ") + get_longest_mechanism_name_length (auth) + strlen (" ") + MAX_DATA_PAYLOAD_LENGTH_BYTES,
+ &line_length,
+ cancellable,
+ error);
debug_print ("SERVER: WaitingForAuth, read '%s'", line);
if (line == NULL)
goto out;
@@ -1272,7 +1318,11 @@ _g_dbus_auth_run_server (GDBusAuth *auth,
case SERVER_STATE_WAITING_FOR_DATA:
debug_print ("SERVER: WaitingForData");
- line = _my_g_data_input_stream_read_line (dis, &line_length, cancellable, error);
+ line = _my_g_input_stream_read_line_safe (g_io_stream_get_input_stream (auth->priv->stream),
+ strlen ("DATA ") + MAX_DATA_PAYLOAD_LENGTH_BYTES,
+ &line_length,
+ cancellable,
+ error);
debug_print ("SERVER: WaitingForData, read '%s'", line);
if (line == NULL)
goto out;
@@ -1319,6 +1369,7 @@ _g_dbus_auth_run_server (GDBusAuth *auth,
* appears after "BEGIN\r\n"....)
*/
line = _my_g_input_stream_read_line_safe (g_io_stream_get_input_stream (auth->priv->stream),
+ MAX (strlen ("BEGIN"), strlen ("NEGOTIATE_UNIX_FD")),
&line_length,
cancellable,
error);
diff --git a/gio/tests/gdbus-auth.c b/gio/tests/gdbus-auth.c
index f44e932e2..0fb780c1e 100644
--- a/gio/tests/gdbus-auth.c
+++ b/gio/tests/gdbus-auth.c
@@ -278,6 +278,127 @@ temp_dbus_keyrings_teardown (void)
g_unsetenv ("G_DBUS_COOKIE_SHA1_KEYRING_DIR_IGNORE_PERMISSION");
}
+static void
+async_result_cb (GObject *obj,
+ GAsyncResult *result,
+ void *user_data)
+{
+ GAsyncResult **result_out = user_data;
+
+ g_assert (result_out != NULL);
+ g_assert (*result_out == NULL);
+
+ *result_out = g_object_ref (result);
+ g_main_context_wakeup (g_main_context_get_thread_default ());
+}
+
+static gboolean
+server_new_connection_unexpected_cb (GDBusServer *server,
+ GDBusConnection *connection,
+ void *user_data)
+{
+ g_assert_not_reached ();
+ return FALSE;
+}
+
+static void
+test_auth_server_read_limit (void)
+{
+ GDBusServer *server = NULL;
+ unsigned long new_connection_id = 0;
+ const char *server_address;
+ GIOStream *client_stream = NULL;
+ GOutputStream *client_output_stream;
+ GInputStream *client_input_stream;
+ GAsyncResult *result = NULL;
+ char *write_buffer = NULL;
+ char read_buffer[100];
+ ssize_t read_len;
+ size_t bytes_written;
+ GError *local_error = NULL;
+
+ g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/3985");
+
+ server = server_new_for_mechanism (NULL);
+
+ new_connection_id = g_signal_connect (server,
+ "new-connection",
+ G_CALLBACK (server_new_connection_unexpected_cb),
+ NULL);
+ server_address = g_dbus_server_get_client_address (server);
+ g_dbus_server_start (server);
+
+ /* Start connecting as a client */
+ g_dbus_address_get_stream (server_address, NULL, async_result_cb, &result);
+
+ while (result == NULL)
+ g_main_context_iteration (NULL, TRUE);
+
+ client_stream = g_dbus_address_get_stream_finish (result, NULL, &local_error);
+ g_assert_no_error (local_error);
+ g_clear_object (&result);
+
+ /* Send an over-long AUTH line, maliciously */
+ client_output_stream = g_io_stream_get_output_stream (client_stream);
+ client_input_stream = g_io_stream_get_input_stream (client_stream);
+
+ write_buffer = g_strdup_printf ("AUTH DBUS_COOKIE_SHA1 context%0*d 123 456\r\n", 5000, 0);
+
+ g_output_stream_write_all_async (client_output_stream,
+ write_buffer,
+ strlen (write_buffer),
+ G_PRIORITY_DEFAULT,
+ NULL,
+ async_result_cb,
+ &result);
+
+ while (result == NULL)
+ g_main_context_iteration (NULL, TRUE);
+
+ g_output_stream_write_all_finish (client_output_stream, result, &bytes_written, &local_error);
+ g_assert_no_error (local_error);
+ g_assert_cmpuint (bytes_written, ==, strlen (write_buffer));
+ g_clear_object (&result);
+
+ g_clear_pointer (&write_buffer, g_free);
+
+ /* Authentication should have been rejected, so reading or writing the stream
+ * should now fail. */
+ read_len = g_input_stream_read (client_input_stream,
+ read_buffer,
+ sizeof (read_buffer),
+ NULL,
+ &local_error);
+ g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED);
+ g_assert_cmpint (read_len, <, 0);
+ g_clear_error (&local_error);
+
+ write_buffer = g_strdup_printf ("AUTH\r\n");
+
+ g_output_stream_write_all (client_output_stream,
+ write_buffer,
+ strlen (write_buffer),
+ &bytes_written,
+ NULL,
+ &local_error);
+ g_assert_error (local_error, G_IO_ERROR, G_IO_ERROR_CONNECTION_CLOSED);
+ g_assert_cmpuint (bytes_written, ==, 0);
+ g_clear_error (&local_error);
+
+ g_clear_pointer (&write_buffer, g_free);
+
+ /* Cleanup */
+ g_clear_object (&client_stream);
+ g_dbus_server_stop (server);
+
+ if (new_connection_id != 0)
+ {
+ g_signal_handler_disconnect (server, new_connection_id);
+ new_connection_id = 0;
+ }
+ g_clear_object (&server);
+}
+
/* ---------------------------------------------------------------------------------------------------- */
int
@@ -297,6 +418,7 @@ main (int argc,
g_test_add_func ("/gdbus/auth/server/ANONYMOUS", auth_server_anonymous);
g_test_add_func ("/gdbus/auth/server/EXTERNAL", auth_server_external);
g_test_add_func ("/gdbus/auth/server/DBUS_COOKIE_SHA1", auth_server_dbus_cookie_sha1);
+ g_test_add_func ("/gdbus/auth/server/read-limit", test_auth_server_read_limit);
/* TODO: we currently don't have tests for
*
From c50053362f01e6927f9347532a93e32074495bde Mon Sep 17 00:00:00 2001
From: RHEL Packaging Agent <redhat-ymir-agent@redhat.com>
Date: Thu, 30 Jul 2026 12:09:40 +0000
Subject: [PATCH 2/2] Fix g_test_bug usage for GLib 2.56.x compatibility
In GLib 2.56.x, g_test_bug() requires g_test_bug_base() to be called first
to set the base URI. The upstream test used a full URL in g_test_bug() which
is only supported in newer GLib versions. Fix by adding g_test_bug_base()
in main() and using just the issue number in g_test_bug().
---
gio/tests/gdbus-auth.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/gio/tests/gdbus-auth.c b/gio/tests/gdbus-auth.c
index 0fb780c1e..53898dbeb 100644
--- a/gio/tests/gdbus-auth.c
+++ b/gio/tests/gdbus-auth.c
@@ -317,7 +317,7 @@ test_auth_server_read_limit (void)
size_t bytes_written;
GError *local_error = NULL;
- g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/3985");
+ g_test_bug ("3985");
server = server_new_for_mechanism (NULL);
@@ -412,6 +412,7 @@ main (int argc,
temp_dbus_keyrings_setup ();
g_test_init (&argc, &argv, NULL);
+ g_test_bug_base ("https://gitlab.gnome.org/GNOME/glib/-/issues/");
g_test_add_func ("/gdbus/auth/client/EXTERNAL", auth_client_external);
g_test_add_func ("/gdbus/auth/client/DBUS_COOKIE_SHA1", auth_client_dbus_cookie_sha1);

View File

@ -1,99 +0,0 @@
From 1e659071a5138209dfefc647eebccbca43250434 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 | 41 ++++++++++++++++++++++++++++++++++++++
2 files changed, 42 insertions(+), 1 deletion(-)
diff --git a/glib/gvariant-serialiser.c b/glib/gvariant-serialiser.c
index c6e400b53..9e862e50b 100644
--- a/glib/gvariant-serialiser.c
+++ b/glib/gvariant-serialiser.c
@@ -1078,7 +1078,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 c4a996c1f..4d3186e46 100644
--- a/glib/tests/gvariant.c
+++ b/glib/tests/gvariant.c
@@ -4842,6 +4842,45 @@ 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 doesn't 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.
+ */
+ guint8 *heap_data = NULL;
+ GBytes *bytes = NULL;
+ const GVariantType *data_type = G_VARIANT_TYPE ("(ynqiuxthdsog)");
+ GVariant *variant = NULL;
+ GVariant *normal_variant = NULL;
+
+ 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);
+
+ g_bytes_unref (bytes);
+ 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
@@ -4941,6 +4980,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 0a26e16a79b589f0fb534612a3febaf089cef981 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 ba8503813..6f9cff5c0 100644
--- a/glib/gdatetime.c
+++ b/glib/gdatetime.c
@@ -123,7 +123,7 @@ struct _GDateTime
gint interval;
/* 1 is 0001-01-01 in Proleptic Gregorian */
- gint32 days;
+ gint32 days; /* in range [MIN_DAYS, MAX_DAYS] */
volatile gint ref_count;
};
@@ -159,6 +159,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 },
@@ -768,7 +771,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;
@@ -804,7 +807,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 727ab7692db7e15889c4ffd455cfac498c7c3ada 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 6f9cff5c0..057746b17 100644
--- a/glib/gdatetime.c
+++ b/glib/gdatetime.c
@@ -1960,7 +1960,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 d0755e722..b0bda25a3 100644
--- a/glib/tests/gdatetime.c
+++ b/glib/tests/gdatetime.c
@@ -925,6 +925,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 502cc4445106a6adc3bf21d47f5adf4c02165299 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 225b8967c..2597abe96 100644
--- a/glib/gregex.c
+++ b/glib/gregex.c
@@ -2640,19 +2640,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;
@@ -2662,22 +2668,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,
@@ -2687,6 +2715,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)
{
@@ -2694,10 +2723,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;
@@ -2705,7 +2734,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;
@@ -2713,7 +2742,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 56bd2d5eb..25c3ff783 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;
+ char *result = NULL;
+ char subject[] = "\xf4\x80";
+ char subject2[] = "\xe6\xb0"; /* 3-byte UTF-8 lead, only 2 bytes */
+
+ g_test_bug ("https://gitlab.gnome.org/GNOME/glib/-/issues/3918");
+
+ /*
+ * 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.
+ */
+ 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);
+
+ 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[])
{
@@ -2202,6 +2254,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,127 +0,0 @@
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 ();
+}

View File

@ -1,74 +0,0 @@
From d3e1d6f9ea497bd1b6bb258c6d078f06b8cd4338 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
---
glib/gkeyfile.c | 2 +-
glib/tests/keyfile.c | 22 ++++++++++++++++++++++
2 files changed, 23 insertions(+), 1 deletion(-)
diff --git a/glib/gkeyfile.c b/glib/gkeyfile.c
index ae3bbbc1d..0ddde22db 100644
--- a/glib/gkeyfile.c
+++ b/glib/gkeyfile.c
@@ -2391,7 +2391,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 16f3b788b..d7c648260 100644
--- a/glib/tests/keyfile.c
+++ b/glib/tests/keyfile.c
@@ -751,6 +751,27 @@ test_locale_string (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_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)
{
@@ -1745,6 +1766,7 @@ main (int argc, char *argv[])
g_test_add_func ("/keyfile/boolean", test_boolean);
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/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,74 +0,0 @@
From 2f486f021aa46d172c7c7e8eef60dc7e22ad49f2 Mon Sep 17 00:00:00 2001
From: RHEL Packaging Agent <redhat-ymir-agent@redhat.com>
Date: Mon, 20 Jul 2026 07:02:12 +0000
Subject: [PATCH] gdbusauthmechanismsha1: Validate cookie context
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 client's 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 | 35 +++++++++++++++++++++++++++++++++++
1 file changed, 35 insertions(+)
diff --git a/gio/gdbusauthmechanismsha1.c b/gio/gdbusauthmechanismsha1.c
index 0cbaf946d..85fb5edc8 100644
--- a/gio/gdbusauthmechanismsha1.c
+++ b/gio/gdbusauthmechanismsha1.c
@@ -1130,6 +1130,34 @@ initial_response = _g_dbus_win32_get_user_sid ();
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 ((guint8) 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,
@@ -1163,6 +1191,13 @@ mechanism_client_data_receive (GDBusAuthMechanism *mechanism,
}
cookie_context = tokens[0];
+ if (!validate_cookie_context (tokens[0]))
+ {
+ g_warning ("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')
{

View File

@ -1,87 +0,0 @@
From 01d02a2d7cb54c7e27174ed344d03e0c49e0b862 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 e9601dbbd..7925af507 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,126 +0,0 @@
From a07f759373d888c837a780cb93f14542b029e19c Mon Sep 17 00:00:00 2001
From: "Rebecca N. Palmer" <rebecca_palmer@zoho.com>
Date: Fri, 11 Oct 2024 09:38:52 +0100
Subject: [PATCH 1/2] gdatetime test: Do not assume PST8PDT was always exactly
-8/-7
In newer tzdata, it is an alias for America/Los_Angeles, which has a
slightly different meaning: DST did not exist there before 1883. As a
result, we can no longer hard-code the knowledge that interval 0 is
standard time and interval 1 is summer time, and instead we need to look
up the correct intervals from known timestamps.
Resolves: https://gitlab.gnome.org/GNOME/glib/-/issues/3502
Bug-Debian: https://bugs.debian.org/1084190
[smcv: expand commit message, fix whitespace]
Signed-off-by: Simon McVittie <smcv@debian.org>
---
glib/tests/gdatetime.c | 23 +++++++++++++++++------
1 file changed, 17 insertions(+), 6 deletions(-)
diff --git a/glib/tests/gdatetime.c b/glib/tests/gdatetime.c
index 5a2190d5f..80098bab3 100644
--- a/glib/tests/gdatetime.c
+++ b/glib/tests/gdatetime.c
@@ -2096,6 +2096,7 @@ test_posix_parse (void)
{
GTimeZone *tz;
GDateTime *gdt1, *gdt2;
+ gint i1, i2;
tz = g_time_zone_new ("PST");
g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 0), ==, "UTC");
@@ -2111,14 +2112,24 @@ test_posix_parse (void)
/* This fails rules_from_identifier on Unix (though not on Windows)
* but passes anyway because PST8PDT is a zone name.
+ *
+ * Intervals i1 and i2 (rather than 0 and 1) are needed because in
+ * recent tzdata, PST8PDT may be an alias for America/Los_Angeles,
+ * and hence be aware that DST has not always existed.
+ * https://bugs.debian.org/1084190
*/
tz = g_time_zone_new ("PST8PDT");
- g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 0), ==, "PST");
- g_assert_cmpint (g_time_zone_get_offset (tz, 0), ==, - 8 * 3600);
- g_assert (!g_time_zone_is_dst (tz, 0));
- g_assert_cmpstr (g_time_zone_get_abbreviation (tz, 1), ==, "PDT");
- g_assert_cmpint (g_time_zone_get_offset (tz, 1), ==,- 7 * 3600);
- g_assert (g_time_zone_is_dst (tz, 1));
+ g_assert_nonnull (tz);
+ /* a date in winter = non-DST */
+ i1 = g_time_zone_find_interval (tz, G_TIME_TYPE_STANDARD, 0);
+ /* approximately 6 months in seconds, i.e. a date in summer = DST */
+ i2 = g_time_zone_find_interval (tz, G_TIME_TYPE_DAYLIGHT, 15000000);
+ g_assert_cmpstr (g_time_zone_get_abbreviation (tz, i1), ==, "PST");
+ g_assert_cmpint (g_time_zone_get_offset (tz, i1), ==, - 8 * 3600);
+ g_assert (!g_time_zone_is_dst (tz, i1));
+ g_assert_cmpstr (g_time_zone_get_abbreviation (tz, i2), ==, "PDT");
+ g_assert_cmpint (g_time_zone_get_offset (tz, i2), ==,- 7 * 3600);
+ g_assert (g_time_zone_is_dst (tz, i2));
g_time_zone_unref (tz);
tz = g_time_zone_new ("PST8PDT6:32:15");
--
2.50.0
From d1e564d6d4f478c9e0b163763ed2b199dfafd500 Mon Sep 17 00:00:00 2001
From: Simon McVittie <smcv@debian.org>
Date: Fri, 18 Oct 2024 11:03:19 +0100
Subject: [PATCH 2/2] gdatetime test: Try to make PST8PDT test more obviously
correct
Instead of using timestamp 0 as a magic number (in this case interpreted
as 1970-01-01T00:00:00-08:00), calculate a timestamp from a recent
year/month/day in winter, in this case 2024-01-01T00:00:00-08:00.
Similarly, instead of using a timestamp 15 million seconds later
(1970-06-23T15:40:00-07:00), calculate a timestamp from a recent
year/month/day in summer, in this case 2024-07-01T00:00:00-07:00.
Signed-off-by: Simon McVittie <smcv@debian.org>
---
glib/tests/gdatetime.c | 15 +++++++--------
1 file changed, 7 insertions(+), 8 deletions(-)
diff --git a/glib/tests/gdatetime.c b/glib/tests/gdatetime.c
index 80098bab3..d0755e722 100644
--- a/glib/tests/gdatetime.c
+++ b/glib/tests/gdatetime.c
@@ -2112,18 +2112,15 @@ test_posix_parse (void)
/* This fails rules_from_identifier on Unix (though not on Windows)
* but passes anyway because PST8PDT is a zone name.
- *
- * Intervals i1 and i2 (rather than 0 and 1) are needed because in
- * recent tzdata, PST8PDT may be an alias for America/Los_Angeles,
- * and hence be aware that DST has not always existed.
- * https://bugs.debian.org/1084190
*/
tz = g_time_zone_new ("PST8PDT");
g_assert_nonnull (tz);
/* a date in winter = non-DST */
- i1 = g_time_zone_find_interval (tz, G_TIME_TYPE_STANDARD, 0);
- /* approximately 6 months in seconds, i.e. a date in summer = DST */
- i2 = g_time_zone_find_interval (tz, G_TIME_TYPE_DAYLIGHT, 15000000);
+ gdt1 = g_date_time_new (tz, 2024, 1, 1, 0, 0, 0);
+ i1 = g_time_zone_find_interval (tz, G_TIME_TYPE_STANDARD, g_date_time_to_unix (gdt1));
+ /* a date in summer = DST */
+ gdt2 = g_date_time_new (tz, 2024, 7, 1, 0, 0, 0);
+ i2 = g_time_zone_find_interval (tz, G_TIME_TYPE_DAYLIGHT, g_date_time_to_unix (gdt2));
g_assert_cmpstr (g_time_zone_get_abbreviation (tz, i1), ==, "PST");
g_assert_cmpint (g_time_zone_get_offset (tz, i1), ==, - 8 * 3600);
g_assert (!g_time_zone_is_dst (tz, i1));
@@ -2131,6 +2128,8 @@ test_posix_parse (void)
g_assert_cmpint (g_time_zone_get_offset (tz, i2), ==,- 7 * 3600);
g_assert (g_time_zone_is_dst (tz, i2));
g_time_zone_unref (tz);
+ g_date_time_unref (gdt1);
+ g_date_time_unref (gdt2);
tz = g_time_zone_new ("PST8PDT6:32:15");
#ifdef G_OS_WIN32
--
2.50.0

View File

@ -1,401 +0,0 @@
From b43d2e06834a9779801f9a68904ae73a26578f01 Mon Sep 17 00:00:00 2001
From: Philip Withnall <withnall@endlessm.com>
Date: Sat, 21 Sep 2019 10:45:36 +0200
Subject: [PATCH 1/2] gatomic: Add various casts to use of g_atomic_*()s to fix
warnings
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
When compiling GLib with `-Wsign-conversion`, we get various warnings
about the atomic calls. A lot of these were fixed by
3ad375a629c91a27d0165a31f0ed298fd553de0a, but some remain. Fix them by
adding appropriate casts at the call sites.
Note that `g_atomic_int_{and,or,xor}()` actually all operate on `guint`s
rather than `gint`s (which is what the rest of the `g_atomic_int_*()`
functions operate on). I cant find any written reasoning for this, but
assume that its because signedness is irrelevant when youre using an
integer as a bit field. Its unfortunate that theyre named a
`g_atomic_int_*()` rather than `g_atomic_uint_*()` functions.
Tested by compiling GLib as:
```
CFLAGS=-Wsign-conversion jhbuild make -ac |& grep atomic
```
Im not going to add `-Wsign-conversion` to the set of default warnings
for building GLib, because it mostly produces false positives throughout
the rest of GLib.
Signed-off-by: Philip Withnall <withnall@endlessm.com>
Fixes: #1565
---
gio/gdbusconnection.c | 8 ++++----
gio/gdbusnamewatching.c | 4 ++--
gio/tests/gdbus-threading.c | 2 +-
glib/gbitlock.c | 2 +-
glib/gquark.c | 2 +-
glib/gthread-posix.c | 2 +-
glib/gthreadpool.c | 10 +++++-----
glib/tests/atomic.c | 20 ++++++++++----------
8 files changed, 25 insertions(+), 25 deletions(-)
diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c
index 117c8df35..1c1f0cf3d 100644
--- a/gio/gdbusconnection.c
+++ b/gio/gdbusconnection.c
@@ -3127,7 +3127,7 @@ g_dbus_connection_add_filter (GDBusConnection *connection,
CONNECTION_LOCK (connection);
data = g_new0 (FilterData, 1);
- data->id = g_atomic_int_add (&_global_filter_id, 1); /* TODO: overflow etc. */
+ data->id = (guint) g_atomic_int_add (&_global_filter_id, 1); /* TODO: overflow etc. */
data->ref_count = 1;
data->filter_function = filter_function;
data->user_data = user_data;
@@ -3482,7 +3482,7 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection,
subscriber.callback = callback;
subscriber.user_data = user_data;
subscriber.user_data_free_func = user_data_free_func;
- subscriber.id = g_atomic_int_add (&_global_subscriber_id, 1); /* TODO: overflow etc. */
+ subscriber.id = (guint) g_atomic_int_add (&_global_subscriber_id, 1); /* TODO: overflow etc. */
subscriber.context = g_main_context_ref_thread_default ();
/* see if we've already have this rule */
@@ -5172,7 +5172,7 @@ g_dbus_connection_register_object (GDBusConnection *connection,
}
ei = g_new0 (ExportedInterface, 1);
- ei->id = g_atomic_int_add (&_global_registration_id, 1); /* TODO: overflow etc. */
+ ei->id = (guint) g_atomic_int_add (&_global_registration_id, 1); /* TODO: overflow etc. */
ei->eo = eo;
ei->user_data = user_data;
ei->user_data_free_func = user_data_free_func;
@@ -6832,7 +6832,7 @@ g_dbus_connection_register_subtree (GDBusConnection *connection,
es->vtable = _g_dbus_subtree_vtable_copy (vtable);
es->flags = flags;
- es->id = g_atomic_int_add (&_global_subtree_registration_id, 1); /* TODO: overflow etc. */
+ es->id = (guint) g_atomic_int_add (&_global_subtree_registration_id, 1); /* TODO: overflow etc. */
es->user_data = user_data;
es->user_data_free_func = user_data_free_func;
es->context = g_main_context_ref_thread_default ();
diff --git a/gio/gdbusnamewatching.c b/gio/gdbusnamewatching.c
index dad8e75ec..01ee6e762 100644
--- a/gio/gdbusnamewatching.c
+++ b/gio/gdbusnamewatching.c
@@ -603,7 +603,7 @@ g_bus_watch_name (GBusType bus_type,
client = g_new0 (Client, 1);
client->ref_count = 1;
- client->id = g_atomic_int_add (&next_global_id, 1); /* TODO: uh oh, handle overflow */
+ client->id = (guint) g_atomic_int_add (&next_global_id, 1); /* TODO: uh oh, handle overflow */
client->name = g_strdup (name);
client->flags = flags;
client->name_appeared_handler = name_appeared_handler;
@@ -665,7 +665,7 @@ guint g_bus_watch_name_on_connection (GDBusConnection *connection,
client = g_new0 (Client, 1);
client->ref_count = 1;
- client->id = g_atomic_int_add (&next_global_id, 1); /* TODO: uh oh, handle overflow */
+ client->id = (guint) g_atomic_int_add (&next_global_id, 1); /* TODO: uh oh, handle overflow */
client->name = g_strdup (name);
client->flags = flags;
client->name_appeared_handler = name_appeared_handler;
diff --git a/gio/tests/gdbus-threading.c b/gio/tests/gdbus-threading.c
index 3e4dc92e5..13ff15bab 100644
--- a/gio/tests/gdbus-threading.c
+++ b/gio/tests/gdbus-threading.c
@@ -514,7 +514,7 @@ test_threaded_singleton (void)
/* We want to be the last ref, so let it finish setting up */
for (j = 0; j < 100; j++)
{
- guint r = g_atomic_int_get (&G_OBJECT (c)->ref_count);
+ guint r = (guint) g_atomic_int_get (&G_OBJECT (c)->ref_count);
if (r == 1)
break;
diff --git a/glib/gbitlock.c b/glib/gbitlock.c
index 46e5f7d06..23024d08c 100644
--- a/glib/gbitlock.c
+++ b/glib/gbitlock.c
@@ -224,7 +224,7 @@ g_bit_lock (volatile gint *address,
guint mask = 1u << lock_bit;
guint v;
- v = g_atomic_int_get (address);
+ v = (guint) g_atomic_int_get (address);
if (v & mask)
{
guint class = ((gsize) address) % G_N_ELEMENTS (g_bit_lock_contended);
diff --git a/glib/gquark.c b/glib/gquark.c
index c4d12b870..df12ff69a 100644
--- a/glib/gquark.c
+++ b/glib/gquark.c
@@ -262,7 +262,7 @@ g_quark_to_string (GQuark quark)
gchar **strings;
gint seq_id;
- seq_id = g_atomic_int_get (&quark_seq_id);
+ seq_id = (guint) g_atomic_int_get (&quark_seq_id);
strings = g_atomic_pointer_get (&quarks);
if (quark < seq_id)
diff --git a/glib/gthread-posix.c b/glib/gthread-posix.c
index 5fff51477..aca9208cc 100644
--- a/glib/gthread-posix.c
+++ b/glib/gthread-posix.c
@@ -1396,7 +1396,7 @@ void
g_cond_wait (GCond *cond,
GMutex *mutex)
{
- guint sampled = g_atomic_int_get (&cond->i[0]);
+ guint sampled = (guint) g_atomic_int_get (&cond->i[0]);
g_mutex_unlock (mutex);
syscall (__NR_futex, &cond->i[0], (gsize) FUTEX_WAIT_PRIVATE, (gsize) sampled, NULL);
diff --git a/glib/gthreadpool.c b/glib/gthreadpool.c
index dd7289370..7d75245b0 100644
--- a/glib/gthreadpool.c
+++ b/glib/gthreadpool.c
@@ -145,7 +145,7 @@ g_thread_pool_wait_for_new_pool (void)
gint last_wakeup_thread_serial;
gboolean have_relayed_thread_marker = FALSE;
- local_max_unused_threads = g_atomic_int_get (&max_unused_threads);
+ local_max_unused_threads = (guint) g_atomic_int_get (&max_unused_threads);
local_max_idle_time = g_atomic_int_get (&max_idle_time);
last_wakeup_thread_serial = g_atomic_int_get (&wakeup_thread_serial);
@@ -209,7 +209,7 @@ g_thread_pool_wait_for_new_pool (void)
DEBUG_MSG (("thread %p updating to new limits.",
g_thread_self ()));
- local_max_unused_threads = g_atomic_int_get (&max_unused_threads);
+ local_max_unused_threads = (guint) g_atomic_int_get (&max_unused_threads);
local_max_idle_time = g_atomic_int_get (&max_idle_time);
last_wakeup_thread_serial = local_wakeup_thread_serial;
@@ -893,7 +893,7 @@ g_thread_pool_get_max_unused_threads (void)
guint
g_thread_pool_get_num_unused_threads (void)
{
- return g_atomic_int_get (&unused_threads);
+ return (guint) g_atomic_int_get (&unused_threads);
}
/**
@@ -1016,7 +1016,7 @@ g_thread_pool_set_max_idle_time (guint interval)
g_atomic_int_set (&max_idle_time, interval);
- i = g_atomic_int_get (&unused_threads);
+ i = (guint) g_atomic_int_get (&unused_threads);
if (i > 0)
{
g_atomic_int_inc (&wakeup_thread_serial);
@@ -1052,5 +1052,5 @@ g_thread_pool_set_max_idle_time (guint interval)
guint
g_thread_pool_get_max_idle_time (void)
{
- return g_atomic_int_get (&max_idle_time);
+ return (guint) g_atomic_int_get (&max_idle_time);
}
diff --git a/glib/tests/atomic.c b/glib/tests/atomic.c
index 35fa705a4..856df12d2 100644
--- a/glib/tests/atomic.c
+++ b/glib/tests/atomic.c
@@ -27,7 +27,7 @@ test_types (void)
cspp = &csp;
g_atomic_int_set (&u, 5);
- u2 = g_atomic_int_get (&u);
+ u2 = (guint) g_atomic_int_get (&u);
g_assert_cmpint (u2, ==, 5);
res = g_atomic_int_compare_and_exchange (&u, 6, 7);
g_assert (!res);
@@ -62,13 +62,13 @@ test_types (void)
res = g_atomic_int_dec_and_test (&s);
g_assert (!res);
g_assert_cmpint (s, ==, 6);
- s2 = g_atomic_int_and (&s, 5);
+ s2 = (gint) g_atomic_int_and (&s, 5);
g_assert_cmpint (s2, ==, 6);
g_assert_cmpint (s, ==, 4);
- s2 = g_atomic_int_or (&s, 8);
+ s2 = (gint) g_atomic_int_or (&s, 8);
g_assert_cmpint (s2, ==, 4);
g_assert_cmpint (s, ==, 12);
- s2 = g_atomic_int_xor (&s, 4);
+ s2 = (gint) g_atomic_int_xor (&s, 4);
g_assert_cmpint (s2, ==, 12);
g_assert_cmpint (s, ==, 8);
@@ -92,7 +92,7 @@ test_types (void)
res = g_atomic_pointer_compare_and_exchange (&gs, 0, 0);
g_assert (res);
g_assert (gs == 0);
- gs2 = g_atomic_pointer_add (&gs, 5);
+ gs2 = (gsize) g_atomic_pointer_add (&gs, 5);
g_assert (gs2 == 0);
g_assert (gs == 5);
gs2 = g_atomic_pointer_and (&gs, 6);
@@ -127,7 +127,7 @@ test_types (void)
#undef g_atomic_pointer_xor
g_atomic_int_set ((gint*)&u, 5);
- u2 = g_atomic_int_get ((gint*)&u);
+ u2 = (guint) g_atomic_int_get ((gint*)&u);
g_assert_cmpint (u2, ==, 5);
res = g_atomic_int_compare_and_exchange ((gint*)&u, 6, 7);
g_assert (!res);
@@ -161,13 +161,13 @@ test_types (void)
res = g_atomic_int_dec_and_test (&s);
g_assert (!res);
g_assert_cmpint (s, ==, 6);
- s2 = g_atomic_int_and ((guint*)&s, 5);
+ s2 = (gint) g_atomic_int_and ((guint*)&s, 5);
g_assert_cmpint (s2, ==, 6);
g_assert_cmpint (s, ==, 4);
- s2 = g_atomic_int_or ((guint*)&s, 8);
+ s2 = (gint) g_atomic_int_or ((guint*)&s, 8);
g_assert_cmpint (s2, ==, 4);
g_assert_cmpint (s, ==, 12);
- s2 = g_atomic_int_xor ((guint*)&s, 4);
+ s2 = (gint) g_atomic_int_xor ((guint*)&s, 4);
g_assert_cmpint (s2, ==, 12);
g_assert_cmpint (s, ==, 8);
G_GNUC_BEGIN_IGNORE_DEPRECATIONS
@@ -196,7 +196,7 @@ G_GNUC_END_IGNORE_DEPRECATIONS
res = g_atomic_pointer_compare_and_exchange (&gs, 0, 0);
g_assert (res);
g_assert (gs == 0);
- gs2 = g_atomic_pointer_add (&gs, 5);
+ gs2 = (gsize) g_atomic_pointer_add (&gs, 5);
g_assert (gs2 == 0);
g_assert (gs == 5);
gs2 = g_atomic_pointer_and (&gs, 6);
--
2.50.0
From 5d6ced6a7fc6dbbc891992a7d16cd465f2ff1290 Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@endlessos.org>
Date: Wed, 22 Feb 2023 12:19:16 +0000
Subject: [PATCH 2/2] gdbusconnection: Rearrange refcount handling of
map_method_serial_to_task
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
It already implicitly held a strong ref on its `GTask` values, but
didnt have a free function set so that they would be automatically
unreffed on removal from the map.
This meant that the functions handling removals from the map,
`on_worker_closed()` (via `cancel_method_on_close()`) and
`send_message_with_reply_cleanup()` had to call unref once more than
they would otherwise.
In `send_message_with_reply_cleanup()`, this behaviour depended on
whether it was called with `remove == TRUE`. If not, it was `(transfer
none)` not `(transfer full)`. This led to bugs in its callers.
For example, this led to a direct leak in `cancel_method_on_close()`, as
it needed to remove tasks from `map_method_serial_to_task`, but called
`send_message_with_reply_cleanup(remove = FALSE)` and erroneously didnt
call unref an additional time.
Try and simplify it all by setting a `GDestroyNotify` on
`map_method_serial_to_task`s values, and making the refcount handling
of `send_message_with_reply_cleanup()` not be conditional on its
arguments.
Signed-off-by: Philip Withnall <pwithnall@endlessos.org>
Helps: #1264
---
gio/gdbusconnection.c | 18 ++++++++++--------
1 file changed, 10 insertions(+), 8 deletions(-)
diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c
index 1c1f0cf3d..63f37ca4b 100644
--- a/gio/gdbusconnection.c
+++ b/gio/gdbusconnection.c
@@ -433,7 +433,7 @@ struct _GDBusConnection
GDBusConnectionFlags flags;
/* Map used for managing method replies, protected by @lock */
- GHashTable *map_method_serial_to_task; /* guint32 -> GTask* */
+ GHashTable *map_method_serial_to_task; /* guint32 -> owned GTask* */
/* Maps used for managing signal subscription, protected by @lock */
GHashTable *map_rule_to_signal_data; /* match rule (gchar*) -> SignalData */
@@ -1067,7 +1067,7 @@ g_dbus_connection_init (GDBusConnection *connection)
g_mutex_init (&connection->lock);
g_mutex_init (&connection->init_lock);
- connection->map_method_serial_to_task = g_hash_table_new (g_direct_hash, g_direct_equal);
+ connection->map_method_serial_to_task = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
connection->map_rule_to_signal_data = g_hash_table_new (g_str_hash,
g_str_equal);
@@ -1759,7 +1759,7 @@ send_message_data_free (SendMessageData *data)
/* ---------------------------------------------------------------------------------------------------- */
-/* can be called from any thread with lock held; @task is (transfer full) */
+/* can be called from any thread with lock held; @task is (transfer none) */
static void
send_message_with_reply_cleanup (GTask *task, gboolean remove)
{
@@ -1789,13 +1789,11 @@ send_message_with_reply_cleanup (GTask *task, gboolean remove)
GUINT_TO_POINTER (data->serial));
g_warn_if_fail (removed);
}
-
- g_object_unref (task);
}
/* ---------------------------------------------------------------------------------------------------- */
-/* Called from GDBus worker thread with lock held; @task is (transfer full). */
+/* Called from GDBus worker thread with lock held; @task is (transfer none). */
static void
send_message_data_deliver_reply_unlocked (GTask *task,
GDBusMessage *reply)
@@ -1813,7 +1811,7 @@ send_message_data_deliver_reply_unlocked (GTask *task,
;
}
-/* Called from a user thread, lock is not held */
+/* Called from a user thread, lock is not held; @task is (transfer none) */
static void
send_message_data_deliver_error (GTask *task,
GQuark domain,
@@ -1830,7 +1828,10 @@ send_message_data_deliver_error (GTask *task,
return;
}
+ /* Hold a ref on @task as send_message_with_reply_cleanup() will remove it
+ * from the task map and could end up dropping the last reference */
g_object_ref (task);
+
send_message_with_reply_cleanup (task, TRUE);
CONNECTION_UNLOCK (connection);
@@ -2363,7 +2364,8 @@ on_worker_message_about_to_be_sent (GDBusWorker *worker,
return message;
}
-/* called with connection lock held, in GDBusWorker thread */
+/* called with connection lock held, in GDBusWorker thread
+ * @key, @value and @user_data are (transfer none) */
static gboolean
cancel_method_on_close (gpointer key, gpointer value, gpointer user_data)
{
--
2.50.0

View File

@ -1,780 +0,0 @@
From c53784092c2d69c4a1d916bb22235ffb8f0b5bad Mon Sep 17 00:00:00 2001
From: Philip Withnall <withnall@endlessm.com>
Date: Fri, 17 Jan 2020 16:11:06 +0000
Subject: [PATCH 1/5] gdbusconnection: Allocate SignalSubscriber structs
individually
The `SignalSubscriber` structs contain the callback and `user_data` of each
subscriber to a signal, along with the `guint id` token held by that
subscriber to identify their subscription. There are one or more
`SignalSubscriber` structs for a given signal match rule, which is
represented as a `SignalData` struct.
Previously, the `SignalSubscriber` structs were stored in a `GArray` in
the `SignalData` struct, to reduce the number of allocations needed
when subscribing to a signal.
However, this means that a `SignalSubscriber` struct cannot have a
lifetime which exceeds the `SignalData` which contains it. In order to
fix the race in #978, one thread needs to be able to unsubscribe from a
signal (destroying the `SignalData` struct) while zero or more other
threads are in the process of calling the callbacks from a previous
emission of that signal (using the callback and `user_data` from zero or
more `SignalSubscriber` structs). Multiple threads could be calling
callbacks because callbacks are invoked in the `GMainContext` which
originally made a subscription, and GDBus supports subscribing to a
signal from multiple threads. In that case, the callbacks are dispatched
to multiple threads.
In order to allow the `SignalSubscriber` structs to outlive the
`SignalData` which contained their old match rule, store them in a
`GPtrArray` in the `SignalData` struct, and refcount them individually.
This commit in itself should make no functional changes to how GDBus
works, but will allow following commits to do so.
Signed-off-by: Philip Withnall <withnall@endlessm.com>
Helps: #978
---
gio/gdbusconnection.c | 105 +++++++++++++++++++++++++-----------------
1 file changed, 63 insertions(+), 42 deletions(-)
diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c
index 63f37ca4b..170057682 100644
--- a/gio/gdbusconnection.c
+++ b/gio/gdbusconnection.c
@@ -3229,18 +3229,9 @@ typedef struct
gchar *object_path;
gchar *arg0;
GDBusSignalFlags flags;
- GArray *subscribers;
+ GPtrArray *subscribers; /* (owned) (element-type SignalSubscriber) */
} SignalData;
-typedef struct
-{
- GDBusSignalCallback callback;
- gpointer user_data;
- GDestroyNotify user_data_free_func;
- guint id;
- GMainContext *context;
-} SignalSubscriber;
-
static void
signal_data_free (SignalData *signal_data)
{
@@ -3251,10 +3242,37 @@ signal_data_free (SignalData *signal_data)
g_free (signal_data->member);
g_free (signal_data->object_path);
g_free (signal_data->arg0);
- g_array_free (signal_data->subscribers, TRUE);
+ g_ptr_array_unref (signal_data->subscribers);
g_free (signal_data);
}
+typedef struct
+{
+ gatomicrefcount ref_count;
+ GDBusSignalCallback callback;
+ gpointer user_data;
+ GDestroyNotify user_data_free_func;
+ guint id;
+ GMainContext *context;
+} SignalSubscriber;
+
+static SignalSubscriber *
+signal_subscriber_ref (SignalSubscriber *subscriber)
+{
+ g_atomic_ref_count_inc (&subscriber->ref_count);
+ return subscriber;
+}
+
+static void
+signal_subscriber_unref (SignalSubscriber *subscriber)
+{
+ if (g_atomic_ref_count_dec (&subscriber->ref_count))
+ {
+ g_main_context_unref (subscriber->context);
+ g_free (subscriber);
+ }
+}
+
static gchar *
args_to_rule (const gchar *sender,
const gchar *interface_name,
@@ -3440,7 +3458,7 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection,
{
gchar *rule;
SignalData *signal_data;
- SignalSubscriber subscriber;
+ SignalSubscriber *subscriber;
GPtrArray *signal_data_array;
const gchar *sender_unique_name;
@@ -3481,17 +3499,19 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection,
else
sender_unique_name = "";
- subscriber.callback = callback;
- subscriber.user_data = user_data;
- subscriber.user_data_free_func = user_data_free_func;
- subscriber.id = (guint) g_atomic_int_add (&_global_subscriber_id, 1); /* TODO: overflow etc. */
- subscriber.context = g_main_context_ref_thread_default ();
+ subscriber = g_new0 (SignalSubscriber, 1);
+ subscriber->ref_count = 1;
+ subscriber->callback = callback;
+ subscriber->user_data = user_data;
+ subscriber->user_data_free_func = user_data_free_func;
+ subscriber->id = (guint) g_atomic_int_add (&_global_subscriber_id, 1); /* TODO: overflow etc. */
+ subscriber->context = g_main_context_ref_thread_default ();
/* see if we've already have this rule */
signal_data = g_hash_table_lookup (connection->map_rule_to_signal_data, rule);
if (signal_data != NULL)
{
- g_array_append_val (signal_data->subscribers, subscriber);
+ g_ptr_array_add (signal_data->subscribers, subscriber);
g_free (rule);
goto out;
}
@@ -3505,8 +3525,8 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection,
signal_data->object_path = g_strdup (object_path);
signal_data->arg0 = g_strdup (arg0);
signal_data->flags = flags;
- signal_data->subscribers = g_array_new (FALSE, FALSE, sizeof (SignalSubscriber));
- g_array_append_val (signal_data->subscribers, subscriber);
+ signal_data->subscribers = g_ptr_array_new_with_free_func ((GDestroyNotify) signal_subscriber_unref);
+ g_ptr_array_add (signal_data->subscribers, subscriber);
g_hash_table_insert (connection->map_rule_to_signal_data,
signal_data->rule,
@@ -3536,12 +3556,12 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection,
out:
g_hash_table_insert (connection->map_id_to_signal_data,
- GUINT_TO_POINTER (subscriber.id),
+ GUINT_TO_POINTER (subscriber->id),
signal_data);
CONNECTION_UNLOCK (connection);
- return subscriber.id;
+ return subscriber->id;
}
/* ---------------------------------------------------------------------------------------------------- */
@@ -3551,7 +3571,7 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection,
static void
unsubscribe_id_internal (GDBusConnection *connection,
guint subscription_id,
- GArray *out_removed_subscribers)
+ GPtrArray *out_removed_subscribers)
{
SignalData *signal_data;
GPtrArray *signal_data_array;
@@ -3567,16 +3587,19 @@ unsubscribe_id_internal (GDBusConnection *connection,
for (n = 0; n < signal_data->subscribers->len; n++)
{
- SignalSubscriber *subscriber;
+ SignalSubscriber *subscriber = signal_data->subscribers->pdata[n];
- subscriber = &(g_array_index (signal_data->subscribers, SignalSubscriber, n));
if (subscriber->id != subscription_id)
continue;
+ /* Its OK to rearrange the array order using the fast #GPtrArray
+ * removal functions, since were going to exit the loop below anyway — we
+ * never move on to the next element. Secondly, subscription IDs are
+ * guaranteed to be unique. */
g_warn_if_fail (g_hash_table_remove (connection->map_id_to_signal_data,
GUINT_TO_POINTER (subscription_id)));
- g_array_append_val (out_removed_subscribers, *subscriber);
- g_array_remove_index (signal_data->subscribers, n);
+ g_ptr_array_add (out_removed_subscribers, signal_subscriber_ref (subscriber));
+ g_ptr_array_remove_index_fast (signal_data->subscribers, n);
if (signal_data->subscribers->len == 0)
{
@@ -3634,13 +3657,13 @@ void
g_dbus_connection_signal_unsubscribe (GDBusConnection *connection,
guint subscription_id)
{
- GArray *subscribers;
+ GPtrArray *subscribers;
guint n;
g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
g_return_if_fail (check_initialized (connection));
- subscribers = g_array_new (FALSE, FALSE, sizeof (SignalSubscriber));
+ subscribers = g_ptr_array_new_with_free_func ((GDestroyNotify) signal_subscriber_unref);
CONNECTION_LOCK (connection);
unsubscribe_id_internal (connection,
@@ -3654,15 +3677,15 @@ g_dbus_connection_signal_unsubscribe (GDBusConnection *connection,
/* call GDestroyNotify without lock held */
for (n = 0; n < subscribers->len; n++)
{
- SignalSubscriber *subscriber;
- subscriber = &(g_array_index (subscribers, SignalSubscriber, n));
+ SignalSubscriber *subscriber = subscribers->pdata[n];
call_destroy_notify (subscriber->context,
subscriber->user_data_free_func,
subscriber->user_data);
- g_main_context_unref (subscriber->context);
+ subscriber->user_data_free_func = NULL;
+ subscriber->user_data = NULL;
}
- g_array_free (subscribers, TRUE);
+ g_ptr_array_unref (subscribers);
}
/* ---------------------------------------------------------------------------------------------------- */
@@ -3852,12 +3875,10 @@ schedule_callbacks (GDBusConnection *connection,
for (m = 0; m < signal_data->subscribers->len; m++)
{
- SignalSubscriber *subscriber;
+ SignalSubscriber *subscriber = signal_data->subscribers->pdata[m];
GSource *idle_source;
SignalInstance *signal_instance;
- subscriber = &(g_array_index (signal_data->subscribers, SignalSubscriber, m));
-
signal_instance = g_new0 (SignalInstance, 1);
signal_instance->subscription_id = subscriber->id;
signal_instance->callback = subscriber->callback;
@@ -3930,7 +3951,7 @@ purge_all_signal_subscriptions (GDBusConnection *connection)
GHashTableIter iter;
gpointer key;
GArray *ids;
- GArray *subscribers;
+ GPtrArray *subscribers;
guint n;
ids = g_array_new (FALSE, FALSE, sizeof (guint));
@@ -3941,7 +3962,7 @@ purge_all_signal_subscriptions (GDBusConnection *connection)
g_array_append_val (ids, subscription_id);
}
- subscribers = g_array_new (FALSE, FALSE, sizeof (SignalSubscriber));
+ subscribers = g_ptr_array_new_with_free_func ((GDestroyNotify) signal_subscriber_unref);
for (n = 0; n < ids->len; n++)
{
guint subscription_id = g_array_index (ids, guint, n);
@@ -3954,15 +3975,15 @@ purge_all_signal_subscriptions (GDBusConnection *connection)
/* call GDestroyNotify without lock held */
for (n = 0; n < subscribers->len; n++)
{
- SignalSubscriber *subscriber;
- subscriber = &(g_array_index (subscribers, SignalSubscriber, n));
+ SignalSubscriber *subscriber = subscribers->pdata[n];
call_destroy_notify (subscriber->context,
subscriber->user_data_free_func,
subscriber->user_data);
- g_main_context_unref (subscriber->context);
+ subscriber->user_data_free_func = NULL;
+ subscriber->user_data = NULL;
}
- g_array_free (subscribers, TRUE);
+ g_ptr_array_unref (subscribers);
}
/* ---------------------------------------------------------------------------------------------------- */
--
2.50.0
From d21d9fb02496f21864e4175d08b5f55416e832e6 Mon Sep 17 00:00:00 2001
From: Philip Withnall <withnall@endlessm.com>
Date: Fri, 17 Jan 2020 19:52:46 +0000
Subject: [PATCH 2/5] gdbusconnection: Tidy up destroy notification for signal
subscriptions
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Tie the destruction of the `user_data` to the destruction of the
`SignalSubscriber` struct. This is tidier, and ensures that the fields
in `SignalSubscriber` are all immutable after being set, so the
structure can safely be used across threads without locking.
It doesnt matter which thread we call `call_destroy_notify()` in, since
it always defers calling `user_data_free_func` to the user-provided
`GMainContext`.
Signed-off-by: Philip Withnall <withnall@endlessm.com>
Helps: #978
---
gio/gdbusconnection.c | 32 +++++++++-----------------------
1 file changed, 9 insertions(+), 23 deletions(-)
diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c
index 170057682..2dba09fcc 100644
--- a/gio/gdbusconnection.c
+++ b/gio/gdbusconnection.c
@@ -3248,6 +3248,7 @@ signal_data_free (SignalData *signal_data)
typedef struct
{
+ /* All fields are immutable after construction. */
gatomicrefcount ref_count;
GDBusSignalCallback callback;
gpointer user_data;
@@ -3268,6 +3269,14 @@ signal_subscriber_unref (SignalSubscriber *subscriber)
{
if (g_atomic_ref_count_dec (&subscriber->ref_count))
{
+ /* Destroy the user data. It doesnt matter which thread
+ * signal_subscriber_unref() is called in (or whether its called with a
+ * lock held), as call_destroy_notify() always defers to the next
+ * #GMainContext iteration. */
+ call_destroy_notify (subscriber->context,
+ subscriber->user_data_free_func,
+ subscriber->user_data);
+
g_main_context_unref (subscriber->context);
g_free (subscriber);
}
@@ -3658,7 +3667,6 @@ g_dbus_connection_signal_unsubscribe (GDBusConnection *connection,
guint subscription_id)
{
GPtrArray *subscribers;
- guint n;
g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
g_return_if_fail (check_initialized (connection));
@@ -3674,17 +3682,6 @@ g_dbus_connection_signal_unsubscribe (GDBusConnection *connection,
/* invariant */
g_assert (subscribers->len == 0 || subscribers->len == 1);
- /* call GDestroyNotify without lock held */
- for (n = 0; n < subscribers->len; n++)
- {
- SignalSubscriber *subscriber = subscribers->pdata[n];
- call_destroy_notify (subscriber->context,
- subscriber->user_data_free_func,
- subscriber->user_data);
- subscriber->user_data_free_func = NULL;
- subscriber->user_data = NULL;
- }
-
g_ptr_array_unref (subscribers);
}
@@ -3972,17 +3969,6 @@ purge_all_signal_subscriptions (GDBusConnection *connection)
}
g_array_free (ids, TRUE);
- /* call GDestroyNotify without lock held */
- for (n = 0; n < subscribers->len; n++)
- {
- SignalSubscriber *subscriber = subscribers->pdata[n];
- call_destroy_notify (subscriber->context,
- subscriber->user_data_free_func,
- subscriber->user_data);
- subscriber->user_data_free_func = NULL;
- subscriber->user_data = NULL;
- }
-
g_ptr_array_unref (subscribers);
}
--
2.50.0
From 5b7c9398a67ad274cb8faef8bf45461540a7198e Mon Sep 17 00:00:00 2001
From: Philip Withnall <withnall@endlessm.com>
Date: Fri, 17 Jan 2020 19:38:55 +0000
Subject: [PATCH 3/5] gdbusconnection: Fix race when emitting D-Bus signal
callbacks
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Instead of storing a copy of the `callback` and `user_data` from a
`SignalSubscriber` in a `SignalInstance` struct (which is the closure
for signal callback data as its sent from the D-Bus worker thread to
the thread which originally subscribed to a signal), store a strong
reference to the `SignalSubscriber` struct itself.
This keeps the `SignalSubscriber` alive until the emission is
complete, which ensures that the `user_data` is not freed prematurely.
It also slightly reduces the allocation size of `SignalInstance` (not
that it matters).
This is threadsafe because the fields in `SignalSubscriber` are all
immutable after construction.
Signed-off-by: Philip Withnall <withnall@endlessm.com>
Helps: #978
---
gio/gdbusconnection.c | 27 ++++++++++++---------------
1 file changed, 12 insertions(+), 15 deletions(-)
diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c
index 2dba09fcc..4cae53b29 100644
--- a/gio/gdbusconnection.c
+++ b/gio/gdbusconnection.c
@@ -3689,9 +3689,7 @@ g_dbus_connection_signal_unsubscribe (GDBusConnection *connection,
typedef struct
{
- guint subscription_id;
- GDBusSignalCallback callback;
- gpointer user_data;
+ SignalSubscriber *subscriber; /* (owned) */
GDBusMessage *message;
GDBusConnection *connection;
const gchar *sender;
@@ -3723,7 +3721,7 @@ emit_signal_instance_in_idle_cb (gpointer data)
#if 0
g_print ("in emit_signal_instance_in_idle_cb (id=%d sender=%s path=%s interface=%s member=%s params=%s)\n",
- signal_instance->subscription_id,
+ signal_instance->subscriber->id,
signal_instance->sender,
signal_instance->path,
signal_instance->interface,
@@ -3735,18 +3733,18 @@ emit_signal_instance_in_idle_cb (gpointer data)
CONNECTION_LOCK (signal_instance->connection);
has_subscription = FALSE;
if (g_hash_table_lookup (signal_instance->connection->map_id_to_signal_data,
- GUINT_TO_POINTER (signal_instance->subscription_id)) != NULL)
+ GUINT_TO_POINTER (signal_instance->subscriber->id)) != NULL)
has_subscription = TRUE;
CONNECTION_UNLOCK (signal_instance->connection);
if (has_subscription)
- signal_instance->callback (signal_instance->connection,
- signal_instance->sender,
- signal_instance->path,
- signal_instance->interface,
- signal_instance->member,
- parameters,
- signal_instance->user_data);
+ signal_instance->subscriber->callback (signal_instance->connection,
+ signal_instance->sender,
+ signal_instance->path,
+ signal_instance->interface,
+ signal_instance->member,
+ parameters,
+ signal_instance->subscriber->user_data);
g_variant_unref (parameters);
@@ -3758,6 +3756,7 @@ signal_instance_free (SignalInstance *signal_instance)
{
g_object_unref (signal_instance->message);
g_object_unref (signal_instance->connection);
+ signal_subscriber_unref (signal_instance->subscriber);
g_free (signal_instance);
}
@@ -3877,9 +3876,7 @@ schedule_callbacks (GDBusConnection *connection,
SignalInstance *signal_instance;
signal_instance = g_new0 (SignalInstance, 1);
- signal_instance->subscription_id = subscriber->id;
- signal_instance->callback = subscriber->callback;
- signal_instance->user_data = subscriber->user_data;
+ signal_instance->subscriber = signal_subscriber_ref (subscriber);
signal_instance->message = g_object_ref (message);
signal_instance->connection = g_object_ref (connection);
signal_instance->sender = sender;
--
2.50.0
From a13bbdc1dc0cd4ec16c3e75d0cc1c07eb791b3bc Mon Sep 17 00:00:00 2001
From: Philip Withnall <withnall@endlessm.com>
Date: Fri, 17 Jan 2020 19:56:58 +0000
Subject: [PATCH 4/5] gdbusconnection: Tidy up unsubscription code
This just removes a now-redundant intermediate array. This means that
the `SignalSubscriber` instances are now potentially freed a little
sooner, inside the locked segment, but they are already careful to only
call their `user_data_free_func` in the right thread. So that should not
deadlock.
Signed-off-by: Philip Withnall <withnall@endlessm.com>
Helps: #978
---
gio/gdbusconnection.c | 33 +++++++++++----------------------
1 file changed, 11 insertions(+), 22 deletions(-)
diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c
index 4cae53b29..4d99f8570 100644
--- a/gio/gdbusconnection.c
+++ b/gio/gdbusconnection.c
@@ -3576,15 +3576,16 @@ g_dbus_connection_signal_subscribe (GDBusConnection *connection,
/* ---------------------------------------------------------------------------------------------------- */
/* called in any thread */
-/* must hold lock when calling this (except if connection->finalizing is TRUE) */
-static void
+/* must hold lock when calling this (except if connection->finalizing is TRUE)
+ * returns the number of removed subscribers */
+static guint
unsubscribe_id_internal (GDBusConnection *connection,
- guint subscription_id,
- GPtrArray *out_removed_subscribers)
+ guint subscription_id)
{
SignalData *signal_data;
GPtrArray *signal_data_array;
guint n;
+ guint n_removed = 0;
signal_data = g_hash_table_lookup (connection->map_id_to_signal_data,
GUINT_TO_POINTER (subscription_id));
@@ -3607,7 +3608,7 @@ unsubscribe_id_internal (GDBusConnection *connection,
* guaranteed to be unique. */
g_warn_if_fail (g_hash_table_remove (connection->map_id_to_signal_data,
GUINT_TO_POINTER (subscription_id)));
- g_ptr_array_add (out_removed_subscribers, signal_subscriber_ref (subscriber));
+ n_removed++;
g_ptr_array_remove_index_fast (signal_data->subscribers, n);
if (signal_data->subscribers->len == 0)
@@ -3649,7 +3650,7 @@ unsubscribe_id_internal (GDBusConnection *connection,
g_assert_not_reached ();
out:
- ;
+ return n_removed;
}
/**
@@ -3666,23 +3667,17 @@ void
g_dbus_connection_signal_unsubscribe (GDBusConnection *connection,
guint subscription_id)
{
- GPtrArray *subscribers;
+ guint n_subscribers_removed G_GNUC_UNUSED /* when compiling with G_DISABLE_ASSERT */;
g_return_if_fail (G_IS_DBUS_CONNECTION (connection));
g_return_if_fail (check_initialized (connection));
- subscribers = g_ptr_array_new_with_free_func ((GDestroyNotify) signal_subscriber_unref);
-
CONNECTION_LOCK (connection);
- unsubscribe_id_internal (connection,
- subscription_id,
- subscribers);
+ n_subscribers_removed = unsubscribe_id_internal (connection, subscription_id);
CONNECTION_UNLOCK (connection);
/* invariant */
- g_assert (subscribers->len == 0 || subscribers->len == 1);
-
- g_ptr_array_unref (subscribers);
+ g_assert (n_subscribers_removed == 0 || n_subscribers_removed == 1);
}
/* ---------------------------------------------------------------------------------------------------- */
@@ -3945,7 +3940,6 @@ purge_all_signal_subscriptions (GDBusConnection *connection)
GHashTableIter iter;
gpointer key;
GArray *ids;
- GPtrArray *subscribers;
guint n;
ids = g_array_new (FALSE, FALSE, sizeof (guint));
@@ -3956,17 +3950,12 @@ purge_all_signal_subscriptions (GDBusConnection *connection)
g_array_append_val (ids, subscription_id);
}
- subscribers = g_ptr_array_new_with_free_func ((GDestroyNotify) signal_subscriber_unref);
for (n = 0; n < ids->len; n++)
{
guint subscription_id = g_array_index (ids, guint, n);
- unsubscribe_id_internal (connection,
- subscription_id,
- subscribers);
+ unsubscribe_id_internal (connection, subscription_id);
}
g_array_free (ids, TRUE);
-
- g_ptr_array_unref (subscribers);
}
/* ---------------------------------------------------------------------------------------------------- */
--
2.50.0
From 0ec439aab1da5df011bed6a1a317439efacb6c25 Mon Sep 17 00:00:00 2001
From: Philip Withnall <withnall@endlessm.com>
Date: Fri, 17 Jan 2020 20:00:22 +0000
Subject: [PATCH 5/5] gdbusnameowning: Fix race between connection shutdown and
NameLost
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
As with all D-Bus signal subscriptions, its possible for a signal
callback to be invoked in one thread (T1) while another thread (T2) is
unsubscribing from that signal. In this case, T1 is the main thread, and
T2 is the D-Bus connection worker thread which is unsubscribing all
signals as its in the process of closing.
Due to this possibility, all `user_data` for signal callbacks needs to
be referenced outside the lifecycle of the code which
subscribes/unsubscribes the signal. In other words, its not safe to
subscribe to a signal, store the subscription ID in a struct,
unsubscribe from the signal when freeing the struct, and dereference the
struct in the signal callback. The data passed to the signal callback
has to have its own strong reference.
Instead, its safe to subscribe to a signal and add a strong reference
to the struct, store the subscription ID in that struct, and unsubscribe
from the signal when the last external reference to your struct is
dropped. That unsubscription should break the refcount cycle between the
signal connection and the struct, and allow the struct to be completely
freed. Only with that approach is it safe to dereference the struct in
the signal callback, if theres any possibility that the signal might be
unsubscribed from a separate thread.
The tests need specific additional main loop cycles to completely emit
the NameLost signal callback. Ideally they need refactoring, but this
will do (1000 test cycles passed).
Signed-off-by: Philip Withnall <withnall@endlessm.com>
Fixes: #978
---
gio/gdbusnameowning.c | 15 ++++++++++-----
gio/tests/gdbus-names.c | 11 ++++++++++-
2 files changed, 20 insertions(+), 6 deletions(-)
diff --git a/gio/gdbusnameowning.c b/gio/gdbusnameowning.c
index 2c2714db2..00554c16a 100644
--- a/gio/gdbusnameowning.c
+++ b/gio/gdbusnameowning.c
@@ -366,7 +366,12 @@ request_name_cb (GObject *source_object,
connection = g_object_ref (client->connection);
G_UNLOCK (lock);
- /* start listening to NameLost and NameAcquired messages */
+ /* Start listening to NameLost and NameAcquired messages. We hold
+ * references to the Client in the signal closures, since its possible
+ * for a signal to be in-flight after unsubscribing the signal handler.
+ * This creates a reference count cycle, but thats explicitly broken by
+ * disconnecting the signal handlers before calling client_unref() in
+ * g_bus_unown_name(). */
if (connection != NULL)
{
client->name_lost_subscription_id =
@@ -378,8 +383,8 @@ request_name_cb (GObject *source_object,
client->name,
G_DBUS_SIGNAL_FLAGS_NONE,
on_name_lost_or_acquired,
- client,
- NULL);
+ client_ref (client),
+ (GDestroyNotify) client_unref);
client->name_acquired_subscription_id =
g_dbus_connection_signal_subscribe (connection,
"org.freedesktop.DBus",
@@ -389,8 +394,8 @@ request_name_cb (GObject *source_object,
client->name,
G_DBUS_SIGNAL_FLAGS_NONE,
on_name_lost_or_acquired,
- client,
- NULL);
+ client_ref (client),
+ (GDestroyNotify) client_unref);
g_object_unref (connection);
}
}
diff --git a/gio/tests/gdbus-names.c b/gio/tests/gdbus-names.c
index 648b54774..b42ba2141 100644
--- a/gio/tests/gdbus-names.c
+++ b/gio/tests/gdbus-names.c
@@ -189,6 +189,7 @@ test_bus_own_name (void)
* Stop owning the name - this should invoke our free func
*/
g_bus_unown_name (id);
+ g_main_loop_run (loop);
g_assert_cmpint (data.num_free_func, ==, 2);
/*
@@ -330,6 +331,7 @@ test_bus_own_name (void)
g_assert_cmpint (data2.num_acquired, ==, 0);
g_assert_cmpint (data2.num_lost, ==, 1);
g_bus_unown_name (id2);
+ g_main_loop_run (loop);
g_assert_cmpint (data2.num_bus_acquired, ==, 0);
g_assert_cmpint (data2.num_acquired, ==, 0);
g_assert_cmpint (data2.num_lost, ==, 1);
@@ -355,6 +357,7 @@ test_bus_own_name (void)
g_assert_cmpint (data2.num_acquired, ==, 0);
g_assert_cmpint (data2.num_lost, ==, 1);
g_bus_unown_name (id2);
+ g_main_loop_run (loop);
g_assert_cmpint (data2.num_bus_acquired, ==, 0);
g_assert_cmpint (data2.num_acquired, ==, 0);
g_assert_cmpint (data2.num_lost, ==, 1);
@@ -365,6 +368,7 @@ test_bus_own_name (void)
*/
data.expect_null_connection = FALSE;
g_bus_unown_name (id);
+ g_main_loop_run (loop);
g_assert_cmpint (data.num_bus_acquired, ==, 1);
g_assert_cmpint (data.num_acquired, ==, 1);
g_assert_cmpint (data.num_free_func, ==, 4);
@@ -418,6 +422,7 @@ test_bus_own_name (void)
g_assert_cmpint (data2.num_acquired, ==, 0);
g_assert_cmpint (data2.num_lost, ==, 1);
g_bus_unown_name (id2);
+ g_main_loop_run (loop);
g_assert_cmpint (data2.num_bus_acquired, ==, 0);
g_assert_cmpint (data2.num_acquired, ==, 0);
g_assert_cmpint (data2.num_lost, ==, 1);
@@ -450,8 +455,9 @@ test_bus_own_name (void)
g_assert_cmpint (data2.num_bus_acquired, ==, 0);
/* ok, make owner2 release the name - then wait for owner to automagically reacquire it */
g_bus_unown_name (id2);
- g_assert_cmpint (data2.num_free_func, ==, 1);
g_main_loop_run (loop);
+ g_main_loop_run (loop);
+ g_assert_cmpint (data2.num_free_func, ==, 1);
g_assert_cmpint (data.num_acquired, ==, 2);
g_assert_cmpint (data.num_lost, ==, 1);
@@ -466,6 +472,7 @@ test_bus_own_name (void)
g_assert_cmpint (data.num_acquired, ==, 2);
g_assert_cmpint (data.num_lost, ==, 2);
g_bus_unown_name (id);
+ g_main_loop_run (loop);
g_assert_cmpint (data.num_free_func, ==, 5);
g_object_unref (c);
@@ -648,6 +655,7 @@ test_bus_watch_name (void)
/* unown the name */
g_bus_unown_name (owner_id);
+ g_main_loop_run (loop);
g_assert_cmpint (data.num_acquired, ==, 1);
g_assert_cmpint (data.num_free_func, ==, 2);
@@ -707,6 +715,7 @@ test_bus_watch_name (void)
g_assert_cmpint (data.num_free_func, ==, 1);
g_bus_unown_name (owner_id);
+ g_main_loop_run (loop);
g_assert_cmpint (data.num_free_func, ==, 2);
session_bus_down ();
--
2.50.0

View File

@ -1,70 +0,0 @@
From f3eecc88f4f45b128c963d695a61b230d2665db5 Mon Sep 17 00:00:00 2001
From: Philip Withnall <pwithnall@gnome.org>
Date: Mon, 3 Feb 2025 18:27:21 +0000
Subject: [PATCH] gdbusconnection: Prevent sending a serial of zero on overflow
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
It finally happened: someone managed to keep a process alive long
enough, and using a single `GDBusConnection`, to overflow the
`last_serial` counter in the connection and send an invalid message with
serial of zero (which is disallowed by the D-Bus specification).
Avoid that happening in future by skipping serials of zero on overflow,
and wrapping straight back around to 1.
This looks a little more confusing than it is, because `last_serial` is
pre-incremented on use, so to skip zero, we explicitly set it to zero.
This is exactly what happens when the `GDBusConnection` is initialised
anyway.
I cant think of a way to add a unit test for this — there is no way to
affect the value of `last_serial` except by sending messages (each one
increments it), and in order to get it to overflow by sending messages
at 1kHz, the test would have to run for 49 days.
Instead, I tested this manually by temporarily modifying
`GDBusConnection` to initialise `last_serial` to `G_MAXUINT32 - 3`, then
checked that the unit tests all still passed, and that the overflow code
was being executed.
Signed-off-by: Philip Withnall <pwithnall@gnome.org>
Fixes: #3592
---
gio/gdbusconnection.c | 17 +++++++++++++++--
1 file changed, 15 insertions(+), 2 deletions(-)
diff --git a/gio/gdbusconnection.c b/gio/gdbusconnection.c
index b4cdc7e..45d7861 100644
--- a/gio/gdbusconnection.c
+++ b/gio/gdbusconnection.c
@@ -1790,9 +1790,22 @@ g_dbus_connection_send_message_unlocked (GDBusConnection *connection,
goto out;
if (flags & G_DBUS_SEND_MESSAGE_FLAGS_PRESERVE_SERIAL)
- serial_to_use = g_dbus_message_get_serial (message);
+ {
+ serial_to_use = g_dbus_message_get_serial (message);
+ }
else
- serial_to_use = ++connection->last_serial; /* TODO: handle overflow */
+ {
+ /* The serial_to_use must not be zero, as per
+ * https://dbus.freedesktop.org/doc/dbus-specification.html#message-protocol-messages. */
+ if (connection->last_serial == G_MAXUINT32)
+ connection->last_serial = 1;
+ else
+ connection->last_serial++;
+
+ serial_to_use = connection->last_serial;
+ }
+
+ g_assert (serial_to_use != 0);
switch (blob[0])
{
--
2.47.3

View File

@ -1,510 +0,0 @@
From 21ca525a8729c6b64133b00b3e382a2ce5478a9a Mon Sep 17 00:00:00 2001
From: Christian Hergert <chergert@redhat.com>
Date: Fri, 21 Nov 2025 12:31:13 -0800
Subject: [PATCH 1/5] gio/gunixmounts: mark some file-system types as system
Since this list was originally created, more file system types have
become commonly used and would benefit from being marked as a system
file-system type.
This was found while tracking down some performance issues in
gnome-settings-daemon trash handling.
---
gio/gunixmounts.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/gio/gunixmounts.c b/gio/gunixmounts.c
index 3514e2e8e..7d7b82980 100644
--- a/gio/gunixmounts.c
+++ b/gio/gunixmounts.c
@@ -306,6 +306,8 @@ g_unix_is_system_fs_type (const char *fs_type)
"auto",
"autofs",
"autofs4",
+ "binfmt_misc",
+ "bpf",
"cgroup",
"configfs",
"cxfs",
@@ -314,6 +316,7 @@ g_unix_is_system_fs_type (const char *fs_type)
"devpts",
"devtmpfs",
"ecryptfs",
+ "efivarfs",
"fdescfs",
"fusectl",
"gfs",
@@ -342,6 +345,7 @@ g_unix_is_system_fs_type (const char *fs_type)
"selinuxfs",
"sysfs",
"tmpfs",
+ "tracefs",
"usbfs",
"zfs",
NULL
--
2.52.0
From b5525b89de94b2a84f4f77603f4cd5eae7c27b1b Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Mon, 1 Dec 2025 15:36:02 +0100
Subject: [PATCH 2/5] gio/gunixmounts: Mark more file systems as system
internal
The commit f1a90a67 updated list of system internal file systems.
I think we can add a few more file systems (i.e. `cgroups2`,
`fuse.gvfsd-fuse`, `fuse.portal`) and `/bin/efi` path. This is to
improve performance of `gvfsd-trash`, `gsd-houskeeping` and similar.
Related: https://gitlab.gnome.org/GNOME/gvfs/-/issues/814
---
gio/gunixmounts.c | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/gio/gunixmounts.c b/gio/gunixmounts.c
index 7d7b82980..f9c3f9fec 100644
--- a/gio/gunixmounts.c
+++ b/gio/gunixmounts.c
@@ -227,6 +227,7 @@ g_unix_is_mount_path_system_internal (const char *mount_path)
*/
"/", /* we already have "Filesystem root" in Nautilus */
"/bin",
+ "/bin/efi",
"/boot",
"/compat/linux/proc",
"/compat/linux/sys",
@@ -309,6 +310,7 @@ g_unix_is_system_fs_type (const char *fs_type)
"binfmt_misc",
"bpf",
"cgroup",
+ "cgroup2",
"configfs",
"cxfs",
"debugfs",
@@ -318,6 +320,8 @@ g_unix_is_system_fs_type (const char *fs_type)
"ecryptfs",
"efivarfs",
"fdescfs",
+ "fuse.gvfsd-fuse",
+ "fuse.portal",
"fusectl",
"gfs",
"gfs2",
--
2.52.0
From e8e7229a72b92844c9928dde48cfa8c660cfe658 Mon Sep 17 00:00:00 2001
From: Ondrej Holy <oholy@redhat.com>
Date: Wed, 3 Dec 2025 10:02:15 +0100
Subject: [PATCH 3/5] gio/gunixmounts: Replace /bin/efi with /boot/efi
The commit 06e9f2c0 added `/bin/efi` instead of `/boot/efi` to the
list of system internal mount paths by mistake. Let's fix it.
---
gio/gunixmounts.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/gio/gunixmounts.c b/gio/gunixmounts.c
index f9c3f9fec..a35e9fc43 100644
--- a/gio/gunixmounts.c
+++ b/gio/gunixmounts.c
@@ -227,8 +227,8 @@ g_unix_is_mount_path_system_internal (const char *mount_path)
*/
"/", /* we already have "Filesystem root" in Nautilus */
"/bin",
- "/bin/efi",
"/boot",
+ "/boot/efi",
"/compat/linux/proc",
"/compat/linux/sys",
"/dev",
--
2.52.0
From b761e2b3917a6cef5814717c06c19b9bd835096e Mon Sep 17 00:00:00 2001
From: Christian Hergert <chergert@redhat.com>
Date: Fri, 21 Nov 2025 18:54:33 -0800
Subject: [PATCH 4/5] gio/unixmounts: use bsearch() to check for set inclusion
This turns out to be about 17% faster than the previous set comparisons
on large (in the thousands) /proc/mounts configurations. It does require
that we keep the lists sorted but ended up faster than gperf hashing.
---
gio/gunixmounts.c | 61 +++++++++++++++++++++++++----------------------
1 file changed, 32 insertions(+), 29 deletions(-)
diff --git a/gio/gunixmounts.c b/gio/gunixmounts.c
index a35e9fc43..ae4174d4f 100644
--- a/gio/gunixmounts.c
+++ b/gio/gunixmounts.c
@@ -39,6 +39,7 @@
#include <unistd.h>
#include <sys/time.h>
#include <errno.h>
+#include <stdlib.h>
#include <string.h>
#include <signal.h>
#include <gstdio.h>
@@ -194,16 +195,17 @@ static guint64 mount_poller_time = 0;
#define endmntent(f) fclose(f)
#endif
+static int
+compare_str (const char * key,
+ const char * const *element)
+{
+ return strcmp (key, *element);
+}
+
static gboolean
-is_in (const char *value, const char *set[])
+is_in (const char *value, const char *set[], gsize set_size)
{
- int i;
- for (i = 0; set[i] != NULL; i++)
- {
- if (strcmp (set[i], value) == 0)
- return TRUE;
- }
- return FALSE;
+ return bsearch (value, set, set_size, sizeof (char *), (GCompareFunc)compare_str) != NULL;
}
/**
@@ -221,11 +223,12 @@ is_in (const char *value, const char *set[])
gboolean
g_unix_is_mount_path_system_internal (const char *mount_path)
{
+ /* keep sorted for bsearch */
const char *ignore_mountpoints[] = {
/* Includes all FHS 2.3 toplevel dirs and other specialized
* directories that we want to hide from the user.
*/
- "/", /* we already have "Filesystem root" in Nautilus */
+ "/", /* we already have "Filesystem root" in Nautilus */
"/bin",
"/boot",
"/boot/efi",
@@ -241,11 +244,15 @@ g_unix_is_mount_path_system_internal (const char *mount_path)
"/live/image",
"/media",
"/mnt",
+ "/net",
"/opt",
+ "/proc",
"/rescue",
"/root",
"/sbin",
+ "/sbin",
"/srv",
+ "/sys",
"/tmp",
"/usr",
"/usr/X11R6",
@@ -262,16 +269,16 @@ g_unix_is_mount_path_system_internal (const char *mount_path)
"/var/mail",
"/var/run",
"/var/tmp", /* https://bugzilla.redhat.com/show_bug.cgi?id=335241 */
- "/proc",
- "/sbin",
- "/net",
- "/sys",
- NULL
};
- if (is_in (mount_path, ignore_mountpoints))
+ if (is_in (mount_path, ignore_mountpoints, G_N_ELEMENTS (ignore_mountpoints)))
return TRUE;
-
+
+ /* Kept separate from sorted list as they may vary */
+ if (g_str_equal ("/var", mount_path) ||
+ g_str_equal ("/run", mount_path))
+ return TRUE;
+
if (g_str_has_prefix (mount_path, "/dev/") ||
g_str_has_prefix (mount_path, "/proc/") ||
g_str_has_prefix (mount_path, "/sys/"))
@@ -301,14 +308,13 @@ g_unix_is_mount_path_system_internal (const char *mount_path)
gboolean
g_unix_is_system_fs_type (const char *fs_type)
{
+ /* keep sorted for bsearch */
const char *ignore_fs[] = {
"adfs",
"afs",
"auto",
"autofs",
"autofs4",
- "binfmt_misc",
- "bpf",
"cgroup",
"cgroup2",
"configfs",
@@ -318,7 +324,6 @@ g_unix_is_system_fs_type (const char *fs_type)
"devpts",
"devtmpfs",
"ecryptfs",
- "efivarfs",
"fdescfs",
"fuse.gvfsd-fuse",
"fuse.portal",
@@ -349,15 +354,13 @@ g_unix_is_system_fs_type (const char *fs_type)
"selinuxfs",
"sysfs",
"tmpfs",
- "tracefs",
"usbfs",
- "zfs",
- NULL
+ "zfs"
};
g_return_val_if_fail (fs_type != NULL && *fs_type != '\0', FALSE);
- return is_in (fs_type, ignore_fs);
+ return is_in (fs_type, ignore_fs, G_N_ELEMENTS (ignore_fs));
}
/**
@@ -379,19 +382,19 @@ g_unix_is_system_fs_type (const char *fs_type)
gboolean
g_unix_is_system_device_path (const char *device_path)
{
+ /* keep sorted for bsearch */
const char *ignore_devices[] = {
- "none",
- "sunrpc",
- "devpts",
- "nfsd",
"/dev/loop",
"/dev/vn",
- NULL
+ "devpts",
+ "nfsd",
+ "none",
+ "sunrpc",
};
g_return_val_if_fail (device_path != NULL && *device_path != '\0', FALSE);
- return is_in (device_path, ignore_devices);
+ return is_in (device_path, ignore_devices, G_N_ELEMENTS (ignore_devices));
}
static gboolean
--
2.52.0
From b9914374503531d5e9773716b865863fa04f6dca Mon Sep 17 00:00:00 2001
From: Christian Hergert <chergert@gnome.org>
Date: Tue, 6 Jan 2026 10:56:59 -0800
Subject: [PATCH 5/5] gio/unixmounts: test that mounts are in sorted order
---
gio/gunixmounts-private.h | 69 +++++++++++++++++++++++++++++++++++++++
gio/gunixmounts.c | 51 ++---------------------------
gio/tests/unix-mounts.c | 24 ++++++++++++++
3 files changed, 95 insertions(+), 49 deletions(-)
create mode 100644 gio/gunixmounts-private.h
diff --git a/gio/gunixmounts-private.h b/gio/gunixmounts-private.h
new file mode 100644
index 000000000..196e81aca
--- /dev/null
+++ b/gio/gunixmounts-private.h
@@ -0,0 +1,69 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2006-2007 Red Hat, Inc.
+ * Copyright 2026 Christian Hergert
+ *
+ * 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/>.
+ */
+
+#pragma once
+
+/* keep sorted for bsearch */
+static const char *system_mount_paths[] = {
+ /* Includes all FHS 2.3 toplevel dirs and other specialized
+ * directories that we want to hide from the user.
+ */
+ "/", /* we already have "Filesystem root" in Nautilus */
+ "/bin",
+ "/boot",
+ "/compat/linux/proc",
+ "/compat/linux/sys",
+ "/dev",
+ "/etc",
+ "/home",
+ "/lib",
+ "/lib64",
+ "/libexec",
+ "/live/cow",
+ "/live/image",
+ "/media",
+ "/mnt",
+ "/net",
+ "/opt",
+ "/proc",
+ "/rescue",
+ "/root",
+ "/sbin",
+ "/sbin",
+ "/srv",
+ "/sys",
+ "/tmp",
+ "/usr",
+ "/usr/X11R6",
+ "/usr/local",
+ "/usr/obj",
+ "/usr/ports",
+ "/usr/src",
+ "/usr/xobj",
+ "/var",
+ "/var/crash",
+ "/var/local",
+ "/var/log",
+ "/var/log/audit", /* https://bugzilla.redhat.com/show_bug.cgi?id=333041 */
+ "/var/mail",
+ "/var/run",
+ "/var/tmp", /* https://bugzilla.redhat.com/show_bug.cgi?id=335241 */
+};
diff --git a/gio/gunixmounts.c b/gio/gunixmounts.c
index ae4174d4f..ca806abaf 100644
--- a/gio/gunixmounts.c
+++ b/gio/gunixmounts.c
@@ -65,6 +65,7 @@
#endif
#include "gunixmounts.h"
+#include "gunixmounts-private.h"
#include "glocalfileprivate.h"
#include "gfile.h"
#include "gfilemonitor.h"
@@ -223,55 +224,7 @@ is_in (const char *value, const char *set[], gsize set_size)
gboolean
g_unix_is_mount_path_system_internal (const char *mount_path)
{
- /* keep sorted for bsearch */
- const char *ignore_mountpoints[] = {
- /* Includes all FHS 2.3 toplevel dirs and other specialized
- * directories that we want to hide from the user.
- */
- "/", /* we already have "Filesystem root" in Nautilus */
- "/bin",
- "/boot",
- "/boot/efi",
- "/compat/linux/proc",
- "/compat/linux/sys",
- "/dev",
- "/etc",
- "/home",
- "/lib",
- "/lib64",
- "/libexec",
- "/live/cow",
- "/live/image",
- "/media",
- "/mnt",
- "/net",
- "/opt",
- "/proc",
- "/rescue",
- "/root",
- "/sbin",
- "/sbin",
- "/srv",
- "/sys",
- "/tmp",
- "/usr",
- "/usr/X11R6",
- "/usr/local",
- "/usr/obj",
- "/usr/ports",
- "/usr/src",
- "/usr/xobj",
- "/var",
- "/var/crash",
- "/var/local",
- "/var/log",
- "/var/log/audit", /* https://bugzilla.redhat.com/show_bug.cgi?id=333041 */
- "/var/mail",
- "/var/run",
- "/var/tmp", /* https://bugzilla.redhat.com/show_bug.cgi?id=335241 */
- };
-
- if (is_in (mount_path, ignore_mountpoints, G_N_ELEMENTS (ignore_mountpoints)))
+ if (is_in (mount_path, system_mount_paths, G_N_ELEMENTS (system_mount_paths)))
return TRUE;
/* Kept separate from sorted list as they may vary */
diff --git a/gio/tests/unix-mounts.c b/gio/tests/unix-mounts.c
index 67b8c8d98..4c1f69a01 100644
--- a/gio/tests/unix-mounts.c
+++ b/gio/tests/unix-mounts.c
@@ -28,6 +28,8 @@
#include <gio/gio.h>
#include <gio/gunixmounts.h>
+#include "../gunixmounts-private.h"
+
static void
test_is_system_fs_type (void)
{
@@ -48,6 +50,27 @@ test_is_system_device_path (void)
g_assert_false (g_unix_is_system_device_path ("/"));
}
+static void
+test_system_mount_paths_sorted (void)
+{
+ size_t i;
+ size_t n_paths = G_N_ELEMENTS (system_mount_paths);
+
+ for (i = 1; i < n_paths; i++)
+ {
+ int cmp = strcmp (system_mount_paths[i - 1], system_mount_paths[i]);
+ if (cmp > 0)
+ {
+ g_fprintf (stderr, "system_mount_paths array is not sorted: "
+ "\"%s\" should come before \"%s\"",
+ system_mount_paths[i - 1],
+ system_mount_paths[i]);
+ g_test_fail ();
+ return;
+ }
+ }
+}
+
int
main (int argc,
char *argv[])
@@ -58,6 +81,7 @@ main (int argc,
g_test_add_func ("/unix-mounts/is-system-fs-type", test_is_system_fs_type);
g_test_add_func ("/unix-mounts/is-system-device-path", test_is_system_device_path);
+ g_test_add_func ("/unix-mounts/system-mount-paths-sorted", test_system_mount_paths_sorted);
return g_test_run ();
}
--
2.52.0

View File

@ -5,7 +5,7 @@
Name: glib2
Version: 2.56.4
Release: 177%{?dist}
Release: 163%{?dist}
Summary: A library of handy utility functions
License: LGPLv2+
@ -33,9 +33,6 @@ BuildRequires: pkgconfig(zlib)
BuildRequires: automake autoconf libtool
BuildRequires: gtk-doc
BuildRequires: python3-devel
# For testsuite
BuildRequires: desktop-file-utils
BuildRequires: shared-mime-info
# for GIO content-type support
Recommends: shared-mime-info
@ -128,71 +125,6 @@ Patch22: 54.patch
# Also: https://gitlab.gnome.org/GNOME/glib/-/commit/d0821da5244fd08c756a5f84ec0d3063c72d1ac6
Patch23: 1549.patch
# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4155
Patch24: 4155.patch
# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4281
Patch25: CVE-2024-52533.patch
# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4588
# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4592
Patch26: CVE-2025-4373.patch
# Contains commits from:
# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1121
# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/3291
Patch27: gdbus-conflict-reduction.patch
# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/1332
Patch28: gdbus-signal-race.patch
# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4038
Patch29: CVE-2024-34397.patch
# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4356
Patch30: gdatetime-test.patch
# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4470
Patch31: gdbusconnection-serial-number-overflow.patch
# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4914
Patch32: CVE-2025-13601.patch
# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4916
# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4918
# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4930
# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4931
Patch33: gunixmount-improvements.patch
# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4933
# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4921
Patch34: CVE-2025-14087.patch
# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/4935
Patch35: CVE-2025-14512.patch
# https://github.com/GNOME/glib/commit/c9da977c178fbfc0e4caf99f9fdf5dc433d6fcc2
Patch36: CVE-2026-58016.patch
# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/5171
Patch37: CVE-2026-58014.patch
# https://gitlab.gnome.org/GNOME/glib/-/commit/8b72ad09c874ddff122b3e67b3470c5e2eab7690
Patch38: CVE-2026-58015.patch
# https://gitlab.gnome.org/GNOME/glib/-/commit/9f557746c52ae2a62fd5929f532b77024a18abe2
Patch39: CVE-2026-58013.patch
# https://gitlab.gnome.org/GNOME/glib/-/commit/49e067570dfa208c45d76f0b602664fd11a629ef
Patch40: CVE-2026-58012.patch
# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/5131
Patch41: CVE-2026-58011.patch
# https://gitlab.gnome.org/GNOME/glib/-/commit/8338414f6560216efe67d3cbf549e32f8630252a
Patch42: CVE-2026-58010.patch
# https://gitlab.gnome.org/GNOME/glib/-/merge_requests/5240
Patch43: CVE-2026-15588.patch
%description
GLib is the low-level core library that forms the basis for projects
such as GTK+ and GNOME. It provides data structure handling for C,
@ -307,10 +239,6 @@ glib-compile-schemas %{_datadir}/glib-2.0/schemas &> /dev/null || :
%transfiletriggerpostun -- %{_datadir}/glib-2.0/schemas
glib-compile-schemas %{_datadir}/glib-2.0/schemas &> /dev/null || :
%check
# One job at a time. The tests are not safe to run in parallel until 2.60. See: glib!505
make check
%files -f glib20.lang
%license COPYING
%doc AUTHORS NEWS README
@ -394,63 +322,6 @@ make check
%{_datadir}/installed-tests
%changelog
* Thu Jul 30 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2.56.4-177
- Fix CVE-2026-15588: limit D-Bus auth line read length
* Thu Jul 30 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2.56.4-176
- Fix CVE-2026-58010: off-by-one in GVariant tuple offset checking
* Mon Jul 20 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2.56.4-175
- Fix CVE-2026-58011: range validation in g_date_time_add_full()
- Resolves: RHEL-212187
* Mon Jul 20 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2.56.4-174
- Fix CVE-2026-58012: buffer overflow in gregex case changing substitutions
- Resolves: RHEL-212204
* Mon Jul 20 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2.56.4-173
- Fix CVE-2026-58013: buffer over-read in GIOChannel with long terminators
- Resolves: RHEL-212229
* Mon Jul 20 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2.56.4-172
- Fix CVE-2026-58015: validate D-Bus DBUS_COOKIE_SHA1 cookie context
- Resolves: RHEL-212254
* Sun Jul 12 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2.56.4-171
- Fix one-byte heap under-read in g_key_file_get_locale_string_list()
- Resolves: RHEL-190587
* Thu Jul 09 2026 RHEL Packaging Agent <redhat-ymir-agent@redhat.com> - 2.56.4-170
- Add patch for CVE-2026-58016
- Resolves: RHEL-190622
* Fri Apr 24 2026 Michael Catanzaro <mcatanzaro@redhat.com> - 2.68.4-169
- Add patch for CVE-2025-14087 and CVE-2025-14512
* Tue Jan 20 2026 Michael Catanzaro <mcatanzaro@redhat.com> - 2.56.4-168
- Add patch for CVE-2025-13601
- Fix GUnixMount issues
- Enable testsuite during RPM check phase
* Wed Sep 17 2025 RHEL Packaging Agent <jotnar@redhat.com> - 2.56.4-167
- gdbusconnection: Prevent sending a serial of zero on overflow
- Resolves: RHEL-114086
* Fri Jul 11 2025 Michael Catanzaro <mcatanzaro@redhat.com> - 2.56.4-166
- Add patches for CVE-2024-34397, CVE-2024-52533, CVE-2025-4373
- Update GDateTime test for new tzdata
- Resolves: RHEL-67084
- Resolves: RHEL-94286
- Resolves: RHEL-94848
* Thu Sep 26 2024 Ondrej Holy <oholy@redhat.com> - 2.56.4-165
- Add support for x-gvfs-trash mount option
- Resolves: RHEL-46828
* Tue Feb 13 2024 Michael Catanzaro <mcatanzaro@redhat.com> - 2.56.4-164
- Revert GUnixMountMonitor changes (it depends on functionality not in RHEL 8)
- Resolves: RHEL-23636
* Thu Feb 01 2024 Michael Catanzaro <mcatanzaro@redhat.com> - 2.56.4-163
- Backport GUnixMountMonitor port to libmnt_monitor
- Make GUnixMountMonitor thread-safe