6.18-stable review patch. If anyone has any objections, please let me know. ------------------ From: Ning Ding [ Upstream commit 180c7000712db77063b3a26f4c97e7dd9038f449 ] In a sleepable BPF program, a spin lock can provide the only RCU protection for a kptr. The final bpf_spin_unlock() ends that protection, but the verifier leaves the pointer valid. Another CPU can then free the object before the pointer is used. A capability-limited runtime PoC triggered a task_struct use-after-free in __bpf_get_task_stack(). Record whether the program is in an RCU-protected context before releasing the lock. Invalidate RCU-protected pointers only when the unlock leaves the final such context. This preserves valid pointers in non-sleepable programs and inside an explicit RCU read-side section. Fixes: 5861d1e8dbc4 ("bpf: Allow bpf_spin_{lock,unlock} in sleepable progs") Assisted-by: Codex:gpt-5.6-sol Assisted-by: ChatGPT:GPT-5.6-Pro Signed-off-by: Ning Ding Link: https://lore.kernel.org/bpf/20260803112615.3362122-2-dingning04@gmail.com Signed-off-by: Kumar Kartikeya Dwivedi Signed-off-by: Sasha Levin --- kernel/bpf/verifier.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 947b501a58aee..d35ac3df9a052 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -8585,6 +8585,10 @@ static int process_spin_lock(struct bpf_verifier_env *env, int regno, int flags) return err; } } else { + u32 clear_mask = (1 << STACK_SPILL) | (1 << STACK_ITER); + struct bpf_func_state *state; + struct bpf_reg_state *rcu_reg; + bool was_in_rcu_cs; void *ptr; int type; @@ -8612,10 +8616,19 @@ static int process_spin_lock(struct bpf_verifier_env *env, int regno, int flags) verbose(env, "%s_unlock cannot be out of order\n", lock_str); return -EINVAL; } + was_in_rcu_cs = in_rcu_cs(env); if (release_lock_state(cur, type, reg->id, ptr)) { verbose(env, "%s_unlock of different lock\n", lock_str); return -EINVAL; } + if (was_in_rcu_cs && !in_rcu_cs(env)) { + bpf_for_each_reg_in_vstate_mask(env->cur_state, state, rcu_reg, clear_mask, ({ + if (rcu_reg->type & MEM_RCU) { + rcu_reg->type &= ~(MEM_RCU | PTR_MAYBE_NULL); + rcu_reg->type |= PTR_UNTRUSTED; + } + })); + } invalidate_non_owning_refs(env); } -- 2.53.0