Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

bpf: Maintain FIFO property for rqspinlock unlock

Since out-of-order unlocks are unsupported for rqspinlock, and irqsave
variants enforce strict FIFO ordering anyway, make the same change for
normal non-irqsave variants, such that FIFO ordering is enforced.

Two new verifier state fields (active_lock_id, active_lock_ptr) are used
to denote the top of the stack, and prev_id and prev_ptr are ascertained
whenever popping the topmost entry through an unlock.

Take special care to make these fields part of the state comparison in
refsafe.

Signed-off-by: Kumar Kartikeya Dwivedi <memxor@gmail.com>
Link: https://lore.kernel.org/r/20250316040541.108729-25-memxor@gmail.com
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

authored by

Kumar Kartikeya Dwivedi and committed by
Alexei Starovoitov
ea21771c 0de20461

+31 -5
+3
include/linux/bpf_verifier.h
··· 268 268 REF_TYPE_LOCK = (1 << 3), 269 269 REF_TYPE_RES_LOCK = (1 << 4), 270 270 REF_TYPE_RES_LOCK_IRQ = (1 << 5), 271 + REF_TYPE_LOCK_MASK = REF_TYPE_LOCK | REF_TYPE_RES_LOCK | REF_TYPE_RES_LOCK_IRQ, 271 272 } type; 272 273 /* Track each reference created with a unique id, even if the same 273 274 * instruction creates the reference multiple times (eg, via CALL). ··· 435 434 u32 active_locks; 436 435 u32 active_preempt_locks; 437 436 u32 active_irq_id; 437 + u32 active_lock_id; 438 + void *active_lock_ptr; 438 439 bool active_rcu_lock; 439 440 440 441 bool speculative;
+28 -5
kernel/bpf/verifier.c
··· 1428 1428 dst->active_preempt_locks = src->active_preempt_locks; 1429 1429 dst->active_rcu_lock = src->active_rcu_lock; 1430 1430 dst->active_irq_id = src->active_irq_id; 1431 + dst->active_lock_id = src->active_lock_id; 1432 + dst->active_lock_ptr = src->active_lock_ptr; 1431 1433 return 0; 1432 1434 } 1433 1435 ··· 1529 1527 s->ptr = ptr; 1530 1528 1531 1529 state->active_locks++; 1530 + state->active_lock_id = id; 1531 + state->active_lock_ptr = ptr; 1532 1532 return 0; 1533 1533 } 1534 1534 ··· 1581 1577 1582 1578 static int release_lock_state(struct bpf_verifier_state *state, int type, int id, void *ptr) 1583 1579 { 1580 + void *prev_ptr = NULL; 1581 + u32 prev_id = 0; 1584 1582 int i; 1585 1583 1586 1584 for (i = 0; i < state->acquired_refs; i++) { 1587 - if (state->refs[i].type != type) 1588 - continue; 1589 - if (state->refs[i].id == id && state->refs[i].ptr == ptr) { 1585 + if (state->refs[i].type == type && state->refs[i].id == id && 1586 + state->refs[i].ptr == ptr) { 1590 1587 release_reference_state(state, i); 1591 1588 state->active_locks--; 1589 + /* Reassign active lock (id, ptr). */ 1590 + state->active_lock_id = prev_id; 1591 + state->active_lock_ptr = prev_ptr; 1592 1592 return 0; 1593 + } 1594 + if (state->refs[i].type & REF_TYPE_LOCK_MASK) { 1595 + prev_id = state->refs[i].id; 1596 + prev_ptr = state->refs[i].ptr; 1593 1597 } 1594 1598 } 1595 1599 return -EINVAL; ··· 8354 8342 type = REF_TYPE_RES_LOCK; 8355 8343 else 8356 8344 type = REF_TYPE_LOCK; 8345 + if (!find_lock_state(cur, type, reg->id, ptr)) { 8346 + verbose(env, "%s_unlock of different lock\n", lock_str); 8347 + return -EINVAL; 8348 + } 8349 + if (reg->id != cur->active_lock_id || ptr != cur->active_lock_ptr) { 8350 + verbose(env, "%s_unlock cannot be out of order\n", lock_str); 8351 + return -EINVAL; 8352 + } 8357 8353 if (release_lock_state(cur, type, reg->id, ptr)) { 8358 8354 verbose(env, "%s_unlock of different lock\n", lock_str); 8359 8355 return -EINVAL; ··· 12554 12534 12555 12535 if (!env->cur_state->active_locks) 12556 12536 return -EINVAL; 12557 - s = find_lock_state(env->cur_state, REF_TYPE_LOCK | REF_TYPE_RES_LOCK | REF_TYPE_RES_LOCK_IRQ, 12558 - id, ptr); 12537 + s = find_lock_state(env->cur_state, REF_TYPE_LOCK_MASK, id, ptr); 12559 12538 if (!s) { 12560 12539 verbose(env, "held lock and object are not in the same allocation\n"); 12561 12540 return -EINVAL; ··· 18608 18589 return false; 18609 18590 18610 18591 if (!check_ids(old->active_irq_id, cur->active_irq_id, idmap)) 18592 + return false; 18593 + 18594 + if (!check_ids(old->active_lock_id, cur->active_lock_id, idmap) || 18595 + old->active_lock_ptr != cur->active_lock_ptr) 18611 18596 return false; 18612 18597 18613 18598 for (i = 0; i < old->acquired_refs; i++) {