summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.pick_status.json2
-rw-r--r--src/util/hash_table.c32
2 files changed, 33 insertions, 1 deletions
diff --git a/.pick_status.json b/.pick_status.json
index 93584afafa2..8e88e23f93f 100644
--- a/.pick_status.json
+++ b/.pick_status.json
@@ -14,7 +14,7 @@
"description": "util/hash_table: Don't leak hash_key_u64 objects when the u64 hash table is destroyed",
"nominated": true,
"nomination_type": 1,
- "resolution": 0,
+ "resolution": 1,
"main_sha": null,
"because_sha": "ff494361bee7506db701cb861073ab194ae3a6e9",
"notes": null
diff --git a/src/util/hash_table.c b/src/util/hash_table.c
index 652c8980b92..a76ebbc039e 100644
--- a/src/util/hash_table.c
+++ b/src/util/hash_table.c
@@ -777,6 +777,13 @@ key_u64_equals(const void *a, const void *b)
#define FREED_KEY_VALUE 0
+static void _mesa_hash_table_u64_delete_keys(void *data)
+{
+ struct hash_table_u64 *ht = ralloc_parent(data);
+
+ _mesa_hash_table_u64_clear(ht);
+}
+
struct hash_table_u64 *
_mesa_hash_table_u64_create(void *mem_ctx)
{
@@ -793,6 +800,31 @@ _mesa_hash_table_u64_create(void *mem_ctx)
} else {
ht->table = _mesa_hash_table_create(ht, key_u64_hash,
key_u64_equals);
+
+ /* Allocate a ralloc sub-context which takes the u64 hash table
+ * as a parent and attach a destructor to it so we can free the
+ * hash_key_u64 objects that were allocated by
+ * _mesa_hash_table_u64_insert().
+ *
+ * The order of creation of this sub-context is crucial: it needs
+ * to happen after the _mesa_hash_table_create() call to guarantee
+ * that the destructor is called before ht->table and its children
+ * are freed, otherwise the _mesa_hash_table_u64_clear() call in the
+ * destructor leads to a use-after-free situation.
+ */
+ if (ht->table) {
+ void *dummy_ctx = ralloc_context(ht);
+
+ /* If we can't allocate a sub-context, free the hash table
+ * immediately and return NULL to avoid future leaks.
+ */
+ if (!dummy_ctx) {
+ ralloc_free(ht);
+ return NULL;
+ }
+
+ ralloc_set_destructor(dummy_ctx, _mesa_hash_table_u64_delete_keys);
+ }
}
if (ht->table)