pointers-memory · intermediate · ~15 min

Destroy a linked list without use-after-free

Save-next-before-free pattern — the universal linked-structure destroy idiom.

Challenge

Free every node of a singly-linked list without reading any node after it has been freed. The naïve loop reads head->next after free(head) — a use-after-free; the fix is to save the next pointer first.

The list node type is:

typedef struct node { int v; struct node *next; } node_t;

The broken version (do NOT do this):

while (head) { free(head); head = head->next; }   // reads freed memory next iteration

Task

Implement void destroy(node_t *head) that frees every node in the list, capturing each node's next pointer before freeing the node. No main — the grader builds the list and calls it.

Input

head — the first node of the list, or NULL for an empty list.

Output

No return value. Every node is freed, with no use-after-free.

Example

list 1 -> 2 -> 3 -> 4,  destroy(head)   ->   all four nodes freed
destroy(NULL)                           ->   no-op

Edge cases

  • NULL head: no-op.
  • Single-node list: free the one node.

Rules

  • Save next before each free. Must run cleanly under AddressSanitizer.

Why this matters

The naïve destroy loop reads n->next AFTER free(n) — a textbook use-after-free. Doing it right is the single most-asked memory-safety question.

Input format

head — the first node, or NULL.

Output format

No return value; every node is freed.

Constraints

No allocations. Save next before free. Must run cleanly under ASan.

Starter code

typedef struct node { int v; struct node *next; } node_t;
void destroy(node_t *head) { /* TODO */ }

Common mistakes

Reading head->next after free(head) — use-after-free.

Edge cases to handle

NULL head (no-op); single-node list.

Complexity

O(n).

Background lessons

Up next

Solve this exercise in the browser editor — compile and run against the test harness, no setup required.