Re: [RFC PATCH 09/12] rcu_txn: Test a list writer against concurrent readers

From: Puranjay Mohan

Date: Fri Oct 23 2026 - 14:23:25 EST


On Fri, Oct 16, 2026 at 09:33:16AM -0400, Mathieu Desnoyers wrote:
[...]

> + /*
> + * Invariant (2). Skipped for the first step, whose
> + * predecessor is the head.
> + */
> + if (insert_only && prev_key) {
> + struct rcu_txn_list_node *back =
> + rcu_txn_list_prev_rcu(p);
> +
> + r->cross_checks++;
> + if (back != &head->node &&
> + container_of(back, struct cn,
> + node)->key < prev_key)
> + r->cross_violations++;
> + }

In an insert-only phase, once a reader has stepped from A to p, A stays
before p, so p's prev can only be A or a node inserted between them,
never the head. The back != &head->node exemption can therefore only
hide failures, and it hides exactly the tear this check is for when the
insertion is at the front: head->next == E, E->next == F, then
F->prev == head. prev_key already skips the first step, so I'd count
back == &head->node as a violation.

[...]

> + for (i = 0; i < CONC_READERS; i++) {
> + readers[i].conc = c;
> + readers[i].task = kthread_run(conc_reader_fn, &readers[i],
> + "rcu_txn_reader/%d", i);
> + if (IS_ERR(readers[i].task)) {
> + readers[i].task = NULL;
> + ok = false;
> + }
> + }

If the writer oopses, KUnit fails the test and frees c and the readers
array while these threads are still walking the list. Registering the
stop with kunit_add_action_or_reset(), and calling
kunit_release_action() where they're stopped now, would avoid the
use-after-free.

[...]

> + for (key = 1; key <= CONC_KEY_MAX; key++)
> + if (c->live[key])
> + conc_del(c, key);

A failed conc_del() here leaks the node without anyone noticing. The
readers are already stopped at this point, so the leftovers could just
be kfree()d.

Thanks,
Puranjay