util/hash_table: Don't leak hash_key_u64 objects when the u64 hash table is destroyed
authorBoris Brezillon <boris.brezillon@collabora.com>
Wed, 29 Nov 2023 20:02:49 +0000 (21:02 +0100)
committerEric Engestrom <eric@engestrom.ch>
Sun, 17 Dec 2023 23:48:00 +0000 (23:48 +0000)
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.

Fixes: ff494361bee7 ("util: rzalloc and free hash_table_u64")
Cc: stable
Signed-off-by: Boris Brezillon <boris.brezillon@collabora.com>
Reviewed-by: Yonggang Luo <luoyonggang@gmail.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/26423>
(cherry picked from commit db5166718d89ba71f8d12fbdceffb05d7c5e9a03)

.pick_status.json
src/util/hash_table.c

index 93584af..8e88e23 100644 (file)
@@ -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
index 652c898..a76ebbc 100644 (file)
@@ -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)