Linux-Kernel Solution to the Zombie-Pointer Problem

The trick is to place the body of list_push() within an RCU read-side critical section and then defer freeing until after for an RCU grace period has elapsed.

 1 struct node_t* _Atomic top;
 2
 3 void list_push(value_t v)
 4 {
 5   struct node_t *newnode = (struct node_t *) kmalloc(sizeof(*newnode), GFP_KERNEL);
 6
 7   set_value(newnode, v);
 8   rcu_read_lock();
 9   newnode->next = atomic_load(&top);
10   do {
11     // newnode->next may have become invalid
12   } while (!atomic_compare_exchange_weak(&top, &newnode->next, newnode));
13   rcu_read_unlock();
14 }
15
16 void list_pop_all()
17 {
18   struct node_t *p = atomic_exchange(&top, NULL);
19
20   while (p) {
21     struct node_t *next = p->next;
22
23     foo(p);
24     kvfree_rcu(p); // Free after a grace period.
25     p = next;
26   }
27 }

The added rcu_read_lock() on line 8 and rcu_read_unlock() on line 13 protects the object referenced by newnode->next. The kfree_rcu() on line 24 passes p to kfree() after an RCU grace period has elapsed.

Note that the single-argument form of kvfree_rcu() can sleep. If this is a problem, add an rcu_head structure to the node_t structure and pass its name as the second argument to kvfree_rcu().

This is textbook code. In real life, please check the return value from kmalloc()!

Alternative Solution

It turns out that hiding the memory allocators from the compiler also suffices, at least until compilers start recognizing what code constitutes an allocator without the current assistance of special names and of compiler attributes.

And recent versions of the Linux kernel do hide the allocator from the compiler.

So you had two ways to win!