1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
|
From: Tobias Stoeckmann <tobias@stoeckmann.org>
Date: Mon, 25 Aug 2025 22:17:31 +0200
Subject: ghash: Handle all table sizes in iterator
A table size of 2**31 cannot be represented in a gint. Adjust
RealIter to use a guint for its current position to support the largest
power of two possible for a hash table size.
This makes sure that g_hash_table_iter_next does not trigger a signed
integer overflow, which would eventually lead to an out of boundary
read.
With input by Philip Withnall.
Bug: https://gitlab.gnome.org/GNOME/glib/-/issues/672
Origin: upstream, 2.86.1, commit:7367c518f1ed9f06e11c336e0fc28bfb1547a652
---
glib/ghash.c | 20 +++++++++++++-------
1 file changed, 13 insertions(+), 7 deletions(-)
diff --git a/glib/ghash.c b/glib/ghash.c
index b170a84..352098c 100644
--- a/glib/ghash.c
+++ b/glib/ghash.c
@@ -189,6 +189,12 @@
#define HASH_IS_TOMBSTONE(h_) ((h_) == TOMBSTONE_HASH_VALUE)
#define HASH_IS_REAL(h_) ((h_) >= 2)
+/* The hash table can never have this as a valid position, as
+ * hash tables are allocated as a power of two, and the allocation
+ * required for this position would overflow. So we’re safe to use
+ * it to represent an invalid iter position. */
+#define ITER_POSITION_INVALID G_MAXUINT
+
/* If int is smaller than void * on our arch, we start out with
* int-sized keys and values and resize to pointer-sized entries as
* needed. This saves a good amount of memory when the HT is being
@@ -237,7 +243,7 @@ typedef struct
GHashTable *hash_table;
gpointer dummy1;
gpointer dummy2;
- gint position;
+ guint position;
gboolean dummy3;
gintptr version;
} RealIter;
@@ -1100,7 +1106,7 @@ g_hash_table_iter_init (GHashTableIter *iter,
g_return_if_fail (hash_table != NULL);
ri->hash_table = hash_table;
- ri->position = -1;
+ ri->position = ITER_POSITION_INVALID;
#ifndef G_DISABLE_ASSERT
ri->version = hash_table->version;
#endif
@@ -1126,20 +1132,20 @@ g_hash_table_iter_next (GHashTableIter *iter,
gpointer *value)
{
RealIter *ri = (RealIter *) iter;
- gint position;
+ guint position;
g_return_val_if_fail (iter != NULL, FALSE);
#ifndef G_DISABLE_ASSERT
g_return_val_if_fail (ri->version == ri->hash_table->version, FALSE);
#endif
- g_return_val_if_fail (ri->position < (gssize) ri->hash_table->size, FALSE);
+ g_return_val_if_fail (ri->position < ri->hash_table->size || ri->position == ITER_POSITION_INVALID, FALSE);
position = ri->position;
do
{
position++;
- if (position >= (gssize) ri->hash_table->size)
+ if (position >= ri->hash_table->size)
{
ri->position = position;
return FALSE;
@@ -1181,7 +1187,7 @@ iter_remove_or_steal (RealIter *ri, gboolean notify)
#ifndef G_DISABLE_ASSERT
g_return_if_fail (ri->version == ri->hash_table->version);
#endif
- g_return_if_fail (ri->position >= 0);
+ g_return_if_fail (ri->position != ITER_POSITION_INVALID);
g_return_if_fail ((gsize) ri->position < ri->hash_table->size);
g_hash_table_remove_node (ri->hash_table, ri->position, notify);
@@ -1365,7 +1371,7 @@ g_hash_table_iter_replace (GHashTableIter *iter,
#ifndef G_DISABLE_ASSERT
g_return_if_fail (ri->version == ri->hash_table->version);
#endif
- g_return_if_fail (ri->position >= 0);
+ g_return_if_fail (ri->position != ITER_POSITION_INVALID);
g_return_if_fail ((gsize) ri->position < ri->hash_table->size);
node_hash = ri->hash_table->hashes[ri->position];
|