summaryrefslogtreecommitdiff
path: root/include/linux
diff options
context:
space:
mode:
authorJosef Bacik <josef@toxicpanda.com>2026-10-17 16:37:04 +0000
committerAlexei Starovoitov <ast@kernel.org>2026-10-17 21:58:22 +0000
commit9bdd5e7f5d8313fa1f3a210621b4007bb5ad0ec6 (patch)
treeb23f0b4775c9589274f9764a6364eded84ff69b8 /include/linux
parentb3ca872947ef322c475ceaa7a76a9ebb3356152e (diff)
bpf: Forbid sleeping in hash map for_each callbacks
A sleepable program (e.g. SEC("syscall"), lsm.s, fentry.s) can pass a callback to bpf_for_each_map_elem() on a hash map and call a sleepable helper or kfunc such as bpf_copy_from_user() from it. The verifier accepts this, but at run time the callback is invoked from inside an RCU read-side critical section: bpf_for_each_hash_elem() rcu_read_lock(); callback_fn(map, key, val, ctx, 0); bpf_copy_from_user() <- may fault and sleep rcu_read_unlock(); bpf_each_rhash_elem() does the same for BPF_MAP_TYPE_RHASH. Sleeping there triggers the "Voluntary context switch within RCU read-side critical section!" warning, delays grace periods, and with CONFIG_PREEMPT_RCU=n silently ends the read-side critical section that the bucket walk relies on. Synchronous callbacks are verified with the caller's state, so kernel/bpf/verifier.c:in_sleepable_context() only looks at the program's sleepability and the locks the program took itself, and does not know that the helper holds rcu_read_lock() around the callback. With a SEC("syscall") program whose callback reads a page that has to be read from disk first: BUG: sleeping function called from invalid context at include/linux/uaccess.h:169 RCU nest depth: 1, expected: 0 __might_fault+0x29/0x80 _copy_from_user+0x26/0x90 bpf_copy_from_user+0x23/0x60 bpf_prog_17d44fa39eebd6cc_cb+0x35/0x46 bpf_for_each_hash_elem+0xdb/0x280 Fix by marking the callback frame of hash map iteration as running under RCU and making in_sleepable_context() false while such a frame is on the call stack, so sleepable helpers, kfuncs and global functions are rejected there as inside bpf_rcu_read_lock(). The rules for trusting RCU protected pointers are left alone, which is conservative and keeps callbacks that verify today and do not sleep working. The flag is compared in func_states_equal() so that pruning never mixes the two contexts. Array map callbacks run without any lock and keep the caller's context. Fixes: 314ee05e2fc6 ("bpf: Add hashtab support for bpf_for_each_map_elem() helper") Fixes: 818e00848227 ("bpf: Implement iteration ops for resizable hashtab") Signed-off-by: Josef Bacik <josef@toxicpanda.com> Signed-off-by: Alexei Starovoitov <ast@kernel.org> Link: https://patch.msgid.link/20261017163745.3341787-3-alexei.starovoitov@gmail.com
Diffstat (limited to 'include/linux')
-rw-r--r--include/linux/bpf_verifier.h5
1 files changed, 5 insertions, 0 deletions
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index 112ca1511da4..816948df8fff 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -379,6 +379,11 @@ struct bpf_func_state {
bool in_async_callback_fn;
bool in_exception_callback_fn;
bool no_stack_arg_load;
+ /*
+ * The helper invokes this callback under rcu_read_lock(), so the
+ * callback and its callees run in an RCU read-side critical section.
+ */
+ bool in_rcu_callback_fn;
/* For callback calling functions that limit number of possible
* callback executions (e.g. bpf_loop) keeps track of current
* simulated iteration number.