hashmap: allow re-use after hashmap_free()

Previously, once map->table had been freed, any calls to hashmap_put(), hashmap_get(), or hashmap_remove() would cause a NULL pointer dereference (since hashmap_free_() also zeros the memory; without that zeroing, calling these functions would cause a use-after-free problem). Modify these functions to check for a NULL table and automatically allocate as needed. Also add a HASHMAP_INIT(fn, data) macro for initializing hashmaps on the stack without calling hashmap_init(). Signed-off-by: Elijah Newren <newren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Elijah Newren committed Nov 2, 2020 at 18:55 UTC b7879b0ba6ee1306a42227f7fd7f4e5f50409184
2 files changed +17 -2
hashmap.c
+14 -2
@@ -114,6 +114,7 @@ int hashmap_bucket(const struct hashmap *map, unsigned int hash)
114
115 static void rehash(struct hashmap *map, unsigned int newsize)
116 {
117 + /* map->table MUST NOT be NULL when this function is called */
118 unsigned int i, oldsize = map->tablesize;
119 struct hashmap_entry **oldtable = map->table;
120
@@ -134,6 +135,7 @@ static void rehash(struct hashmap *map, unsigned int newsize)
135 static inline struct hashmap_entry **find_entry_ptr(const struct hashmap *map,
136 const struct hashmap_entry *key, const void *keydata)
137 {
138 + /* map->table MUST NOT be NULL when this function is called */
139 struct hashmap_entry **e = &map->table[bucket(map, key)];
140 while (*e && !entry_equals(map, *e, key, keydata))
141 e = &(*e)->next;
@@ -196,6 +198,8 @@ struct hashmap_entry *hashmap_get(const struct hashmap *map,
198 const struct hashmap_entry *key,
199 const void *keydata)
200 {
201 + if (!map->table)
202 + return NULL;
203 return *find_entry_ptr(map, key, keydata);
204 }
205
@@ -211,8 +215,12 @@ struct hashmap_entry *hashmap_get_next(const struct hashmap *map,
215
216 void hashmap_add(struct hashmap *map, struct hashmap_entry *entry)
217 {
214 - unsigned int b = bucket(map, entry);
218 + unsigned int b;
219 +
220 + if (!map->table)
221 + alloc_table(map, HASHMAP_INITIAL_SIZE);
222
223 + b = bucket(map, entry);
224 /* add entry */
225 entry->next = map->table[b];
226 map->table[b] = entry;
@@ -230,7 +238,11 @@ struct hashmap_entry *hashmap_remove(struct hashmap *map,
238 const void *keydata)
239 {
240 struct hashmap_entry *old;
233 - struct hashmap_entry **e = find_entry_ptr(map, key, keydata);
241 + struct hashmap_entry **e;
242 +
243 + if (!map->table)
244 + return NULL;
245 + e = find_entry_ptr(map, key, keydata);
246 if (!*e)
247 return NULL;
248
hashmap.h
+3
@@ -210,6 +210,9 @@ struct hashmap {
210
211 /* hashmap functions */
212
213 +#define HASHMAP_INIT(fn, data) { .cmpfn = fn, .cmpfn_data = data, \
214 + .do_count_items = 1 }
215 +
216 /*
217 * Initializes a hashmap structure.
218 *