flawopen.com/Simulators/Heap Use-After-Free
An interactive visual memory model demonstrating how dangling pointers survive memory deallocation, how heap chunk re-use occurs, and how function pointer hijacking leads to privilege escalation.
Imagine checking out of hotel room #304 and returning your keycard to the front desk, but keeping a secret duplicate key in your pocket. The hotel assigns room #304 to a new guest who unpacks their personal diary onto the desk. Later that night, you use your old duplicate key to unlock room #304 and read the new guest's diary! That duplicate key is a dangling pointer: it still unlocks room #304 even though you no longer own the room.
A legitimate kernel subsystem allocates an object (e.g. user_session) via kmalloc() at address 0x55a0. The system stores pointer *sess.
A cleanup routine calls kfree(sess), pushing chunk 0x55a0 to the tcache freelist, but neglects to set sess = NULL, creating a live dangling pointer.
The attacker allocates a crafted payload structure of the exact same size (80 bytes). The allocator reuses the freed chunk at 0x55a0 for the attacker's payload.
The kernel later calls sess->callback(). Because sess still references 0x55a0, the CPU executes the attacker's supplied function pointer, achieving root code execution.
// Vulnerable driver/kernel cleanup routine
void release_session(struct session_mgr *mgr) {
if (mgr->active_session) {
kfree(mgr->active_session);
// FLAW: Pointer is not zeroed! mgr->active_session remains dangling.
// Any subsequent invocation of mgr->active_session->callback()
// will dereference whatever is re-allocated into this memory slot.
}
}
// Hardened cleanup routine with atomic pointer zeroization
void release_session(struct session_mgr *mgr) {
struct user_session *sess;
// HARDENED: Atomically detach pointer before freeing memory
sess = xchg(&mgr->active_session, NULL);
if (sess) {
// Zero out memory contents before releasing to allocator to thwart re-use
memzero_explicit(sess, sizeof(*sess));
kfree(sess);
}
}
#define SAFE_FREE(p) do { free(p); (p) = NULL; } while(0).CONFIG_SLAB_FREELIST_HARDENED=y and CONFIG_SLAB_FREELIST_RANDOM=y to obfuscate freelist pointers.memzero_explicit() before releasing chunk memory.