How to Safely Free and Destroy a Linux Kernel Hashtable with kmalloc
Understanding Hashtable Cleanup in the Linux Kernel
When developing Linux kernel modules, managing dynamic memory properly is critical. Failing to release memory allocated with kmalloc causes memory leaks that persist until the entire system reboots. If you are using the kernel's built-in hashtable implementation (defined in <linux/hashtable.h>) to store dynamic objects, you must properly traverse and free every node during module teardown (such as in your module_exit callback).
A common pitfall is attempting to loop through the table with standard iterators while deallocating nodes. Doing so results in a use-after-free bug. Here is how to destroy dynamic hashtables cleanly and safely.
The Core Problem: Why hash_for_each Fails
In your original attempt, you used the standard hash_for_each macro:
/* WARNING: Unsafe for node deletion! */
hash_for_each(my_table, bkt, cur, node) {
kfree(node->process_name);
kfree(node);
}This causes undefined behavior or kernel panics. The standard hash_for_each() loop evaluates the pointer to the next element after executing your loop body. If you call kfree() on the current node, the iterator tries to access freed memory to locate the next entry.
The Solution: Use hash_for_each_safe
The Linux kernel provides "safe" variants of all list and hashtable iterators. The hash_for_each_safe macro caches the pointer to the next element before the loop body runs, allowing you to delete and free the current element safely.
Correct Implementation
Here is the proper way to iterate through your hashtable, remove each node, and free all associated memory in your module exit handler:
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/hashtable.h>
struct h_node {
char *process_name;
struct hlist_node node;
};
static void cleanup_process_hashtable(void)
{
struct h_node *cur;
struct hlist_node *tmp;
int bkt;
/* Use hash_for_each_safe to prevent use-after-free bugs */
hash_for_each_safe(process_exceptions_hashtable, bkt, tmp, cur, node) {
/* 1. Unlink the entry from the hashtable bucket */
hash_del(&cur->node);
/* 2. Free any nested dynamic allocations first */
if (cur->process_name)
kfree(cur->process_name);
/* 3. Free the outer node structure */
kfree(cur);
}
}Key Details to Notice
- The
tmpPointer:hash_for_each_safetakes an extra temporary cursor parameter (struct hlist_node *tmp). This holds the reference to the next node in the bucket so freeingcurdoes not break iteration. - Unlinking with
hash_del(): Always callhash_del(&cur->node)before freeing the structure. While your module is exiting and the whole table is being discarded, explicitly unlinking nodes is the standard kernel idiom and prevents stale pointer references in case error handling routes back through table lookups. - Freeing Order: Always free nested dynamically allocated pointers (e.g.,
cur->process_name) before freeing the containing struct (cur).
Bonus Tip: Simplify String Allocation with kstrdup
In your node insertion logic, instead of manually calculating lengths and calling kmalloc:
newnode->process_name = kmalloc(strlen(childname) + 1, GFP_KERNEL);
strcpy(newnode->process_name, childname);Use kstrdup(), which handles the length measurement, null-terminator sizing, allocation, and copy in a single step:
newnode->process_name = kstrdup(childname, GFP_KERNEL);
if (!newnode->process_name) {
kfree(newnode);
return -ENOMEM;
}This reduces boilerplate and avoids accidental off-by-one errors when managing dynamic strings in kernel space.