You are viewing a plain text version of this content. The canonical link for it is here.
Posted to c-commits@axis.apache.org by ro...@apache.org on 2026/01/17 03:34:04 UTC

(axis-axis2-c-core) 11/11: Fix hash key memory management to prevent leaks and crashes (AXIS2C-1632)

This is an automated email from the ASF dual-hosted git repository.

robertlazarski pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/axis-axis2-c-core.git

commit 8f7b38a58e234b3f7b0aca0012454dd7b466d664
Author: Robert Lazarski <ro...@gmail.com>
AuthorDate: Fri Jan 16 02:19:48 2026 -1000

    Fix hash key memory management to prevent leaks and crashes (AXIS2C-1632)
    
    The hash table was storing pointers to keys without copying them,
    which caused:
    1. Memory leaks when callers strdup'd keys but couldn't track them
    2. Use-after-free when callers freed keys while hash still referenced them
    
    Fix by having the hash internally copy string keys (klen > 0):
    - Add key_is_copy flag to track internally managed keys
    - Copy keys in axutil_hash_find_entry when adding new entries
    - Free copied keys when entries are deleted or hash is freed
    - Update axutil_hash_copy and axutil_hash_merge to copy keys
    
    This allows callers to immediately free their key allocations after
    calling axutil_hash_set, as the hash now manages its own key copies.
    
    Co-Authored-By: Claude Opus 4.5 <no...@anthropic.com>
---
 util/src/hash.c | 99 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 95 insertions(+), 4 deletions(-)

diff --git a/util/src/hash.c b/util/src/hash.c
index c44e4f621..9d9804c14 100644
--- a/util/src/hash.c
+++ b/util/src/hash.c
@@ -38,6 +38,8 @@ struct axutil_hash_entry_t
     const void *key;
     axis2_ssize_t klen;
     const void *val;
+    /* AXIS2C-1632 FIX: Flag to track if key was internally copied and should be freed */
+    axis2_bool_t key_is_copy;
 };
 
 /*
@@ -346,7 +348,26 @@ axutil_hash_find_entry(
         he = AXIS2_MALLOC(ht->env->allocator, sizeof(*he));
     he->next = NULL;
     he->hash = hash;
-    he->key = key;
+    /* AXIS2C-1632 FIX: Copy string keys to avoid dangling pointers and memory leaks.
+     * The hash now manages string key memory internally. */
+    if(klen > 0)
+    {
+        /* String key - copy it */
+        axis2_char_t *key_copy = AXIS2_MALLOC(ht->env->allocator, klen + 1);
+        if(key_copy)
+        {
+            memcpy(key_copy, key, klen);
+            key_copy[klen] = '\0';
+        }
+        he->key = key_copy;
+        he->key_is_copy = AXIS2_TRUE;
+    }
+    else
+    {
+        /* Non-string key or error - store pointer directly */
+        he->key = key;
+        he->key_is_copy = AXIS2_FALSE;
+    }
     he->klen = klen;
     he->val = val;
     *hep = he;
@@ -384,7 +405,23 @@ axutil_hash_copy(
         {
             *new_entry = &new_vals[j++];
             (*new_entry)->hash = orig_entry->hash;
-            (*new_entry)->key = orig_entry->key;
+            /* AXIS2C-1632 FIX: Copy keys for the new hash to avoid sharing pointers */
+            if(orig_entry->klen > 0)
+            {
+                axis2_char_t *key_copy = AXIS2_MALLOC(env->allocator, orig_entry->klen + 1);
+                if(key_copy)
+                {
+                    memcpy(key_copy, orig_entry->key, orig_entry->klen);
+                    key_copy[orig_entry->klen] = '\0';
+                }
+                (*new_entry)->key = key_copy;
+                (*new_entry)->key_is_copy = AXIS2_TRUE;
+            }
+            else
+            {
+                (*new_entry)->key = orig_entry->key;
+                (*new_entry)->key_is_copy = AXIS2_FALSE;
+            }
             (*new_entry)->klen = orig_entry->klen;
             (*new_entry)->val = orig_entry->val;
             new_entry = &((*new_entry)->next);
@@ -439,6 +476,13 @@ axutil_hash_set(
             /* delete entry */
             axutil_hash_entry_t *old = *hep;
             *hep = (*hep)->next;
+            /* AXIS2C-1632 FIX: Free the key if it was internally copied */
+            if(old->key_is_copy && old->key)
+            {
+                AXIS2_FREE(ht->env->allocator, (void*)old->key);
+                old->key = NULL;
+            }
+            old->key_is_copy = AXIS2_FALSE;
             old->next = ht->free;
             ht->free = old;
             --ht->count;
@@ -537,7 +581,23 @@ axutil_hash_merge(
             i = iter->hash & res->max;
             new_vals = AXIS2_MALLOC(env->allocator, sizeof(axutil_hash_entry_t));
             new_vals->klen = iter->klen;
-            new_vals->key = iter->key;
+            /* AXIS2C-1632 FIX: Copy keys for merged hash */
+            if(iter->klen > 0)
+            {
+                axis2_char_t *key_copy = AXIS2_MALLOC(env->allocator, iter->klen + 1);
+                if(key_copy)
+                {
+                    memcpy(key_copy, iter->key, iter->klen);
+                    key_copy[iter->klen] = '\0';
+                }
+                new_vals->key = key_copy;
+                new_vals->key_is_copy = AXIS2_TRUE;
+            }
+            else
+            {
+                new_vals->key = iter->key;
+                new_vals->key_is_copy = AXIS2_FALSE;
+            }
             new_vals->val = iter->val;
             new_vals->hash = iter->hash;
             new_vals->next = res->array[i];
@@ -569,7 +629,23 @@ axutil_hash_merge(
             {
                 new_vals = AXIS2_MALLOC(env->allocator, sizeof(axutil_hash_entry_t));
                 new_vals->klen = iter->klen;
-                new_vals->key = iter->key;
+                /* AXIS2C-1632 FIX: Copy keys for merged hash */
+                if(iter->klen > 0)
+                {
+                    axis2_char_t *key_copy = AXIS2_MALLOC(env->allocator, iter->klen + 1);
+                    if(key_copy)
+                    {
+                        memcpy(key_copy, iter->key, iter->klen);
+                        key_copy[iter->klen] = '\0';
+                    }
+                    new_vals->key = key_copy;
+                    new_vals->key_is_copy = AXIS2_TRUE;
+                }
+                else
+                {
+                    new_vals->key = iter->key;
+                    new_vals->key_is_copy = AXIS2_FALSE;
+                }
                 new_vals->val = iter->val;
                 new_vals->hash = iter->hash;
                 new_vals->next = res->array[i];
@@ -635,6 +711,11 @@ axutil_hash_free(
             while(current)
             {
                 next = current->next;
+                /* AXIS2C-1632 FIX: Free the key if it was internally copied */
+                if(current->key_is_copy && current->key)
+                {
+                    AXIS2_FREE(env->allocator, (void*)current->key);
+                }
                 AXIS2_FREE(env->allocator, current);
                 current = NULL;
                 current = next;
@@ -647,6 +728,11 @@ axutil_hash_free(
             while(current)
             {
                 next = current->next;
+                /* AXIS2C-1632 FIX: Free the key if it was internally copied */
+                if(current->key_is_copy && current->key)
+                {
+                    AXIS2_FREE(env->allocator, (void*)current->key);
+                }
                 AXIS2_FREE(env->allocator, current);
                 current = NULL;
                 current = next;
@@ -686,6 +772,11 @@ axutil_hash_free_void_arg(
             while(current)
             {
                 next = current->next;
+                /* AXIS2C-1632 FIX: Free the key if it was internally copied */
+                if(current->key_is_copy && current->key)
+                {
+                    AXIS2_FREE(env->allocator, (void*)current->key);
+                }
                 AXIS2_FREE(env->allocator, current);
                 current = next;
             }